want), say M here and read Documentation/modules.txt. Note that the
filesystem of your root partition cannot be compiled as a module.
+fat fs support
+CONFIG_FAT_FS
+ If you want to use one of the FAT-based filesystems (the MS-DOS,
+ VFAT (Windows'95) and UMSDOS filesystems), then you must include
+ FAT support. This is not a filesystem in itself, but it provides
+ the foundation for the other filesystems. This option will enlarge
+ your kernel about 24 kB. If unsure, say Y. If you want to compile
+ this as a module however ( = code which can be inserted in and
+ removed from the running kernel whenever you want), say M here and
+ read Documentation/modules.txt. Note that if you compile the FAT
+ support as a module, you cannot compile any of the FAT-based file-
+ systems into the kernel - they will have to be modules as well.
+ The filesystem of your root partition cannot be a module.
+
msdos fs support
CONFIG_MSDOS_FS
This allows you to mount MSDOS partitions of your harddrive (unless
which doesn't require the msdos filesystem support. If you want to
use umsdos, the Unix-like filesystem on top of DOS, which allows you
to run Linux from within a DOS partition without repartitioning,
- you'll have to say Y here. This option will enlarge your kernel by
- about 25 kB. If unsure, say Y. If you want to compile this as a
- module however ( = code which can be inserted in and removed from
- the running kernel whenever you want), say M here and read
- Documentation/modules.txt. Note that the filesystem of your root
+ you'll have to say Y or M here. If your have Windows'95 or Windows NT
+ installed on your MSDOS partitions, you should use the VFAT
+ filesystem instead, or you will not be able to see the long filenames
+ generated by Windows'95 / Windows NT. This option will enlarge your
+ kernel by about 7 kB. If unsure, say Y. If you want to compile this
+ as a module however ( = code which can be inserted in and removed from
+ the running kernel whenever you want), say M here and read
+ Documentation/modules.txt. Note that the filesystem of your root
partition cannot be a module.
-umsdos: Unix like fs on top of std MSDOS FAT fs
+vfat fs support
+CONFIG_VFAT_FS
+ This allows you to mount MSDOS partitions of your harddrive. It
+ will let you use filenames in a way compatible with the long
+ filenames used by Windows'95 and Windows NT fat-based (not NTFS)
+ partitions. It does not support Windows'95 compressed filesystems.
+ You cannot use the VFAT filesystem for your root partition; use
+ UMSDOS instead. This option enlarges your kernel by about 10 kB.
+ If unsure, say N. If you want to compile this as a module ( = code
+ which can be inserted in and removed from the running kernel whenever
+ you want), say M here and read Documentation/modules.txt. Note that
+ the filesystem of your root partition cannot be a module.
+
+umsdos: Unix like fs on top of std MSDOS fs
CONFIG_UMSDOS_FS
Say Y here if you want to run Linux from within an existing DOS
partition of your harddrive. The advantage of this is that you can
-
+
-----------------------------------------------------------------------------
1) This file is a supplement to arcnet.txt. Please read that for general
driver configuration help.
types, as far as I'm aware, are not compatible and so you cannot wire a
100Mbps card to a 2.5Mbps card, and so on. From what I hear, my driver does
work with 100Mbps cards, but I haven't been able to verify this myself,
-since I only have the 2.5Mbps variety.
+since I only have the 2.5Mbps variety. It is probably not going to saturate
+your 100Mbps card. Stop complaining :)
You also cannot connect an ARCnet card to any kind of ethernet card and
expect it to work.
break, or because the destination computer does not exist) it will at least
tell the sender about it.
+Because of the carefully defined action of the "token", it will always make
+a pass around the "ring" within a maximum length of time. This makes it
+useful for realtime networks.
+
In addition, all known ARCnet cards have an (almost) identical programming
interface. This means that with one "arcnet" driver you can support any
card; whereas, with ethernet, each manufacturer uses what is sometimes a
up to 508 bytes in length. This is smaller than the internet "bare minimum"
of 576 bytes, let alone the ethernet MTU of 1500. To compensate, an extra
level of encapsulation is defined by RFC1201, which I call "packet
-splitting," which allows "virtual packets" to grow as large as 64K each,
+splitting," that allows "virtual packets" to grow as large as 64K each,
although they are generally kept down to the ethernet-style 1500 bytes.
+For more information on the advantages and disadvantages (mostly the
+advantages) of ARCnet networks, you might try the "ARCnet Trade Association"
+WWW page:
+ http://www.arcnet.com
+
CABLING ARCNET NETWORKS
-----------------------
that IRQ2 is the same as IRQ9, as far as Linux is concerned. You can
"cat /proc/interrupts" for a somewhat complete list of which ones are in
use at any given time. Here is a list of common usages from Vojtech
- Pavlik <vpav4328@diana.troja.mff.cuni.cz>:
+ Pavlik <Vojtech.Pavlik@st.mff.cuni.cz>:
("Not on bus" means there is no way for a card to generate this
interrupt)
IRQ 0 - Timer 0 (Not on bus)
IRQ 2 - IRQ Controller 2 (Not on bus, nor does interrupt the CPU)
IRQ 3 - COM2
IRQ 4 - COM1
- IRQ 5 - FREE (LPT2 if you have it; sometimes COM3)
+ IRQ 5 - FREE (LPT2 if you have it; sometimes COM3; maybe PLIP)
IRQ 6 - Floppy disk controller
- IRQ 7 - FREE (LPT1 if you don't use the polling driver or PLIP)
+ IRQ 7 - FREE (LPT1 if you don't use the polling driver; PLIP)
IRQ 8 - Realtime Clock Interrupt (Not on bus)
IRQ 9 - FREE (VGA vertical sync interrupt if enabled)
IRQ 10 - FREE
IRQ 13 - Numeric Coprocessor (Not on bus)
IRQ 14 - Fixed Disk Controller
IRQ 15 - FREE (Fixed Disk Controller 2 if you have it)
+
+ Note: IRQ 9 is used on some video cards for the "vertical retrace"
+ interrupt. This interrupt would have been handy for things like
+ video games, as it occurs exactly once per screen refresh, but
+ unfortunately IBM cancelled this feature starting with the original
+ VGA and thus many VGA/SVGA cards do not support it. For this
+ reason, no modern software uses this interrupt and it can almost
+ always be safely disabled, if your video card supports it at all.
+
+ If your card for some reason CANNOT disable this IRQ (usually there
+ is a jumper), one solution would be to clip the printed circuit
+ contact on the board: it's the fourth contact from the left on the
+ back side. I take no responsibility if you try this.
- Avery's favourite: IRQ2 (actually IRQ9). Watch that VGA, though.
network.
Also, on many cards (not mine, though) there are red and green LED's.
-Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz> tells me this is what they
+Vojtech Pavlik <Vojtech.Pavlik@st.mff.cuni.cz> tells me this is what they
mean:
GREEN RED Status
----- --- ------
SMC PC550Longboard 16
SMC PC600 16
SMC PC710 8
- SMC? LCS-8830-T 16?
+ SMC? LCS-8830(-T) 8/16
Puredata PDI507 8
CNet Tech CN120-Series 8
CNet Tech CN160-Series 16
*****************************************************************************
** Possibly SMC **
-LCS-8830-T (16-bit card)
-------------------------
+LCS-8830(-T) (8 and 16-bit cards)
+---------------------------------
- from Mathias Katzer <mkatzer@HRZ.Uni-Bielefeld.DE>
+ - Marek Michalkiewicz <marekm@i17linuxb.ists.pwr.wroc.pl> says the
+ LCS-8830 is slightly different from LCS-8830-T. These are 8 bit, BUS
+ only (the JP0 jumper is hardwired), and BNC only.
This is a LCS-8830-T made by SMC, I think ('SMC' only appears on one PLCC,
nowhere else, not even on the few xeroxed sheets from the manual).
** Acer **
8-bit card, Model 5210-003
--------------------------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz> using portions of
+ - from Vojtech Pavlik <Vojtech.Pavlik@st.mff.cuni.cz> using portions of
the existing arcnet-hardware file.
This is a 90C26 based card. Its configuration seems similar to
** Datapoint? **
LAN-ARC-8, an 8-bit card
------------------------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+ - from Vojtech Pavlik <Vojtech.Pavlik@st.mff.cuni.cz>
This is another SMC 90C65 based arcnet card. I couldn't identify the
manufacturer, but it might be DataPoint, becsuse the card has the
** Topware **
8-bit card, TA-ARC/10
-------------------------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+ - from Vojtech Pavlik <Vojtech.Pavlik@st.mff.cuni.cz>
This is another very similar 90C65 card. Most of the switches and jumpers
are the same as on other clones.
** No Name **
8-bit cards ("Made in Taiwan R.O.C.")
-----------
- - from Vojtech Pavlik <vpav4328@diana.troja.mff.cuni.cz>
+ - from Vojtech Pavlik <Vojtech.Pavlik@st.mff.cuni.cz>
I have named this ARCnet card "NONAME", since I got only the card with
no manual at all and the only text identifying the manufacturer is
Since no one seems to listen to me otherwise, perhaps a poem will get your
attention:
- This is scary software
- If it works I DO CARE.
+ This driver's getting fat and beefy,
+ But my cat is still named Fifi.
Hmm, I think I'm allowed to call that a poem, even though it's only two
lines. Hey, I'm in Computer Science, not English. Give me a break.
These are the ARCnet drivers for Linux.
This new release has resulted from many months of on-and-off effort from me
-(Avery Pennarun), many bug reports from users, and in particular a lot of
-input and coding from Tomasz Motylewski. Starting with ARCnet 2.10 ALPHA,
-Tomasz's all-new-and-improved RFC1051 support has been included and seems to
-be working fine!
+(Avery Pennarun), many bug reports/fixes and suggestions from others, and in
+particular a lot of input and coding from Tomasz Motylewski. Starting with
+ARCnet 2.10 ALPHA, Tomasz's all-new-and-improved RFC1051 support has been
+included and seems to be working fine!
Where do I discuss these drivers?
---------------------------------
-BOINGY - the linux-arcnet@807-city.on.ca mailing list is now so unstable
+HEY!! - the linux-arcnet@807-city.on.ca mailing list is now so unstable
that I can't recommend people even bother with it. I no longer have an
account on 807-CITY (though they still graciously forward my mail to me) so
there's not much I can do.
linux-arcnet YOUR REAL NAME" to listserv@tichy.ch.uj.edu.pl. Then, to
submit messages to the list, mail to linux-arcnet@tichy.ch.uj.edu.pl.
-There are mailing list archives at:
+There are archives of the mailing list at:
http://tichy.ch.uj.edu.pl/lists/linux-arcnet
-Send all bug (or success) reports to me, then, not the list, since (as I
-mentioned) the list doesn't work.
-
The people on linux-net@vger.rutgers.edu have also been known to be very
helpful, especially when we're talking about ALPHA Linux kernels that may or
may not work right in the first place.
The arc0s support was contributed by Tomasz Motylewski
and modified somewhat by me. Bugs are probably my fault.
+You can choose not to compile arc0e and arc0s into the driver if you want -
+this will save you a bit of memory and avoid confusion when eg. trying to
+use the "NFS-root" stuff in recent Linux kernels.
+
The arc0e and arc0s devices are created automatically when you first
-'ifconfig' the arc0 device. To actually use them, though, you need to also
-'ifconfig' the other virtual devices you need. There are a number of ways
-you can set up your network then:
+ifconfig the arc0 device. To actually use them, though, you need to also
+ifconfig the other virtual devices you need. There are a number of ways you
+can set up your network then:
1. Single Protocol.
its own IP address and needs to use freedom as its default gateway. The
XT (patience), however, does not have its own internet IP address and so
I assigned it one on a "private subnet" (as defined by RFC1597).
-
+
To start with, take a simple network with just insight and freedom.
Insight needs to:
- talk to freedom via RFC1201 (arc0) protocol, because I like it
ifconfig arc0 freedom
route add freedom arc0
route add insight arc0
- /* and default gateway is configured by PPP */
+ /* and default gateway is configured by pppd */
Great, now insight talks to freedom directly on arc0, and sends packets
to the internet through freedom. If you didn't know how to do the above,
both insight and patience are using freedom as their default gateway, the
two can already talk to each other.
- It's quite fortunate that I set things up like this the first time
- through (cough cough) because it's really handy when I boot insight into
- DOS. There, it runs the Novell ODI protocol stack, which only works with
- RFC1201 ARCnet. In this mode it would be impossible for insight to
- communicate directly with patience, since the Novell stack is
- incompatible with Microsoft's Ethernet-Encap. Without changing any
- settings on freedom or patience, I simply set freedom as the default
- gateway for insight (now in DOS, remember) and all the forwarding happens
- "automagically" between the two hosts that would normally not be able to
- communicate at all.
+ It's quite fortunate that I set things up like this the first time (cough
+ cough) because it's really handy when I boot insight into DOS. There, it
+ runs the Novell ODI protocol stack, which only works with RFC1201 ARCnet.
+ In this mode it would be impossible for insight to communicate directly
+ with patience, since the Novell stack is incompatible with Microsoft's
+ Ethernet-Encap. Without changing any settings on freedom or patience, I
+ simply set freedom as the default gateway for insight (now in DOS,
+ remember) and all the forwarding happens "automagically" between the two
+ hosts that would normally not be able to communicate at all.
For those who like diagrams, I have created two "virtual subnets" on the
same physical ARCnet wire. You can picture it like this:
| | * | |
| +-Freedom-*-Gatekeeper-+ |
| | | * | |
- \-------+-------/ | \-------+-------/
+ \-------+-------/ | * \-------+-------/
| | |
Insight | Patience
(Internet)
-
-
It works: what now?
-------------------
--------------------------
Do the same as above, but also include the output of the ifconfig and route
-commands, as well as any pertinent log entries (ie: anything that starts
+commands, as well as any pertinent log entries (ie. anything that starts
with "arcnet:" and has shown up since the last reboot) in your mail.
If you want to try fixing it yourself (I strongly recommend that you mail me
about the problem first, since it might already have been solved) you may
want to try some of the debug levels available. For heavy testing on
D_DURING or more, it would be a REALLY good idea to kill your klogd daemon
-first! D_DURING displays 4-5 lines for each packet sent or received. D_TX
-and RX actually DISPLAY each packet as it is sent or received, which is
-obviously quite big.
-
-You can run the arcdump shell script (available from me or in the full
-ARCnet package if you got it) as root to list the contents of the arcnet
-buffers at any time. To make any sense at all out of this, you should grab
-the pertinent RFC's. (some are listed near the top of arcnet.c). arcdump
-assumes your card is at 0xD0000. If it isn't, edit the script.
-
-Buffers #0 and 1 are used for receiving, and Buffers #2 and 3 are for
-sending. Ping-pong buffers are implemented both ways.
-
-If your debug level includes D_DURING, the buffers are cleared to a constant
-value of 0x42 every time the card is reset (which should only happen when
-you do an ifconfig up, or when Linux decides that the driver is broken).
-This is to make it easier to figure out which bytes are being used by a
-packet.
+first! D_DURING displays 4-5 lines for each packet sent or received. D_TX,
+D_RX, and D_SKB actually DISPLAY each packet as it is sent or received,
+which is obviously quite big.
+
+Starting with v2.40 ALPHA, the autoprobe routines have changed
+significantly. In particular, they won't tell you why the card was not
+found unless you turn on the D_INIT_REASONS debugging flag.
+
+Once the driver is running, you can run the arcdump shell script (available
+from me or in the full ARCnet package, if you have it) as root to list the
+contents of the arcnet buffers at any time. To make any sense at all out of
+this, you should grab the pertinent RFC's. (some are listed near the top of
+arcnet.c). arcdump assumes your card is at 0xD0000. If it isn't, edit the
+script.
+
+Buffers 0 and 1 are used for receiving, and Buffers 2 and 3 are for sending.
+Ping-pong buffers are implemented both ways.
+
+If your debug level includes D_DURING and you did NOT define SLOW_XMIT_COPY,
+the buffers are cleared to a constant value of 0x42 every time the card is
+reset (which should only happen when you do an ifconfig up, or when Linux
+decides that the driver is broken). During a transmit, unused parts of the
+buffer will be cleared to 0x42 as well. This is to make it easier to figure
+out which bytes are being used by a packet.
You can change the debug level without recompiling the kernel by typing:
ifconfig arc0 down metric 1xxx
where "xxx" is the debug level you want. For example, "metric 1015" would put
you at debug level 15. Debug level 7 is currently the default.
-Note that the debug level is (as of v1.90 ALPHA) a binary combination of
-different debug flags; so debug level 7 is really 1+2+4 or
-D_NORMAL+D_INIT+D_EXTRA. To reach D_DURING, you would add 8 to this,
-resulting in debug level 15.
+Note that the debug level is (starting with v1.90 ALPHA) a binary
+combination of different debug flags; so debug level 7 is really 1+2+4 or
+D_NORMAL+D_EXTRA+D_INIT. To include D_DURING, you would add 16 to this,
+resulting in debug level 23.
If you don't understand that, you probably don't want to know anyway.
E-mail me about your problem.
VERSION = 1
PATCHLEVEL = 3
-SUBLEVEL = 63
+SUBLEVEL = 64
ARCH = i386
{ 0, 0, 2, 1, 0}, /* idsel 11 (slot furthest from ISA) KN25_PCI_SLOT0 */
{ 1, 1, 0, 2, 1}, /* idsel 12 (middle slot) KN25_PCI_SLOT1 */
#ifdef CONFIG_ALPHA_AVANTI
- { 1, 1, -1, -1, -1}, /* idsel 13 KN25_PCI_SLOT2 */
+ { 1, 2, 1, 0, 2}, /* idsel 13 KN25_PCI_SLOT2 */
#endif /* CONFIG_ALPHA_AVANTI */
};
/*
cabriolet_and_eb66p_device_interrupt(vector, ®s);
#elif NR_IRQS == 32
eb66_and_eb64p_device_interrupt(vector, ®s);
+#elif NR_IRQS == 16
+ isa_device_interrupt(vector, ®s);
#endif
return;
case 4:
#include <linux/module.h>
#include <linux/smp.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+extern void dump_thread(struct pt_regs *, struct user *);
+extern int dump_fpu(elf_fpregset_t *);
static struct symbol_table arch_symbol_table = {
#include <linux/symtab_begin.h>
/* platform dependent support */
+ X(dump_thread),
+ X(dump_fpu),
#ifdef __SMP__
X(apic_reg), /* Needed internally for the I386 inlines */
X(cpu_data),
/* do print messages for unexpected interrupts */
static int print_unex=1;
-
+#include <linux/utsname.h>
#include <linux/module.h>
/* the following is the mask of allowed drives. By default units 2 and
static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
{
- int i, orig_size, ocmd;
+ int i;
- orig_size = _IOC_SIZE(*cmd);
- ocmd = *cmd;
for (i=0; i < ARRAY_SIZE(translation_table); i++) {
- if ((*cmd & 0xff3f) == (translation_table[i].newcmd & 0xff3f)){
+ if ((*cmd & 0xffff) == (translation_table[i].newcmd & 0xffff)){
+ *size = _IOC_SIZE(*cmd);
*cmd = translation_table[i].newcmd;
- if (!orig_size && _IOC_SIZE(*cmd)) {
- /* kernels 1.3.34 to 1.3.39 : */
- *size = _IOC_SIZE(*cmd);
- DPRINT1("warning: obsolete ioctl 0x%x\n",ocmd);
- DPRINT("please recompile your program\n");
- /* these ioctls only existed
- * in six (development)
- * kernels anyways. That's why we
- * complain about these, and not about
- * the much older 0x00xx ioctl's
- */
- } else {
- *size = orig_size;
- if (*size > _IOC_SIZE(*cmd)) {
- printk("ioctl not yet supported\n");
- return -EFAULT;
- }
+ if (*size > _IOC_SIZE(*cmd)) {
+ printk("ioctl not yet supported\n");
+ return -EFAULT;
}
return 0;
}
static inline int xlate_0x00xx_ioctl(int *cmd, int *size)
{
int i;
- /* kernels <= 1.3.33 */
- /* we might want to start warning in 1.4.x (by then, no user
- * programs will have an excuse to use the old ioctls: there
- * will be a stable kernel supporting them :)
- *
- * when the first 1.5.x kernel will come out, this loop will
- * be removed as well.
- */
+ /* old ioctls' for kernels <= 1.3.33 */
+ /* When the next even release will come around, we'll start
+ * warning against these.
+ * When the next odd release will come around, we'll fail with
+ * -EINVAL */
+ if(strcmp(system_utsname.version, "1.4.0") >= 0)
+ printk("obsolete floppy ioctl %x\n", *cmd);
+ if((system_utsname.version[0] == '1' &&
+ strcmp(system_utsname.version, "1.5.0") >= 0) ||
+ (system_utsname.version[0] >= '2' &&
+ strcmp(system_utsname.version, "2.1.0") >= 0))
+ return -EINVAL;
for (i=0; i < ARRAY_SIZE(translation_table); i++) {
if (*cmd == translation_table[i].oldcmd) {
*size = translation_table[i].oldsize;
* NOTE that writes may use only the low 2/3 of these: reads
* take precedence.
*/
-#define NR_REQUEST 16
+#define NR_REQUEST 64
static struct request all_requests[NR_REQUEST];
/*
outfile.f_op->write(outfile.f_inode, &outfile, buf,
BLOCK_SIZE);
if (!(i % 16)) {
- printk("%c\b", rotator[rotate & 0x3]);
+ printk(KERN_NOTICE "%c\b", rotator[rotate & 0x3]);
rotate++;
}
}
--- /dev/null
+
+Stallion Multiport Serial Drivers
+---------------------------------
+
+Version: 1.0.2
+Date: 01FEB96
+Author: Greg Ungerer (gerg@stallion.oz.au)
+
+
+1. INTRODUCTION
+
+There are two drivers that work with the different families of Stallion
+multiport serial boards. One is for the Stallion smart boards - that is
+EasyIO and EasyConnection 8/32, the other for the true Stallion intelligent
+multiport boards - Stallion, Brumby, ONboard and EasyConnection 8/64.
+
+If you are using any of the Stallion intelligent multiport boards (Brumby,
+ONboard, Stallion, EasyConnection 8/64) with Linux you will need to get the
+driver utility package. This package is available at most of the Linux
+archive sites (and on CD's that contain these archives). The file will be
+called stallion-X.X.X.tar.gz where X.X.X will be the version number. In
+particular this package contains the board embedded executable images that
+are required for these boards. It also contains the downloader program.
+These boards cannot be used without this.
+
+The following ftp sites (and their mirrors) definately have the stallion
+driver utility package: ftp.stallion.com, tsx-11.mit.edu, sunsite.unc.edu.
+
+ftp.stallion.com:/drivers/ata5/Linux/stallion-1.0.1.tar.gz
+tsx-11.mit.edu:/pub/linux/BETA/serial/stallion/stallion-1.0.1.tar.gz
+sunsite.unc.edu:/pub/Linux/kernel/patches/serial/stallion-1.0.1.tar.gz
+
+If you are using the EasyIO or EasyConnection 8/32 boards then you don't
+need this package. Although it does have a handy script to create the
+/dev device nodes for these boards.
+
+If you require DIP switch settings, EISA/MCA configuration files, or any
+other information related to Stallion boards then have a look at
+http://www.stallion.com.
+
+
+
+2. INSTALLATION
+
+The drivers can be used as loadable modules or compiled into the kernel.
+You can choose which when doing a "make config" on the kernel.
+
+All ISA, EISA and MCA boards that you want to use need to be entered into
+the driver(s) configuration structures. All PCI boards will be automatically
+detected when you load the driver - so they do not need to be entered into
+the driver(s) configuration structure. (Note that kernel PCI BIOS32 support
+is required to use PCI boards.)
+
+Entering ISA, EISA and MCA boards into the driver(s) configuration structure
+involves editing the driver(s) source file. It's pretty easy if you follow
+the instructions below. Both drivers can support up to 4 boards. The smart
+card driver (the stallion.c driver) supports any combination of EasyIO and
+EasyConnection 8/32 boards (up to a total of 4). The intelligent driver
+supports any combination of ONboards, Brumbys, Stallions and EasyConnection
+8/64 boards (up to a total of 4).
+
+To set up the driver(s) for the boards that you want to use you need to
+edit the appropriate driver file and add configuration entries.
+
+If using EasyIO or EasyConnection 8/32 ISA or MCA boards, do:
+ vi stallion.c
+ - find the definition of the stl_brdconf array (of structures)
+ near the top of the file
+ - modify this to match the boards you are going to install
+ (the comments before this structure should help)
+ - save and exit
+
+If using ONboard, Brumby, Stallion or EasyConnection 8/64 boards then do:
+ vi istallion.c
+ - find the definition of the stli_brdconf array (of structures)
+ near the top of the file
+ - modify this to match the boards you are going to install
+ (the comments before this structure should help)
+ - save and exit
+
+Once you have set up the board configurations then you are ready to build
+the kernel or modules.
+
+When the new kernel is booted, or the loadable module loaded then the
+driver will emit some kernel trace messages about whether the configured
+boards where detected or not. Depending on how your system logger is set
+up these may come out on the console, or just be logged to
+/usr/adm/messages. You should check the messages to confirm that all is well.
+
+
+2.1 SHARING INTERRUPTS
+
+It is possible to share interrupts between multiple EasyIO and
+EasyConnection 8/32 boards in an EISA system. To do this you will need to
+do a couple of things:
+
+1. When entering the board resources into the stallion.c file you need to
+ mark the boards as using level triggered interrupts. Do this by replacing
+ the "0" entry at field position 6 (the last field) in the board
+ configuration structure with a "1". (This is the structure that defines
+ the board type, I/O locations, etc. for each board). All boards that are
+ sharing an interrupt must be set this way, and each board should have the
+ same interrupt number specified here as well. Now build the module or
+ kernel as you would normally.
+
+2. When physically installing the boards into the system you must enter
+ the system EISA configuration utility. You will need to install the EISA
+ configuration files for *all* the EasyIO and EasyConnection 8/32 boards
+ that are sharing interrupts. The Stallion EasyIO and EasyConnection 8/32
+ EISA configuration files required are supplied by Stallion Technologies
+ on the DOS Utilities floppy (usually supplied in the box with the board
+ when purchased. If not, you can pick it up from Stallion's FTP site,
+ ftp.stallion.com). You will need to edit the board resources to choose
+ level triggered interrupts, and make sure to set each board's interrupt
+ to the same IRQ number.
+
+You must complete both the above steps for this to work. When you reboot
+or load the driver your EasyIO and EasyConnection 8/32 boards will be
+sharing interrupts.
+
+
+2.2 USING HIGH SHARED MEMORY
+
+The EasyConnection 8/64-EI, ONboard and Stallion boards are capable of
+using shared memory addresses above the usual 640K - 1Mb range. The ONboard
+ISA and the Stallion boards can be programmed to use memory addresses up to
+16Mb (the ISA bus addressing limit), and the EasyConnection 8/64-EI and
+ONboard/E can be programmed for memory addresses up to 4Gb (the EISA bus
+addressing limit).
+
+The istallion intelligent driver does offer support for these higher memory
+addresses with a couple of limitations. The higher memory support can only
+be used in the loadable module form of the driver, since the kernel memory
+management routines it relies on can not be run from the drivers static
+kernel init routine.
+
+By default, support for these higher memory addresses is not compiled into
+the driver. This is because it relies on kernel functions that are not
+normally exported as part of the Linux loadable module system.
+
+To add the appropriate symbols into the kernel export code you need to:
+
+1. cd /usr/src/linux/kernel
+ (assuming your Linux kernel code is in /usr/src/linux)
+2. vi ksyms.c
+ - find the line that reads
+ X(vfree),
+ - after this line insert the following line
+ X(remap_page_range),
+ - save and exit
+3. cd ..
+4. build a new kernel (usually just make)
+
+This will export the "remap_page_range" function for loadable modules
+which is required for the higher memory support code.
+
+Finally you need to enable the code in the istallion driver. To do this
+edit the istallion.c file and search for the symbol STLI_HIMEMORY. It is
+near the top of the file in a line that looks like:
+
+#define STLI_HIMEMORY 0
+
+Change the "0" to a "1". This enables the high memory support code in
+the driver. You will then need to rebuild the module or rebuild the
+kernel to incorporate the change. You will also need to modify
+the board resource configuration information to use a higher memory
+address.
+
+Once these changes are in place the driver will work as it did before.
+Note that the physical memory address range is software programmed on the
+EasyConnection 8/64-EI and ONboards, but must be set via DIP switches on
+the original Stallion boards.
+
+
+2.3 TROUBLE SHOOTING
+
+If a board is not found by the driver but is actually in the system then the
+most likely problem is that the I/O address is wrong. Change it in the driver
+stallion.c or istallion.c configuration structure and rebuild the kernel or
+modules, or change it on the board. On EasyIO and EasyConnection 8/32 boards
+the IRQ is software programmable, so if there is a conflict you may need to
+change the IRQ used for a board in the stallion.c configuration structure.
+There are no interrupts to worry about for ONboard, Brumby, Stallion or
+EasyConnection 8/64 boards. The memory region on EasyConnection 8/64 and
+ONboard boards is software programmable, but not on the Brumbys or Stallions.
+
+
+
+3. USING THE DRIVERS
+
+3.1 INTELLIGENT DRIVER OPERATION
+
+The intelligent boards also need to have their "firmware" code downloaded
+to them. This is done via a user level application supplied in the driver
+package called "stlload". Compile this program where ever you dropped the
+package files, by typing "make". In its simplest form you can then type
+ ./stlload -i cdk.sys
+in this directory and that will download board 0 (assuming board 0 is an
+EasyConnection 8/64 board). To download to an ONboard, Brumby or Stallion do:
+ ./stlload -i 2681.sys
+
+Normally you would want all boards to be downloaded as part of the standard
+system startup. To achieve this, add one of the lines above into the
+/etc/rc.d/rc.S or /etc/rc.d/rc.serial file. To download each board just add
+the "-b <brd-number>" option to the line. You will need to download code for
+every board. You should probably move the stlload program into a system
+directory, such as /usr/sbin. Also, the default location of the cdk.sys image
+file in the stlload down-loader is /usr/lib/stallion. Create that directory
+and put the cdk.sys and 2681.sys files in it. (It's a convenient place to put
+them anyway). As an example your /etc/rc.d/rc.S file might have the
+following lines added to it (if you had 3 boards):
+ /usr/sbin/stlload -b 0 -i /usr/lib/stallion/cdk.sys
+ /usr/sbin/stlload -b 1 -i /usr/lib/stallion/2681.sys
+ /usr/sbin/stlload -b 2 -i /usr/lib/stallion/2681.sys
+
+The image files cdk.sys and 2681.sys are specific to the board types. The
+cdk.sys will only function correctly on an EasyConnection 8/64 board. Similarly
+the 2681.sys image fill only operate on ONboard, Brumby and Stallion boards.
+If you load the wrong image file into a board it will fail to start up, and
+of course the ports will not be operational!
+
+If you are using the modularized version of the driver you might want to put
+the insmod calls in the startup script as well (before the download lines
+obviously).
+
+
+3.2 USING THE SERIAL PORTS
+
+Once the driver is installed you will need to setup some device nodes to
+access the serial ports. The simplest method is to use the stallion utility
+"mkdevnods" script. It will automatically create all possible device entries
+required for all 4 boards. This will create the normal serial port devices as
+/dev/ttyE# where # is the port number starting from 0. A bank of 64 minor
+device numbers is allocated to each board, so the first port on the second
+board is port 64, etc. A set of callout type devices is also created. They
+are created as the devices /dev/cue# where # is the same as for the ttyE
+devices.
+
+For the most part the Stallion driver tries to emulate the standard PC system
+COM ports and the standard Linux serial driver. The idea is that you should
+be able to use Stallion board ports and COM ports interchangeably without
+modifying anything but the device name. Anything that doesn't work like that
+should be considered a bug in this driver!
+
+If you look at the driver code you will notice that it is fairly closely
+based on the Linux serial driver (linux/drivers/char/serial.c). This is
+intentional, obviously this is the easiest way to emulate its behavior!
+
+Since this driver tries to emulate the standard serial ports as much as
+possible, most system utilities should work as they do for the standard
+COM ports. Most importantly "stty" works as expected and "setserial" can be
+also be used (excepting the ability to auto-configure the I/O and IRQ
+addresses of boards). Higher baud rates are supported in the
+usual fashion through setserial or using the CBAUDEX extensions. Note that
+the EasyIO and EasyConnection (all types) support 57600 and 115200 baud. The
+older boards including ONboard, Brumby and the original Stallion support a
+maximum baud rate of 38400.
+
+If you are unfamiliar with how to use serial ports, then get the Serial-HOWTO
+by Greg Hankins. It will explain everything you need to know!
+
+
+
+4. NOTES
+
+You can use both drivers at once if you have a mix of board types installed
+in a system. However to do this you will need to change the major numbers
+used by one of the drivers. Currently both drivers use major numbers 24 and
+25 for their port devices. Change one driver to use some other major numbers,
+and then modify the mkdevnods script to make device nodes based on those new
+major numbers. For example, you could change the stallion.c driver to use
+major numbers 30 and 31 (don't use 28, it's used by istallion.c driver for its
+sio memory device!). You will also need to create device nodes with different
+names for the ports...
+
+Finding a free physical memory address range can be a problem. The older
+boards like the Stallion and ONboard need large areas (64K or even 128K), so
+they can be very difficult to get into a system. If you have 16 Mb of RAM
+then you have no choice but to put them somewhere in the 640K -> 1Mb range.
+ONboards require 64K, so typically 0xd0000 is good, or 0xe0000 on some
+systems. If you have an original Stallion board, "V4.0" or Rev.O,
+then you need a 64K memory address space, so again 0xd0000 and 0xe0000 are
+good. Older Stallion boards are a much bigger problem. They need 128K of
+address space and must be on a 128K boundary. If you don't have a VGA card
+then 0xc0000 might be usable - there is really no other place you can put
+them below 1Mb.
+
+Both the ONboard and old Stallion boards can use higher memory addresses as
+well, but you must have less than 16Mb of RAM to be able to use them. Usual
+high memory addresses used include 0xec0000 and 0xf00000.
+
+The Brumby boards only require 16Kb of address space, so you can usually
+squeeze them in somewhere. Common addresses are 0xc8000, 0xcc000, or in
+the 0xd0000 range. EasyConnection 8/64 boards are even better, they only
+require 4Kb of address space, again usually 0xc8000, 0xcc000 or 0xd0000
+are good.
+
+If you are using an EasyConnection 8/64-EI or ONboard/E then usually the
+0xd0000 or 0xe0000 ranges are the best options below 1Mb. If neither of
+them can be used then the high memory support to use the really high address
+ranges is the best option. Typically the 2Gb range is convenient for them,
+and gets them well out of the way.
+
+There is a new utility program in the stallion utility package called
+"stlstty". Most people will not need to use this. If you have an ONboard/16
+which has partial signals on the upper 12 ports then this program can be used
+to set the upper ports to have modem control instead of hardware flow control.
+Use the "mapcts maprts" flag options to this utility on the port(s) that you
+wish to do this mapping on, eg
+ ./stlstty maprts mapcts < /dev/cue0
+This enables RTS to act like DTR and CTS to act like DCD on the specified
+port.
+
+
+
+5. DISCLAIMER
+
+I do not speak for Stallion Technologies in any capacity, officially or
+unofficially.
+
/*
* istallion.c -- stallion intelligent multiport serial driver.
*
- * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others.
/*****************************************************************************/
#include <linux/module.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <asm/pgtable.h>
#endif
+/*
+ * There is some experimental EISA board detection code in this driver.
+ * By default it is disabled, but for those that want to try it out,
+ * then set the define below to be 1.
+ */
+#define STLI_EISAPROBE 0
+
/*****************************************************************************/
/*
* all the local structures required by a serial tty driver.
*/
static char *stli_drvname = "Stallion Intelligent Multiport Serial Driver";
-static char *stli_drvversion = "1.0.0";
+static char *stli_drvversion = "1.0.2";
static char *stli_serialname = "ttyE";
static char *stli_calloutname = "cue";
unsigned long addr;
unsigned long rxoffset;
unsigned long txoffset;
+ unsigned long sigs;
+ unsigned long pflag;
unsigned int rxsize;
unsigned int txsize;
- unsigned long sigs;
unsigned char reqbit;
unsigned char portidx;
unsigned char portbit;
int nrports;
int nrdevs;
unsigned int iobase;
+ unsigned long memaddr;
void *membase;
int memsize;
int pagesize;
stliport_t *ports[STL_MAXPORTS];
} stlibrd_t;
-static stlibrd_t *stli_brds;
+static stlibrd_t *stli_brds[STL_MAXBRDS];
static int stli_shared = 0;
"EC8/32-PCI",
};
+/*
+ * Set up a default memory address table for EISA board probing.
+ * The default addresses are all bellow 1Mbyte, which has to be the
+ * case anyway. They should be safe, since we only read values from
+ * them, and interrupts are disabled while we do it. If the higher
+ * memory support is compiled in then we also try probing around
+ * the 1Gb, 2Gb and 3Gb areas as well...
+ */
+static unsigned long stli_eisamemprobeaddrs[] = {
+ 0xc0000, 0xd0000, 0xe0000, 0xf0000,
+ 0x80000000, 0x80010000, 0x80020000, 0x80030000,
+ 0x40000000, 0x40010000, 0x40020000, 0x40030000,
+ 0xc0000000, 0xc0010000, 0xc0020000, 0xc0030000,
+ 0xff000000, 0xff010000, 0xff020000, 0xff030000,
+};
+
+#if STLI_HIMEMORY
+static int stli_eisamempsize = sizeof(stli_eisamemprobeaddrs) / sizeof(unsigned long);
+#else
+static int stli_eisamempsize = 4;
+#endif
+
+int stli_eisaprobe = STLI_EISAPROBE;
+
/*****************************************************************************/
/*
#define ECP_EIPAGESIZE (64 * 1024)
#define ECP_MCPAGESIZE (4 * 1024)
+#define STL_EISAID 0x8c4e
+
/*
* Important defines for the ISA class of ECP board.
*/
#define ECP_EIADDRSHFTH 24
#define ECP_EIBRDENAB 0xc84
+#define ECP_EISAID 0x4
+
/*
* Important defines for the Micro-channel class of ECP board.
* (It has a lot in common with the ISA boards.)
#define ONB_EIADDRSHFTH 24
#define ONB_EIBRDENAB 0xc84
+#define ONB_EISAID 0x1
+
/*
* Important defines for the Brumby boards. They are pretty simple,
* there is not much that is programmably configurable.
(* brdp->getmemptr)(brdp, offset, __LINE__)
/*
- * Define the maximal baud rate, and he default baud base for ports.
+ * Define the maximal baud rate, and the default baud base for ports.
*/
#define STL_MAXBAUD 230400
#define STL_BAUDBASE 115200
int init_module(void);
void cleanup_module(void);
#endif
-static void *stli_memalloc(int len);
int stli_init(void);
static int stli_open(struct tty_struct *tty, struct file *filp);
static void stli_flushbuffer(struct tty_struct *tty);
static void stli_hangup(struct tty_struct *tty);
-static int stli_brdinit(void);
-static int stli_initecp(stlibrd_t *brdp, stlconf_t *confp);
-static int stli_initonb(stlibrd_t *brdp, stlconf_t *confp);
+static int stli_initbrds(void);
+static int stli_brdinit(stlibrd_t *brdp);
+static int stli_initecp(stlibrd_t *brdp);
+static int stli_initonb(stlibrd_t *brdp);
+static int stli_eisamemprobe(stlibrd_t *brdp);
+static int stli_findeisabrds(void);
static int stli_initports(stlibrd_t *brdp);
static int stli_startbrd(stlibrd_t *brdp);
static int stli_memread(struct inode *ip, struct file *fp, char *buf, int count);
static void stli_read(stlibrd_t *brdp, stliport_t *portp);
static void stli_getserial(stliport_t *portp, struct serial_struct *sp);
static int stli_setserial(stliport_t *portp, struct serial_struct *sp);
+static void *stli_memalloc(int len);
static void stli_ecpinit(stlibrd_t *brdp);
static void stli_ecpenable(stlibrd_t *brdp);
static int stli_timeron = 0;
/*
- * This is hack to allow for the kernel changes made to add_timer
- * in the newer 1.3.X kernels (changed around 1.3.1X).
+ * Define the calculation for the timeout routine.
*/
-#ifdef LINUX_1_2_X_COMPAT
-#define STLI_TIMEOUT 0
-#else
#define STLI_TIMEOUT (jiffies + 1)
-#endif
/*****************************************************************************/
#ifdef MODULE
+/*
+ * Loadable module initialization stuff.
+ */
+
int init_module()
{
unsigned long flags;
kfree_s(stli_txcookbuf, STLI_TXBUFSIZE);
for (i = 0; (i < stli_nrbrds); i++) {
- brdp = &stli_brds[i];
+ brdp = stli_brds[i];
+ if (brdp == (stlibrd_t *) NULL)
+ continue;
for (j = 0; (j < STL_MAXPORTS); j++) {
portp = brdp->ports[j];
if (portp != (stliport_t *) NULL) {
release_region(brdp->iobase, ECP_IOSIZE);
else
release_region(brdp->iobase, ONB_IOSIZE);
+ kfree_s(brdp, sizeof(stlibrd_t));
+ stli_brds[i] = (stlibrd_t *) NULL;
}
- kfree_s(stli_brds, (sizeof(stlibrd_t) * stli_nrbrds));
restore_flags(flags);
}
/*****************************************************************************/
/*
- * Local memory allocation routines. These are used so we can deal with
- * memory allocation at init time and during run-time in a consistent
- * way. Everbody just calls the stli_memalloc routine to allocate
- * memory and it will do the right thing. There is no common memory
- * deallocation code - since this is only done is special cases, all of
- * which are tightly controlled.
+ * Local driver kernel malloc routine.
*/
static void *stli_memalloc(int len)
{
- return (void *) kmalloc(len, GFP_KERNEL);
+ return((void *) kmalloc(len, GFP_KERNEL));
}
/*****************************************************************************/
brdnr = MKDEV2BRD(minordev);
if (brdnr >= stli_nrbrds)
return(-ENODEV);
- if (stli_brds == (stlibrd_t *) NULL)
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
return(-ENODEV);
- brdp = &stli_brds[brdnr];
if ((brdp->state & BST_STARTED) == 0)
return(-ENODEV);
portnr = MKDEV2PORT(minordev);
clear_bit(TTY_IO_ERROR, &tty->flags);
}
clear_bit(ST_INITIALIZING, &portp->state);
- wake_up_interruptible(&portp->open_wait);
+ wake_up_interruptible(&portp->raw_wait);
if (rc < 0)
return(rc);
}
}
portp->flags &= ~ASYNC_INITIALIZED;
- brdp = &stli_brds[portp->brdnr];
- stli_rawclose(brdp, portp, 0, 1);
+ brdp = stli_brds[portp->brdnr];
+ stli_rawclose(brdp, portp, 0, 0);
if (tty->termios->c_cflag & HUPCL) {
stli_mkasysigs(&portp->asig, 0, 0);
- stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
+ if (test_bit(ST_CMDING, &portp->state))
+ set_bit(ST_DOSIGS, &portp->state);
+ else
+ stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0);
}
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
return(-ENODEV);
if ((portp->brdnr < 0) && (portp->brdnr >= stli_nrbrds))
return(-ENODEV);
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(-ENODEV);
stli_mkasyport(portp, &aport, portp->tty->termios);
return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
return(0);
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return(0);
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(0);
chbuf = (unsigned char *) buf;
/*
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
save_flags(flags);
cli();
return(0);
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return(0);
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(0);
save_flags(flags);
cli();
return(0);
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return(0);
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(0);
save_flags(flags);
cli();
static void stli_getserial(stliport_t *portp, struct serial_struct *sp)
{
struct serial_struct sio;
+ stlibrd_t *brdp;
#if DEBUG
printk("stli_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
memset(&sio, 0, sizeof(struct serial_struct));
sio.type = PORT_UNKNOWN;
sio.line = portp->portnr;
- sio.port = stli_brdconf[portp->brdnr].ioaddr1;
- sio.irq = stli_brdconf[portp->brdnr].irq;
+ sio.irq = 0;
sio.flags = portp->flags;
sio.baud_base = portp->baud_base;
sio.close_delay = portp->close_delay;
sio.custom_divisor = portp->custom_divisor;
sio.xmit_fifo_size = 0;
sio.hub6 = 0;
+
+ brdp = stli_brds[portp->brdnr];
+ if (brdp != (stlibrd_t *) NULL)
+ sio.port = brdp->iobase;
+
memcpy_tofs(sp, &sio, sizeof(struct serial_struct));
}
return(-ENODEV);
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return(0);
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(0);
rc = 0;
if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(struct serial_struct))) == 0)
rc = stli_setserial(portp, (struct serial_struct *) arg);
break;
+ case STL_GETPFLAG:
+ if ((rc = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned long))) == 0)
+ put_fs_long(portp->pflag, (unsigned long *) arg);
+ break;
+ case STL_SETPFLAG:
+ if ((rc = verify_area(VERIFY_READ, (void *) arg, sizeof(unsigned long))) == 0) {
+ portp->pflag = get_fs_long((unsigned long *) arg);
+ stli_setport(portp);
+ }
+ break;
case TIOCSERCONFIG:
case TIOCSERGWILD:
case TIOCSERSWILD:
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
tiosp = tty->termios;
if ((tiosp->c_cflag == old->c_cflag) && (tiosp->c_iflag == old->c_iflag))
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
memset(&actrl, 0, sizeof(asyctrl_t));
actrl.txctrl = CT_STOPFLOW;
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
memset(&actrl, 0, sizeof(asyctrl_t));
actrl.txctrl = CT_STARTFLOW;
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
portp->flags &= ~ASYNC_INITIALIZED;
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
- brdp = &stli_brds[portp->brdnr];
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return;
save_flags(flags);
cli();
* carefull of data that will be copied out from shared memory -
* containing command results. The command completion is all done from
* a poll routine that does not have user coontext. Therefore you cannot
- * copy back directly into user space, or to the kernel stack. This
- * routine does not sleep, so can be called from anywhere.
+ * copy back directly into user space, or to the kernel stack of a
+ * process. This routine does not sleep, so can be called from anywhere.
*/
static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
printk("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback);
#endif
+ save_flags(flags);
+ cli();
+
if (test_bit(ST_CMDING, &portp->state)) {
printk("STALLION: command already busy, cmd=%x!\n", (int) cmd);
+ restore_flags(flags);
return;
}
- save_flags(flags);
- cli();
EBRDENABLE(brdp);
cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
if (size > 0) {
* Check each board and do any servicing required.
*/
for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
- brdp = &stli_brds[brdnr];
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ continue;
if ((brdp->state & BST_STARTED) == 0)
continue;
pp->iflag |= FI_1MARKRXERRS;
if (tiosp->c_iflag & BRKINT)
portp->rxmarkmsk |= BRKINT;
+
+/*
+ * Transfer any persistent flags into the asyport structure.
+ */
+ pp->pflag = portp->pflag;
}
/*****************************************************************************/
outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
udelay(100);
- memconf = (((unsigned long) brdp->membase) & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
+ memconf = (brdp->memaddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT;
outb(memconf, (brdp->iobase + ECP_ATMEMAR));
}
outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
udelay(500);
- memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
+ memconf = (brdp->memaddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL;
outb(memconf, (brdp->iobase + ECP_EIMEMARL));
- memconf = (((unsigned long) brdp->membase) & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
+ memconf = (brdp->memaddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH;
outb(memconf, (brdp->iobase + ECP_EIMEMARH));
}
outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
udelay(10);
outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
- memconf = (((unsigned long) brdp->membase) & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
+ memconf = (brdp->memaddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT;
outb(memconf, (brdp->iobase + ONB_ATMEMAR));
outb(0x1, brdp->iobase);
udelay(1000);
outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
udelay(10);
outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
}
outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
udelay(10);
outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
- memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
+ memconf = (brdp->memaddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL;
outb(memconf, (brdp->iobase + ONB_EIMEMARL));
- memconf = (((unsigned long) brdp->membase) & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
+ memconf = (brdp->memaddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH;
outb(memconf, (brdp->iobase + ONB_EIMEMARH));
outb(0x1, brdp->iobase);
udelay(1000);
outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
udelay(10);
outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
}
outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
udelay(10);
outb(0, (brdp->iobase + BBY_ATCONFR));
- for (i = 0; (i < 500); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
outb(0x1, brdp->iobase);
udelay(1000);
outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
udelay(10);
outb(0, (brdp->iobase + BBY_ATCONFR));
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
}
#endif
outb(0x1, brdp->iobase);
- for (i = 0; (i < 100); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
}
vecp = (volatile unsigned long *) (brdp->membase + 0x30);
*vecp = 0xffff0000;
outb(0, brdp->iobase);
- for (i = 0; (i < 500); i++)
+ for (i = 0; (i < 1000); i++)
udelay(1000);
}
* board types.
*/
-static int stli_initecp(stlibrd_t *brdp, stlconf_t *confp)
+static int stli_initecp(stlibrd_t *brdp)
{
cdkecpsig_t sig;
cdkecpsig_t *sigsp;
int panelnr;
#if DEBUG
- printk("stli_initecp(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+ printk("stli_initecp(brdp=%x)\n", (int) brdp);
#endif
+/*
+ * Do a basic sanity check on the IO and memory addresses.
+ */
+ if ((brdp->iobase == 0) || (brdp->memaddr == 0))
+ return(-ENODEV);
+
/*
* Based on the specific board type setup the common vars to access
* and enable shared memory. Set all board specific information now
*/
switch (brdp->brdtype) {
case BRD_ECP:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ECP_MEMSIZE;
brdp->pagesize = ECP_ATPAGESIZE;
brdp->init = stli_ecpinit;
break;
case BRD_ECPE:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ECP_MEMSIZE;
brdp->pagesize = ECP_EIPAGESIZE;
brdp->init = stli_ecpeiinit;
break;
case BRD_ECPMC:
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ECP_MEMSIZE;
- brdp->membase = (void *) confp->memaddr;
brdp->pagesize = ECP_MCPAGESIZE;
- brdp->iobase = confp->ioaddr1;
brdp->init = NULL;
brdp->enable = stli_ecpmcenable;
brdp->reenable = stli_ecpmcenable;
EBRDINIT(brdp);
#if STLI_HIMEMORY
- if (confp->memaddr > 0x100000) {
- brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+ if (brdp->memaddr > 0x100000) {
+ brdp->membase = stli_mapbrdmem(brdp->memaddr, brdp->memsize);
if (brdp->membase == (void *) NULL)
return(-ENOMEM);
}
/*
* Now that all specific code is set up, enable the shared memory and
* look for the a signature area that will tell us exactly what board
- * this is, and what is connected to it.
+ * this is, and what it is connected to it.
*/
EBRDENABLE(brdp);
sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
* This handles only these board types.
*/
-static int stli_initonb(stlibrd_t *brdp, stlconf_t *confp)
+static int stli_initonb(stlibrd_t *brdp)
{
cdkonbsig_t sig;
cdkonbsig_t *sigsp;
int i;
#if DEBUG
- printk("stli_initonb(brdp=%x,confp=%x)\n", (int) brdp, (int) confp);
+ printk("stli_initonb(brdp=%x)\n", (int) brdp);
#endif
+/*
+ * Do a basic sanity check on the IO and memory addresses.
+ */
+ if ((brdp->iobase == 0) || (brdp->memaddr == 0))
+ return(-ENODEV);
+
/*
* Based on the specific board type setup the common vars to access
* and enable shared memory. Set all board specific information now
case BRD_ONBOARD2:
case BRD_ONBOARD2_32:
case BRD_ONBOARDRS:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ONB_MEMSIZE;
brdp->pagesize = ONB_ATPAGESIZE;
brdp->init = stli_onbinit;
break;
case BRD_ONBOARDE:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ONB_EIMEMSIZE;
brdp->pagesize = ONB_EIPAGESIZE;
brdp->init = stli_onbeinit;
case BRD_BRUMBY4:
case BRD_BRUMBY8:
case BRD_BRUMBY16:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = BBY_MEMSIZE;
brdp->pagesize = BBY_PAGESIZE;
brdp->init = stli_bbyinit;
break;
case BRD_STALLION:
- brdp->iobase = confp->ioaddr1;
- brdp->membase = (void *) confp->memaddr;
+ brdp->membase = (void *) brdp->memaddr;
brdp->memsize = STAL_MEMSIZE;
brdp->pagesize = STAL_PAGESIZE;
brdp->init = stli_stalinit;
EBRDINIT(brdp);
#if STLI_HIMEMORY
- if (confp->memaddr > 0x100000) {
- brdp->membase = stli_mapbrdmem(confp->memaddr, brdp->memsize);
+ if (brdp->memaddr > 0x100000) {
+ brdp->membase = stli_mapbrdmem(brdp->memaddr, brdp->memsize);
if (brdp->membase == (void *) NULL)
return(-ENOMEM);
}
/*****************************************************************************/
+/*
+ * Probe and initialize the specified board.
+ */
+
+static int stli_brdinit(stlibrd_t *brdp)
+{
+#if DEBUG
+ printk("stli_brdinit(brdp=%x)\n", (int) brdp);
+#endif
+
+ stli_brds[brdp->brdnr] = brdp;
+
+ switch (brdp->brdtype) {
+ case BRD_ECP:
+ case BRD_ECPE:
+ case BRD_ECPMC:
+ stli_initecp(brdp);
+ break;
+ case BRD_ONBOARD:
+ case BRD_ONBOARDE:
+ case BRD_ONBOARD2:
+ case BRD_ONBOARD32:
+ case BRD_ONBOARD2_32:
+ case BRD_ONBOARDRS:
+ case BRD_BRUMBY4:
+ case BRD_BRUMBY8:
+ case BRD_BRUMBY16:
+ case BRD_STALLION:
+ stli_initonb(brdp);
+ break;
+ case BRD_EASYIO:
+ case BRD_ECH:
+ case BRD_ECHMC:
+ case BRD_ECHPCI:
+ printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
+ return(ENODEV);
+ default:
+ printk("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype);
+ return(ENODEV);
+ }
+
+ if ((brdp->state & BST_FOUND) == 0) {
+ printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr);
+ return(ENODEV);
+ }
+
+ stli_initports(brdp);
+ printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->memaddr, brdp->nrpanels, brdp->nrports);
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Probe around trying to find where the EISA boards shared memory
+ * might be. This is a bit if hack, but it is the best we can do.
+ */
+
+static int stli_eisamemprobe(stlibrd_t *brdp)
+{
+ cdkecpsig_t ecpsig, *ecpsigp;
+ cdkonbsig_t onbsig, *onbsigp;
+ int i, foundit;
+
+#if DEBUG
+ printk("stli_eisamemprobe(brdp=%x)\n", (int) brdp);
+#endif
+
+/*
+ * First up we reset the board, to get it into a known state. There
+ * is only 2 board types here we need to worry about. Don;t use the
+ * standard board init routine here, it programs up the shared
+ * memopry address, and we don't know it yet...
+ */
+ if (brdp->brdtype == BRD_ECPE) {
+ outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
+ outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
+ udelay(10);
+ outb(ECP_EIDISABLE, (brdp->iobase + ECP_EICONFR));
+ udelay(500);
+ stli_ecpeienable(brdp);
+ } else if (brdp->brdtype == BRD_ONBOARDE) {
+ outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
+ outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
+ udelay(10);
+ outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
+ for (i = 0; (i < 100); i++)
+ udelay(1000);
+ outb(0x1, brdp->iobase);
+ udelay(1000);
+ stli_onbeenable(brdp);
+ } else {
+ return(-ENODEV);
+ }
+
+ foundit = 0;
+ brdp->memsize = ECP_MEMSIZE;
+
+/*
+ * Board shared memory is enabled, so now we have a poke around and
+ * see if we can find it.
+ */
+ for (i = 0; (i < stli_eisamempsize); i++) {
+ brdp->memaddr = stli_eisamemprobeaddrs[i];
+ brdp->membase = (void *) brdp->memaddr;
+#if STLI_HIMEMORY
+ if (brdp->memaddr > 0x100000) {
+ brdp->membase = stli_mapbrdmem(brdp->memaddr, brdp->memsize);
+ if (brdp->membase == (void *) NULL)
+ continue;
+ }
+#endif
+ if (brdp->brdtype == BRD_ECPE) {
+ ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp, CDK_SIGADDR, __LINE__);
+ memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
+ if (ecpsig.magic == ECP_MAGIC)
+ foundit = 1;
+ } else {
+ onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp, CDK_SIGADDR, __LINE__);
+ memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t));
+ if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) &&
+ (onbsig.magic2 == ONB_MAGIC2) && (onbsig.magic3 == ONB_MAGIC3))
+ foundit = 1;
+ }
+#if STLI_HIMEMORY
+ if (brdp->memaddr >= 0x100000)
+ vfree(brdp->membase);
+#endif
+ if (foundit)
+ break;
+ }
+
+/*
+ * Regardless of whether we found the shared memory or not we must
+ * disable the region. After that return success or failure.
+ */
+ if (brdp->brdtype == BRD_ECPE)
+ stli_ecpeidisable(brdp);
+ else
+ stli_onbedisable(brdp);
+
+ if (! foundit) {
+ brdp->memaddr = 0;
+ brdp->membase = 0;
+ printk("STALLION: failed to probe shared memory region for %s in EISA slot=%d\n", stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
+ return(-ENODEV);
+ }
+ return(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Probe around and try to find any EISA boards in system. The biggest
+ * problem here is finding out what memory address is associated with
+ * an EISA board after it is found. The registers of the ECPE and
+ * ONboardE are not readable - so we can't read them from there. We
+ * don't have access to the EISA CMOS (or EISA BIOS) so we don't
+ * actually have any way to find out the real value. The best we can
+ * do is go probing around in the usual places hoping we can find it.
+ */
+
+static int stli_findeisabrds()
+{
+ stlibrd_t *brdp;
+ unsigned int iobase, eid;
+ int i;
+
+#if DEBUG
+ printk("stli_findeisabrds()\n");
+#endif
+
+/*
+ * Firstly check if this is an EISA system. Do this by probing for
+ * the system board EISA ID. If this is not an EISA system then
+ * don't bother going any further!
+ */
+ outb(0xff, 0xc80);
+ if (inb(0xc80) == 0xff)
+ return(0);
+
+/*
+ * Looks like an EISA system, so go searching for EISA boards.
+ */
+ for (iobase = 0x1000; (iobase <= 0xc000); iobase += 0x1000) {
+ outb(0xff, (iobase + 0xc80));
+ eid = inb(iobase + 0xc80);
+ eid |= inb(iobase + 0xc81) << 8;
+ if (eid != STL_EISAID)
+ continue;
+
+/*
+ * We have found a board. Need to check if this board was
+ * statically configured already (just in case!).
+ */
+ for (i = 0; (i < STL_MAXBRDS); i++) {
+ brdp = stli_brds[i];
+ if (brdp == (stlibrd_t *) NULL)
+ continue;
+ if (brdp->iobase == iobase)
+ break;
+ }
+ if (i < STL_MAXBRDS)
+ continue;
+
+/*
+ * Check that we have room for this new board in our board
+ * info table.
+ */
+ if (stli_nrbrds >= STL_MAXBRDS) {
+ printk("STALLION: no room for more probed boards, maximum supported %d\n", STL_MAXBRDS);
+ break;
+ }
+
+/*
+ * We have found a Stallion board and it is not configured already.
+ * Allocate a board structure and initialize it.
+ */
+ brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
+ if (brdp == (stlibrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+ return(-ENOMEM);
+ }
+ memset(brdp, 0, sizeof(stlibrd_t));
+
+ brdp->brdnr = stli_nrbrds++;
+ eid = inb(iobase + 0xc82);
+ if (eid == ECP_EISAID)
+ brdp->brdtype = BRD_ECPE;
+ else if (eid == ONB_EISAID)
+ brdp->brdtype = BRD_ONBOARDE;
+ else
+ brdp->brdtype = BRD_UNKNOWN;
+ brdp->iobase = iobase;
+ outb(0x1, (iobase + 0xc84));
+ if (stli_eisamemprobe(brdp))
+ outb(0, (iobase + 0xc84));
+ stli_brdinit(brdp);
+ }
+
+ return(0);
+}
+
+/*****************************************************************************/
+
/*
* Scan through all the boards in the configuration and see what we
* can find.
*/
-static int stli_brdinit()
+static int stli_initbrds()
{
- stlibrd_t *brdp;
+ stlibrd_t *brdp, *nxtbrdp;
stlconf_t *confp;
int i, j;
#if DEBUG
- printk("stli_brdinit()\n");
+ printk("stli_initbrds()\n");
#endif
- if (stli_nrbrds > STL_MAXBRDS)
- return(-EINVAL);
-
- stli_brds = (stlibrd_t *) stli_memalloc((sizeof(stlibrd_t) * stli_nrbrds));
- if (stli_brds == (stlibrd_t *) NULL) {
- printk("STALLION: failed to allocate board structures\n");
- return(-ENOMEM);
+ if (stli_nrbrds > STL_MAXBRDS) {
+ printk("STALLION: too many boards in configuration table, truncating to %d\n", STL_MAXBRDS);
+ stli_nrbrds = STL_MAXBRDS;
}
- memset(stli_brds, 0, (sizeof(stlibrd_t) * stli_nrbrds));
+/*
+ * Firstly scan the list of static boards configured. Allocate
+ * resources and initialize the boards as found.
+ */
for (i = 0; (i < stli_nrbrds); i++) {
- brdp = &stli_brds[i];
confp = &stli_brdconf[i];
- brdp->brdnr = i;
- brdp->brdtype = confp->brdtype;
-
- switch (confp->brdtype) {
- case BRD_ECP:
- case BRD_ECPE:
- case BRD_ECPMC:
- stli_initecp(brdp, confp);
- break;
- case BRD_ONBOARD:
- case BRD_ONBOARDE:
- case BRD_ONBOARD2:
- case BRD_ONBOARD32:
- case BRD_ONBOARD2_32:
- case BRD_ONBOARDRS:
- case BRD_BRUMBY4:
- case BRD_BRUMBY8:
- case BRD_BRUMBY16:
- case BRD_STALLION:
- stli_initonb(brdp, confp);
- break;
- case BRD_EASYIO:
- case BRD_ECH:
- case BRD_ECHMC:
- case BRD_ECHPCI:
- printk("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]);
- break;
- default:
- printk("STALLION: unit=%d is unknown board type=%d\n", i, confp->brdtype);
- break;
- }
-
- if ((brdp->state & BST_FOUND) == 0) {
- printk("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr);
- continue;
+ brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
+ if (brdp == (stlibrd_t *) NULL) {
+ printk("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t));
+ return(-ENOMEM);
}
+ memset(brdp, 0, sizeof(stlibrd_t));
- stli_initports(brdp);
- printk("STALLION: %s found, unit=%d io=%x mem=%x nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype], i, confp->ioaddr1, (int) confp->memaddr, brdp->nrpanels, brdp->nrports);
+ brdp->brdnr = i;
+ brdp->brdtype = confp->brdtype;
+ brdp->iobase = confp->ioaddr1;
+ brdp->memaddr = confp->memaddr;
+ stli_brdinit(brdp);
}
+/*
+ * Now go probing for EISA boards if enabled.
+ */
+ if (stli_eisaprobe)
+ stli_findeisabrds();
+
/*
* All found boards are initialized. Now for a little optimization, if
* no boards are sharing the "shared memory" regions then we can just
stli_shared = 0;
if (stli_nrbrds > 1) {
for (i = 0; (i < stli_nrbrds); i++) {
+ brdp = stli_brds[i];
+ if (brdp == (stlibrd_t *) NULL)
+ continue;
for (j = i + 1; (j < stli_nrbrds); j++) {
- brdp = &stli_brds[i];
- if ((brdp->membase >= stli_brds[j].membase) &&
- (brdp->membase <= (stli_brds[j].membase + stli_brds[j].memsize - 1))) {
+ nxtbrdp = stli_brds[j];
+ if (nxtbrdp == (stlibrd_t *) NULL)
+ continue;
+ if ((brdp->membase >= nxtbrdp->membase) && (brdp->membase <= (nxtbrdp->membase + nxtbrdp->memsize - 1))) {
stli_shared++;
break;
}
if (stli_shared == 0) {
for (i = 0; (i < stli_nrbrds); i++) {
- brdp = &stli_brds[i];
+ brdp = stli_brds[i];
+ if (brdp == (stlibrd_t *) NULL)
+ continue;
if (brdp->state & BST_FOUND) {
EBRDENABLE(brdp);
brdp->enable = NULL;
brdnr = MINOR(ip->i_rdev);
if (brdnr >= stli_nrbrds)
return(-ENODEV);
- brdp = &stli_brds[brdnr];
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(-ENODEV);
if (brdp->state == 0)
return(-ENODEV);
if (fp->f_pos >= brdp->memsize)
brdnr = MINOR(ip->i_rdev);
if (brdnr >= stli_nrbrds)
return(-ENODEV);
- brdp = &stli_brds[brdnr];
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(-ENODEV);
if (brdp->state == 0)
return(-ENODEV);
if (fp->f_pos >= brdp->memsize)
brdnr = MINOR(ip->i_rdev);
if (brdnr >= stli_nrbrds)
return(-ENODEV);
- brdp = &stli_brds[brdnr];
+ brdp = stli_brds[brdnr];
+ if (brdp == (stlibrd_t *) NULL)
+ return(-ENODEV);
if (brdp->state == 0)
return(-ENODEV);
/*****************************************************************************/
-int stli_init(void)
+int stli_init()
{
printk("%s: version %s\n", stli_drvname, stli_drvversion);
- stli_brdinit();
+ stli_initbrds();
/*
* Allocate a temporary write buffer.
if (tty_register_driver(&stli_callout))
printk("STALLION: failed to register callout driver\n");
- return 0;
+ return(0);
}
/*****************************************************************************/
/*
* stallion.c -- stallion multiport serial driver.
*
- * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
*
* This code is loosely based on the Linux serial driver, written by
* Linus Torvalds, Theodore T'so and others.
/*****************************************************************************/
#include <linux/module.h>
-
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/config.h> /* for CONFIG_PCI */
-
#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
* all the local structures required by a serial tty driver.
*/
static char *stl_drvname = "Stallion Multiport Serial Driver";
-static char *stl_drvversion = "1.0.0";
+static char *stl_drvversion = "1.0.2";
static char *stl_serialname = "ttyE";
static char *stl_calloutname = "cue";
int init_module(void);
void cleanup_module(void);
#endif
-static void *stl_memalloc(int len);
int stl_init(void);
static int stl_open(struct tty_struct *tty, struct file *filp);
static void stl_delay(int len);
static void stl_intr(int irq, struct pt_regs *regs);
static void stl_offintr(void *private);
+static void *stl_memalloc(int len);
#ifdef CONFIG_PCI
static int stl_findpcibrds(void);
#ifdef MODULE
/*
- * Use the kernel version number for modules.
+ * Loadable module initialization stuff.
*/
+
int init_module()
{
unsigned long flags;
/*****************************************************************************/
/*
- * Local memory allocation routines. These are used so we can deal with
- * memory allocation at init time and during run-time in a consistent
- * way. Everbody just calls the stl_memalloc routine to allocate
- * memory and it will do the right thing.
+ * Local driver kernel memory allocation routine.
*/
static void *stl_memalloc(int len)
{
- return (void *) kmalloc(len, GFP_KERNEL);
+ return((void *) kmalloc(len, GFP_KERNEL));
}
/*****************************************************************************/
}
brdp->irq = irq;
-#if 0
- ioaddr = 0x0c000001;
- if ((rc = pcibios_write_config_dword(busnr, devnr, 0x40, ioaddr))) {
- printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
- continue;
- }
- if ((rc = pcibios_write_config_dword(busnr, devnr, 0x48, ioaddr))) {
- printk("STALLION: failed to write register on PCI board, errno=%x\n", rc);
- continue;
- }
-#endif
-
stl_brdinit(brdp);
}
}
if (tty_register_driver(&stl_callout))
printk("STALLION: failed to register callout driver\n");
- return 0;
+ return(0);
}
/*****************************************************************************/
-/* arcnet.c
+/* arcnet.c:
Written 1994-1996 by Avery Pennarun,
derived from skeleton.c by Donald Becker.
This is ONLY A SUMMARY. The complete ChangeLog is available in
the full Linux-ARCnet package.
+
+ v2.41 ALPHA (96/02/10)
+ - Incorporated changes someone made to arcnet_setup in 1.3.60.
+ - Increased reset timeout to 3/10 of a second because some cards
+ are too slow.
+ - Removed a useless delay from autoprobe stage 4; I wonder
+ how that got there! (oops)
+ - If FAST_IFCONFIG is defined, don't reset the card during
+ arcnet_open; rather, do it in arcnet_close but don't delay.
+ This speeds up calls to ifconfig up/down. (Thanks to Vojtech
+ for the idea to speed this up.)
+ - If FAST_PROBE is defined, don't bother to reset the card in
+ autoprobe Stage 5 when there is only one io port and one shmem
+ left in the list. They "obviously" correspond to each other.
+ Another good idea from Vojtech.
+
+ v2.40 ALPHA (96/02/03)
+ - Checked the RECON flag last in the interrupt handler (when
+ enabled) so the flag will show up in "status" when reporting
+ other errors. (I had a cabling problem that was hard to notice
+ until I saw the huge RECON count in /proc/net/dev.)
+ - Moved "IRQ for unknown device" message to D_DURING.
+ - "transmit timed out" and "not acknowledged" messages are
+ now D_EXTRA, because they very commonly happen when things
+ are working fine. "transmit timed out" due to missed IRQ's
+ is still D_NORMAL, because it's probably a bug.
+ - "Transmit timed out" messages now include destination station id.
+ - The virtual arc0e and arc0s devices can now be disabled.
+ Massive (but simple) code rearrangement to support this with
+ fewer ifdef's.
+ - SLOW_XMIT_COPY option so fast computers don't hog the bus. It's
+ weird, but it works.
+ - Finally redesigned autoprobe after some discussion with Vojtech
+ Pavlik <Vojtech.Pavlik@st.mff.cuni.cz>. It should be much safer
+ and more reliable now, I think. We now probe several more io
+ ports and many more shmem addresses.
+ - Rearranged D_* debugging flags slightly. Watch out! There is
+ now a new flag, disabled by default, that will tell you the
+ reason a particular port or shmem is discarded from the list.
v2.30 ALPHA (96/01/10)
- Abandoned support for Linux 1.2 to simplify coding and allow me
TO DO: (it it just me, or does this list only get longer?)
- - Test in systems with NON-ARCnet network cards, just to see if
- autoprobe kills anything. Currently, we do cause some NE2000's
- to die. Autoprobe is also way too slow and verbose, particularly
- if there aren't any ARCnet cards in the system.
- - Rewrite autoprobe. Try the Linux-generic-autoirq function instead
- of the net-specific one.
- - Make sure RESET flag is cleared during an IRQ even if dev->start==0;
- mainly a portability fix. This will confuse autoprobe a bit, so
- test that too.
+ - Make sure autoprobe puts TESTvalue back into shmem locations
+ that it determines aren't ARCnet cards.
+ - Probe for multiple devices in one shot (there's supposed to
+ be a way to do that now, but I can't remember what it is!)
+ - Support printk's priority levels.
+ - Have people test the new autoprobe a bit more - then remove
+ D_INIT from the default debug level.
+ - Move the SKB and memory dump code into separate functions.
+ - Debug level should be changed with a system call, not a hack to
+ the "metric" flag.
- What about cards with shared memory that can be "turned off?"
(or that have none at all, like the SMC PC500longboard)
- Autoconfigure PDI5xxPlus cards. (I now have a PDI508Plus to play
- with temporarily)
+ with temporarily.) Update: yes, the Pure Data config program
+ for DOS works fine, but the PDI508Plus I have doesn't! :)
- Try to implement promiscuous (receive-all-packets) mode available
on some newer cards with COM20020 and similar chips. I don't have
one, but SMC sent me the specs.
- Add support for the new 1.3.x IP header cache features.
- - Support printk's priority levels.
- Make arcnetE_send_packet use arcnet_prepare_tx for loading the
packet into ARCnet memory.
- - Move the SKB and memory dump code into separate functions.
- ATA protocol support??
- VINES TCP/IP encapsulation?? (info needed)
*/
static const char *version =
- "arcnet.c: v2.30 ALPHA 96/01/10 Avery Pennarun <apenwarr@foxnet.net>\n";
+ "arcnet.c: v2.41 ALPHA 96/02/10 Avery Pennarun <apenwarr@foxnet.net>\n";
/**************************************************************************/
+/* This driver supports three different "virtual ARCnet devices" running
+ * on the same adapter, in order to communicate with various different
+ * TCP/IP-over-ARCnet implementations. They are:
+ * arc0 - RFC1201 Internet-standard protocol
+ * arc0e - Ethernet-encapsulation protocol (as used by Windows)
+ * arc0s - "Simpler" (but outdated) RFC1051 internet standard.
+ *
+ * arc0e and arc0s are created when arc0 is ifconfig'ed up. You can disable
+ * either or both of them by undefining CONFIG_ARCNET_ETH and/or
+ * CONFIG_ARCNET_1051.
+ */
+#define CONFIG_ARCNET_ETH
+#define CONFIG_ARCNET_1051
+
+/* On a fast computer, the buffer copy from memory to the ARCnet card during
+ * a transmit can hog the bus just a little too long. SLOW_XMIT_COPY
+ * replaces the fast memcpy() with a slower for() loop that seems to solve
+ * my problems with ftape.
+ *
+ * Probably a better solution would be to use memcpy_toio (more portable
+ * anyway) and modify that routine to support REALLY_SLOW_IO-style
+ * defines; ARCnet probably is not the only driver that can screw up an
+ * ftape DMA transfer.
+ *
+ * Turn this off if you don't have timing-sensitive DMA (ie. a tape drive)
+ * and would like the little bit of speed back. It's on by default because
+ * - trust me - it's very difficult to figure out that you need it!
+ */
+#define SLOW_XMIT_COPY
+
/* The card sends the reconfiguration signal when it loses the connection to
* the rest of its network. It is a 'Hello, is anybody there?' cry. This
* usually happens when a new computer on the network is powered on or when
*/
#define RECON_THRESHOLD 30
-/* Define this if you want to make sure transmitted packets are "acknowledged"
- * by the destination host, as long as they're not to the broadcast address.
- *
- * That way, if one segment of a split packet doesn't get through, it can be
- * resent immediately rather than confusing the other end. We don't
- * actually do that yet, though.
- *
- * Disable this to return to 1.02-style behaviour, if you have problems.
- */
-#define VERIFY_ACK
-
/* Define this to the minimum "timeout" value. If a transmit takes longer
* than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large
* network, or one with heavy network traffic, this timeout may need to be
- * increased.
+ * increased. The larger it is, though, the longer it will be between
+ * necessary transmits - don't set this too large.
*/
#define TX_TIMEOUT 20
+/* Define this to speed up the autoprobe by assuming if only one io port and
+ * shmem are left in the list at Stage 5, they must correspond to each
+ * other.
+ *
+ * This is undefined by default because it might not always be true. Speed
+ * demons can turn it on - I think it should be fine if you only have one
+ * ARCnet card installed.
+ *
+ * If no ARCnet cards are installed, this delay never happens anyway and thus
+ * the option has no effect.
+ */
+#undef FAST_PROBE
+
+/* Define this to speed up "ifconfig up" by moving the card reset command
+ * around. This is a new option in 2.41 ALPHA. If it causes problems,
+ * undefine this to get the old behaviour; then send me email, because if
+ * there are no problems, this option will go away very soon.
+ */
+#define FAST_IFCONFIG
+
/* Define this if you want to make it easier to use the "call trace" when
* a kernel NULL pointer assignment occurs. Hopefully unnecessary, most of
* the time. It will make all the function names (and other things) show
* and HOSTNAME is your hostname/ip address
* and then resetting your routes.
*
+ * An ioctl() should be used for this instead, someday.
+ *
* Note: only debug flags included in the ARCNET_DEBUG_MAX define will
* actually be available. GCC will (at least, GCC 2.7.0 will) notice
* lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize
* them out.
*/
-#define D_NORMAL 1 /* D_NORMAL normal operational info */
-#define D_INIT 2 /* D_INIT show init/probe messages */
-#define D_EXTRA 4 /* D_EXTRA extra information */
-/* debug levels past this point give LOTS of output! */
-#define D_DURING 8 /* D_DURING during normal use (irq's) */
-#define D_TX 16 /* D_TX show tx packets */
-#define D_RX 32 /* D_RX show rx packets */
-#define D_SKB 64 /* D_SKB dump skb's */
+#define D_NORMAL 1 /* important operational info */
+#define D_EXTRA 2 /* useful, but non-vital information */
+#define D_INIT 4 /* show init/probe messages */
+#define D_INIT_REASONS 8 /* show reasons for discarding probes */
+/* debug levels below give LOTS of output during normal operation! */
+#define D_DURING 16 /* trace operations (including irq's) */
+#define D_TX 32 /* show tx packets */
+#define D_RX 64 /* show rx packets */
+#define D_SKB 128 /* show skb's */
#ifndef ARCNET_DEBUG_MAX
#define ARCNET_DEBUG_MAX (~0) /* enable ALL debug messages */
-/*#define ARCNET_DEBUG_MAX (D_NORMAL|D_INIT|D_EXTRA) */
+/*#define ARCNET_DEBUG_MAX (D_NORMAL|D_EXTRA|D_INIT|D_INIT_REASONS) */
/*#define ARCNET_DEBUG_MAX 0 */ /* enable NO debug messages */
#endif
#ifndef ARCNET_DEBUG
-#define ARCNET_DEBUG (D_NORMAL|D_INIT|D_EXTRA)
-/*#define ARCNET_DEBUG (D_NORMAL|D_INIT)*/
+#define ARCNET_DEBUG (D_NORMAL|D_EXTRA|D_INIT)
+/*#define ARCNET_DEBUG (D_NORMAL)*/
#endif
int arcnet_debug = ARCNET_DEBUG;
/* macros to simplify debug checking */
#define BUGLVL(x) if ((ARCNET_DEBUG_MAX)&arcnet_debug&(x))
-#define BUGMSG(x,msg,args...) BUGLVL(x) printk("%6s: " msg, dev->name , ## args);
-
-/* Some useful multiprotocol macros */
-#define TBUSY lp->adev->tbusy \
- =lp->edev->tbusy \
- =lp->sdev->tbusy
-#define IF_TBUSY (lp->adev->tbusy \
- || lp->edev->tbusy \
- || lp->sdev->tbusy)
-
-#define INTERRUPT lp->adev->interrupt \
- =lp->edev->interrupt \
- =lp->sdev->interrupt
-#define IF_INTERRUPT (lp->adev->interrupt \
- || lp->edev->interrupt \
- || lp->sdev->interrupt)
-
-#define START lp->adev->start \
- =lp->edev->start \
- =lp->sdev->start
-
+#define BUGMSG2(x,msg,args...) BUGLVL(x) printk(msg , ## args)
+#define BUGMSG(x,msg,args...) BUGMSG2(x,"%6s: " msg, dev->name , ## args)
+
+/* Some useful multiprotocol macros. The idea here is that GCC will
+ * optimize away multiple tests or assignments to lp->adev. Relying on this
+ * results in the cleanest mess possible.
+ */
+#define ADEV lp->adev
+
+#ifdef CONFIG_ARCNET_ETH
+ #define EDEV lp->edev
+#else
+ #define EDEV lp->adev
+#endif
+
+#ifdef CONFIG_ARCNET_1051
+ #define SDEV lp->sdev
+#else
+ #define SDEV lp->adev
+#endif
+
+#define TBUSY ADEV->tbusy=EDEV->tbusy=SDEV->tbusy
+#define IF_TBUSY (ADEV->tbusy||EDEV->tbusy||SDEV->tbusy)
+
+#define INTERRUPT ADEV->interrupt=EDEV->interrupt=SDEV->interrupt
+#define IF_INTERRUPT (ADEV->interrupt||EDEV->interrupt||SDEV->interrupt)
+
+#define START ADEV->start=EDEV->start=SDEV->start
+
/* The number of low I/O ports used by the ethercard. */
#define ARCNET_TOTAL_SIZE 16
#define SETMASK outb(lp->intmask,INTMASK);
- /* Time needed for various things (in clock ticks, 1/100 sec) */
- /* We mostly don't bother with these - watch out. */
-#define RESETtime 30 /* reset */
+ /* Time needed to reset the card - in jiffies. This works on my SMC
+ * PC100. I can't find a reference that tells me just how long I
+ * should wait.
+ */
+#define RESETtime (HZ * 3 / 10) /* reset */
/* these are the max/min lengths of packet data. (including
* ClientData header)
#define RECONflag 0x04 /* system reconfigured */
#define TESTflag 0x08 /* test flag */
#define RESETflag 0x10 /* power-on-reset */
-#define RES1flag 0x20 /* unused */
-#define RES2flag 0x40 /* unused */
+#define RES1flag 0x20 /* reserved - usually set by jumper */
+#define RES2flag 0x40 /* reserved - usually set by jumper */
#define NORXflag 0x80 /* receiver inhibited */
/* in the command register, the following bits have these meanings:
intmask; /* current value of INTMASK register */
short intx, /* in TX routine? */
in_txhandler, /* in TX_IRQ handler? */
- sending; /* transmit in progress? */
-
-#ifdef VERIFY_ACK
- short lastload_dest, /* can last loaded packet be acked? */
- lasttrans_dest; /* can last TX'd packet be acked? */
-#endif
+ sending, /* transmit in progress? */
+ lastload_dest, /* can last loaded packet be acked? */
+ lasttrans_dest; /* can last TX'd packet be acked? */
#if defined(DETECT_RECONFIGS) && defined(RECON_THRESHOLD)
time_t first_recon, /* time of "first" RECON message to count */
struct Incoming incoming[256]; /* one from each address */
struct Outgoing outgoing; /* packet currently being sent */
- struct device *adev, /* RFC1201 protocol device */
- *edev, /* Ethernet-Encap device */
- *sdev; /* RFC1051 protocol device */
+ struct device *adev; /* RFC1201 protocol device */
+
+#ifdef CONFIG_ARCNET_ETH
+ struct device *edev; /* Ethernet-Encap device */
+#endif
+
+#ifdef CONFIG_ARCNET_1051
+ struct device *sdev; /* RFC1051 protocol device */
+#endif
};
/* Index to functions, as function prototypes. */
extern int arcnet_probe(struct device *dev);
-static int arcnet_memprobe(struct device *dev,u_char *addr);
-static int arcnet_ioprobe(struct device *dev, short ioaddr);
+static int arcnet_found(struct device *dev,int port,int airq,u_long shmem);
static void arcnet_setup(struct device *dev);
-static int arcnetE_init(struct device *dev);
-static int arcnetS_init(struct device *dev);
-
static int arcnet_open(struct device *dev);
static int arcnet_close(struct device *dev);
-static int arcnet_reset(struct device *dev);
+static int arcnet_reset(struct device *dev,int reset_delay);
static int arcnet_send_packet_bad(struct sk_buff *skb,struct device *dev);
static int arcnetA_send_packet(struct sk_buff *skb, struct device *dev);
-static int arcnetE_send_packet(struct sk_buff *skb, struct device *dev);
-static int arcnetS_send_packet(struct sk_buff *skb, struct device *dev);
static void arcnetA_continue_tx(struct device *dev);
static void arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
char *data,int length,int daddr,int exceptA);
static void arcnet_rx(struct device *dev,int recbuf);
static void arcnetA_rx(struct device *dev,u_char *buf,
int length,u_char saddr, u_char daddr);
-static void arcnetE_rx(struct device *dev,u_char *arcsoft,
- int length,u_char saddr, u_char daddr);
-static void arcnetS_rx(struct device *dev,u_char *buf,
- int length,u_char saddr, u_char daddr);
static struct enet_statistics *arcnet_get_stats(struct device *dev);
-/*
-static void set_multicast_list(struct device *dev);
-*/
- /* functions for header/arp/etc building */
+
int arcnetA_header(struct sk_buff *skb,struct device *dev,
unsigned short type,void *daddr,void *saddr,unsigned len);
-int arcnetS_header(struct sk_buff *skb,struct device *dev,
- unsigned short type,void *daddr,void *saddr,unsigned len);
int arcnetA_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
struct sk_buff *skb);
+unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev);
+
+#ifdef CONFIG_ARCNET_ETH
+ /* functions specific to Ethernet-Encap */
+static int arcnetE_init(struct device *dev);
+static int arcnetE_send_packet(struct sk_buff *skb, struct device *dev);
+static void arcnetE_rx(struct device *dev,u_char *arcsoft,
+ int length,u_char saddr, u_char daddr);
+#endif
+
+#ifdef CONFIG_ARCNET_1051
+ /* functions specific to RFC1051 */
+static int arcnetS_init(struct device *dev);
+static int arcnetS_send_packet(struct sk_buff *skb, struct device *dev);
+static void arcnetS_rx(struct device *dev,u_char *buf,
+ int length,u_char saddr, u_char daddr);
+int arcnetS_header(struct sk_buff *skb,struct device *dev,
+ unsigned short type,void *daddr,void *saddr,unsigned len);
int arcnetS_rebuild_header(void *eth,struct device *dev,unsigned long raddr,
struct sk_buff *skb);
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev);
unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev);
+#endif
#ifdef MODULE
int init_module(void);
#define tx_done(dev) 1
-#define JIFFER(time) for (delayval=0; delayval<(time*10); delayval++) \
- udelay(1000);
+#define JIFFER(time) for (delayval=jiffies+time; jiffies<delayval;) ;
/****************************************************************************
* *
****************************************************************************/
-/* Check for a network adaptor of this type, and return '0' if one exists.
+/* Check for an ARCnet network adaptor, and return '0' if one exists.
* If dev->base_addr == 0, probe all likely locations.
* If dev->base_addr == 1, always return failure.
* If dev->base_addr == 2, allocate space for the device and return success
- * (detachable devices only).
+ * (detachable devices only).
+ *
+ * NOTE: the list of possible ports/shmems is static, so it is retained
+ * across calls to arcnet_probe. So, if more than one ARCnet probe is made,
+ * values that were discarded once will not even be tried again.
*/
-int
-arcnet_probe(struct device *dev)
+int arcnet_probe(struct device *dev)
{
- /* I refuse to probe anything less than 0x200, because anyone using
- * an address like that should probably be shot.
- */
- int *port, ports[] = {/* first the suggested values! */
- 0x300,0x2E0,0x2F0,0x2D0,
- /* ...now everything else possible. */
- 0x200,0x210,0x220,0x230,0x240,0x250,0x260,0x270,
- 0x280,0x290,0x2a0,0x2b0,0x2c0,
- 0x310,0x320,0x330,0x340,0x350,0x360,0x370,
- 0x380,0x390,0x3a0,/* video ports, */0x3e0,0x3f0,
- /* a null ends the list */
- 0};
- /* I'm not going to probe below 0xA0000 either, for similar reasons.
- */
- unsigned long *addr, addrs[] = {0xD0000,0xE0000,0xA0000,0xB0000,
- 0xC0000,0xF0000,
- /* from <mdrejhon@magi.com> */
- 0xE1000,
- 0xDD000,0xDC000,
- 0xD9000,0xD8000,0xD5000,0xD4000,0xD1000,
- 0xCD000,0xCC000,
- 0xC9000,0xC8000,0xC5000,0xC4000,
- /* terminator */
- 0};
- int base_addr=dev->base_addr, status=0;
- int delayval,ioaddr;
- struct arcnet_local *lp;
+ static int init_once = 0;
+ static int ports[(0x3f0 - 0x200) / 16 + 1];
+ static u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1];
+ static int numports=sizeof(ports)/sizeof(ports[0]),
+ numshmems=sizeof(shmems)/sizeof(shmems[0]);
- printk(version);
+ int count,status,delayval,ioaddr,numprint,airq;
+ unsigned long airqmask;
+ int *port;
+ u_long *shmem;
-#if 1
- BUGLVL(D_NORMAL)
+ if (!init_once)
{
- printk("arcnet: ***\n");
- printk("arcnet: * Read README.arcnet for important release notes!\n");
- printk("arcnet: *\n");
- printk("arcnet: * This is an ALPHA version! (Last stable release: v2.00) E-mail me if\n");
- printk("arcnet: * you have any questions, comments, or bug reports.\n");
- printk("arcnet: ***\n");
+ for (count=0x200; count<=0x3f0; count+=16)
+ ports[(count-0x200)/16] = count;
+ for (count=0xA0000; count<=0xFF800; count+=2048)
+ shmems[(count-0xA0000)/2048] = count;
}
-#else
+ else
+ init_once=1;
+
+ BUGLVL(D_NORMAL) printk(version);
+
+ BUGMSG(D_DURING,"space used for probe buffers: %d+%d=%d bytes\n",
+ sizeof(ports),sizeof(shmems),
+ sizeof(ports)+sizeof(shmems));
+
+
+#if 1
BUGLVL(D_EXTRA)
{
printk("arcnet: ***\n");
- printk("arcnet: * Read README.arcnet for important release notes!\n");
+ printk("arcnet: * Read arcnet.txt for important release notes!\n");
printk("arcnet: *\n");
- printk("arcnet: * This version should be stable, but please e-mail\n");
- printk("arcnet: * me if you have any questions or comments.\n");
+ printk("arcnet: * This is an ALPHA version! (Last stable release: v2.22) E-mail me if\n");
+ printk("arcnet: * you have any questions, comments, or bug reports.\n");
printk("arcnet: ***\n");
}
#endif
BUGMSG(D_INIT,"given: base %lXh, IRQ %d, shmem %lXh\n",
dev->base_addr,dev->irq,dev->mem_start);
- if (base_addr > 0x1ff) /* Check a single specified location. */
- status=arcnet_ioprobe(dev, base_addr);
- else if (base_addr > 0) /* Don't probe at all. */
+ if (dev->base_addr > 0x1ff) /* Check a single specified port */
+ {
+ ports[0]=dev->base_addr;
+ numports=1;
+ }
+ else if (dev->base_addr > 0) /* Don't probe at all. */
return -ENXIO;
- else for (port = &ports[0]; *port; port++)
+
+ if (dev->mem_start)
+ {
+ shmems[0]=dev->mem_start;
+ numshmems=1;
+ }
+
+
+ /* Stage 1: abandon any reserved ports, or ones with status==0xFF
+ * (empty), and reset any others by reading the reset port.
+ */
+ BUGMSG(D_INIT,"Stage 1: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
{
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 1: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+
+ ioaddr=*port;
+
if (check_region(*port, ARCNET_TOTAL_SIZE))
{
- BUGMSG(D_INIT,"Skipping %Xh because of check_region...\n",
- *port);
+ BUGMSG2(D_INIT_REASONS,"(check_region)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
continue;
}
+
+ if (inb(STATUS) == 0xFF)
+ {
+ BUGMSG2(D_INIT_REASONS,"(empty)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ inb(RESET); /* begin resetting card */
- status=arcnet_ioprobe(dev, *port);
- if (!status) break;
+ BUGMSG2(D_INIT_REASONS,"\n");
+ BUGMSG(D_INIT_REASONS,"Stage 1: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
}
+ BUGMSG2(D_INIT,"\n");
- if (status) return status;
+ if (!numports)
+ {
+ BUGMSG(D_INIT,"Stage 1: failed. No ARCnet cards found.\n");
+ return -ENODEV;
+ }
- /* arcnet_ioprobe set this */
- ioaddr=dev->base_addr;
- /* ioprobe turned out okay. Now reset the card, so we have
- * a test byte to look for in shared memory...
+ /* Stage 2: we have now reset any possible ARCnet cards, so we can't
+ * do anything until they finish. If D_INIT, print the list of
+ * cards that are left.
*/
- BUGMSG(D_INIT,"ioprobe okay! Waiting for reset...\n");
- inb(RESET);
+ BUGMSG(D_INIT,"Stage 2: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 2: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+ }
+ BUGMSG2(D_INIT,"\n");
JIFFER(RESETtime);
+
+
+ /* Stage 3: abandon any shmem addresses that don't have the signature
+ * 0xD1 byte in the right place, or are read-only.
+ */
+ BUGMSG(D_INIT,"Stage 3: ");
+ numprint=0;
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ {
+ u_char *cptr;
+
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 3: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%lXh ",*shmem);
+
+ cptr=(u_char *)(*shmem);
+
+ if (*cptr != TESTvalue)
+ {
+ BUGMSG2(D_INIT_REASONS,"(mem=%Xh, not %Xh)\n",
+ *cptr,TESTvalue);
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+ shmem--;
+ continue;
+ }
+
+ /* By writing 0x42 to the TESTvalue location, we also make
+ * sure no "mirror" shmem areas show up - if they occur
+ * in another pass through this loop, they will be discarded
+ * because *cptr != TESTvalue.
+ */
+ *cptr=0x42;
+ if (*cptr != 0x42)
+ {
+ BUGMSG2(D_INIT_REASONS,"(read only)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+ shmem--;
+ continue;
+ }
+
+ BUGMSG2(D_INIT_REASONS,"\n");
+ BUGMSG(D_INIT_REASONS,"Stage 3: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ }
+ BUGMSG2(D_INIT,"\n");
- /* okay, now we have to find the shared memory area. */
- BUGMSG(D_INIT,"starting memory probe, given %lXh\n",
- dev->mem_start);
- if (dev->mem_start) /* value given - probe just that one */
+ if (!numshmems)
{
- status=arcnet_memprobe(dev,(u_char *)dev->mem_start);
- if (status) return status;
+ BUGMSG(D_INIT,"Stage 3: failed. No ARCnet cards found.\n");
+ return -ENODEV;
}
- else /* no value given - probe everything */
+
+ /* Stage 4: something of a dummy, to report the shmems that are
+ * still possible after stage 3.
+ */
+ BUGMSG(D_INIT,"Stage 4: ");
+ numprint=0;
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
{
- for (addr = &addrs[0]; *addr; addr++) {
- status=arcnet_memprobe(dev,(u_char *)(*addr));
- if (!status) break;
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 4: ");
+ numprint=1;
}
-
- if (status) return status;
+ BUGMSG2(D_INIT,"%lXh ",*shmem);
}
+ BUGMSG2(D_INIT,"\n");
+
+
+ /* Stage 5: for any ports that have the correct status, can disable
+ * the RESET flag, and (if no irq is given) generate an autoirq,
+ * register an ARCnet device.
+ *
+ * Currently, we can only register one device per probe, so quit
+ * after the first one is found.
+ */
+ BUGMSG(D_INIT,"Stage 5: ");
+ numprint=0;
+ for (port = &ports[0]; port-ports<numports; port++)
+ {
+ numprint++;
+ if (numprint>8)
+ {
+ BUGMSG2(D_INIT,"\n");
+ BUGMSG(D_INIT,"Stage 5: ");
+ numprint=1;
+ }
+ BUGMSG2(D_INIT,"%Xh ",*port);
+
+ ioaddr=*port;
+ status=inb(STATUS);
+
+ if ((status & 0x9F)
+ != (NORXflag|RECONflag|TXFREEflag|RESETflag))
+ {
+ BUGMSG2(D_INIT_REASONS,"(status=%Xh)\n",status);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+
+ outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
+ status=inb(STATUS);
+ if (status & RESETflag)
+ {
+ BUGMSG2(D_INIT_REASONS," (eternal reset, status=%Xh)\n",
+ status);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
- /* now reserve the irq... */
- {
- int irqval = request_irq(dev->irq, &arcnet_interrupt, 0,
- "arcnet");
- if (irqval)
+ /* skip this if an IRQ was given, because maybe we're on a
+ * machine that locks during autoirq!
+ */
+ if (!dev->irq)
+ {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+ airqmask = probe_irq_on();
+ outb(NORXflag,INTMASK);
+ /*udelay(1);*/
+ outb(0,INTMASK);
+ airq = probe_irq_off(airqmask);
+
+ if (airq<=0)
+ {
+ BUGMSG2(D_INIT_REASONS,"(airq=%d)\n",airq);
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
+ continue;
+ }
+ }
+ else
{
- BUGMSG(D_NORMAL,"unable to get IRQ %d (irqval=%d).\n",
- dev->irq, irqval);
- return -EIO;
+ airq=dev->irq;
}
- irq2dev_map[dev->irq]=dev;
+ BUGMSG2(D_INIT,"(%d,", airq);
+
+ /* Everything seems okay. But which shmem, if any, puts
+ * back its signature byte when the card is reset?
+ *
+ * If there are multiple cards installed, there might be
+ * multiple shmems still in the list.
+ */
+#ifdef FAST_PROBE
+ if (numports>1 || numshmems>1)
+ {
+ inb(RESET);
+ JIFFER(RESETtime);
+ }
+ else
+ {
+ *(u_char *)(shmems[0]) = TESTvalue;
+ }
+#else
+ inb(RESET);
+ JIFFER(RESETtime);
+#endif
+
+
+ for (shmem = &shmems[0]; shmem-shmems<numshmems; shmem++)
+ {
+ u_char *cptr;
+ cptr=(u_char *)(*shmem);
+
+ if (*cptr == TESTvalue) /* found one */
+ {
+ BUGMSG2(D_INIT,"%lXh)\n", *shmem);
+
+ /* register the card */
+ status=arcnet_found(dev,*port,airq,*shmem);
+
+ /* remove port and shmem from the lists */
+ *port=ports[numports-1];
+ numports--;
+ *shmem=shmems[numshmems-1];
+ numshmems--;
+
+ return status;
+ }
+ else
+ {
+ BUGMSG2(D_INIT_REASONS,"%Xh-", *cptr);
+ }
+ }
+
+ BUGMSG2(D_INIT,"no matching shmem)\n");
+ BUGMSG(D_INIT_REASONS,"Stage 5: ");
+ BUGLVL(D_INIT_REASONS) numprint=0;
+ *port=ports[numports-1];
+ numports--;
+ port--;
}
-
- /* Grab the region so we can find another board if autoIRQ fails. */
- request_region(dev->base_addr, ARCNET_TOTAL_SIZE,"arcnet");
+ BUGMSG(D_INIT_REASONS,"\n");
+
+ return -ENODEV;
+}
+
+/* Set up the struct device associated with this card. Called after
+ * probing succeeds.
+ */
+int arcnet_found(struct device *dev,int port,int airq, u_long shmem)
+{
+ int irqval;
+ u_char *first_mirror,*last_mirror;
+ struct arcnet_local *lp;
+
+ /* reserve the I/O region */
+ request_region(port,ARCNET_TOTAL_SIZE,"arcnet");
+ dev->base_addr=port;
+
+ /* reserve the irq */
+ irqval = request_irq(airq,&arcnet_interrupt,0,"arcnet");
+ if (irqval)
+ {
+ BUGMSG(D_NORMAL,"unable to get IRQ %d (irqval=%d).\n",
+ airq, irqval);
+ return -ENODEV;
+ }
+ irq2dev_map[airq]=dev;
+ dev->irq=airq;
- BUGMSG(D_NORMAL,"ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n",
- dev->base_addr, dev->irq, dev->mem_start);
+ /* find the real shared memory start/end points, including mirrors */
+
+ #define BUFFER_SIZE (512)
+ #define MIRROR_SIZE (BUFFER_SIZE*4)
+
+ first_mirror=last_mirror=(u_char *)shmem;
+ while (*first_mirror==TESTvalue) first_mirror-=MIRROR_SIZE;
+ first_mirror+=MIRROR_SIZE;
- /* Initialize the device structure. */
+ while (*last_mirror==TESTvalue) last_mirror+=MIRROR_SIZE;
+ last_mirror-=MIRROR_SIZE;
+
+ dev->mem_start=(u_long)first_mirror;
+ dev->mem_end=(u_long)last_mirror+MIRROR_SIZE-1;
+ dev->rmem_start=dev->mem_start+BUFFER_SIZE*0;
+ dev->rmem_end=dev->mem_start+BUFFER_SIZE*2-1;
+
+ /* Initialize the rest of the device structure. */
+
dev->priv = kmalloc(sizeof(struct arcnet_local), GFP_KERNEL);
if (dev->priv == NULL)
return -ENOMEM;
- memset(dev->priv, 0, sizeof(struct arcnet_local));
+ memset(dev->priv,0,sizeof(struct arcnet_local));
lp=(struct arcnet_local *)(dev->priv);
-
+
dev->open=arcnet_open;
dev->stop=arcnet_close;
dev->hard_start_xmit=arcnetA_send_packet;
/* And now fill particular fields with arcnet values */
dev->mtu=1500; /* completely arbitrary - agrees with ether, though */
dev->hard_header_len=sizeof(struct ClientData);
+ dev->hard_header=arcnetA_header;
+ dev->rebuild_header=arcnetA_rebuild_header;
+ lp->sequence=1;
+ lp->recbuf=0;
+
BUGMSG(D_DURING,"ClientData header size is %d.\n",
sizeof(struct ClientData));
BUGMSG(D_DURING,"HardHeader size is %d.\n",
sizeof(struct HardHeader));
- /* since we strip EXTRA_CLIENTDATA bytes off before sending,
- * we let Linux add that many bytes to the packet data...
- */
-
- BUGMSG(D_INIT,"arcnet_probe: resetting card.\n");
- arcnet_reset(dev);
- BUGMSG(D_NORMAL,"We appear to be station %d (%02Xh)\n",
- lp->stationid,lp->stationid);
+ /* get and check the station ID from offset 1 in shmem */
+ lp->stationid = first_mirror[1];
if (lp->stationid==0)
- BUGMSG(D_NORMAL,"WARNING! Station address 0 is reserved for broadcasts!\n");
- if (lp->stationid==255)
- BUGMSG(D_NORMAL,"WARNING! Station address 255 may confuse DOS networking programs!\n");
+ BUGMSG(D_NORMAL,"WARNING! Station address 0 is reserved "
+ "for broadcasts!\n");
+ else if (lp->stationid==255)
+ BUGMSG(D_NORMAL,"WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
dev->dev_addr[0]=lp->stationid;
- lp->sequence=1;
- lp->recbuf=0;
- dev->hard_header=arcnetA_header;
- dev->rebuild_header=arcnetA_rebuild_header;
-
+ BUGMSG(D_NORMAL,"ARCnet station %02Xh found at %03lXh, IRQ %d, "
+ "ShMem %lXh (%ld bytes).\n",
+ lp->stationid,
+ dev->base_addr,dev->irq,dev->mem_start,
+ dev->mem_end-dev->mem_start+1);
+
return 0;
}
-int arcnet_ioprobe(struct device *dev, short ioaddr)
+/* Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int arcnet_reset(struct device *dev,int reset_delay)
{
- int airq;
- unsigned long airqmask;
-
- BUGMSG(D_INIT,"probing address %Xh\n",ioaddr);
- BUGMSG(D_INIT," status1=%Xh\n",inb(STATUS));
-
-
- /* very simple - all we have to do is reset the card, and if there's
- * no irq, it's not an ARCnet. We can also kill two birds with
- * one stone because we detect the IRQ at the same time :)
- *
- * BUT: some newer cards don't seem to IRQ upon reset, or worse,
- * don't reset when BASE+0x08 is read. This code is no longer
- * so simple, and in fact quite dangerous. It should be redesigned.
- */
-
-#if 0
- /* reset the card by reading the reset port */
- inb(RESET);
- JIFFER(RESETtime);
-#endif
+ struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
+ short ioaddr=dev->base_addr;
+ int delayval,recbuf=lp->recbuf;
+ u_char *cardmem;
+
+ /* no IRQ's, please! */
+ lp->intmask=0;
+ SETMASK;
+
+ BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
+ dev->name,inb(STATUS));
- /* if status port is FF, there's certainly no arcnet... give up. */
- if (inb(STATUS)==0xFF)
+ if (reset_delay)
{
- BUGMSG(D_INIT," probe failed. Status port empty.\n");
- return -ENODEV;
+ /* reset the card */
+ inb(RESET);
+ JIFFER(RESETtime);
}
-#if 0
- /* we'll try to be reasonably sure it's an arcnet by making sure
- * the value of the COMMAND port changes automatically once in a
- * while. I have no idea what those values ARE, but at least
- * they work.
- */
+ outb(CFLAGScmd|RESETclear, COMMAND); /* clear flags & end reset */
+ outb(CFLAGScmd|CONFIGclear,COMMAND);
+
+ /* verify that the ARCnet signature byte is present */
+ cardmem = (u_char *) dev->mem_start;
+ if (cardmem[0] != TESTvalue)
{
- int initval,curval;
-
- curval=initval=inb(COMMAND);
- delayval=jiffies+5;
- while (delayval>=jiffies && curval==initval)
- curval=inb(COMMAND);
-
- if (curval==initval)
- {
- BUGLVL(D_INIT," probe failed. never-changing command port (%02Xh).\n",
- initval);
- return -ENODEV;
- }
- }
-#endif
-
- BUGMSG(D_INIT," status2=%Xh\n",inb(STATUS));
-
- /* now we turn the reset bit off so we can IRQ next reset... */
- outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
- if (inb(STATUS) & RESETflag) /* reset flag STILL on */
- {
- BUGMSG(D_INIT," probe failed. eternal reset flag1...(status=%Xh)\n",
- inb(STATUS));
- return -ENODEV;
- }
-
- outb(NORXcmd,COMMAND);
- outb(0,INTMASK);
- udelay(1);
-
- /* set up automatic IRQ detection */
- airqmask = probe_irq_on();
- BUGMSG(D_INIT," airqmask=%lXh\n",airqmask);
-
- /* if we do this, we're sure to get an IRQ since the card
- * has just reset and the NORXflag is on until we tell it to
- * start receiving.
- */
- outb(NORXflag,INTMASK);
- udelay(1);
- outb(0,INTMASK);
-
- /* and turn the reset flag back off. On some systems, we NEED to do
- * this before we re-enable the IRQ!
- */
- outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
-
- /* get autoirq results */
- airq = probe_irq_off(airqmask);
-
- if (airq>0)
- {
- BUGMSG(D_INIT," autoirq is %d\n", airq);
- }
- else if (airq<0)
- {
- BUGMSG(D_INIT," autoirq MAY be %d (please send mail to Avery)\n",
- -airq);
- airq=0;
- }
-
- /* if there was no autoirq AND the user hasn't set any defaults,
- * give up.
- */
- if (!airq && !(dev->base_addr && dev->irq))
- {
- BUGMSG(D_INIT," probe failed. no autoirq...\n");
- return -ENODEV;
- }
-
- /* otherwise we probably have a card. Let's make sure. */
-
-#if 0
- if (inb(STATUS) & RESETflag) /* reset flag on */
- {
- /* now we turn the reset bit off */
- outb(CFLAGScmd|RESETclear|CONFIGclear,COMMAND);
- }
-#endif
-
- if (inb(STATUS) & RESETflag) /* reset flag STILL on */
- {
- BUGMSG(D_INIT," probe failed. eternal reset flag...(status=%Xh)\n",
- inb(STATUS));
- return -ENODEV;
- }
-
- /* okay, we've got a real, live ARCnet on our hands.
- * I hope.
- */
- if (!dev->base_addr) dev->base_addr=ioaddr;
-
- if (dev->irq < 2) /* "Auto-IRQ" */
- {
- /* we already did the autoirq above, so store the value */
- dev->irq=airq;
- }
- else if (dev->irq == 2)
- {
- BUGMSG(D_NORMAL,"IRQ2 == IRQ9, don't worry.\n");
- dev->irq = 9;
- }
-
- BUGMSG(D_INIT,"irq and base address seem okay. (%lXh, IRQ %d)\n",
- dev->base_addr,dev->irq);
- return 0;
-}
-
-
-/* A memory probe that is called after the card is reset.
- * It checks for the official TESTvalue in byte 0 and makes sure the buffer
- * has certain characteristics of an ARCnet.
- */
-int arcnet_memprobe(struct device *dev,u_char *addr)
-{
- BUGMSG(D_INIT,"probing memory at %lXh\n",(u_long)addr);
-
- dev->mem_start=0;
-
- /* ARCnet memory byte 0 is TESTvalue */
- if (addr[0]!=TESTvalue)
- {
- BUGMSG(D_INIT," probe failed. addr=%lXh, addr[0]=%Xh (not %Xh)\n",
- (unsigned long)addr,addr[0],TESTvalue);
- return -ENODEV;
- }
-
- /* now verify the shared memory writability */
- addr[0]=0x42;
- if (addr[0]!=0x42)
- {
- BUGMSG(D_INIT," probe failed. addr=%lXh, addr[0]=%Xh (not 42h)\n",
- (unsigned long)addr,addr[0]);
- return -ENODEV;
- }
-
- /* got it! fill in dev */
- dev->mem_start=(unsigned long)addr;
- dev->mem_end=dev->mem_start+512*4-1;
- dev->rmem_start=dev->mem_start+512*0;
- dev->rmem_end=dev->mem_start+512*2-1;
-
- return 0;
-}
-
-
-/* Do a hardware reset on the card.
- */
-int arcnet_reset(struct device *dev)
-{
- struct arcnet_local *lp=(struct arcnet_local *)dev->priv;
- short ioaddr=dev->base_addr;
- int delayval,recbuf=lp->recbuf;
- u_char *cardmem;
-
- lp->intmask=0;
- SETMASK; /* no IRQ's, please! */
-
- BUGMSG(D_INIT,"Resetting %s (status=%Xh)\n",
- dev->name,inb(STATUS));
-
- inb(RESET); /* Reset by reading this port */
- JIFFER(RESETtime);
-
- outb(CFLAGScmd|RESETclear, COMMAND); /* clear flags & end reset */
- outb(CFLAGScmd|CONFIGclear,COMMAND);
-
- /* after a reset, the first byte of shared mem is TESTvalue and the
- * second byte is our 8-bit ARCnet address.
- */
- cardmem = (u_char *) dev->mem_start;
- if (cardmem[0] != TESTvalue)
- {
- BUGMSG(D_INIT,"reset failed: TESTvalue not present.\n");
+ BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n");
return 1;
}
- lp->stationid=cardmem[1]; /* save address for later use */
/* clear out status variables */
recbuf=lp->recbuf=0;
/* enable extended (512-byte) packets */
outb(CONFIGcmd|EXTconf,COMMAND);
+#ifndef SLOW_XMIT_COPY
/* clean out all the memory to make debugging make more sense :) */
BUGLVL(D_DURING)
memset((void *)dev->mem_start,0x42,2048);
+#endif
/* and enable receive of our first packet to the first buffer */
EnableReceiver();
}
-/*
- * Setup a struct device for ARCnet. This should really be in net_init.c
+/* Setup a struct device for ARCnet. This should really be in net_init.c
* but since there are three different ARCnet devices ANYWAY... <gargle>
*
* Actually, the whole idea of having all this kernel-dependent stuff (ie.
*
* Intelligent defaults?! Nah.
*/
-
void arcnet_setup(struct device *dev)
{
int i;
for (i=0; i<DEV_NUMBUFFS; i++)
skb_queue_head_init(&dev->buffs[i]);
- dev->broadcast[0] = 0x00; /* broadcasts on ARCnet are address 0 */
+ dev->broadcast[0] = 0x00; /* for us, broadcasts are address 0 */
dev->addr_len = 1;
dev->type = ARPHRD_ARCNET;
- dev->tx_queue_len = 30; /* Fairly long queue, arcnet is quite speedy */
+ dev->tx_queue_len = 30; /* fairly long queue - arcnet is
+ * quite speedy.
+ */
/* New-style flags. */
dev->flags = IFF_BROADCAST;
}
-/* Initialize the arc0e device.
- */
-static int arcnetE_init(struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
- ether_setup(dev); /* we're emulating ether here, not ARCnet */
- dev->dev_addr[0]=0;
- dev->dev_addr[5]=lp->stationid;
- dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len-1;
- dev->open=NULL;
- dev->stop=NULL;
- dev->hard_start_xmit=arcnetE_send_packet;
-
- BUGMSG(D_EXTRA,"ARCnet Ethernet-Encap protocol initialized.\n");
-
- return 0;
-}
-
-/* Initialize the arc0s device.
- */
-static int arcnetS_init(struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
- arcnet_setup(dev);
-
- /* And now fill particular fields with arcnet values */
- dev->dev_addr[0]=lp->stationid;
- dev->hard_header_len=sizeof(struct S_ClientData);
- dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len
- + S_EXTRA_CLIENTDATA;
- dev->open=NULL;
- dev->stop=NULL;
- dev->hard_start_xmit=arcnetS_send_packet;
- dev->hard_header=arcnetS_header;
- dev->rebuild_header=arcnetS_rebuild_header;
- BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
-
- return 0;
-}
-
-
/****************************************************************************
* *
* Open and close the driver *
dev->metric=1;
}
- BUGLVL(D_EXTRA) printk(version);
-
- BUGMSG(D_EXTRA,"arcnet_open: resetting card.\n");
+ BUGMSG(D_INIT,"arcnet_open: resetting card.\n");
- /* try to reset - twice if it fails the first time */
- if (arcnet_reset(dev) && arcnet_reset(dev))
+#ifdef FAST_IFCONFIG
+ /* try to put the card in a defined state - if it fails the first
+ * time, actually reset it.
+ */
+ if (arcnet_reset(dev,0) && arcnet_reset(dev,1))
+ return -ENODEV;
+#else
+ /* reset the card twice in case something goes wrong the first time.
+ */
+ if (arcnet_reset(dev,1) && arcnet_reset(dev,1))
return -ENODEV;
+#endif
dev->tbusy=0;
dev->interrupt=0;
/* The RFC1201 driver is the default - just store */
lp->adev=dev;
BUGMSG(D_EXTRA,"ARCnet RFC1201 protocol initialized.\n");
-
+
+#ifdef CONFIG_ARCNET_ETH
/* Initialize the ethernet-encap protocol driver */
lp->edev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
if (lp->edev == NULL)
sprintf(lp->edev->name,"%se",dev->name);
lp->edev->init=arcnetE_init;
register_netdev(lp->edev);
+#else
+ BUGMSG(D_EXTRA,"Ethernet-Encap protocol not available (disabled).\n");
+#endif
+#ifdef CONFIG_ARCNET_1051
/* Initialize the RFC1051-encap protocol driver */
lp->sdev=(struct device *)kmalloc(sizeof(struct device),GFP_KERNEL);
memcpy(lp->sdev,dev,sizeof(struct device));
sprintf(lp->sdev->name,"%ss",dev->name);
lp->sdev->init=arcnetS_init;
register_netdev(lp->sdev);
+#else
+ BUGMSG(D_EXTRA,"RFC1051 protocol not available (disabled).\n");
+#endif
/* we're started */
START=1;
TBUSY=1;
START=0;
- /* Flush TX and disable RX */
+ /* Shut down the card */
+#ifdef FAST_IFCONFIG
+ inb(RESET); /* reset IRQ won't run if START=0 */
+#else
lp->intmask=0;
SETMASK; /* no IRQ's (except RESET, of course) */
outb(NOTXcmd,COMMAND); /* stop transmit */
outb(NORXcmd,COMMAND); /* disable receive */
+#endif
/* reset more flags */
INTERRUPT=0;
/* do NOT free lp->adev!! It's static! */
lp->adev=NULL;
+#ifdef CONFIG_ARCNET_ETH
/* free the ethernet-encap protocol device */
lp->edev->priv=NULL;
dev_close(lp->edev);
kfree(lp->edev->name);
kfree(lp->edev);
lp->edev=NULL;
+#endif
+#ifdef CONFIG_ARCNET_1051
/* free the RFC1051-encap protocol device */
lp->sdev->priv=NULL;
dev_close(lp->sdev);
kfree(lp->sdev->name);
kfree(lp->sdev);
lp->sdev=NULL;
+#endif
/* Update the statistics here. (not necessary in ARCnet) */
if (status&TXFREEflag) /* transmit _DID_ finish */
{
- BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, inTXh=%d, ticks=%d, mask=%Xh)\n",
- status,lp->in_txhandler,tickssofar,
- lp->intmask);
+ BUGMSG(D_NORMAL,"tx timeout - missed IRQ? (status=%Xh, ticks=%d, mask=%Xh, dest=%d)\n",
+ status,tickssofar,lp->intmask,lp->lasttrans_dest);
lp->stats.tx_errors++;
}
else
{
- BUGMSG(D_NORMAL,"tx timed out (status=%Xh, inTXh=%d, tickssofar=%d, intmask=%Xh)\n",
- status,lp->in_txhandler,tickssofar,
- lp->intmask);
+ BUGMSG(D_EXTRA,"tx timed out (status=%Xh, tickssofar=%d, intmask=%Xh, dest=%d)\n",
+ status,tickssofar,lp->intmask,lp->lasttrans_dest);
lp->stats.tx_errors++;
lp->stats.tx_aborted_errors++;
}
-/* Called by the kernel in order to transmit an ethernet-type packet.
+/* After an RFC1201 split packet has been set up, this function calls
+ * arcnetAS_prepare_tx to load the next segment into the card. This function
+ * does NOT automatically call arcnet_go_tx.
*/
-static int
-arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
+static void arcnetA_continue_tx(struct device *dev)
{
struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=dev->base_addr,bad;
- union ArcPacket *arcpacket =
- (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
- u_char *arcsoft,daddr;
- short offset,length=skb->len+1;
-
- lp->intx++;
+ int ioaddr=dev->base_addr,maxsegsize=XMTU-4;
+ struct Outgoing *out=&(lp->outgoing);
- bad=arcnet_send_packet_bad(skb,dev);
- if (bad)
+ BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
+ inb(STATUS),lp->intx,lp->in_txhandler,lp->intmask);
+
+ if (lp->txready)
{
- lp->intx--;
- return bad;
+ BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
+ return;
}
-
- TBUSY=1;
-
- if (length>XMTU)
+
+ if (out->segnum>=out->numsegs)
{
- BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
- length);
- BUGMSG(D_NORMAL,"transmit aborted.\n");
-
- dev_kfree_skb(skb,FREE_WRITE);
- lp->intx--;
- return 0;
+ BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
+ out->segnum+1,out->numsegs);
}
+
+ if (!out->segnum) /* first packet */
+ out->hdr->split_flag=((out->numsegs-2)<<1)+1;
+ else
+ out->hdr->split_flag=out->segnum<<1;
+
+ out->seglen=maxsegsize;
+ if (out->seglen>out->dataleft) out->seglen=out->dataleft;
+
+ BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
+ out->segnum+1,out->seglen,out->numsegs,
+ out->length,out->hdr->split_flag);
+
+ arcnetAS_prepare_tx(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
+ sizeof(struct ClientData)-EXTRA_CLIENTDATA,
+ out->data,out->seglen,out->hdr->daddr,1);
- BUGMSG(D_DURING,"starting tx sequence...\n");
+ out->dataleft-=out->seglen;
+ out->data+=out->seglen;
+ out->segnum++;
+}
- lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate btw 2 & 3 */
+/* Given an skb, copy a packet into the ARCnet buffers for later transmission
+ * by arcnet_go_tx.
+ */
+static void
+arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
+ char *data,int length,int daddr,int exceptA)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct ClientData *arcsoft;
+ union ArcPacket *arcpacket =
+ (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
+ int offset;
+
+#ifdef SLOW_XMIT_COPY
+ char *iptr,*iend,*optr;
+#endif
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
+
+ length+=hdrlen;
+
+ BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
+ hdr,length,data);
+
+#ifndef SLOW_XMIT_COPY
/* clean out the page to make debugging make more sense :) */
BUGLVL(D_DURING)
memset((void *)dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
- /* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
- if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
- daddr=arcpacket->hardheader.destination=0;
- else
- daddr=arcpacket->hardheader.destination=
- ((struct ethhdr*)(skb->data))->h_dest[5];
+ arcpacket->hardheader.destination=daddr;
/* load packet into shared memory */
- offset=512-length;
- if (length>MTU) /* long/exception packet */
+ if (length<=MTU) /* Normal (256-byte) Packet */
{
- if (length<MinTU) offset-=3;
- arcpacket->hardheader.offset1=0;
- arcpacket->hardheader.offset2=offset;
- }
- else /* short packet */
- {
- arcpacket->hardheader.offset1=(offset-=256);
- }
-
- BUGMSG(D_DURING," length=%Xh, offset=%Xh, offset1=%Xh, offset2=%Xh\n",
- length,offset,arcpacket->hardheader.offset1,
- arcpacket->hardheader.offset2);
-
- arcsoft=&arcpacket->raw[offset];
- arcsoft[0]=ARC_P_ETHER;
- arcsoft++;
-
- /* copy the packet into ARCnet shmem
- * - the first bytes of ClientData header are skipped
- */
- BUGMSG(D_DURING,"ready to memcpy\n");
-
- memcpy(arcsoft,skb->data,skb->len);
-
- BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
- daddr,length);
-
- BUGLVL(D_TX)
- {
- int countx,county;
-
- printk("%6s: packet dump [tx] follows:",dev->name);
-
- for (county=0; county<16+(length>=240)*16; county++)
- {
- printk("\n[%04X] ",county*16);
- for (countx=0; countx<16; countx++)
- printk("%02X ",
- arcpacket->raw[county*16+countx]);
- }
-
- printk("\n");
- }
-
-#ifdef VERIFY_ACK
- lp->lastload_dest=daddr;
-#endif
- lp->txready=lp->txbuf; /* packet is ready for sending */
-
- dev_kfree_skb(skb,FREE_WRITE);
-
- if (arcnet_go_tx(dev,1))
- {
- /* inform upper layers */
- TBUSY=0;
- mark_bh(NET_BH);
- }
-
- dev->trans_start=jiffies;
- lp->intx--;
-
- /* make sure we didn't ignore a TX IRQ while we were in here */
- lp->intmask |= TXFREEflag;
- SETMASK;
-
- return 0;
-}
-
-
-/* Called by the kernel in order to transmit an RFC1051-type packet.
- */
-static int
-arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=dev->base_addr,bad,length;
- struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
-
- lp->intx++;
-
- bad=arcnet_send_packet_bad(skb,dev);
- if (bad)
- {
- lp->intx--;
- return bad;
- }
-
- TBUSY=1;
-
- length = 1 < skb->len ? skb->len : 1;
-
- BUGLVL(D_SKB)
- {
- short i;
- for(i=0; i<skb->len; i++)
- {
- if (i%16 == 0) printk("\n[%04hX] ",i);
- printk("%02hX ",((unsigned char*)skb->data)[i]);
- }
- printk("\n");
- }
-
- /* fits in one packet? */
- if (length-S_EXTRA_CLIENTDATA<=XMTU)
- {
- arcnetAS_prepare_tx(dev,
- skb->data+S_EXTRA_CLIENTDATA,
- sizeof(struct S_ClientData)-S_EXTRA_CLIENTDATA,
- skb->data+sizeof(struct S_ClientData),
- length-sizeof(struct S_ClientData),
- hdr->daddr,0);
-
- /* done right away */
- dev_kfree_skb(skb,FREE_WRITE);
-
- if (arcnet_go_tx(dev,1))
- {
- /* inform upper layers */
- TBUSY=0;
- mark_bh(NET_BH);
- }
- }
- else /* too big for one - not accepted */
- {
- BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
- length);
- dev_kfree_skb(skb,FREE_WRITE);
- lp->stats.tx_dropped++;
- TBUSY=0;
- mark_bh(NET_BH);
- }
-
- dev->trans_start=jiffies;
- lp->intx--;
-
- /* make sure we didn't ignore a TX IRQ while we were in here */
- lp->intmask |= TXFREEflag;
- SETMASK;
-
- return 0;
-}
-
-
-/* After an RFC1201 split packet has been set up, this function calls
- * arcnetAS_prepare_tx to load the next segment into the card. This function
- * does NOT automatically call arcnet_go_tx.
- */
-static void arcnetA_continue_tx(struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- int ioaddr=dev->base_addr,maxsegsize=XMTU-4;
- struct Outgoing *out=&(lp->outgoing);
-
- BUGMSG(D_DURING,"continue_tx called (status=%Xh, intx=%d, intxh=%d, intmask=%Xh\n",
- inb(STATUS),lp->intx,lp->in_txhandler,lp->intmask);
-
- if (lp->txready)
- {
- BUGMSG(D_NORMAL,"continue_tx: called with packet in buffer!\n");
- return;
- }
-
- if (out->segnum>=out->numsegs)
- {
- BUGMSG(D_NORMAL,"continue_tx: building segment %d of %d!\n",
- out->segnum+1,out->numsegs);
- }
-
- if (!out->segnum) /* first packet */
- out->hdr->split_flag=((out->numsegs-2)<<1)+1;
- else
- out->hdr->split_flag=out->segnum<<1;
-
- out->seglen=maxsegsize;
- if (out->seglen>out->dataleft) out->seglen=out->dataleft;
-
- BUGMSG(D_TX,"building packet #%d (%d bytes) of %d (%d total), splitflag=%d\n",
- out->segnum+1,out->seglen,out->numsegs,
- out->length,out->hdr->split_flag);
-
- arcnetAS_prepare_tx(dev,((char *)out->hdr)+EXTRA_CLIENTDATA,
- sizeof(struct ClientData)-EXTRA_CLIENTDATA,
- out->data,out->seglen,out->hdr->daddr,1);
-
- out->dataleft-=out->seglen;
- out->data+=out->seglen;
- out->segnum++;
-}
-
-
-/* Given an skb, copy a packet into the ARCnet buffers for later transmission
- * by arcnet_go_tx.
- */
-static void
-arcnetAS_prepare_tx(struct device *dev,u_char *hdr,int hdrlen,
- char *data,int length,int daddr,int exceptA)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- struct ClientData *arcsoft;
- union ArcPacket *arcpacket =
- (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
- int offset;
-
- lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate between 2 and 3 */
-
- length+=hdrlen;
-
- BUGMSG(D_TX,"arcnetAS_prep_tx: hdr:%ph, length:%d, data:%ph\n",
- hdr,length,data);
-
- /* clean out the page to make debugging make more sense :) */
- BUGLVL(D_DURING)
- memset((void *)dev->mem_start+lp->txbuf*512,0x42,512);
-
- arcpacket->hardheader.destination=daddr;
-
- /* load packet into shared memory */
- if (length<=MTU) /* Normal (256-byte) Packet */
- {
- arcpacket->hardheader.offset1=offset=256-length;
- arcsoft=(struct ClientData *)
- (&arcpacket->raw[offset]);
+ arcpacket->hardheader.offset1=offset=256-length;
+ arcsoft=(struct ClientData *)
+ (&arcpacket->raw[offset]);
}
else if (length>=MinTU) /* Extended (512-byte) Packet */
{
*/
memcpy((u_char*)arcsoft,
(u_char*)hdr,hdrlen);
+#ifdef SLOW_XMIT_COPY
+ for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcsoft+hdrlen;
+ iptr<iend; iptr++,optr++)
+ {
+ *optr=*iptr;
+ /*udelay(5);*/
+ }
+#else
memcpy((u_char*)arcsoft+hdrlen,
data,length-hdrlen);
+#endif
BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
daddr,length);
printk("\n");
}
-#ifdef VERIFY_ACK
lp->lastload_dest=daddr;
-#endif
lp->txready=lp->txbuf; /* packet is ready for sending */
}
}
return 0;
}
-
+
/* start sending */
outb(TXcmd|(lp->txready<<3),COMMAND);
lp->txready=0;
lp->sending++;
-#ifdef VERIFY_ACK
lp->lasttrans_dest=lp->lastload_dest;
lp->lastload_dest=0;
-#endif
lp->intmask |= TXFREEflag;
arcnet_interrupt(int irq,struct pt_regs *regs)
{
struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr;
- BUGMSG(D_DURING,"in arcnet_interrupt\n");
-
if (dev==NULL)
{
- BUGLVL(D_EXTRA)
+ BUGLVL(D_DURING)
printk("arcnet: irq %d for unknown device.\n", irq);
return;
}
- if (!dev->start) return;
+ BUGMSG(D_DURING,"in arcnet_interrupt\n");
+
+ /* RESET flag was enabled - if !dev->start, we must clear it right
+ * away (but nothing else) since inthandler() is never called.
+ */
+ ioaddr=dev->base_addr;
+ if (!dev->start)
+ {
+ if (inb(STATUS) & RESETflag)
+ outb(CFLAGScmd|RESETclear, COMMAND);
+ return;
+ }
+ /* Call the "real" interrupt handler. */
arcnet_inthandler(dev);
}
status = inb(STATUS);
didsomething=0;
+
/* RESET flag was enabled - card is resetting and if RX
* is disabled, it's NOT because we just got a packet.
*/
if (status & RESETflag)
{
- outb(CFLAGScmd|RESETclear,COMMAND);
- BUGMSG(D_INIT,"reset irq (status=%Xh)\n",
+ BUGMSG(D_NORMAL,"spurious reset (status=%Xh)\n",
status);
+ arcnet_reset(dev,0);
+
+ /* all other flag values are just garbage */
+ break;
}
-#ifdef DETECT_RECONFIGS
- if (status & (lp->intmask) & RECONflag)
+
+
+ /* RX is inhibited - we must have received something. */
+ if (status & lp->intmask & NORXflag)
{
- outb(CFLAGScmd|CONFIGclear,COMMAND);
- lp->stats.tx_carrier_errors++;
-
- #ifdef SHOW_RECONFIGS
- BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
- status);
- #endif /* SHOW_RECONFIGS */
-
- #ifdef RECON_THRESHOLD
- /* is the RECON info empty or old? */
- if (!lp->first_recon || !lp->last_recon ||
- jiffies-lp->last_recon > HZ*10)
- {
- if (lp->network_down)
- BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
- lp->first_recon=lp->last_recon=jiffies;
- lp->num_recons=lp->network_down=0;
-
- BUGMSG(D_DURING,"recon: clearing counters.\n");
- }
- else /* add to current RECON counter */
- {
- lp->last_recon=jiffies;
- lp->num_recons++;
-
- BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
- lp->num_recons,
- (lp->last_recon-lp->first_recon)/HZ,
- lp->network_down);
-
- /* if network is marked up;
- * and first_recon and last_recon are 60+ sec
- * apart;
- * and the average no. of recons counted is
- * > RECON_THRESHOLD/min;
- * then print a warning message.
- */
- if (!lp->network_down
- && (lp->last_recon-lp->first_recon)<=HZ*60
- && lp->num_recons >= RECON_THRESHOLD)
- {
- lp->network_down=1;
- BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
- }
- else if (!lp->network_down
- && lp->last_recon-lp->first_recon > HZ*60)
- {
- /* reset counters if we've gone for
- * over a minute.
- */
- lp->first_recon=lp->last_recon;
- lp->num_recons=1;
- }
- }
- #endif
- }
- #ifdef RECON_THRESHOLD
- else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
- {
- if (lp->network_down)
- BUGMSG(D_NORMAL,"cabling restored?\n");
- lp->first_recon=lp->last_recon=0;
- lp->num_recons=lp->network_down=0;
-
- BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
- }
- #endif
-#endif /* DETECT_RECONFIGS */
-
- /* RX is inhibited - we must have received something. */
- if (status & lp->intmask & NORXflag)
- {
- int recbuf=lp->recbuf=!lp->recbuf;
-
- BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
+ int recbuf=lp->recbuf=!lp->recbuf;
+
+ BUGMSG(D_DURING,"receive irq (status=%Xh)\n",
status);
/* enable receive of our next packet */
BUGMSG(D_DURING,"TX IRQ (stat=%Xh, numsegs=%d, segnum=%d, skb=%ph)\n",
status,out->numsegs,out->segnum,out->skb);
-#ifdef VERIFY_ACK
if (was_sending && !(status&TXACKflag))
{
if (lp->lasttrans_dest != 0)
{
- BUGMSG(D_NORMAL,"transmit was not acknowledged! (status=%Xh, dest=%d)\n",
+ BUGMSG(D_EXTRA,"transmit was not acknowledged! (status=%Xh, dest=%d)\n",
status,lp->lasttrans_dest);
lp->stats.tx_errors++;
lp->stats.tx_carrier_errors++;
lp->lasttrans_dest);
}
}
-#endif
+
/* send packet if there is one */
arcnet_go_tx(dev,0);
didsomething++;
arcnet_go_tx(dev,0);
didsomething++;
}
+
+#ifdef DETECT_RECONFIGS
+ if (status & (lp->intmask) & RECONflag)
+ {
+ outb(CFLAGScmd|CONFIGclear,COMMAND);
+ lp->stats.tx_carrier_errors++;
+
+ #ifdef SHOW_RECONFIGS
+ BUGMSG(D_NORMAL,"Network reconfiguration detected (status=%Xh)\n",
+ status);
+ #endif /* SHOW_RECONFIGS */
+
+ #ifdef RECON_THRESHOLD
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"reconfiguration detected: cabling restored?\n");
+ lp->first_recon=lp->last_recon=jiffies;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"recon: clearing counters.\n");
+ }
+ else /* add to current RECON counter */
+ {
+ lp->last_recon=jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING,"recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon-lp->first_recon)/HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ sec
+ * apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon-lp->first_recon)<=HZ*60
+ && lp->num_recons >= RECON_THRESHOLD)
+ {
+ lp->network_down=1;
+ BUGMSG(D_NORMAL,"many reconfigurations detected: cabling problem?\n");
+ }
+ else if (!lp->network_down
+ && lp->last_recon-lp->first_recon > HZ*60)
+ {
+ /* reset counters if we've gone for
+ * over a minute.
+ */
+ lp->first_recon=lp->last_recon;
+ lp->num_recons=1;
+ }
+ }
+ #endif
+ }
+ #ifdef RECON_THRESHOLD
+ else if (lp->network_down && jiffies-lp->last_recon > HZ*10)
+ {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL,"cabling restored?\n");
+ lp->first_recon=lp->last_recon=0;
+ lp->num_recons=lp->network_down=0;
+
+ BUGMSG(D_DURING,"not recon: clearing counters anyway.\n");
+ }
+ #endif
+#endif /* DETECT_RECONFIGS */
} while (--boguscount && didsomething);
BUGMSG(D_DURING,"net_interrupt complete (status=%Xh, count=%d)\n\n",
case ARC_P_NOVELL_EC:
arcnetA_rx(lp->adev,arcsoft,length,saddr,daddr);
break;
+#ifdef CONFIG_ARCNET_ETH
case ARC_P_ETHER:
arcnetE_rx(lp->edev,arcsoft,length,saddr,daddr);
- break;
+ break;
+#endif
+#ifdef CONFIG_ARCNET_1051
case ARC_P_IP_RFC1051:
case ARC_P_ARP_RFC1051:
arcnetS_rx(lp->sdev,arcsoft,length,saddr,daddr);
break;
+#endif
case ARC_P_LANSOFT: /* don't understand. fall through. */
default:
BUGMSG(D_NORMAL,"received unknown protocol %d (%Xh) from station %d.\n",
}
+#ifndef SLOW_XMIT_COPY
/* clean out the page to make debugging make more sense :) */
BUGLVL(D_DURING)
memset((void *)arcpacket->raw,0x42,512);
+#endif
/* If any worth-while packets have been received, a mark_bh(NET_BH)
if (in->skb && in->sequence!=arcsoft->sequence)
{
- BUGMSG(D_NORMAL,"wrong seq number, aborting assembly (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+ BUGMSG(D_NORMAL,"wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
saddr,in->sequence,arcsoft->sequence,
arcsoft->split_flag);
kfree_skb(in->skb,FREE_WRITE);
}
-/* Packet receiver for non-standard ethernet-style packets
+
+
+/****************************************************************************
+ * *
+ * Miscellaneous routines *
+ * *
+ ****************************************************************************/
+
+
+/* Get the current statistics. This may be called with the card open or
+ * closed.
*/
-static void
-arcnetE_rx(struct device *dev,u_char *arcsoft,
- int length,u_char saddr, u_char daddr)
+
+static struct enet_statistics *
+arcnet_get_stats(struct device *dev)
{
struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
- struct sk_buff *skb;
-
- BUGMSG(D_DURING,"it's an ethernet-encap packet (length=%d)\n",
+
+ return &lp->stats;
+}
+
+#if 0
+/* Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ * best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+#if 0 /* no promiscuous mode at all on most ARCnet models */
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ short ioaddr = dev->base_addr;
+ if (num_addrs) {
+ outw(69, ioaddr); /* Enable promiscuous mode */
+ } else
+ outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
+#endif
+}
+#endif
+
+/* Create the ARCnet ClientData header for an arbitrary protocol layer
+ *
+ * saddr=NULL means use device source address (always will anyway)
+ * daddr=NULL means leave destination address (eg unresolved arp)
+ */
+int arcnetA_header(struct sk_buff *skb,struct device *dev,
+ unsigned short type,void *daddr,void *saddr,unsigned len)
+{
+ struct ClientData *head = (struct ClientData *)
+ skb_push(skb,dev->hard_header_len);
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+
+ BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
+ saddr ? *(u_char*)saddr : -1,
+ daddr ? *(u_char*)daddr : -1,
+ type,type,len);
+
+ /* set the protocol ID according to RFC1201 */
+ switch(type)
+ {
+ case ETH_P_IP:
+ head->protocol_id=ARC_P_IP;
+ break;
+ case ETH_P_ARP:
+ head->protocol_id=ARC_P_ARP;
+ break;
+ case ETH_P_RARP:
+ head->protocol_id=ARC_P_RARP;
+ break;
+ case ETH_P_IPX:
+ case ETH_P_802_3:
+ case ETH_P_802_2:
+ head->protocol_id=ARC_P_IPX;
+ break;
+ case ETH_P_ATALK:
+ head->protocol_id=ARC_P_ATALK;
+ break;
+ default:
+ BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
+ type,type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ return 0;
+ }
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help
+ * in debugging. saddr is stored in the ClientData header and
+ * removed before sending the packet (since ARCnet does not allow
+ * us to change the source address in the actual packet sent)
+ */
+ if(saddr)
+ head->saddr=((u_char*)saddr)[0];
+ else
+ head->saddr=((u_char*)(dev->dev_addr))[0];
+
+ head->split_flag=0; /* split packets are done elsewhere */
+ head->sequence=0; /* so are sequence numbers */
+
+ /* supposedly if daddr is NULL, we should ignore it... */
+ if(daddr)
+ {
+ head->daddr=((u_char*)daddr)[0];
+ return dev->hard_header_len;
+ }
+ else
+ head->daddr=0; /* better fill one in anyway */
+
+ return -dev->hard_header_len;
+}
+
+
+
+/* Rebuild the ARCnet ClientData header. This is called after an ARP
+ * (or in future other address resolution) has completed on this
+ * sk_buff. We now let ARP fill in the other fields.
+ */
+int arcnetA_rebuild_header(void *buff,struct device *dev,unsigned long dst,
+ struct sk_buff *skb)
+{
+ struct ClientData *head = (struct ClientData *)buff;
+ struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
+ int status;
+
+ /*
+ * Only ARP and IP are currently supported
+ */
+
+ if(head->protocol_id != ARC_P_IP)
+ {
+ BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
+ head->protocol_id,head->protocol_id);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ head->daddr=0;
+ /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
+ return 0;
+ }
+
+ /*
+ * Try and get ARP to resolve the header.
+ */
+#ifdef CONFIG_INET
+ BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
+ head->saddr,head->daddr,head->protocol_id);
+ status=arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
+ BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
+ head->saddr,head->daddr,head->protocol_id);
+ return status;
+#else
+ return 0;
+#endif
+}
+
+
+/* Determine a packet's protocol ID.
+ *
+ * With ARCnet we have to convert everything to Ethernet-style stuff.
+ */
+unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
+{
+ struct ClientData *head;
+ struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
+
+ /* Pull off the arcnet header. */
+ skb->mac.raw=skb->data;
+ skb_pull(skb,dev->hard_header_len);
+ head=(struct ClientData *)skb->mac.raw;
+
+ if (head->daddr==0)
+ skb->pkt_type=PACKET_BROADCAST;
+ else if (dev->flags&IFF_PROMISC)
+ {
+ /* if we're not sending to ourselves :) */
+ if (head->daddr != dev->dev_addr[0])
+ skb->pkt_type=PACKET_OTHERHOST;
+ }
+
+ /* now return the protocol number */
+ switch (head->protocol_id)
+ {
+ case ARC_P_IP: return htons(ETH_P_IP);
+ case ARC_P_ARP: return htons(ETH_P_ARP);
+ case ARC_P_RARP: return htons(ETH_P_RARP);
+
+ case ARC_P_IPX:
+ case ARC_P_NOVELL_EC:
+ return htons(ETH_P_802_3);
+ default:
+ BUGMSG(D_NORMAL,"received packet of unknown protocol id %d (%Xh)\n",
+ head->protocol_id,head->protocol_id);
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ return 0;
+ }
+
+ return htons(ETH_P_IP);
+}
+
+
+#ifdef CONFIG_ARCNET_ETH
+/****************************************************************************
+ * *
+ * Ethernet-Encap Support *
+ * *
+ ****************************************************************************/
+
+/* Initialize the arc0e device.
+ */
+static int arcnetE_init(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ ether_setup(dev); /* we're emulating ether here, not ARCnet */
+ dev->dev_addr[0]=0;
+ dev->dev_addr[5]=lp->stationid;
+ dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len-1;
+ dev->open=NULL;
+ dev->stop=NULL;
+ dev->hard_start_xmit=arcnetE_send_packet;
+
+ BUGMSG(D_EXTRA,"ARCnet Ethernet-Encap protocol initialized.\n");
+
+ return 0;
+}
+
+
+/* Called by the kernel in order to transmit an ethernet-type packet.
+ */
+static int
+arcnetE_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr,bad;
+ union ArcPacket *arcpacket =
+ (union ArcPacket *)(dev->mem_start+512*(lp->txbuf^1));
+ u_char *arcsoft,daddr;
+ short offset,length=skb->len+1;
+
+ lp->intx++;
+
+ bad=arcnet_send_packet_bad(skb,dev);
+ if (bad)
+ {
+ lp->intx--;
+ return bad;
+ }
+
+ TBUSY=1;
+
+ if (length>XMTU)
+ {
+ BUGMSG(D_NORMAL,"MTU must be <= 493 for ethernet encap (length=%d).\n",
+ length);
+ BUGMSG(D_NORMAL,"transmit aborted.\n");
+
+ dev_kfree_skb(skb,FREE_WRITE);
+ lp->intx--;
+ return 0;
+ }
+
+ BUGMSG(D_DURING,"starting tx sequence...\n");
+
+ lp->txbuf=lp->txbuf^1; /* XOR with 1 to alternate btw 2 & 3 */
+
+#ifndef SLOW_XMIT_COPY
+ /* clean out the page to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset((void *)dev->mem_start+lp->txbuf*512,0x42,512);
+#endif
+
+ /* broadcasts have address FF:FF:FF:FF:FF:FF in etherspeak */
+ if (((struct ethhdr*)(skb->data))->h_dest[0] == 0xFF)
+ daddr=arcpacket->hardheader.destination=0;
+ else
+ daddr=arcpacket->hardheader.destination=
+ ((struct ethhdr*)(skb->data))->h_dest[5];
+
+ /* load packet into shared memory */
+ offset=512-length;
+ if (length>MTU) /* long/exception packet */
+ {
+ if (length<MinTU) offset-=3;
+ arcpacket->hardheader.offset1=0;
+ arcpacket->hardheader.offset2=offset;
+ }
+ else /* short packet */
+ {
+ arcpacket->hardheader.offset1=(offset-=256);
+ }
+
+ BUGMSG(D_DURING," length=%Xh, offset=%Xh, offset1=%Xh, offset2=%Xh\n",
+ length,offset,arcpacket->hardheader.offset1,
+ arcpacket->hardheader.offset2);
+
+ arcsoft=&arcpacket->raw[offset];
+ arcsoft[0]=ARC_P_ETHER;
+ arcsoft++;
+
+ /* copy the packet into ARCnet shmem
+ * - the first bytes of ClientData header are skipped
+ */
+ BUGMSG(D_DURING,"ready to memcpy\n");
+
+ memcpy(arcsoft,skb->data,skb->len);
+
+ BUGMSG(D_DURING,"transmitting packet to station %02Xh (%d bytes)\n",
+ daddr,length);
+
+ BUGLVL(D_TX)
+ {
+ int countx,county;
+
+ printk("%6s: packet dump [tx] follows:",dev->name);
+
+ for (county=0; county<16+(length>=240)*16; county++)
+ {
+ printk("\n[%04X] ",county*16);
+ for (countx=0; countx<16; countx++)
+ printk("%02X ",
+ arcpacket->raw[county*16+countx]);
+ }
+
+ printk("\n");
+ }
+
+ lp->lastload_dest=daddr;
+ lp->txready=lp->txbuf; /* packet is ready for sending */
+
+ dev_kfree_skb(skb,FREE_WRITE);
+
+ if (arcnet_go_tx(dev,1))
+ {
+ /* inform upper layers */
+ TBUSY=0;
+ mark_bh(NET_BH);
+ }
+
+ dev->trans_start=jiffies;
+ lp->intx--;
+
+ /* make sure we didn't ignore a TX IRQ while we were in here */
+ lp->intmask |= TXFREEflag;
+ SETMASK;
+
+ return 0;
+}
+
+
+/* Packet receiver for ethernet-encap packets.
+ */
+static void
+arcnetE_rx(struct device *dev,u_char *arcsoft,
+ int length,u_char saddr, u_char daddr)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ struct sk_buff *skb;
+
+ BUGMSG(D_DURING,"it's an ethernet-encap packet (length=%d)\n",
+ length);
+
+ skb = alloc_skb(length, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+
+ skb->len = length;
+ skb->dev = dev;
+
+ memcpy(skb->data,(u_char *)arcsoft+1,length-1);
+
+ BUGLVL(D_SKB)
+ {
+ short i;
+ printk("%6s: rx skb dump follows:\n",dev->name);
+ for(i=0; i<skb->len; i++)
+ {
+ if (i%16==0)
+ printk("\n[%04hX] ",i);
+ else
+ printk("%02hX ",((u_char *)skb->data)[i]);
+ }
+ printk("\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+
+ netif_rx(skb);
+}
+
+#endif /* CONFIG_ARCNET_ETH */
+
+#ifdef CONFIG_ARCNET_1051
+/****************************************************************************
+ * *
+ * RFC1051 Support *
+ * *
+ ****************************************************************************/
+
+/* Initialize the arc0s device.
+ */
+static int arcnetS_init(struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+
+ arcnet_setup(dev);
+
+ /* And now fill particular fields with arcnet values */
+ dev->dev_addr[0]=lp->stationid;
+ dev->hard_header_len=sizeof(struct S_ClientData);
+ dev->mtu=512-sizeof(struct HardHeader)-dev->hard_header_len
+ + S_EXTRA_CLIENTDATA;
+ dev->open=NULL;
+ dev->stop=NULL;
+ dev->hard_start_xmit=arcnetS_send_packet;
+ dev->hard_header=arcnetS_header;
+ dev->rebuild_header=arcnetS_rebuild_header;
+ BUGMSG(D_EXTRA,"ARCnet RFC1051 (NetBSD, AmiTCP) protocol initialized.\n");
+
+ return 0;
+}
+
+
+/* Called by the kernel in order to transmit an RFC1051-type packet.
+ */
+static int
+arcnetS_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
+ int ioaddr=dev->base_addr,bad,length;
+ struct S_ClientData *hdr=(struct S_ClientData *)skb->data;
+
+ lp->intx++;
+
+ bad=arcnet_send_packet_bad(skb,dev);
+ if (bad)
+ {
+ lp->intx--;
+ return bad;
+ }
+
+ TBUSY=1;
+
+ length = 1 < skb->len ? skb->len : 1;
+
+ BUGLVL(D_SKB)
+ {
+ short i;
+ for(i=0; i<skb->len; i++)
+ {
+ if (i%16 == 0) printk("\n[%04hX] ",i);
+ printk("%02hX ",((unsigned char*)skb->data)[i]);
+ }
+ printk("\n");
+ }
+
+ /* fits in one packet? */
+ if (length-S_EXTRA_CLIENTDATA<=XMTU)
+ {
+ arcnetAS_prepare_tx(dev,
+ skb->data+S_EXTRA_CLIENTDATA,
+ sizeof(struct S_ClientData)-S_EXTRA_CLIENTDATA,
+ skb->data+sizeof(struct S_ClientData),
+ length-sizeof(struct S_ClientData),
+ hdr->daddr,0);
+
+ /* done right away */
+ dev_kfree_skb(skb,FREE_WRITE);
+
+ if (arcnet_go_tx(dev,1))
+ {
+ /* inform upper layers */
+ TBUSY=0;
+ mark_bh(NET_BH);
+ }
+ }
+ else /* too big for one - not accepted */
+ {
+ BUGMSG(D_NORMAL,"packet too long (length=%d)\n",
length);
+ dev_kfree_skb(skb,FREE_WRITE);
+ lp->stats.tx_dropped++;
+ TBUSY=0;
+ mark_bh(NET_BH);
+ }
- skb = alloc_skb(length, GFP_ATOMIC);
- if (skb == NULL) {
- BUGMSG(D_NORMAL,"Memory squeeze, dropping packet.\n");
- lp->stats.rx_dropped++;
- return;
- }
-
- skb->len = length;
- skb->dev = dev;
-
- memcpy(skb->data,(u_char *)arcsoft+1,length-1);
+ dev->trans_start=jiffies;
+ lp->intx--;
- BUGLVL(D_SKB)
- {
- short i;
- printk("%6s: rx skb dump follows:\n",dev->name);
- for(i=0; i<skb->len; i++)
- {
- if (i%16==0)
- printk("\n[%04hX] ",i);
- else
- printk("%02hX ",((u_char *)skb->data)[i]);
- }
- printk("\n");
- }
-
- skb->protocol=eth_type_trans(skb,dev);
-
- netif_rx(skb);
+ /* make sure we didn't ignore a TX IRQ while we were in here */
+ lp->intmask |= TXFREEflag;
+ SETMASK;
+
+ return 0;
}
+
/* Packet receiver for RFC1051 packets;
*/
static void
}
-
-/****************************************************************************
- * *
- * Miscellaneous routines *
- * *
- ****************************************************************************/
-
-
-/* Get the current statistics. This may be called with the card open or
- * closed.
- */
-
-static struct enet_statistics *
-arcnet_get_stats(struct device *dev)
-{
- struct arcnet_local *lp = (struct arcnet_local *)dev->priv;
-
- return &lp->stats;
-}
-
-#if 0
-/* Set or clear the multicast filter for this adaptor.
- * num_addrs == -1 Promiscuous mode, receive all packets
- * num_addrs == 0 Normal mode, clear multicast list
- * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
- * best-effort filtering.
- */
-static void
-set_multicast_list(struct device *dev)
-{
-#if 0 /* no promiscuous mode at all on most ARCnet models */
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
- short ioaddr = dev->base_addr;
- if (num_addrs) {
- outw(69, ioaddr); /* Enable promiscuous mode */
- } else
- outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
-#endif
-}
-#endif
-
-/* Create the ARCnet ClientData header for an arbitrary protocol layer
- *
- * saddr=NULL means use device source address (always will anyway)
- * daddr=NULL means leave destination address (eg unresolved arp)
- */
-int arcnetA_header(struct sk_buff *skb,struct device *dev,
- unsigned short type,void *daddr,void *saddr,unsigned len)
-{
- struct ClientData *head = (struct ClientData *)
- skb_push(skb,dev->hard_header_len);
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
-
- BUGMSG(D_DURING,"create header from %d to %d; protocol %d (%Xh); size %u.\n",
- saddr ? *(u_char*)saddr : -1,
- daddr ? *(u_char*)daddr : -1,
- type,type,len);
-
- /* set the protocol ID according to RFC1201 */
- switch(type)
- {
- case ETH_P_IP:
- head->protocol_id=ARC_P_IP;
- break;
- case ETH_P_ARP:
- head->protocol_id=ARC_P_ARP;
- break;
- case ETH_P_RARP:
- head->protocol_id=ARC_P_RARP;
- break;
- case ETH_P_IPX:
- case ETH_P_802_3:
- case ETH_P_802_2:
- head->protocol_id=ARC_P_IPX;
- break;
- case ETH_P_ATALK:
- head->protocol_id=ARC_P_ATALK;
- break;
- default:
- BUGMSG(D_NORMAL,"I don't understand protocol %d (%Xh)\n",
- type,type);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- return 0;
- }
-
- /*
- * Set the source hardware address.
- *
- * This is pretty pointless for most purposes, but it can help
- * in debugging. saddr is stored in the ClientData header and
- * removed before sending the packet (since ARCnet does not allow
- * us to change the source address in the actual packet sent)
- */
- if(saddr)
- head->saddr=((u_char*)saddr)[0];
- else
- head->saddr=((u_char*)(dev->dev_addr))[0];
-
- head->split_flag=0; /* split packets are done elsewhere */
- head->sequence=0; /* so are sequence numbers */
-
- /* supposedly if daddr is NULL, we should ignore it... */
- if(daddr)
- {
- head->daddr=((u_char*)daddr)[0];
- return dev->hard_header_len;
- }
- else
- head->daddr=0; /* better fill one in anyway */
-
- return -dev->hard_header_len;
-}
-
-
/* Create the ARCnet ClientData header for an arbitrary protocol layer
*
* saddr=NULL means use device source address (always will anyway)
}
-
/* Rebuild the ARCnet ClientData header. This is called after an ARP
* (or in future other address resolution) has completed on this
* sk_buff. We now let ARP fill in the other fields.
*/
-int arcnetA_rebuild_header(void *buff,struct device *dev,unsigned long dst,
- struct sk_buff *skb)
-{
- struct ClientData *head = (struct ClientData *)buff;
- struct arcnet_local *lp=(struct arcnet_local *)(dev->priv);
- int status;
-
- /*
- * Only ARP and IP are currently supported
- */
-
- if(head->protocol_id != ARC_P_IP)
- {
- BUGMSG(D_NORMAL,"I don't understand protocol type %d (%Xh) addresses!\n",
- head->protocol_id,head->protocol_id);
- lp->stats.tx_errors++;
- lp->stats.tx_aborted_errors++;
- head->daddr=0;
- /*memcpy(eth->h_source, dev->dev_addr, dev->addr_len);*/
- return 0;
- }
-
- /*
- * Try and get ARP to resolve the header.
- */
-#ifdef CONFIG_INET
- BUGMSG(D_DURING,"rebuild header from %d to %d; protocol %Xh\n",
- head->saddr,head->daddr,head->protocol_id);
- status=arp_find(&(head->daddr), dst, dev, dev->pa_addr, skb)? 1 : 0;
- BUGMSG(D_DURING," rebuilt: from %d to %d; protocol %Xh\n",
- head->saddr,head->daddr,head->protocol_id);
- return status;
-#else
- return 0;
-#endif
-}
-
-
int arcnetS_rebuild_header(void *buff,struct device *dev,unsigned long dst,
struct sk_buff *skb)
{
*
* With ARCnet we have to convert everything to Ethernet-style stuff.
*/
-unsigned short arcnetA_type_trans(struct sk_buff *skb,struct device *dev)
-{
- struct ClientData *head;
- struct arcnet_local *lp=(struct arcnet_local *) (dev->priv);
-
- /* Pull off the arcnet header. */
- skb->mac.raw=skb->data;
- skb_pull(skb,dev->hard_header_len);
- head=(struct ClientData *)skb->mac.raw;
-
- if (head->daddr==0)
- skb->pkt_type=PACKET_BROADCAST;
- else if (dev->flags&IFF_PROMISC)
- {
- /* if we're not sending to ourselves :) */
- if (head->daddr != dev->dev_addr[0])
- skb->pkt_type=PACKET_OTHERHOST;
- }
-
- /* now return the protocol number */
- switch (head->protocol_id)
- {
- case ARC_P_IP: return htons(ETH_P_IP);
- case ARC_P_ARP: return htons(ETH_P_ARP);
- case ARC_P_RARP: return htons(ETH_P_RARP);
-
- case ARC_P_IPX:
- case ARC_P_NOVELL_EC:
- return htons(ETH_P_802_3);
- default:
- BUGMSG(D_NORMAL,"received packet of unknown protocol id %d (%Xh)\n",
- head->protocol_id,head->protocol_id);
- lp->stats.rx_errors++;
- lp->stats.rx_crc_errors++;
- return 0;
- }
-
- return htons(ETH_P_IP);
-}
-
-
unsigned short arcnetS_type_trans(struct sk_buff *skb,struct device *dev)
{
struct S_ClientData *head;
return htons(ETH_P_IP);
}
-
+#endif /* CONFIG_ARCNET_1051 */
/****************************************************************************
* *
/*+M*************************************************************************
- * Adaptec 274x/284x/294x device driver for Linux.
+ * Adaptec AIC7xxx device driver for Linux.
*
* Copyright (c) 1994 John Aycock
* The University of Calgary Department of Computer Science.
*
* -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95
*
- * $Id: aic7xxx.c,v 2.15 1995/12/17 19:43:20 deang Exp $
+ * $Id: aic7xxx.c,v 2.22 1996/02/10 09:22:50 deang Exp deang $
*-M*************************************************************************/
#ifdef MODULE
#include "scsi.h"
#include "hosts.h"
#include "aic7xxx.h"
+#include "aic7xxx_reg.h"
#include <linux/stat.h>
#include <linux/config.h> /* for CONFIG_PCI */
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
-#define AIC7XXX_C_VERSION "$Revision: 2.15 $"
+#define AIC7XXX_C_VERSION "$Revision: 2.22 $"
#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
#define MIN(a,b) ((a < b) ? a : b)
*
* o PCI bus support - this has been implemented and working since
* the December 1, 1994 release of this driver. If you don't have
- * a PCI bus and do not wish to configure your kernel with PCI
- * support, then make sure this define is set to the cprrect
- * define for PCI support (CONFIG_PCI) and configure your kernel
- * without PCI support (make config).
+ * a PCI bus, then you can configure your kernel without PCI
+ * support because all PCI dependent code is bracketed with
+ * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
*
* o Twin bus support - this has been tested and does work.
*
* 1 SCB for each device and ensure that there will always be
* a free SCB for up to 4 devices active at the same time.
*
- * Daniel M. Eischen, deischen@iworks.InterWorks.org, 03/11/95
+ * o 3985 support - The 3985 adapter is much like the 3940, but
+ * has three 7870 controllers as opposed to two for the 3940.
+ * It will get probed and recognized as three different adapters,
+ * but all three controllers share the same bank of 255 SCBs
+ * instead of each controller having their own bank (like the
+ * controllers on the 3940). For this reason, it is important
+ * that all devices be resident on just one channel of the 3985.
+ * In the near future, we'll modify the driver to reserve 1/3
+ * of the SCBs for each controller.
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/11/96
*/
/* Uncomment this for testing twin bus support. */
typedef enum {
LIST_HEAD,
- LIST_SECOND,
- LIST_TAIL
+ LIST_SECOND
} insert_type;
typedef enum {
/*
* Standard EISA Host ID regs (Offset from slot base)
*/
-#define HID0(x) ((x) + 0xC80) /* 0,1: msb of ID2, 2-7: ID1 */
-#define HID1(x) ((x) + 0xC81) /* 0-4: ID3, 5-7: LSB ID2 */
-#define HID2(x) ((x) + 0xC82) /* product */
-#define HID3(x) ((x) + 0xC83) /* firmware revision */
+#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
+#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
+#define HID2 0x82 /* product */
+#define HID3 0x83 /* firmware revision */
/*
* AIC-7770 I/O range to reserve for a card
*/
-#define MINREG(x) ((x) + 0xC00ul)
-#define MAXREG(x) ((x) + 0xCBFul)
-
-/* -------------------- AIC-7770 offset definitions ----------------------- */
-
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-#define SCSISEQ(x) ((x) + 0xC00ul)
-#define TEMODEO 0x80
-#define ENSELO 0x40
-#define ENSELI 0x20
-#define ENRSELI 0x10
-#define ENAUTOATNO 0x08
-#define ENAUTOATNI 0x04
-#define ENAUTOATNP 0x02
-#define SCSIRSTO 0x01
-
-
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-#define SXFRCTL0(x) ((x) + 0xC01ul)
-#define DFON 0x80
-#define DFPEXP 0x40
-#define ULTRAEN 0x20
-#define CLRSTCNT 0x10
-#define SPIOEN 0x08
-#define SCAMEN 0x04
-#define CLRCHN 0x02
-/* UNUSED 0x01 */
-
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-#define SXFRCTL1(x) ((x) + 0xC02ul)
-#define BITBUCKET 0x80
-#define SWRAPEN 0x40
-#define ENSPCHK 0x20
-#define STIMESEL 0x18
-#define ENSTIMER 0x04
-#define ACTNEGEN 0x02
-#define STPWEN 0x01 /* Powered Termination */
-
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-#define SCSISIGI(x) ((x) + 0xC03ul)
-#define CDI 0x80
-#define IOI 0x40
-#define MSGI 0x20
-#define ATNI 0x10
-#define SELI 0x08
-#define BSYI 0x04
-#define REQI 0x02
-#define ACKI 0x01
-
-/*
- * SCSI Contol Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus. Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-#define SCSISIGO(x) ((x) + 0xC03ul)
-#define CDO 0x80
-#define IOO 0x40
-#define MSGO 0x20
-#define ATNO 0x10
-#define SELO 0x08
-#define BSYO 0x04
-#define REQO 0x02
-#define ACKO 0x01
-
-/*
- * SCSI Rate
- */
-#define SCSIRATE(x) ((x) + 0xC04ul)
-#define WIDEXFER 0x80 /* Wide transfer control */
-#define SXFR 0x70 /* Sync transfer rate */
-#define SOFS 0x0F /* Sync offset */
-
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel
- */
-#define SCSIID(x) ((x) + 0xC05ul)
-#define TID 0xF0 /* Target ID mask */
-#define OID 0x0F /* Our ID mask */
-
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transfered
- * across the SCSI bus. The counter is decremented only once
- * the data has been safely transfered. SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */
-#define STCNT(x) ((x) + 0xC08ul)
-
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-#define SSTAT0(x) ((x) + 0xC0Bul)
-#define TARGET 0x80 /* Board is a target */
-#define SELDO 0x40 /* Selection Done */
-#define SELDI 0x20 /* Board has been selected */
-#define SELINGO 0x10 /* Selection In Progress */
-#define SWRAP 0x08 /* 24bit counter wrap */
-#define SDONE 0x04 /* STCNT = 0x000000 */
-#define SPIORDY 0x02 /* SCSI PIO Ready */
-#define DMADONE 0x01 /* DMA transfer completed */
-
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-#define CLRSINT1(x) ((x) + 0xC0Cul)
-#define CLRSELTIMEO 0x80
-#define CLRATNO 0x40
-#define CLRSCSIRSTI 0x20
-/* UNUSED 0x10 */
-#define CLRBUSFREE 0x08
-#define CLRSCSIPERR 0x04
-#define CLRPHASECHG 0x02
-#define CLRREQINIT 0x01
-
-/*
- * SCSI Status 1 (p. 3-24)
- * These interrupt bits are of interest to the kernel driver
- */
-#define SSTAT1(x) ((x) + 0xC0Cul)
-#define SELTO 0x80
-#define ATNTARG 0x40
-#define SCSIRSTI 0x20
-#define PHASEMIS 0x10
-#define BUSFREE 0x08
-#define SCSIPERR 0x04
-#define PHASECHG 0x02
-#define REQINIT 0x01
-
-/*
- * SCSI Interrrupt Mode 1 (pp. 3-28,29).
- * Set bits in this register enable the corresponding
- * interrupt source.
- */
-#define SIMODE1(x) ((x) + 0xC11ul)
-#define ENSELTIMO 0x80
-#define ENATNTARG 0x40
-#define ENSCSIRST 0x20
-#define ENPHASEMIS 0x10
-#define ENBUSFREE 0x08
-#define ENSCSIPERR 0x04
-#define ENPHASECHG 0x02
-#define ENREQINIT 0x01
-
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transfered on the SCSI bus. They are counted up in the same
- * manner as STCNT is counted down. SHADDR should always be used
- * to determine the address of the last byte transfered since HADDR
- * can be squewed by write ahead.
- */
-#define SHADDR(x) ((x) + 0xC14ul)
-
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id. The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-#define SELID(x) ((x) + 0xC19ul)
-#define SELID_MASK 0xF0
-#define ONEBIT 0x08
-/* UNUSED 0x07 */
-
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection. In a twin channel configuration
- * addresses 0x00-0x1E are gated to the appropriate channel based on this
- * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-#define SBLKCTL(x) ((x) + 0xC1Ful)
-/* UNUSED 0xC0 */
-#define DIAGLEDEN 0x80
-#define DIAGLEDON 0x40
-#define AUTOFLUSHDIS 0x20 /* used for Rev C check */
-/* UNUSED 0x10 */
-#define SELBUS_MASK 0x0F
-#define SELBUSB 0x08
-/* UNUSED 0x04 */
-#define SELWIDE 0x02
-/* UNUSED 0x01 */
-#define SELSINGLE 0x00
-
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-#define SEQCTL(x) ((x) + 0xC60ul)
-#define PERRORDIS 0x80
-#define PAUSEDIS 0x40
-#define FAILDIS 0x20
-#define FASTMODE 0x10
-#define BRKADRINTEN 0x08
-#define STEP 0x04
-#define SEQRESET 0x02
-#define LOADRAM 0x01
-
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
- * four bytes in sucessesion. The SEQADDRs will increment after the most
- * significant byte is written
- */
-#define SEQRAM(x) ((x) + 0xC61ul)
-
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-#define SEQADDR0(x) ((x) + 0xC62ul)
-#define SEQADDR1(x) ((x) + 0xC63ul)
-
-#define ACCUM(x) ((x) + 0xC64ul) /* accumulator */
-#define SINDEX(x) ((x) + 0xC65ul)
-
-/*
- * Board Control (p. 3-43)
- */
-#define BCTL(x) ((x) + 0xC84ul)
-/* RSVD 0xF0 */
-#define ACE 0x08 /* Support for external processors */
-/* RSVD 0x06 */
-#define ENABLE 0x01
-
-/*
- * Bus On/Off Time (p. 3-44)
- */
-#define BUSTIME(x) ((x) + 0xC85ul)
-#define BOFF 0xF0
-#define BON 0x0F
-
-/*
- * Bus Speed (p. 3-45)
- */
-#define BUSSPD(x) ((x) + 0xC86ul)
-#define DFTHRSH 0xC0
-#define STBOFF 0x38
-#define STBON 0x07
-
-/*
- * Host Control (p. 3-47) R/W
- * Overal host control of the device.
- */
-#define HCNTRL(x) ((x) + 0xC87ul)
-/* UNUSED 0x80 */
-#define POWRDN 0x40
-/* UNUSED 0x20 */
-#define SWINT 0x10
-#define IRQMS 0x08
-#define PAUSE 0x04
-#define INTEN 0x02
-#define CHIPRST 0x01
-#define REQ_PAUSE IRQMS | INTEN | PAUSE
-#define UNPAUSE_274X IRQMS | INTEN
-#define UNPAUSE_284X INTEN
-#define UNPAUSE_294X IRQMS | INTEN
-
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transfered across the host bus.
- */
-#define HADDR(x) ((x) + 0xC88ul)
-
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-#define SCBPTR(x) ((x) + 0xC90ul)
-
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-#define INTSTAT(x) ((x) + 0xC91ul)
-#define SEQINT_MASK 0xF0 /* SEQINT Status Codes */
-#define BAD_PHASE 0x00
-#define SEND_REJECT 0x10
-#define NO_IDENT 0x20
-#define NO_MATCH 0x30
-#define MSG_SDTR 0x40
-#define MSG_WDTR 0x50
-#define MSG_REJECT 0x60
-#define BAD_STATUS 0x70
-#define RESIDUAL 0x80
-#define ABORT_TAG 0x90
-#define AWAITING_MSG 0xA0
-#define IMMEDDONE 0xB0
-#define BRKADRINT 0x08
-#define SCSIINT 0x04
-#define CMDCMPLT 0x02
-#define SEQINT 0x01
-#define INT_PEND (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
-
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors. You usually cannot recover from
- * these without a full board reset.
- */
-#define ERROR(x) ((x) + 0xC92ul)
-/* UNUSED 0xF0 */
-#define PARERR 0x08
-#define ILLOPCODE 0x04
-#define ILLSADDR 0x02
-#define ILLHADDR 0x01
-
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-#define CLRINT(x) ((x) + 0xC92ul)
-#define CLRBRKADRINT 0x08
-#define CLRSCSIINT 0x04
-#define CLRCMDINT 0x02
-#define CLRSEQINT 0x01
-
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-#define SCBCNT(x) ((x) + 0xC9Aul)
-#define SCBAUTO 0x80
-#define SCBCNT_MASK 0x1F
-
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the seqencer has yet to start)
- */
-#define QINFIFO(x) ((x) + 0xC9Bul)
-
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-#define QINCNT(x) ((x) + 0xC9Cul)
-
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-#define QOUTFIFO(x) ((x) + 0xC9Dul)
-
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-#define QOUTCNT(x) ((x) + 0xC9Eul)
-
-#define SCBARRAY(x) ((x) + 0xCA0ul)
-
-/* ---------------- END AIC-7770 Register Definitions ----------------- */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-#define SEECTL_2840(x) ((x) + 0xCC0ul)
-/* UNUSED 0xF8 */
-#define CS_2840 0x04
-#define CK_2840 0x02
-#define DO_2840 0x01
-
-#define STATUS_2840(x) ((x) + 0xCC1ul)
-#define EEPROM_TF 0x80
-#define BIOS_SEL 0x60
-#define ADSEL 0x1E
-#define DI_2840 0x01
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-#define DSPCISTATUS(x) ((x) + 0xC86ul)
-#define DFTHRESH 0xC0
-
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device. In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device. When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM. When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted. See aic7xxx_read_eprom for detailed information on
- * the protocol necessary to read the serial EEPROM.
- */
-#define SEECTL(x) ((x) + 0xC1Eul)
-#define EXTARBACK 0x80
-#define EXTARBREQ 0x40
-#define SEEMS 0x20
-#define SEERDY 0x10
-#define SEECS 0x08
-#define SEECK 0x04
-#define SEEDO 0x02
-#define SEEDI 0x01
-
-#define DEVREVID 0x08ul
-
-#define DEVSTATUS 0x40ul
-#define MPORTMODE 0x04 /* aic7870 only */
-#define RAMPSM 0x02 /* aic7870 only */
-#define VOLSENSE 0x01
-
-#define DEVCONFIG 0x41ul
-#define SCBRAMSEL 0x80
-#define MRDCEN 0x40
-#define EXTSCBTIME 0x20 /* aic7870 only */
-#define EXTSCBPEN 0x10 /* aic7870 only */
-#define BERREN 0x08
-#define DACEN 0x04
-#define STPWLEVEL 0x02
-#define DIFACTNEGEN 0x01 /* aic7870 only */
-
-/* Scratch RAM offset definitions */
-
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the Linux sequencer code. If I can figure out
- * how to read the EISA configuration info at probe time, the cards could
- * be run without BIOS support installed
- */
+#define MINREG 0xC00
+#define MAXREG 0xCBF
-/*
- * 1 byte per target starting at this address for configuration values
- */
-#define HA_TARG_SCRATCH(x) ((x) + 0xC20ul)
-
-/*
- * The sequencer will stick the first byte of any rejected message here so
- * we can see what is getting thrown away.
- */
-#define HA_REJBYTE(x) ((x) + 0xC31ul)
+#define INTDEF 0x5C /* Interrupt Definition Register */
/*
- * Bit vector of targets that have disconnection disabled.
+ * Some defines for the HCNTRL register.
*/
-#define HA_DISC_DSB(x) ((x) + 0xC32ul)
+#define REQ_PAUSE IRQMS | INTEN | PAUSE
+#define UNPAUSE_274X IRQMS | INTEN
+#define UNPAUSE_284X INTEN
+#define UNPAUSE_294X IRQMS | INTEN
/*
- * Length of pending message
+ * AIC-78X0 PCI registers
*/
-#define HA_MSG_LEN(x) ((x) + 0xC34ul)
-
-/*
- * Outgoing Message Body
- */
-#define HA_MSG_START(x) ((x) + 0xC35ul)
-
-/*
- * These are offsets into the card's scratch ram. Some of the values are
- * specified in the AHA2742 technical reference manual and are initialized
- * by the BIOS at boot time.
- */
-#define HA_ARG_1(x) ((x) + 0xC4Aul) /* sdtr <-> rate parameters */
-#define HA_RETURN_1(x) ((x) + 0xC4Aul)
-#define SEND_SENSE 0x80
-#define SEND_SDTR 0x80
-#define SEND_WDTR 0x80
-#define SEND_REJ 0x40
-
-#define SG_COUNT(x) ((x) + 0xC4Dul)
-#define SG_NEXT(x) ((x) + 0xC4Eul)
-#define HA_SIGSTATE(x) ((x) + 0xC4Bul) /* value in SCSISIGO */
-#define HA_SCBCOUNT(x) ((x) + 0xC52ul) /* number of hardware SCBs */
-
-#define HA_FLAGS(x) ((x) + 0xC53ul) /* TWIN and WIDE bus flags */
-#define SINGLE_BUS 0x00
-#define TWIN_BUS 0x01
-#define WIDE_BUS 0x02
-#define ACTIVE_MSG 0x20
-#define IDENTIFY_SEEN 0x40
-#define RESELECTING 0x80
-
-#define HA_ACTIVE0(x) ((x) + 0xC54ul) /* Active bits; targets 0-7 */
-#define HA_ACTIVE1(x) ((x) + 0xC55ul) /* Active bits; targets 8-15 */
-#define SAVED_TCL(x) ((x) + 0xC56ul) /* Saved target, channel, LUN */
-#define WAITING_SCBH(x) ((x) + 0xC57ul) /* Head of disconnected targets list. */
-#define WAITING_SCBT(x) ((x) + 0xC58ul) /* Tail of disconnected targets list. */
-
-#define HA_SCSICONF(x) ((x) + 0xC5Aul) /* SCSI config register */
-#define HA_INTDEF(x) ((x) + 0xC5Cul) /* interrupt def'n register */
-#define HA_HOSTCONF(x) ((x) + 0xC5Dul) /* host config def'n register */
-
-#define HA_274_BIOSCTRL(x) ((x) + 0xC5Ful) /* BIOS enabled for 274x */
-#define BIOSMODE 0x30
-#define BIOSDISABLED 0x30
-
-#define MSG_ABORT 0x06
-#define MSG_BUS_DEVICE_RESET 0x0C
-#define BUS_8_BIT 0x00
-#define BUS_16_BIT 0x01
-#define BUS_32_BIT 0x02
-
+#define CLASS_PROGIF_REVID 0x08
+#define DEVREVID 0x000000FFul
+#define PROGINFC 0x0000FF00ul
+#define SUBCLASS 0x00FF0000ul
+#define BASECLASS 0xFF000000ul
+
+#define CSIZE_LATTIME 0x0C
+#define CACHESIZE 0x0000003Ful /* only 5 bits */
+#define LATTIME 0x0000FF00ul
+
+#define DEVCONFIG 0x40
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
/*
*
unsigned short res_1[11]; /* words 20-30 */
unsigned short checksum; /* word 31 */
-
};
/*
* sections.
*/
#define PAUSE_SEQUENCER(p) \
- outb(p->pause, HCNTRL(p->base)); \
- while ((inb(HCNTRL(p->base)) & PAUSE) == 0) \
+ outb(p->pause, HCNTRL + p->base); \
+ while ((inb(HCNTRL + p->base) & PAUSE) == 0) \
; \
/*
* warrant an easy way to do it.
*/
#define UNPAUSE_SEQUENCER(p) \
- outb(p->unpause, HCNTRL(p->base))
+ outb(p->unpause, HCNTRL + p->base)
/*
* Restart the sequencer program from address zero
*/
#define RESTART_SEQUENCER(p) \
do { \
- outb(SEQRESET | FASTMODE, SEQCTL(p->base)); \
- } while (inb(SEQADDR0(p->base)) != 0 && \
- inb(SEQADDR1(p->base)) != 0); \
+ outb(SEQRESET | FASTMODE, SEQCTL + p->base); \
+ } while (inb(SEQADDR0 + p->base) != 0 && \
+ inb(SEQADDR1 + p->base) != 0); \
UNPAUSE_SEQUENCER(p);
/*
/*
* The driver keeps up to four scb structures per card in memory. Only the
- * first 26 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookeeping.
+ * first 25 bytes of the structure are valid for the hardware, the rest used
+ * for driver level bookkeeping.
*/
-#define SCB_DOWNLOAD_SIZE 26 /* amount to actually download */
-#define SCB_UPLOAD_SIZE 26 /* amount to actually upload */
struct aic7xxx_scb {
/* ------------ Begin hardware supported fields ---------------- */
-/*1 */ unsigned char control;
-#define SCB_NEEDWDTR 0x80 /* Initiate Wide Negotiation */
-#define SCB_DISCENB 0x40 /* Disconnection Enable */
-#define SCB_TE 0x20 /* Tag enable */
-#define SCB_NEEDSDTR 0x10 /* Initiate Sync Negotiation */
-#define SCB_NEEDDMA 0x08 /* Refresh SCB from host ram */
-#define SCB_DIS 0x04
-#define SCB_TAG_TYPE 0x03
-#define SIMPLE_QUEUE 0x00
-#define HEAD_QUEUE 0x01
-#define OR_QUEUE 0x02
-/* ILLEGAL 0x03 */
-/*2 */ unsigned char target_channel_lun; /* 4/1/3 bits */
-/*3 */ unsigned char SG_segment_count;
-/*7 */ unsigned char SG_list_pointer[4] __attribute__ ((packed));
-/*11*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
-/*12*/ unsigned char SCSI_cmd_length;
-/*14*/ unsigned char RESERVED[2]; /* must be zero */
-/*15*/ unsigned char target_status;
-/*18*/ unsigned char residual_data_count[3];
-/*19*/ unsigned char residual_SG_segment_count;
-/*23*/ unsigned char data_pointer[4] __attribute__ ((packed));
-/*26*/ unsigned char data_count[3];
-/*30*/ unsigned char host_scb[4] __attribute__ ((packed));
-/*31*/ u_char next_waiting; /* Used to thread SCBs awaiting selection. */
-#define SCB_LIST_NULL 0xFF /* SCB list equivelent to NULL */
-#if 0
- /*
- * No real point in transferring this to the
- * SCB registers.
- */
- unsigned char RESERVED[1];
-#endif
-
+/* 0*/ unsigned char control;
+/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
+/* 2*/ unsigned char target_status;
+/* 3*/ unsigned char SG_segment_count;
+/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 8*/ unsigned char residual_SG_segment_count;
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned char data_pointer[4] __attribute__ ((packed));
+/*16*/ unsigned long data_count;
+/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/*24*/ unsigned char SCSI_cmd_length;
+#define SCB_PIO_TRANSFER_SIZE 25 /*
+ * amount we need to upload/download
+ * via rep in/outsb to perform
+ * a request sense. The second
+ * RESERVED byte is initialized to
+ * 0 in getscb().
+ */
+/*25*/ u_char next_waiting; /* Used to thread SCBs awaiting selection. */
/*-----------------end of hardware supported fields----------------*/
struct aic7xxx_scb *next; /* next ptr when in free list */
Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */
};
-typedef void (*timeout_fn)(unsigned long);
-
static struct {
unsigned char errno;
const char *errmesg;
static int num_aic7xxx_syncrates =
sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
+#ifdef CONFIG_PCI
static int number_of_39xxs = 0;
+#endif CONFIG_PCI
#ifdef AIC7XXX_DEBUG
-
static void
debug(const char *fmt, ...)
{
static int SST[] = { 256, 128, 64, 32 };
static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
- host_conf = inb(HA_HOSTCONF(p->base));
- scsi_conf = inb(HA_SCSICONF(p->base));
+ host_conf = inb(HOSTCONF + p->base);
+ scsi_conf = inb(SCSICONF + p->base);
/*
* The 7870 gets the bus release time and data FIFO threshold
scb->SCSI_cmd_length);
printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
(scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
- scb->residual_SG_segment_count, (scb->residual_data_count[2] << 16) |
- (scb->residual_data_count[1] << 8) | scb->residual_data_count[0]);
- printk("data ptr 0x%x, data count %d, host scb 0x%x, next waiting %d\n",
+ scb->residual_SG_segment_count, scb->residual_data_count);
+ printk("data ptr 0x%x, data count %d, next waiting %d\n",
(scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
(scb->data_pointer[1] << 8) | scb->data_pointer[0],
- (scb->data_count[2] << 16) | (scb->data_count[1] << 8) | scb->data_count[0],
- (unsigned int) scb->host_scb, scb->next_waiting);
+ scb->data_count, scb->next_waiting);
printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
(unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
scb->position);
* sequencer RAM parity error detection while loading, and
* make sure the LOADRAM bit is enabled for loading.
*/
- outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL(base));
+ outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (seqprog), "c" (sizeof(seqprog)), "d" (SEQRAM(base))
- :"si", "cx", "dx");
+ outsb(SEQRAM + base, seqprog, sizeof(seqprog));
/*
* WARNING! This is a magic sequence! After extensive
* the address shows up as
* zero just to be safe..
*/
- outb(SEQRESET | FASTMODE, SEQCTL(base));
- } while ((inb(SEQADDR0(base)) != 0) && (inb(SEQADDR1(base)) != 0));
+ outb(SEQRESET | FASTMODE, SEQCTL + base);
+ } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
}
/*+F*************************************************************************
*-F*************************************************************************/
static void
aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
- unsigned char period, unsigned char offset,
+ short period, unsigned char offset,
int target, char channel)
{
int i;
{
if (!(aic7xxx_syncrates[i].rate & ULTRA_SXFR))
{
- printk ("aic7xxx: Target %d, channel %c, requests %sMB/s transfers, "
- "but adapter in Ultra mode can only sync at 10MB/s or "
+ printk ("aic7xxx: Target %d, channel %c, requests %sMHz transfers, "
+ "but adapter in Ultra mode can only sync at 10MHz or "
"above.\n", target, channel, aic7xxx_syncrates[i].english);
break; /* Use asynchronous transfers. */
}
}
}
*scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
- printk("aic7xxx: Target %d, channel %c, now synchronous at %sMB/s, "
+ printk("aic7xxx: Target %d, channel %c, now synchronous at %sMHz, "
"offset(0x%x).\n",
target, channel, aic7xxx_syncrates[i].english, offset);
return;
* Description:
* Transfer a SCB to the controller.
*-F*************************************************************************/
-static void
-aic7xxx_putscb(int base, struct aic7xxx_scb *scb)
+static inline void
+aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
- /*
- * All we need to do, is to output the position
- * of the SCB in the SCBARRAY to the QINFIFO
- * of the host adapter.
- */
- outb(scb->position, QINFIFO(base));
-}
+ unsigned char curscb;
+ int base = p->base;
+
+ curscb = inb(SCBPTR + base);
+ outb(scb->position, SCBPTR + base);
+ outb(SCBAUTO, SCBCNT + base);
-/*+F*************************************************************************
- * Function:
- * aic7xxx_putscb_dma
- *
- * Description:
- * DMA a SCB to the controller.
- *-F*************************************************************************/
-static void
-aic7xxx_putscb_dma(int base, struct aic7xxx_scb *scb)
-{
/*
* By turning on the SCB auto increment, any reference
* to the SCB I/O space postincrements the SCB address
* we're looking at. So turn this on and dump the relevant
* portion of the SCB to the card.
+ *
+ * We can do 16bit transfers on all but 284x.
*/
- outb(SCBAUTO, SCBCNT(base));
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (scb), "c" (31), "d" (SCBARRAY(base))
- :"si", "cx", "dx");
+ if (p->type == AIC_284x)
+ {
+ outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ }
+ else
+ {
+ outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+ }
- outb(0, SCBCNT(base));
+ outb(0, SCBCNT + base);
+ outb(curscb, SCBPTR + base);
}
/*+F*************************************************************************
* Description:
* Get a SCB from the controller.
*-F*************************************************************************/
-static void
-aic7xxx_getscb(int base, struct aic7xxx_scb *scb)
+static inline void
+aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
{
+ int base = p->base;
+
/*
* This is almost identical to aic7xxx_putscb().
*/
- outb(SCBAUTO, SCBCNT(base));
-
- asm volatile("cld\n\t"
- "rep\n\t"
- "insb"
- : /* no output */
- :"D" (scb), "c" (SCB_UPLOAD_SIZE), "d" (SCBARRAY(base))
- :"di", "cx", "dx");
-
- outb(0, SCBCNT(base));
+ outb(SCBAUTO, SCBCNT + base);
+ insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ outb(0, SCBCNT + base);
}
/*+F*************************************************************************
}
}
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_busy_target
+ *
+ * Description:
+ * Set the specified target active.
+ *-F*************************************************************************/
+static void
+aic7xxx_busy_target(unsigned char target, char channel, int base)
+{
+ unsigned char active;
+ unsigned long active_port = ACTIVE_A + base;
+
+ if ((target > 0x07) || (channel == 'B'))
+ {
+ /*
+ * targets on the Second channel or above id 7 store info in byte two
+ * of ACTIVE
+ */
+ active_port++;
+ }
+ active = inb(active_port);
+ active |= (0x01 << (target & 0x07));
+ outb(active, active_port);
+}
+
/*+F*************************************************************************
* Function:
* aic7xxx_unbusy_target
aic7xxx_unbusy_target(unsigned char target, char channel, int base)
{
unsigned char active;
- unsigned long active_port = HA_ACTIVE0(base);
+ unsigned long active_port = ACTIVE_A + base;
-#ifdef AIC7XXX_DEBUG_ABORT
+#ifdef 0
printk ("aic7xxx: (unbusy_target) target/channel %d/%c\n",
target, channel);
#endif
{
/*
* targets on the Second channel or above id 7 store info in byte two
- * of HA_ACTIVE
+ * of ACTIVE
*/
active_port++;
}
active = inb(active_port);
active &= ~(0x01 << (target & 0x07));
- outb(active_port, active);
+ outb(active, active_port);
}
/*+F*************************************************************************
*/
scb->state = SCB_FREE;
scb->next = p->free_scb;
- p->free_scb = &(p->scb_array[scb->position]);
+ p->free_scb = scb;
scb->cmd = NULL;
restore_flags(flags);
struct aic7xxx_scb *scb,
insert_type where)
{
- unsigned char head, tail;
+ unsigned char head;
unsigned char curscb;
- curscb = inb(SCBPTR(base));
- head = inb(WAITING_SCBH(base));
- tail = inb(WAITING_SCBT(base));
+ curscb = inb(SCBPTR + base);
+ head = inb(WAITING_SCBH + base);
if (head == SCB_LIST_NULL)
{
/*
* List was empty
*/
head = scb->position;
- tail = SCB_LIST_NULL;
}
else
{
if (where == LIST_HEAD)
{
- outb(scb->position, SCBPTR(base));
- outb(head, SCBARRAY(base) + 30);
+ outb(scb->position, SCBPTR + base);
+ outb(head, SCB_NEXT_WAITING + base);
head = scb->position;
}
else
{
- if (tail == SCB_LIST_NULL)
- {
- /*
- * List had one element
- */
- tail = scb->position;
- outb(head, SCBPTR(base));
- outb(tail, SCBARRAY(base) + 30);
- }
- else
- {
- if (where == LIST_SECOND)
- {
- unsigned char third_scb;
-
- outb(head, SCBPTR(base));
- third_scb = inb(SCBARRAY(base) + 30);
- outb(scb->position, SCBARRAY(base) + 30);
- outb(scb->position, SCBPTR(base));
- outb(third_scb, SCBARRAY(base) + 30);
- }
- else
- {
- outb(tail, SCBPTR(base));
- tail = scb->position;
- outb(tail, SCBARRAY(base) + 30);
- }
- }
+ /* where == LIST_SECOND */
+ unsigned char third_scb;
+
+ outb(head, SCBPTR + base);
+ third_scb = inb(SCB_NEXT_WAITING + base);
+ outb(scb->position, SCB_NEXT_WAITING + base);
+ outb(scb->position, SCBPTR + base);
+ outb(third_scb, SCB_NEXT_WAITING + base);
}
}
- outb(head, WAITING_SCBH(base));
- outb(tail, WAITING_SCBT(base));
- outb(curscb, SCBPTR(base));
+ outb(head, WAITING_SCBH + base);
+ outb(curscb, SCBPTR + base);
}
/*+F*************************************************************************
* Select the SCB we want to abort and
* pull the next pointer out of it.
*/
- curscb = inb(SCBPTR(base));
- outb(scb->position, SCBPTR(base));
- next = inb(SCBARRAY(base) + 30);
+ curscb = inb(SCBPTR + base);
+ outb(scb->position, SCBPTR + base);
+ next = inb(SCB_NEXT_WAITING + base);
/*
* Clear the necessary fields
*/
- outb(SCB_NEEDDMA, SCBARRAY(base));
- outb(SCB_LIST_NULL, SCBARRAY(base) + 30);
+ outb(0, SCBARRAY + base);
+ outb(SCB_LIST_NULL, SCB_NEXT_WAITING + base);
aic7xxx_unbusy_target(target, channel, base);
/*
/*
* First in the list
*/
- outb(next, WAITING_SCBH(base));
+ outb(next, WAITING_SCBH + base);
}
else
{
/*
* Select the scb that pointed to us and update its next pointer.
*/
- outb(prev, SCBPTR(base));
- outb(next, SCBARRAY(base) + 30);
+ outb(prev, SCBPTR + base);
+ outb(next, SCB_NEXT_WAITING + base);
}
/*
- * Update the tale pointer
+ * Update the tail pointer
*/
- if (inb(WAITING_SCBT(base)) == scb->position)
+ if (inb(WAITING_SCBT + base) == scb->position)
{
- outb(prev, WAITING_SCBT(base));
+ outb(prev, WAITING_SCBT + base);
}
/*
* and inform the SCSI system that the command
* has been aborted.
*/
- outb(curscb, SCBPTR(base));
+ outb(curscb, SCBPTR + base);
scb->state |= SCB_ABORTED;
scb->cmd->result = (DID_RESET << 16);
aic7xxx_done(p, scb);
/*
* Restore this when we're done
*/
- active_scb = inb(SCBPTR(base));
+ active_scb = inb(SCBPTR + base);
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (reset_device) target/channel %d/%c, to_scb %d, "
*/
{
int saved_queue[AIC7XXX_MAXSCB];
- int queued = inb(QINCNT(base));
+ int queued = inb(QINCNT + base);
for (i = 0; i < (queued - found); i++)
{
- saved_queue[i] = inb(QINFIFO(base));
+ saved_queue[i] = inb(QINFIFO + base);
scb = &(p->scb_array[saved_queue[i]]);
if (aic7xxx_match_scb(scb, target, channel))
{
scb->state |= SCB_ABORTED;
scb->cmd->result = (DID_RESET << 16);
aic7xxx_done(p, scb);
- outb(scb->position, SCBPTR(base));
- outb(SCB_NEEDDMA, SCBARRAY(base));
+ outb(scb->position, SCBPTR + base);
+ outb(0, SCBARRAY + base);
i--;
found++;
}
*/
for (queued = 0; queued < i; queued++)
{
- outb(saved_queue[queued], QINFIFO(base));
+ outb(saved_queue[queued], QINFIFO + base);
}
}
{
unsigned char next, prev;
- next = inb(WAITING_SCBH(base)); /* Start at head of list. */
+ next = inb(WAITING_SCBH + base); /* Start at head of list. */
prev = SCB_LIST_NULL;
while (next != SCB_LIST_NULL)
}
else
{
- outb(scb->position, SCBPTR(base));
+ outb(scb->position, SCBPTR + base);
prev = next;
- next = inb(SCBARRAY(base) + 30);
+ next = inb(SCB_NEXT_WAITING + base);
}
}
}
* Ensure the target is "free"
*/
aic7xxx_unbusy_target(target, channel, base);
- outb(scb->position, SCBPTR(base));
- outb(SCB_NEEDDMA, SCBARRAY(base));
+ outb(scb->position, SCBPTR + base);
+ outb(0, SCBARRAY + base);
scb->state |= SCB_ABORTED;
scb->cmd->result = (DID_RESET << 16);
aic7xxx_done(p, scb);
}
}
- outb(active_scb, SCBPTR(base));
+ outb(active_scb, SCBPTR + base);
return (found);
}
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (reset_current_bus)\n");
#endif
- outb(SCSIRSTO, SCSISEQ(base));
+ outb(SCSIRSTO, SCSISEQ + base);
udelay(1000);
- outb(0, SCSISEQ(base));
+ outb(0, SCSISEQ + base);
}
/*+F*************************************************************************
{
p->needsdtr |= (p->needsdtr_copy & 0xFF00);
p->sdtr_pending &= 0x00FF;
- outb(0, HA_ACTIVE1(base));
- offset = HA_TARG_SCRATCH(base) + 8;
- offset_max = HA_TARG_SCRATCH(base) + 16;
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base + 8;
+ offset_max = TARG_SCRATCH + base + 16;
}
else
{
{
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
- p->sdtr_pending = 0;
- p->wdtr_pending = 0;
- outb(0, HA_ACTIVE0(base));
- outb(0, HA_ACTIVE1(base));
- offset = HA_TARG_SCRATCH(base);
- offset_max = HA_TARG_SCRATCH(base) + 16;
+ p->sdtr_pending = 0x0;
+ p->wdtr_pending = 0x0;
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 16;
}
else
{
p->needsdtr |= (p->needsdtr_copy & 0x00FF);
p->sdtr_pending &= 0xFF00;
- outb(0, HA_ACTIVE0(base));
- offset = HA_TARG_SCRATCH(base);
- offset_max = HA_TARG_SCRATCH(base) + 8;
+ outb(0, ACTIVE_A + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 8;
}
}
while (offset < offset_max)
/*
* Case 1: Command for another bus is active
*/
- sblkctl = inb(SBLKCTL(base));
+ sblkctl = inb(SBLKCTL + base);
cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
if (cur_channel != channel)
{
/*
* Stealthily reset the other bus without upsetting the current bus
*/
- outb(sblkctl ^ SELBUSB, SBLKCTL(base));
+ outb(sblkctl ^ SELBUSB, SBLKCTL + base);
aic7xxx_reset_current_bus(base);
- outb(sblkctl, SBLKCTL(base));
+ outb(sblkctl, SBLKCTL + base);
UNPAUSE_SEQUENCER(p);
}
int base, intstat;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
- unsigned char ha_flags, transfer;
+ unsigned char ha_flags;
+ short transfer;
unsigned char scsi_id, bus_width;
unsigned char offset, rate, scratch, scratch_offset;
unsigned char max_offset, rej_byte;
- unsigned short target_mask, active;
+ unsigned short target_mask;
char channel;
void *addr;
int actual;
* Search for the host with a pending interrupt. If we can't find
* one, then we've encountered a spurious interrupt.
*/
- while ((p != NULL) && !(inb(INTSTAT(p->base)) & INT_PEND))
+ while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
{
if (p->next == NULL)
{
*/
p->isr_count++;
- if ((p->a_scanned == 0) && (p->isr_count == 1))
+ if (!p->a_scanned && (p->isr_count == 1))
{
/*
* We must only have one card at this IRQ and it must have been
* Handle all the interrupt sources - especially for SCSI
* interrupts, we won't get a second chance at them.
*/
- intstat = inb(INTSTAT(base));
+ intstat = inb(INTSTAT + base);
if (intstat & BRKADRINT)
{
int i;
- unsigned char errno = inb(ERROR(base));
+ unsigned char errno = inb(ERROR + base);
printk("aic7xxx: (aic7xxx_isr) BRKADRINT error(0x%x):\n", errno);
for (i = 0; i < NUMBER(hard_error); i++)
}
panic("aic7xxx: (aic7xxx_isr) BRKADRINT, error(0x%x) seqaddr(0x%x).\n",
- inb(ERROR(base)), (inb(SEQADDR1(base)) << 8) | inb(SEQADDR0(base)));
+ inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
}
if (intstat & SEQINT)
*/
PAUSE_SEQUENCER(p);
- scsi_id = (inb(SCSIID(base)) >> 4) & 0x0F;
+ scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
scratch_offset = scsi_id;
channel = 'A';
- if (inb(SBLKCTL(base)) & SELBUSB)
+ if (inb(SBLKCTL + base) & SELBUSB)
{
channel = 'B';
scratch_offset += 8;
break;
case SEND_REJECT:
- rej_byte = inb(HA_REJBYTE(base));
- if (rej_byte != 0x20)
- {
- debug("aic7xxx: Warning - Issuing message reject, 1st byte(0x%x)\n",
- rej_byte);
- }
- else
+ rej_byte = inb(REJBYTE + base);
+ if ((rej_byte & 0xF0) == 0x20)
{
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- printk("aic7xxx: Warning - Tagged message rejected for target %d,"
- " channel %c.\n", scsi_id, channel);
+ printk("aic7xxx: Warning - Tagged message received without identify."
+ "Disabling tagged commands for target %d channel %c.\n",
+ scsi_id, channel);
scb->cmd->device->tagged_supported = 0;
scb->cmd->device->tagged_queue = 0;
}
+ else
+ {
+ debug("aic7xxx: Warning - Rejecting unknown message (0x%x) received "
+ "from target %d channel %c.\n", rej_byte, scsi_id, channel);
+ }
break;
case NO_IDENT:
panic("aic7xxx: Target %d, channel %c, did not send an IDENTIFY "
"message. SAVED_TCL(0x%x).\n",
- scsi_id, channel, inb(SAVED_TCL(base)));
+ scsi_id, channel, inb(SAVED_TCL + base));
break;
case NO_MATCH:
printk("aic7xxx: No active SCB for reconnecting target %d, "
"channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
- scsi_id, channel, inb(SAVED_TCL(base)));
+ scsi_id, channel, inb(SAVED_TCL + base));
aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(SCB_NEEDDMA, SCBARRAY(base));
-
- outb(CLRSELTIMEO, CLRSINT1(base));
+ outb(0, SCBARRAY + base);
+ outb(CLRSELTIMEO, CLRSINT1 + base);
RESTART_SEQUENCER(p);
break;
- case MSG_SDTR:
+ case SDTR_MSG:
/*
* Help the sequencer to translate the negotiated
* transfer rate. Transfer is 1/4 the period
* in ns as is returned by the sync negotiation
* message. So, we must multiply by four.
*/
- transfer = (inb(HA_ARG_1(base)) << 2);
- offset = inb(ACCUM(base));
- scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+ transfer = (inb(ARG_1 + base) << 2);
+ offset = inb(ACCUM + base);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
/*
* The maximum offset for a wide device is 0x08; for a
* 8-bit bus device the maximum offset is 0x0F.
{
max_offset = 0x0F;
}
- aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset), scsi_id, channel);
+ aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
+ scsi_id, channel);
/*
* Preserve the wide transfer flag.
*/
scratch = rate | (scratch & WIDEXFER);
- outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
- outb(scratch, SCSIRATE(base));
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
if ((scratch & 0x0F) == 0)
{ /*
* The requested rate was so low that asynchronous transfers
* them), so we issue a reject to ensure we go to asynchronous
* transfers.
*/
- outb(SEND_REJ, HA_RETURN_1(base));
+ outb(SEND_REJ, RETURN_1 + base);
}
else
{
/*
* Don't send an SDTR back to the target.
*/
- outb(0, HA_RETURN_1(base));
+ outb(0, RETURN_1 + base);
}
else
{
* Send our own SDTR in reply.
*/
printk("aic7xxx: Sending SDTR!!\n");
- outb(SEND_SDTR, HA_RETURN_1(base));
+ outb(SEND_SDTR, RETURN_1 + base);
}
}
/*
*/
p->needsdtr &= ~target_mask;
p->sdtr_pending &= ~target_mask;
-#if 0
- scb_index = inb(SCBPTR(base));
- scb = &(p->scb_array[scb_index]);
- debug_scb(scb);
-#endif
-
break;
- case MSG_WDTR:
+ case WDTR_MSG:
{
- bus_width = inb(ACCUM(base));
+ bus_width = inb(ARG_1 + base);
printk("aic7xxx: Received MSG_WDTR, Target %d, channel %c "
"needwdtr(0x%x).\n", scsi_id, channel, p->needwdtr);
- scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
if (p->wdtr_pending & target_mask)
{
/*
* Don't send an WDTR back to the target, since we asked first.
*/
- outb(0, HA_RETURN_1(base));
+ outb(0, RETURN_1 + base);
switch (bus_width)
{
case BUS_8_BIT:
"transfers.\n", scsi_id, channel);
scratch |= 0x80;
break;
+
+ case BUS_32_BIT:
+ outb(SEND_REJ, RETURN_1 + base);
+ printk("aic7xxx: Target %d, channel %c, requesting 32 bit "
+ "transfers, rejecting...\n", scsi_id, channel);
+ break;
}
}
else
scratch |= 0x80;
break;
}
- outb(bus_width | SEND_WDTR, HA_RETURN_1(base));
+ outb(bus_width | SEND_WDTR, RETURN_1 + base);
}
p->needwdtr &= ~target_mask;
p->wdtr_pending &= ~target_mask;
- outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
- outb(scratch, SCSIRATE(base));
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
break;
}
- case MSG_REJECT:
+ case REJECT_MSG:
{
/*
* What we care about here is if we had an
* the target is refusing negotiation.
*/
- scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
if (p->wdtr_pending & target_mask)
{
scratch &= 0x7F;
p->needwdtr &= ~target_mask;
p->wdtr_pending &= ~target_mask;
- outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
printk("aic7xxx: Target %d, channel %c, refusing WIDE negotiation. "
"Using 8 bit transfers.\n", scsi_id, channel);
}
scratch &= 0xF0;
p->needsdtr &= ~target_mask;
p->sdtr_pending &= ~target_mask;
- outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
printk("aic7xxx: Target %d, channel %c, refusing synchronous "
"negotiation. Using asynchronous transfers.\n",
scsi_id, channel);
* Otherwise, we ignore it.
*/
}
- outb(scratch, HA_TARG_SCRATCH(base) + scratch_offset);
- outb(scratch, SCSIRATE(base));
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
break;
}
case BAD_STATUS:
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- outb(0, HA_RETURN_1(base)); /* CHECK_CONDITION may change this */
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
"scb(%d) state(0x%x) cmd(0x%x).\n",
else
{
cmd = scb->cmd;
- aic7xxx_getscb(base, scb);
+ aic7xxx_getscb(p, scb);
aic7xxx_status(cmd) = scb->target_status;
cmd->result |= scb->target_status;
void *req_buf;
tcl = scb->target_channel_lun;
+
/*
* Send a sense command to the requesting target.
*/
req_buf = &scb->sense_sg;
cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
control = scb->control;
- memset(scb, 0, SCB_DOWNLOAD_SIZE);
- scb->control = control & SCB_DISCENB;
+
+ memset(scb, 0, SCB_PIO_TRANSFER_SIZE);
+ scb->control = control & DISCENB;
scb->target_channel_lun = tcl;
addr = scb->sense_cmd;
scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
scb->SG_segment_count = 1;
memcpy(scb->SG_list_pointer, &req_buf,
sizeof(scb->SG_list_pointer));
- scb->data_count[0] = scb->sense_sg.length & 0xFF;
- scb->data_count[1] = (scb->sense_sg.length >> 8) & 0xFF;
- scb->data_count[2] = (scb->sense_sg.length >> 16) & 0xFF;
+ scb->data_count = scb->sense_sg.length;
memcpy(scb->data_pointer, &(scb->sense_sg.address), 4);
- outb(SCBAUTO, SCBCNT(base));
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
- :"si", "cx", "dx");
- outb(0, SCBCNT(base));
- outb(SCB_LIST_NULL, (SCBARRAY(base) + 30));
+ aic7xxx_putscb(p, scb);
+ outb(SCB_LIST_NULL, SCB_NEXT_WAITING + base);
/*
* Ensure that the target is "BUSY" so we don't get overlapping
* commands if we happen to be doing tagged I/O.
*/
- active = inb(HA_ACTIVE0(base)) | (inb(HA_ACTIVE1(base)) << 8);
- active |= target_mask;
- outb(active & 0xFF, HA_ACTIVE0(base));
- outb((active >> 8) & 0xFF, HA_ACTIVE1(base));
+ aic7xxx_busy_target(scsi_id, channel, base);
aic7xxx_add_waiting_scb(base, scb, LIST_HEAD);
- outb(SEND_SENSE, HA_RETURN_1(base));
+ outb(SEND_SENSE, RETURN_1 + base);
} /* first time sense, no errors */
- else
- {
- /*
- * Indicate that we asked for sense, have the sequencer do
- * a normal command complete, and have the scsi driver handle
- * this condition.
- */
- cmd->flags |= ASKED_FOR_SENSE;
- }
+
+ cmd->flags &= ~ASKED_FOR_SENSE;
+ if (aic7xxx_error(cmd) == 0)
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
break;
case BUSY:
break;
case RESIDUAL:
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
"scb(%d) state(0x%x) cmd(0x%x).\n",
*/
actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
- actual -= ((inb(SCBARRAY(base + 17)) << 16) |
- (inb(SCBARRAY(base + 16)) << 8) |
- inb(SCBARRAY(base + 15)));
+ actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
+ (inb(SCB_RESID_DCNT1 + base) << 8) |
+ inb(SCB_RESID_DCNT0 + base);
if (actual < cmd->underflow)
{
printk("aic7xxx: Target %d underflow - "
"Wanted (at least) (%u) got(%u) count(%d).\n",
cmd->target, cmd->underflow, actual,
- inb(SCBARRAY(base + 18)));
+ inb(SCB_RESID_SGCNT + base));
aic7xxx_error(cmd) = DID_RETRY_COMMAND;
aic7xxx_status(cmd) = scb->target_status;
}
break;
case ABORT_TAG:
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
"scb(%d) state(0x%x) cmd(0x%x)\n",
break;
case AWAITING_MSG:
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
"scb(%d) state(0x%x) cmd(0x%x).\n",
printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
scsi_id);
#endif
- outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base));
- outb(1, HA_MSG_LEN(base));
+ outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
+ outb(1, MSG_LEN + base);
}
else
{
break;
case IMMEDDONE:
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (isr) received IMMEDDONE for target %d, scb %d, state %d\n",
p->needwdtr |= (p->needwdtr_copy & target_mask);
p->sdtr_pending &= ~target_mask;
p->wdtr_pending &= ~target_mask;
- scratch = inb(HA_TARG_SCRATCH(base) + scratch_offset);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
scratch &= SXFR;
- outb(scratch, HA_TARG_SCRATCH(base));
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL);
}
else
}
break;
+#if AIC7XXX_NOT_YET
+ /* XXX Fill these in later */
+ case MESG_BUFFER_BUSY:
+ break;
+ case MSGIN_PHASEMIS:
+ break;
+#endif
+
+ case PARITY_ERROR:
+ {
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x).\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ char *phase;
+ unsigned char mesg_out = MSG_NOP;
+ unsigned char lastphase = inb(LASTPHASE + base);
+
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERROR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERROR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk("aic7xxx: Parity error during phase %s on target %d, "
+ "channel %d, lun %d.\n", phase,
+ cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. In phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOP)
+ {
+ outb(mesg_out, MSG0 + base);
+ outb(1, MSG_LEN + base);
+ aic7xxx_error(cmd) = DID_PARITY;
+ }
+ else
+ {
+ /*
+ * Should we allow the target to make this decision for us?
+ */
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ }
+ break;
+ }
default: /* unknown */
debug("aic7xxx: SEQINT, INTSTAT(0x%x) SCSISIGI(0x%x).\n",
- intstat, inb(SCSISIGI(base)));
+ intstat, inb(SCSISIGI + base));
break;
}
- outb(CLRSEQINT, CLRINT(base));
+
+ outb(CLRSEQINT, CLRINT + base);
UNPAUSE_SEQUENCER(p);
}
if (intstat & SCSIINT)
{
- int status = inb(SSTAT1(base));
+ int status = inb(SSTAT1 + base);
+ scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
+ channel = 'A';
+ if (inb(SBLKCTL + base) & SELBUSB)
+ {
+ channel = 'B';
+ }
- scb_index = inb(SCBPTR(base));
+ scb_index = inb(SCBPTR + base);
scb = &(p->scb_array[scb_index]);
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: No command for SCB (SCSIINT).\n");
/*
* to zero, so that it falls through the
* reset of the SCSIINT code.
*/
- outb(status, CLRSINT1(base));
+ outb(status, CLRSINT1 + base);
UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT(base));
- status = 0;
+ outb(CLRSCSIINT, CLRINT + base);
scb = NULL;
}
else
*/
if (status & SELTO)
{
- unsigned char target_mask = (1 << (cmd->target & 0x07));
unsigned char waiting;
/*
* Hardware selection timer has expired. Turn
* off SCSI selection sequence.
*/
- outb(ENRSELI, SCSISEQ(base));
+ outb(ENRSELI, SCSISEQ + base);
cmd->result = (DID_TIME_OUT << 16);
/*
* Clear an pending messages for the timed out
* target and mark the target as free.
*/
- ha_flags = inb(HA_FLAGS(base));
- outb(ha_flags & ~ACTIVE_MSG, HA_FLAGS(base));
-
- if (scb->target_channel_lun & 0x88)
- {
- active = inb(HA_ACTIVE1(base));
- active = active & ~(target_mask);
- outb(active, HA_ACTIVE1(base));
- }
- else
- {
- active = inb(HA_ACTIVE0(base));
- active &= ~(target_mask);
- outb(active, HA_ACTIVE0(base));
- }
+ ha_flags = inb(FLAGS + base);
+ outb(0, MSG_LEN + base);
+ aic7xxx_unbusy_target(scsi_id, channel, base);
- outb(SCB_NEEDDMA, SCBARRAY(base));
+ outb(0, SCBARRAY + base);
/*
* Shut off the offending interrupt sources, reset
* with an Illegal Host Address status, so the
* sequencer has to be restarted first.
*/
- outb(CLRSELTIMEO, CLRSINT1(base));
+ outb(CLRSELTIMEO, CLRSINT1 + base);
- outb(CLRSCSIINT, CLRINT(base));
+ outb(CLRSCSIINT, CLRINT + base);
/*
* Shift the waiting for selection queue forward
*/
- waiting = inb(WAITING_SCBH(base));
- outb(waiting, SCBPTR(base));
- waiting = inb(SCBARRAY(base) + 30);
- outb(waiting, WAITING_SCBH(base));
+ waiting = inb(WAITING_SCBH + base);
+ outb(waiting, SCBPTR + base);
+ waiting = inb(SCB_NEXT_WAITING + base);
+ outb(waiting, WAITING_SCBH + base);
RESTART_SEQUENCER(p);
aic7xxx_done(p, scb);
}
else
{
- if (status & SCSIPERR)
- {
- /*
- * A parity error has occurred during a data
- * transfer phase. Flag it and continue.
- */
- printk("aic7xxx: Parity error on target %d, channel %d, lun %d.\n",
- cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
- aic7xxx_error(cmd) = DID_PARITY;
-
- /*
- * Clear interrupt and resume as above.
- */
- outb(CLRSCSIPERR, CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
-
- outb(CLRSCSIINT, CLRINT(base));
- scb = NULL;
- }
- else
- {
- if (!(status & BUSFREE))
- {
- /*
- * We don't know what's going on. Turn off the
- * interrupt source and try to continue.
- */
- printk("aic7xxx: SSTAT1(0x%x).\n", status);
- outb(status, CLRSINT1(base));
- UNPAUSE_SEQUENCER(p);
- outb(CLRSCSIINT, CLRINT(base));
- scb = NULL;
- }
- }
+ if (!(status & BUSFREE))
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ printk("aic7xxx: SSTAT1(0x%x).\n", status);
+ outb(status, CLRSINT1 + base);
+ UNPAUSE_SEQUENCER(p);
+ outb(CLRSCSIINT, CLRINT + base);
+ }
}
} /* else */
}
* finished, so loop until we've processed them all.
*/
do {
- complete = inb(QOUTFIFO(base));
+ complete = inb(QOUTFIFO + base);
scb = &(p->scb_array[complete]);
- if ((scb->state != SCB_ACTIVE) || (scb->cmd == NULL))
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
{
printk("aic7xxx: Warning - No command for SCB %d (CMDCMPLT).\n"
" QOUTCNT(%d) SCB state(0x%x) cmd(0x%x) pos(%d).\n",
- complete, inb(QOUTFIFO(base)),
+ complete, inb(QOUTFIFO + base),
scb->state, (unsigned int) scb->cmd, scb->position);
- outb(CLRCMDINT, CLRINT(base));
+ outb(CLRCMDINT, CLRINT + base);
continue;
}
cmd = scb->cmd;
- cmd->result = (aic7xxx_error(cmd) << 16) | aic7xxx_status(cmd);
if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
{
/*
* command being complete never made it back
* up to the kernel.
*/
- outb(CLRCMDINT, CLRINT(base));
+ outb(CLRCMDINT, CLRINT + base);
aic7xxx_done(p, scb);
#if 0
if (scb != &p->scb_array[scb->position])
* XXX: for each command, but apparently that's too difficult.
*/
actual = aic7xxx_length(cmd, 0);
- if (((cmd->flags & WAS_SENSE) == 0) && (actual > 0))
+ if (!(cmd->flags & WAS_SENSE) && (actual > 0))
{
struct aic7xxx_xferstats *sp;
long *ptr;
}
#endif /* AIC7XXX_PROC_STATS */
- } while (inb(QOUTCNT(base)));
+ } while (inb(QOUTCNT + base));
}
}
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(STATUS_2840(base)) & EEPROM_TF) == 0) \
+ while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \
{ \
; /* Do nothing */ \
} \
- (void) inb(SEECTL_2840(base));
+ (void) inb(SEECTL_2840 + base);
/*
* Read the first 32 registers of the seeprom. For the 2840,
/*
* Send chip select for one clock cycle.
*/
- outb(CK_2840 | CS_2840, SEECTL_2840(base));
+ outb(CK_2840 | CS_2840, SEECTL_2840 + base);
CLOCK_PULSE(base);
/*
for (i = 0; i < seeprom_read.len; i++)
{
temp = CS_2840 | seeprom_read.bits[i];
- outb(temp, SEECTL_2840(base));
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840(base));
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
}
/*
temp = k;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = CS_2840 | temp;
- outb(temp, SEECTL_2840(base));
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- outb(temp, SEECTL_2840(base));
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
}
for (i = 0; i <= 16; i++)
{
temp = CS_2840;
- outb(temp, SEECTL_2840(base));
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
temp = temp ^ CK_2840;
- seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840(base)) & DI_2840);
- outb(temp, SEECTL_2840(base));
+ seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
+ outb(temp, SEECTL_2840 + base);
CLOCK_PULSE(base);
}
/*
/*
* Reset the chip select for the next command cycle.
*/
- outb(0, SEECTL_2840(base));
+ outb(0, SEECTL_2840 + base);
CLOCK_PULSE(base);
- outb(CK_2840, SEECTL_2840(base));
+ outb(CK_2840, SEECTL_2840 + base);
CLOCK_PULSE(base);
- outb(0, SEECTL_2840(base));
+ outb(0, SEECTL_2840 + base);
CLOCK_PULSE(base);
}
struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
#define CLOCK_PULSE(p) \
- while ((inb(SEECTL(base)) & SEERDY) == 0) \
+ while ((inb(SEECTL + base) & SEERDY) == 0) \
{ \
; /* Do nothing */ \
}
* is needed. Reason: after the 7870 chip reset, there
* should be no contention.
*/
- outb(SEEMS, SEECTL(base));
+ outb(SEEMS, SEECTL + base);
timeout = jiffies + 100; /* 1 second timeout */
- while ((jiffies < timeout) && ((inb(SEECTL(base)) & SEERDY) == 0))
+ while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
{
; /* Do nothing! Wait for access to be granted. */
}
- if ((inb(SEECTL(base)) & SEERDY) == 0)
+ if ((inb(SEECTL + base) & SEERDY) == 0)
{
- outb(0, SEECTL(base));
+ outb(0, SEECTL + base);
return (0);
}
/*
* Send chip select for one clock cycle.
*/
- outb(SEEMS | SEECK | SEECS, SEECTL(base));
+ outb(SEEMS | SEECK | SEECS, SEECTL + base);
CLOCK_PULSE(base);
/*
for (i = 0; i < seeprom_read.len; i++)
{
temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
- outb(temp, SEECTL(base));
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
temp = temp ^ SEECK;
- outb(temp, SEECTL(base));
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
}
/*
temp = k + offset;
temp = (temp >> i) & 1; /* Mask out all but lower bit. */
temp = SEEMS | SEECS | (temp << 1);
- outb(temp, SEECTL(base));
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
temp = temp ^ SEECK;
- outb(temp, SEECTL(base));
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
}
for (i = 0; i <= 16; i++)
{
temp = SEEMS | SEECS;
- outb(temp, SEECTL(base));
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
temp = temp ^ SEECK;
- seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL(base)) & SEEDI);
- outb(temp, SEECTL(base));
+ seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
+ outb(temp, SEECTL + base);
CLOCK_PULSE(base);
}
/*
* Reset the chip select for the next command cycle.
*/
- outb(SEEMS, SEECTL(base));
+ outb(SEEMS, SEECTL + base);
CLOCK_PULSE(base);
- outb(SEEMS | SEECK, SEECTL(base));
+ outb(SEEMS | SEECK, SEECTL + base);
CLOCK_PULSE(base);
- outb(SEEMS, SEECTL(base));
+ outb(SEEMS, SEECTL + base);
CLOCK_PULSE(base);
}
/*
* Release access to the memory port and the serial EEPROM.
*/
- outb(0, SEECTL(base));
+ outb(0, SEECTL + base);
#if 0
printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
* we limit them, along with the Rev C chips, to 4 SCBs.
*
* The Rev E boards have a read/write autoflush bit in the
- * SBLKCTL registor, while in the Rev C boards it is read only.
+ * SBLKCTL register, while in the Rev C boards it is read only.
*/
- sblkctl_reg = inb(SBLKCTL(base)) ^ AUTOFLUSHDIS;
- outb(sblkctl_reg, SBLKCTL(base));
- if (inb(SBLKCTL(base)) == sblkctl_reg)
+ sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
+ outb(sblkctl_reg, SBLKCTL + base);
+ if (inb(SBLKCTL + base) == sblkctl_reg)
{
/*
* We detected a Rev E board.
*/
- printk("aic7770: Rev E and subsequent; using 4 SCB's.\n");
- outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL(base));
+ printk("aic7xxx: %s Rev E and subsequent.\n", board_names[type]);
+ outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
maxscb = 4;
}
else
{
- printk("aic7770: Rev C and previous; using 4 SCB's.\n");
+ printk("aic7xxx: %s Rev C and previous.\n", board_names[type]);
maxscb = 4;
}
break;
*/
break;
}
+
if (walk_scbs)
{
/*
i = 0;
while (i < AIC7XXX_MAXSCB)
{
- outb(i, SCBPTR(base));
- scb_byte = ~(inb(SCBARRAY(base))); /* complement the byte */
- outb(scb_byte, SCBARRAY(base)); /* write it back out */
- if (inb(SCBARRAY(base)) != scb_byte)
+ outb(i, SCBPTR + base);
+ scb_byte = ~(inb(SCBARRAY + base)); /* complement the byte */
+ outb(scb_byte, SCBARRAY + base); /* write it back out */
+ if (inb(SCBARRAY + base) != scb_byte)
{
break;
}
i++;
}
maxscb = i;
+
+ printk("aic7xxx: Using %d SCB's after checking for SCB memory.\n", maxscb);
+ }
+ else
+ {
+ printk("aic7xxx: Using %d SCB's; No SCB memory check.\n", maxscb);
}
return (maxscb);
unsigned char sblkctl;
int max_targets;
int found = 1, base;
- int bios_disabled = 0;
+ int bios_disabled = FALSE;
unsigned char target_settings;
unsigned char scsi_conf, host_conf;
- int have_seeprom = 0;
+ int have_seeprom = FALSE;
struct Scsi_Host *host;
struct aic7xxx_host *p;
struct seeprom_config sc;
/*
* Lock out other contenders for our i/o space.
*/
- request_region(MINREG(base), MAXREG(base) - MINREG(base), "aic7xxx");
+ request_region(MINREG + base, MAXREG - MINREG, "aic7xxx");
switch (config->type)
{
* trigger type (level or edge) and use this value
* for pausing and unpausing the sequencer.
*/
- config->unpause = (inb(HCNTRL(base)) & IRQMS) | INTEN;
+ config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN;
config->pause = config->unpause | PAUSE;
config->extended = aic7xxx_extended;
- outb(config->pause | CHIPRST, HCNTRL(base));
+ outb(config->pause | CHIPRST, HCNTRL + base);
aic7xxx_delay(1);
- if (inb(HCNTRL(base)) & CHIPRST)
+ if (inb(HCNTRL + base) & CHIPRST)
{
printk("aic7xxx: Chip reset not cleared; clearing manually.\n");
}
- outb(config->pause, HCNTRL(base));
+ outb(config->pause, HCNTRL + base);
/*
* Just to be on the safe side with the 274x, we will re-read the irq
- * since there was some issue about reseting the board.
+ * since there was some issue about resetting the board.
*/
- config->irq = inb(HA_INTDEF(base)) & 0x0F;
- if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+ config->irq = inb(INTDEF + base) & 0x0F;
+ if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
{
- bios_disabled = 1;
+ bios_disabled = TRUE;
}
- host_conf = inb(HA_HOSTCONF(base));
+ host_conf = inb(HOSTCONF + base);
config->busrtime = host_conf & 0x3C;
/* XXX Is this valid for motherboard based controllers? */
/* Setup the FIFO threshold and the bus off time */
- outb(host_conf & DFTHRSH, BUSSPD(base));
- outb((host_conf << 2) & BOFF, BUSTIME(base));
+ outb(host_conf & DFTHRSH, BUSSPD + base);
+ outb((host_conf << 2) & BOFF, BUSTIME + base);
/*
* A reminder until this can be detected automatically.
break;
case AIC_284x:
- outb(CHIPRST, HCNTRL(base));
+ outb(CHIPRST, HCNTRL + base);
config->unpause = UNPAUSE_284X;
config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
aic7xxx_delay(1);
- outb(config->pause, HCNTRL(base));
+ outb(config->pause, HCNTRL + base);
config->extended = aic7xxx_extended;
- config->irq = inb(HA_INTDEF(base)) & 0x0F;
- if ((inb(HA_274_BIOSCTRL(base)) & BIOSMODE) == BIOSDISABLED)
+ config->irq = inb(INTDEF + base) & 0x0F;
+ if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
{
- bios_disabled = 1;
+ bios_disabled = TRUE;
}
- host_conf = inb(HA_HOSTCONF(base));
+ host_conf = inb(HOSTCONF + base);
printk("aic7xxx: Reading SEEPROM...");
have_seeprom = read_2840_seeprom(base, &sc);
}
/* XXX Is this valid for motherboard based controllers? */
/* Setup the FIFO threshold and the bus off time */
- outb(host_conf & DFTHRSH, BUSSPD(base));
- outb((host_conf << 2) & BOFF, BUSTIME(base));
+ outb(host_conf & DFTHRSH, BUSSPD + base);
+ outb((host_conf << 2) & BOFF, BUSTIME + base);
printk("aic7xxx: Extended translation %sabled.\n",
config->extended ? "en" : "dis");
case AIC_7882:
case AIC_7883:
case AIC_7884:
- outb(CHIPRST, HCNTRL(base));
+ outb(CHIPRST, HCNTRL + base);
config->unpause = UNPAUSE_294X;
config->pause = config->unpause | PAUSE;
aic7xxx_delay(1);
- outb(config->pause, HCNTRL(base));
+ outb(config->pause, HCNTRL + base);
config->extended = aic7xxx_extended;
config->scsi_id = 7;
/*
* XXX - force data fifo threshold to 100%. Why does this
* need to be done?
+ *
+ * We don't know where this is set in the SEEPROM or by the BIOS,
+ * so we default it to 100%.
*/
- outb(inb(DSPCISTATUS(base)) | DFTHRESH, DSPCISTATUS(base));
- outb(config->scsi_id | DFTHRESH, HA_SCSICONF(base));
+ outb(config->scsi_id | DFTHRSH_100, SCSICONF + base);
+ outb(DFTHRSH_100, DSPCISTATUS + base);
/*
* In case we are a wide card, place scsi ID in second conf byte.
*/
- outb(config->scsi_id, (HA_SCSICONF(base) + 1));
+ outb(config->scsi_id, (SCSICONF + base + 1));
printk("aic7xxx: Extended translation %sabled.\n",
config->extended ? "en" : "dis");
* Read the bus type from the SBLKCTL register. Set the FLAGS
* register in the sequencer for twin and wide bus cards.
*/
- sblkctl = inb(SBLKCTL(base));
+ sblkctl = inb(SBLKCTL + base);
switch (sblkctl & SELBUS_MASK)
{
- case SELSINGLE: /* narrow/normal bus */
- config->scsi_id = inb(HA_SCSICONF(base)) & 0x07;
+ case SELNARROW: /* narrow/normal bus */
+ config->scsi_id = inb(SCSICONF + base) & 0x07;
config->bus_type = AIC_SINGLE;
- outb(SINGLE_BUS, HA_FLAGS(base));
+ outb(SINGLE_BUS, FLAGS + base);
break;
case SELWIDE: /* Wide bus */
- config->scsi_id = inb(HA_SCSICONF(base) + 1) & 0x0F;
+ config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
config->bus_type = AIC_WIDE;
printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
board_names[config->type]);
- outb(WIDE_BUS, HA_FLAGS(base));
+ outb(WIDE_BUS, FLAGS + base);
break;
case SELBUSB: /* Twin bus */
- config->scsi_id = inb(HA_SCSICONF(base)) & 0x07;
+ config->scsi_id = inb(SCSICONF + base) & 0x07;
#ifdef AIC7XXX_TWIN_SUPPORT
- config->scsi_id_b = inb(HA_SCSICONF(base) + 1) & 0x07;
+ config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
config->bus_type = AIC_TWIN;
printk("aic7xxx: Enabled channel B of %s-Twin.\n",
board_names[config->type]);
- outb(TWIN_BUS, HA_FLAGS(base));
+ outb(TWIN_BUS, FLAGS + base);
#else
config->bus_type = AIC_SINGLE;
printk("aic7xxx: Channel B of %s-Twin will be ignored.\n",
board_names[config->type]);
- outb(0, HA_FLAGS(base));
+ outb(0, FLAGS + base);
#endif
break;
default:
printk("aic7xxx: Unsupported type 0x%x, please "
- "mail deang@ims.com\n", inb(SBLKCTL(base)));
- outb(0, HA_FLAGS(base));
+ "mail deang@ims.com\n", inb(SBLKCTL + base));
+ outb(0, FLAGS + base);
return (0);
}
* take the card out of diagnostic mode and make the host adatper
* LED follow bus activity (will not always be on).
*/
- outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL(base));
+ outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
/*
* The IRQ level in i/o port 4 maps directly onto the real
}
p->isr_count = 0;
- p->a_scanned = 0;
- p->b_scanned = 0;
+ p->a_scanned = FALSE;
+ p->b_scanned = FALSE;
p->base = base;
p->maxscb = config->maxscb;
p->numscb = 0;
/*
* Set Fast Mode and Enable the board
*/
- outb(FASTMODE, SEQCTL(base));
+ outb(FASTMODE, SEQCTL + base);
if (p->chip_type == AIC_777x)
{
- outb(ENABLE, BCTL(base));
+ outb(ENABLE, BCTL + base);
}
printk("done.\n");
/*
* Select Channel B.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL(base));
+ outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
- outb(config->scsi_id_b, SCSIID(base));
- scsi_conf = inb(HA_SCSICONF(base) + 1) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
- outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
+ outb(config->scsi_id_b, SCSIID + base);
+ scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
+ outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
+ outb(ENSELTIMO , SIMODE1 + base);
if (p->ultra_enabled)
{
- outb(ULTRAEN, SXFRCTL0(base));
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ }
+ else
+ {
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
}
/*
* Select Channel A
*/
- outb((sblkctl & ~SELBUS_MASK) | SELSINGLE, SBLKCTL(base));
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
}
- outb(config->scsi_id, SCSIID(base));
- scsi_conf = inb(HA_SCSICONF(base)) & (ENSPCHK | STIMESEL);
- outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1(base));
- outb(ENSELTIMO | ENSCSIPERR, SIMODE1(base));
+ outb(config->scsi_id, SCSIID + base);
+ scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
+ outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
+ outb(ENSELTIMO , SIMODE1 + base);
if (p->ultra_enabled)
{
- outb(ULTRAEN, SXFRCTL0(base));
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ }
+ else
+ {
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
}
/*
* BIOS has decided to disable synchronous negotiation to that
* target so we don't activate the needsdtr flag.
*/
- p->needsdtr_copy = 0;
- p->sdtr_pending = 0;
- p->needwdtr_copy = 0;
- p->wdtr_pending = 0;
+ p->needsdtr_copy = 0x0;
+ p->sdtr_pending = 0x0;
+ p->needwdtr_copy = 0x0;
+ p->wdtr_pending = 0x0;
if (p->bus_type == AIC_SINGLE)
{
max_targets = 8;
*/
if (have_seeprom)
{
- p->discenable = 0;
+ p->discenable = 0x0;
}
else
{
}
else
{
- p->discenable = ~((inb(HA_DISC_DSB(base) + 1) << 8) |
- inb(HA_DISC_DSB(base)));
+ p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
+ inb(DISC_DSB + base));
}
}
{
p->needsdtr_copy |= (0x01 << i);
}
- if ((sc.device_flags[i] & CFWIDEB) && (p->bus_type == AIC_WIDE))
+ if (sc.device_flags[i] & CFWIDEB)
{
p->needwdtr_copy |= (0x01 << i);
}
}
else
{
- target_settings = inb(HA_TARG_SCRATCH(base) + i);
- if (target_settings & 0x0F)
+ if (bios_disabled)
{
- p->needsdtr_copy |= (0x01 << i);
- /*
- * Default to asynchronous transfers (0 offset)
- */
- target_settings &= 0xF0;
+ target_settings = 0; /* 10 MHz */
+ p->needsdtr_copy |= (0x01 << i);
+ p->needwdtr_copy |= (0x01 << i);
}
- /*
- * If we are not wide, forget WDTR. This makes the driver
- * work on some cards that don't leave these fields cleared
- * when BIOS is not installed.
- */
- if ((target_settings & 0x80) && (p->bus_type == AIC_WIDE))
+ else
{
- p->needwdtr_copy |= (0x01 << i);
- target_settings &= 0x7F;
+ target_settings = inb(TARG_SCRATCH + base + i);
+ if (target_settings & 0x0F)
+ {
+ p->needsdtr_copy |= (0x01 << i);
+ /*
+ * Default to asynchronous transfers (0 offset)
+ */
+ target_settings &= 0xF0;
+ }
+ if (target_settings & 0x80)
+ {
+ p->needwdtr_copy |= (0x01 << i);
+ target_settings &= 0x7F;
+ }
}
}
- outb(target_settings, (HA_TARG_SCRATCH(base) + i));
+ outb(target_settings, (TARG_SCRATCH + base + i));
}
+ /*
+ * If we are not wide, forget WDTR. This makes the driver
+ * work on some cards that don't leave these fields cleared
+ * when BIOS is not installed.
+ */
+ if (p->bus_type != AIC_WIDE)
+ {
+ p->needwdtr = 0;
+ }
p->needsdtr = p->needsdtr_copy;
p->needwdtr = p->needwdtr_copy;
#if 0
*/
for (i = 0; i < config->maxscb; i++)
{
- outb(i, SCBPTR(base));
- outb(0, SCBARRAY(base));
+ outb(i, SCBPTR + base);
+ outb(0, SCBARRAY + base);
}
/*
* For reconnecting targets, the sequencer code needs to
* know how many SCBs it has to search through.
*/
- outb(config->maxscb, HA_SCBCOUNT(base));
+ outb(config->maxscb, SCBCOUNT + base);
+
+ /*
+ * 2s compliment of SCBCOUNT
+ */
+ i = p->maxscb;
+ outb(-i & 0xff, COMP_SCBCOUNT + base);
/*
* Clear the active flags - no targets are busy.
*/
- outb(0, HA_ACTIVE0(base));
- outb(0, HA_ACTIVE1(base));
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
/*
* We don't have any waiting selections
*/
- outb(SCB_LIST_NULL, WAITING_SCBH(base));
- outb(SCB_LIST_NULL, WAITING_SCBT(base));
+ outb(SCB_LIST_NULL, WAITING_SCBH + base);
+ outb(SCB_LIST_NULL, WAITING_SCBT + base);
/*
* Reset the SCSI bus. Is this necessary?
/*
* Select Channel B.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL(base));
+ outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
- outb(SCSIRSTO, SCSISEQ(base));
+ outb(SCSIRSTO, SCSISEQ + base);
udelay(1000);
- outb(0, SCSISEQ(base));
+ outb(0, SCSISEQ + base);
/*
* Select Channel A.
*/
- outb((sblkctl & ~SELBUS_MASK) | SELSINGLE, SBLKCTL(base));
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
}
- outb(SCSIRSTO, SCSISEQ(base));
+ outb(SCSIRSTO, SCSISEQ + base);
udelay(1000);
- outb(0, SCSISEQ(base));
+ outb(0, SCSISEQ + base);
aic7xxx_delay(AIC7XXX_RESET_DELAY);
*/
for (slot = MINSLOT; slot <= MAXSLOT; slot++)
{
- base = SLOTBASE(slot);
+ base = SLOTBASE(slot) + MINREG;
- if (check_region(MINREG(base), MAXREG(base) - MINREG(base)))
+ if (check_region(MINREG + base, MAXREG - MINREG))
{
/*
* Some other driver has staked a
continue;
}
- config.type = aic7xxx_probe(slot, HID0(base));
+ config.type = aic7xxx_probe(slot, HID0 + base);
if (config.type != AIC_NONE)
{
/*
unsigned int io_port;
unsigned short index = 0;
unsigned char pci_bus, pci_device_fn;
- unsigned char devrevid, devconfig, devstatus;
+ unsigned int csize_lattime;
+ unsigned int class_revid;
+ unsigned int devconfig;
char rev_id[] = {'B', 'C', 'D'};
for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
{
config.type = aic7xxx_pci_devices[i].card_type;
config.chip_type = aic7xxx_pci_devices[i].chip_type;
+ config.chan_num = 0;
+ config.walk_scbs = FALSE;
switch (config.type)
{
case AIC_7872: /* 3940 */
case AIC_7882: /* 3940-Ultra */
+ config.walk_scbs = TRUE;
config.chan_num = number_of_39xxs & 0x1; /* Has 2 controllers */
number_of_39xxs++;
if (number_of_39xxs == 2)
break;
}
- /*
- * Read esundry information from PCI BIOS.
- */
- error = pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_0, &io_port);
- if (error)
- {
- panic("aic7xxx: (aic7xxx_detect) Error %d reading I/O port.\n",
- error);
- }
-
- error = pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &irq);
- if (error)
- {
- panic("aic7xxx: (aic7xxx_detect) Error %d reading IRQ.\n",
- error);
- }
-
- error = pcibios_read_config_byte(pci_bus, pci_device_fn,
- DEVREVID, &devrevid);
- if (error)
- {
- panic("aic7xxx: (aic7xxx_detect) Error %d reading device "
- "revision ID.\n", error);
- }
-
- if (devrevid < 3)
- {
- printk("aic7xxx: %s Rev %c.\n", board_names[config.type],
- rev_id[devrevid]);
- }
+ /*
+ * Read esundry information from PCI BIOS.
+ */
+ error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ error += pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
- error = pcibios_read_config_byte(pci_bus, pci_device_fn,
- DEVCONFIG, &devconfig);
- if (error)
- {
- panic("aic7xxx: (aic7xxx_detect) Error %d reading device "
- "configuration.\n", error);
- }
+ /*
+ * Ensure that we are using good values for the PCI burst size
+ * and latency timer.
+ */
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CSIZE_LATTIME, &csize_lattime);
+ if ((csize_lattime & CACHESIZE) == 0)
+ {
+ /* Default to 8DWDs - what's the PCI define for this? */
+ csize_lattime |= 8;
+ }
+ if((csize_lattime & LATTIME) == 0)
+ {
+ /* Default to 64 PCLKS (is this a good value?) */
+ /* This may also be availble in the SEEPROM?? */
+ csize_lattime |= (64 << 8);
+ }
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ CSIZE_LATTIME, csize_lattime);
+ printk("aic7xxx: BurstLen = %d DWDs, Latency Timer = %d PCLKS\n",
+ (int) (csize_lattime & CACHESIZE),
+ (csize_lattime >> 8) & 0x000000ff);
+
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CLASS_PROGIF_REVID, &class_revid);
+ if ((class_revid & DEVREVID) < 3)
+ {
+ printk("aic7xxx: %s Rev %c.\n", board_names[config.type],
+ rev_id[class_revid & DEVREVID]);
+ }
- error = pcibios_read_config_byte(pci_bus, pci_device_fn,
- DEVSTATUS, &devstatus);
- if (error)
- {
- panic("aic7xxx: (aic7xxx_detect) Error %d reading device status.\n",
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, &devconfig);
+ if (error)
+ {
+ panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
error);
- }
+ }
- printk("aic7xxx: devconfig(0x%x) devstatus(0x%x).\n",
- devconfig, devstatus);
+ printk("aic7xxx: devconfig = 0x%x.\n", devconfig);
- /*
- * Make the base I/O register look like EISA and VL-bus.
- */
- base = io_port - 0xC01;
+ /*
+ * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+ * we mask it off.
+ */
+ base = io_port & 0xfffffffe;
- /*
- * I don't think we need to bother with allowing
- * spurious interrupts for the 787x/7850, but what
- * the hey.
- */
- aic7xxx_spurious_count = 1;
+ /*
+ * I don't think we need to bother with allowing
+ * spurious interrupts for the 787x/7850, but what
+ * the hey.
+ */
+ aic7xxx_spurious_count = 1;
config.base = base;
config.irq = irq;
config.low_term = AIC_UNKNOWN;
config.high_term = AIC_UNKNOWN;
config.busrtime = 0;
- config.walk_scbs = FALSE;
config.ultra_enabled = FALSE;
- if ((devstatus & RAMPSM) || (devconfig & SCBRAMSEL))
- {
+ if (devconfig & RAMPSM)
+ {
+ /*
+ * External SRAM present. Have the probe walk the SCBs to see
+ * how much SRAM we have and set the number of SCBs accordingly.
+ * We have to turn off SCBRAMSEL to access the external SCB
+ * SRAM.
+ *
+ * It seems that early versions of the aic7870 didn't use these
+ * bits, hence the hack for the 3940 above. I would guess that
+ * recent 3940s using later aic7870 or aic7880 chips do actually
+ * set RAMPSM.
+ *
+ * The documentation isn't clear, but it sounds like the value
+ * written to devconfig must not have RAMPSM set. The second
+ * sixteen bits of the register are R/O anyway, so it shouldn't
+ * affect RAMPSM either way.
+ */
+ printk ("aic7xxx: External RAM detected. Enabling RAM access.\n");
+ devconfig &= ~(RAMPSM | SCBRAMSEL);
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, devconfig);
config.walk_scbs = TRUE;
- }
- found += aic7xxx_register(template, &config);
+ }
+ found += aic7xxx_register(template, &config);
- /*
- * Disable spurious interrupts.
- */
- aic7xxx_spurious_count = 0;
+ /*
+ * Disable spurious interrupts.
+ */
+ aic7xxx_spurious_count = 0;
- index++;
+ index++;
} /* Found an Adaptec PCI device. */
}
}
}
cmd->tag = cmd->device->current_tag;
cmd->device->current_tag++;
- scb->control |= SCB_TE;
+ scb->control |= TAG_ENB;
}
#endif
mask = (0x01 << (cmd->target | (cmd->channel << 3)));
if (p->discenable & mask)
{
- scb->control |= SCB_DISCENB;
+ scb->control |= DISCENB;
}
if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
{
p->wdtr_pending |= mask;
- scb->control |= SCB_NEEDWDTR;
+ scb->control |= NEEDWDTR;
#if 0
printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
#endif
if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
{
p->sdtr_pending |= mask;
- scb->control |= SCB_NEEDSDTR;
+ scb->control |= NEEDSDTR;
#if 0
printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
#endif
if (cmd->use_sg)
{
-#if 0
- debug("aic7xxx: (build_scb) SG used, %d segments, length(%u).\n",
- cmd->use_sg, length);
-#endif
scb->SG_segment_count = cmd->use_sg;
memcpy(scb->SG_list_pointer, &cmd->request_buffer,
sizeof(scb->SG_list_pointer));
memcpy(&sg, &cmd->request_buffer, sizeof(sg));
memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer));
- scb->data_count[0] = sg[0].length & 0xFF;
- scb->data_count[1] = (sg[0].length >> 8) & 0xFF;
- scb->data_count[2] = (sg[0].length >> 16) & 0xFF;
+ scb->data_count = sg[0].length;
+#if 0
+ debug("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
+ cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+#endif
}
else
{
scb->SG_segment_count = 0;
memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- memset(scb->data_count, 0, sizeof(scb->data_count));
+ scb->data_count = 0;
}
else
{
scb->sg.length = cmd->request_bufflen;
addr = &scb->sg;
memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
- scb->data_count[0] = scb->sg.length & 0xFF;
- scb->data_count[1] = (scb->sg.length >> 8) & 0xFF;
- scb->data_count[2] = (scb->sg.length >> 16) & 0xFF;
+ scb->data_count = scb->sg.length;
memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
}
}
long flags;
struct aic7xxx_host *p;
struct aic7xxx_scb *scb;
- unsigned char curscb;
p = (struct aic7xxx_host *) cmd->host->hostdata;
if (!p->a_scanned && (cmd->channel == 0))
{
printk("aic7xxx: Scanning channel A for devices.\n");
- p->a_scanned = 1;
+ p->a_scanned = TRUE;
}
else
{
if (!p->b_scanned && (cmd->channel == 1))
{
printk("aic7xxx: Scanning channel B for devices.\n");
- p->b_scanned = 1;
+ p->b_scanned = TRUE;
}
}
#if 0
- debug("aic7xxx_queue: cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
+ debug("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
cmd->lun & 0x07);
#endif
scb->position = p->numscb;
p->numscb++;
scb->state = SCB_ACTIVE;
- scb->next_waiting = SCB_LIST_NULL;
- memcpy(scb->host_scb, &scb, sizeof(scb));
- scb->control = SCB_NEEDDMA;
- PAUSE_SEQUENCER(p);
- curscb = inb(SCBPTR(p->base));
- outb(scb->position, SCBPTR(p->base));
- aic7xxx_putscb_dma(p->base, scb);
- outb(curscb, SCBPTR(p->base));
- UNPAUSE_SEQUENCER(p);
- scb->control = 0;
}
}
* the SCB, then write its pointer into the queue in FIFO
* and restore the saved SCB pointer.
*/
- aic7xxx_putscb(p->base, scb);
+ aic7xxx_putscb(p, scb);
+ outb(scb->position, QINFIFO + p->base);
/*
* Make sure the Scsi_Cmnd pointer is saved, the struct it
unsigned char errcode)
{
int base = p->base;
- int found = 0;
+ int found = FALSE;
aha_abort_reset_type scb_status = ABORT_RESET_SUCCESS;
char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
*/
PAUSE_SEQUENCER(p);
-#ifdef AIC7XXX_DEBUG_ABORT
+#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (abort_scb) scb %d, scb_aborted 0x%x\n",
scb->position, (scb->state & SCB_ABORTED));
#endif
* may also be that we're timing out on a command that just takes
* too much time, so we try the bus device reset there first.
*/
- active_scb = inb(SCBPTR(base));
+ active_scb = inb(SCBPTR + base);
active_scbp = &(p->scb_array[active_scb]);
- control = inb(SCBARRAY(base));
+ control = inb(SCBARRAY + base);
/*
* Test to see if scbp is disconnected
*/
- outb(scb->position, SCBPTR(base));
- if (inb(SCBARRAY(base)) & SCB_DIS)
+ outb(scb->position, SCBPTR + base);
+ if (inb(SCBARRAY + base) & DISCONNECTED)
{
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (abort_scb) scb %d is disconnected.\n", scb->position);
scb->SG_segment_count = 0;
memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
- memset(scb->data_count, 0, sizeof(scb->data_count));
- outb(SCBAUTO, SCBCNT(base));
- asm volatile("cld\n\t"
- "rep\n\t"
- "outsb"
- : /* no output */
- :"S" (scb), "c" (SCB_DOWNLOAD_SIZE), "d" (SCBARRAY(base))
- :"si", "cx", "dx");
- outb(0, SCBCNT(base));
+ scb->data_count = 0;
+ aic7xxx_putscb(p, scb);
aic7xxx_error(scb->cmd) = errcode;
scb_status = ABORT_RESET_PENDING;
aic7xxx_add_waiting_scb(base, scb, LIST_SECOND);
/*
* Is the active SCB really active?
*/
- if ((active_scbp->state & SCB_ACTIVE) && (control & SCB_NEEDDMA))
+ if (active_scbp->state & SCB_ACTIVE)
{
- unsigned char flags = inb(HA_FLAGS(base));
- if (flags & ACTIVE_MSG)
+ unsigned char msg_len = inb(MSG_LEN + base);
+ if (msg_len != 0)
{
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
- "ha_flags active.\n");
+ "msg_len is non-zero.\n");
#endif
/*
* If we're in a message phase, tacking on another message
{
#ifdef AIC7XXX_DEBUG_ABORT
printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
- "ha_flags inactive.\n");
+ "msg_len is zero.\n");
#endif
/*
* Load the message buffer and assert attention.
*/
active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
- outb(flags | ACTIVE_MSG, HA_FLAGS(base));
- outb(1, HA_MSG_LEN(base));
- outb(MSG_BUS_DEVICE_RESET, HA_MSG_START(base));
+ outb(1, MSG_LEN + base);
+ outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
if (active_scbp->target_channel_lun != scb->target_channel_lun)
{
/*
*/
/* cmd->retries = 0; */
aic7xxx_error(cmd) = errcode;
- aic7xxx_done (p, scb);
+ aic7xxx_done(p, scb);
}
else
{
* tab-width: 8
* End:
*/
+
-##+M#########################################################################
-# Adaptec 274x/284x/294x device driver for Linux and FreeBSD.
-#
-# Copyright (c) 1994 John Aycock
-# The University of Calgary Department of Computer Science.
-#
-# Modifications/enhancements:
-# Copyright (c) 1994, 1995 Justin Gibbs. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; see the file COPYING. If not, write to
-# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-#
-# FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other
-# optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
-##-M#########################################################################
-
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.3 1995/11/10 10:51:22 deang Exp $"
-
-SCBMASK = 0xff
-
-SCSISEQ = 0x00
-ENRSELI = 0x10
-SXFRCTL0 = 0x01
-ULTRAEN = 0x20
-SXFRCTL1 = 0x02
-SCSISIGI = 0x03
-SCSISIGO = 0x03
-SCSIRATE = 0x04
-SCSIID = 0x05
-SCSIDATL = 0x06
-STCNT = 0x08
-STCNT+0 = 0x08
-STCNT+1 = 0x09
-STCNT+2 = 0x0a
-CLRSINT0 = 0x0b
-SSTAT0 = 0x0b
-SELDO = 0x40
-SELDI = 0x20
-CLRSINT1 = 0x0c
-SSTAT1 = 0x0c
-PHASEMIS = 0x10
-SIMODE1 = 0x11
-SCSIBUSL = 0x12
-SHADDR = 0x14
-SELID = 0x19
-SBLKCTL = 0x1f
-SEQCTL = 0x60
-A = 0x64 # == ACCUM
-SINDEX = 0x65
-DINDEX = 0x66
-ALLZEROS = 0x6a
-NONE = 0x6a
-SINDIR = 0x6c
-DINDIR = 0x6d
-FUNCTION1 = 0x6e
-HADDR = 0x88
-HADDR+1 = 0x89
-HADDR+2 = 0x8a
-HADDR+3 = 0x8b
-HCNT = 0x8c
-HCNT+0 = 0x8c
-HCNT+1 = 0x8d
-HCNT+2 = 0x8e
-SCBPTR = 0x90
-INTSTAT = 0x91
-DFCNTRL = 0x93
-DFSTATUS = 0x94
-DFDAT = 0x99
-QINFIFO = 0x9b
-QINCNT = 0x9c
-QOUTFIFO = 0x9d
-
-SCSICONF_A = 0x5a
-SCSICONF_B = 0x5b
-
-# The two reserved bytes at SCBARRAY+1[23] are expected to be set to
-# zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
-# whether or not to DMA an SCB from host ram. This flag prevents the
-# "re-fetching" of transactions that are requed because the target is
-# busy with another command. We also use bits 6 & 7 to indicate whether
-# or not to initiate SDTR or WDTR repectively when starting this command.
-#
-SCBARRAY+0 = 0xa0
-
-DISCONNECTED = 0x04
-NEEDDMA = 0x08
-NEEDSDTR = 0x10
-TAG_ENB = 0x20
-DISCENB = 0x40
-NEEDWDTR = 0x80
-
-SCBARRAY+1 = 0xa1
-SCBARRAY+2 = 0xa2
-SCBARRAY+3 = 0xa3
-SCBARRAY+4 = 0xa4
-SCBARRAY+5 = 0xa5
-SCBARRAY+6 = 0xa6
-SCBARRAY+7 = 0xa7
-SCBARRAY+8 = 0xa8
-SCBARRAY+9 = 0xa9
-SCBARRAY+10 = 0xaa
-SCBARRAY+11 = 0xab
-SCBARRAY+12 = 0xac
-SCBARRAY+13 = 0xad
-SCBARRAY+14 = 0xae
-SCBARRAY+15 = 0xaf
-SCBARRAY+16 = 0xb0
-SCBARRAY+17 = 0xb1
-SCBARRAY+18 = 0xb2
-SCBARRAY+19 = 0xb3
-SCBARRAY+20 = 0xb4
-SCBARRAY+21 = 0xb5
-SCBARRAY+22 = 0xb6
-SCBARRAY+23 = 0xb7
-SCBARRAY+24 = 0xb8
-SCBARRAY+25 = 0xb9
-SCBARRAY+26 = 0xba
-SCBARRAY+27 = 0xbb
-SCBARRAY+28 = 0xbc
-SCBARRAY+29 = 0xbd
-SCBARRAY+30 = 0xbe
-
-BAD_PHASE = 0x01 # unknown scsi bus phase
-CMDCMPLT = 0x02 # Command Complete
-SEND_REJECT = 0x11 # sending a message reject
-NO_IDENT = 0x21 # no IDENTIFY after reconnect
-NO_MATCH = 0x31 # no cmd match for reconnect
-MSG_SDTR = 0x41 # SDTR message received
-MSG_WDTR = 0x51 # WDTR message received
-MSG_REJECT = 0x61 # Reject message received
-BAD_STATUS = 0x71 # Bad status from target
-RESIDUAL = 0x81 # Residual byte count != 0
-ABORT_TAG = 0x91 # Sent an ABORT_TAG message
-AWAITING_MSG = 0xa1 # Kernel requested to specify
- # a message to this target
- # (command was null), so tell
- # it that it can fill the
- # message buffer.
-IMMEDDONE = 0xb1
-
-
-# The host adapter card (at least the BIOS) uses 20-2f for SCSI
-# device information, 32-33 and 5a-5f as well. As it turns out, the
-# BIOS trashes 20-2f, writing the synchronous negotiation results
-# on top of the BIOS values, so we re-use those for our per-target
-# scratchspace (actually a value that can be copied directly into
-# SCSIRATE). The kernel driver will enable synchronous negotiation
-# for all targets that have a value other than 0 in the lower four
-# bits of the target scratch space. This should work regardless of
-# whether the bios has been installed. NEEDSDTR and NEEDWDTR are the
-# fouth and sevent bits of the SCB control byte. The kernel driver
-# will set these when a WDTR or SDTR message should be sent to the
-# target the SCB's command references.
-#
-# REJBYTE contains the first byte of a MESSAGE IN message, so the driver
-# can report an intelligible error if a message is rejected.
-#
-# FLAGS's high bit is true if we are currently handling a reselect;
-# its next-highest bit is true ONLY IF we've seen an IDENTIFY message
-# from the reselecting target. If we haven't had IDENTIFY, then we have
-# no idea what the lun is, and we can't select the right SCB register
-# bank, so force a kernel panic if the target attempts a data in/out or
-# command phase instead of corrupting something. FLAGS also contains
-# configuration bits so that we can optimize for TWIN and WIDE controllers,
-# the MAX_OFFSET bit which we set when we want to negotiate for maximum sync
-# offset irregardless of what the per target scratch space says.
-#
-# Note that SG_NEXT occupies four bytes.
-#
-SYNCNEG = 0x20
-
-REJBYTE = 0x31
-DISC_DSB_A = 0x32
-DISC_DSB_B = 0x33
-
-MSG_LEN = 0x34
-MSG_START+0 = 0x35
-MSG_START+1 = 0x36
-MSG_START+2 = 0x37
-MSG_START+3 = 0x38
-MSG_START+4 = 0x39
-MSG_START+5 = 0x3a
--MSG_START+0 = 0xcb # 2's complement of MSG_START+0
-
-ARG_1 = 0x4a # sdtr conversion args & return
-BUS_16_BIT = 0x01
-RETURN_1 = 0x4a
-
-SIGSTATE = 0x4b # value written to SCSISIGO
-
-# Linux users should use 0xc (12) for SG_SIZEOF
-#SG_SIZEOF = 0x8 # sizeof(struct ahc_dma)
-SG_SIZEOF = 0xc # sizeof(struct scatterlist)
-SCB_SIZEOF = 0x1a # sizeof SCB to DMA (26 bytes)
-
-DMAPARAMS = 0x4c # Parameters for DMA
-SG_COUNT = 0x4d # working value of SG count
-SG_NEXT = 0x4e # working value of SG pointer
-SG_NEXT+0 = 0x4e
-SG_NEXT+1 = 0x4f
-SG_NEXT+2 = 0x50
-SG_NEXT+3 = 0x51
-
-SCBCOUNT = 0x52 # the actual number of SCBs
-FLAGS = 0x53 # Device configuration flags
-TWIN_BUS = 0x01
-WIDE_BUS = 0x02
-DPHASE = 0x04
-MAX_OFFSET = 0x08
-ACTIVE_MSG = 0x20
-IDENTIFY_SEEN = 0x40
-RESELECTED = 0x80
-
-MAX_OFFSET_8BIT = 0x0f
-MAX_OFFSET_WIDE = 0x08
-
-ACTIVE_A = 0x54
-ACTIVE_B = 0x55
-SAVED_TCL = 0x56 # Temporary storage for the
- # target/channel/lun of a
- # reconnecting target
-# After starting the selection hardware, we return to the "poll_for_work"
-# loop so that we can check for reconnecting targets as well as for our
-# selection to complete just in case the reselection wins bus arbitration.
-# The problem with this is that we must keep track of the SCB that we've
-# already pulled from the QINFIFO and started the selection on just in case
-# the reselection wins so that we can retry the selection at a later time.
-# This problem cannot be resolved by holding a single entry in scratch
-# ram since a reconnecting target can request sense and this will create
-# yet another SCB waiting for selection. The solution used here is to
-# use byte 31 of the SCB as a psuedo-next pointer and to thread a list
-# of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets,
-# SCB_LIST_NULL is 0xff which is out of range. The kernel driver must
-# add an entry to this list everytime a request sense occurs. The sequencer
-# will automatically consume the entries.
-
-WAITING_SCBH = 0x57 # head of list of SCBs awaiting
- # selection
-WAITING_SCBT = 0x58 # tail of list of SCBs awaiting
- # selection
-SCB_LIST_NULL = 0xff
-
-
-# Poll QINCNT for work - the lower bits contain
-# the number of entries in the Queue In FIFO.
-#
+/*+M*************************************************************************
+ * Adaptec 274x/284x/294x device driver for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ *Modifications/enhancements:
+ * Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other
+ * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
+ *-M*************************************************************************/
+
+VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 2.8 1996/02/10 06:23:39 deang Exp $"
+
+#ifdef linux
+#include "aic7xxx_reg.h"
+#else
+#include "../../dev/aic7xxx/aic7xxx_reg.h"
+#endif
+
+/*
+ * We can't just use ACCUM in the sequencer code because it
+ * must be treated specially by the assembler, and it currently
+ * looks for the symbol 'A'. This is the only register defined in
+ * the assembler's symbol space.
+ */
+A = ACCUM
+
+/* After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration. The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time. This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection. The solution used here is to
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection. Since 0-0xfe are valid SCB offsets,
+ * SCB_LIST_NULL is 0xff which is out of range. The kernel driver must
+ * add an entry to this list everytime a request sense occurs. The sequencer
+ * will automatically consume the entries.
+ */
+
+/*
+ * Initialize any state valid during the idle loop here. This code is
+ * executed on startup and after every bus free.
+ */
+start:
+ mvi SCSISEQ,ENRSELI /* Always allow reselection */
poll_for_work:
- test FLAGS,TWIN_BUS jz start2 # Are we a twin channel device?
-# For fairness, we check the other bus first, since we just finished a
-# transaction on the current channel.
- xor SBLKCTL,0x08 # Toggle to the other bus
+ /*
+ * Are we a twin channel device?
+ * For fairness, we check the other bus first,
+ * since we just finished a transaction on the
+ * current channel.
+ */
+ test FLAGS,TWIN_BUS jz start2
+ xor SBLKCTL,SELBUSB /* Toggle to the other bus */
test SSTAT0,SELDI jnz reselect
- xor SBLKCTL,0x08 # Toggle to the original bus
+ xor SBLKCTL,SELBUSB /* Toggle to the original bus */
start2:
test SSTAT0,SELDI jnz reselect
cmp WAITING_SCBH,SCB_LIST_NULL jne start_waiting
- test QINCNT,SCBMASK jz poll_for_work
-
-# We have at least one queued SCB now and we don't have any
-# SCBs in the list of SCBs awaiting selection. Set the SCB
-# pointer from the FIFO so we see the right bank of SCB
-# registers, then set SCSI options and set the initiator and
-# target SCSI IDs.
-#
+ test QINCNT,0xff jz poll_for_work
+
+/*
+ * We have at least one queued SCB now and we don't have any
+ * SCBs in the list of SCBs awaiting selection. Set the SCB
+ * pointer from the FIFO so we see the right bank of SCB
+ * registers.
+ */
mov SCBPTR,QINFIFO
-# If the control byte of this SCB has the NEEDDMA flag set, we have
-# yet to DMA it from host memory
-
-test SCBARRAY+0,NEEDDMA jz test_busy
- clr HCNT+2
- clr HCNT+1
- mvi HCNT+0,SCB_SIZEOF
-
- mvi DINDEX,HADDR
- mvi SCBARRAY+26 call bcopy_4
-
- mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET
-
-# Wait for DMA from host memory to data FIFO to complete, then disable
-# DMA and wait for it to acknowledge that it's off.
-#
- call dma_finish
-
-# Copy the SCB from the FIFO to the SCBARRAY
-
- mvi DINDEX, SCBARRAY+0
- call bcopy_5_dfdat
- call bcopy_7_dfdat
- call bcopy_7_dfdat
- call bcopy_7_dfdat
-
-# See if there is not already an active SCB for this target. This code
-# locks out on a per target basis instead of target/lun. Although this
-# is not ideal for devices that have multiple luns active at the same
-# time, it is faster than looping through all SCB's looking for active
-# commands. It may be benificial to make findscb a more general procedure
-# to see if the added cost of the search is negligible. This code also
-# assumes that the kernel driver will clear the active flags on board
-# initialization, board reset, and a target's SELTO.
+/*
+ * See if there is not already an active SCB for this target. This code
+ * locks out on a per target basis instead of target/lun. Although this
+ * is not ideal for devices that have multiple luns active at the same
+ * time, it is faster than looping through all SCB's looking for active
+ * commands. It may be benificial to make findscb a more general procedure
+ * to see if the added cost of the search is negligible. This code also
+ * assumes that the kernel driver will clear the active flags on board
+ * initialization, board reset, and a target SELTO. Tagged commands
+ * don't set the active bits since you can queue more than one command
+ * at a time. We do, however, look to see if there are any non-tagged
+ * I/Os in progress, and requeue the command if there are. Tagged and
+ * non-tagged commands cannot be mixed to a single target.
+ */
test_busy:
- and FUNCTION1,0x70,SCBARRAY+1
+ mov FUNCTION1,SCB_TCL
mov A,FUNCTION1
- test SCBARRAY+1,0x88 jz test_a # Id < 8 && A channel
+ test SCB_TCL,0x88 jz test_a /* Id < 8 && A channel */
test ACTIVE_B,A jnz requeue
- test SCBARRAY+0,TAG_ENB jnz start_scb
- or ACTIVE_B,A # Mark the current target as busy
+ test SCB_CONTROL,TAG_ENB jnz start_scb
+ /* Mark the current target as busy */
+ or ACTIVE_B,A
jmp start_scb
-# Place the currently active back on the queue for later processing
+/* Place the currently active SCB back on the queue for later processing */
requeue:
mov QINFIFO, SCBPTR
jmp poll_for_work
-# Pull the first entry off of the waiting for selection list
+/*
+ * Pull the first entry off of the waiting for selection list
+ * We don't have to "test_busy" because only transactions that
+ * have passed that test can be in the waiting_scb list.
+ */
start_waiting:
mov SCBPTR,WAITING_SCBH
- jmp start_scb
+ jmp start_scb2
test_a:
- test ACTIVE_A,A jnz requeue
- test SCBARRAY+0,TAG_ENB jnz start_scb
- or ACTIVE_A,A # Mark the current target as busy
+ test ACTIVE_A,A jnz requeue
+ test SCB_CONTROL,TAG_ENB jnz start_scb
+ /* Mark the current target as busy */
+ or ACTIVE_A,A
start_scb:
- and SINDEX,0xf7,SBLKCTL #Clear the channel select bit
- and A,0x08,SCBARRAY+1 #Get new channel bit
- or SINDEX,A
- mov SBLKCTL,SINDEX # select channel
- mov SCBARRAY+1 call initialize_scsiid
-
-# Enable selection phase as an initiator, and do automatic ATN
-# after the selection. We do this now so that we can overlap the
-# rest of our work to set up this target with the arbitration and
-# selection bus phases.
-#
-start_selection:
- or SCSISEQ,0x48 # ENSELO|ENAUTOATNO
+ mov SCB_NEXT_WAITING,WAITING_SCBH
mov WAITING_SCBH, SCBPTR
- and FLAGS,0x3f # !RESELECTING
-
-# As soon as we get a successful selection, the target should go
-# into the message out phase since we have ATN asserted. Prepare
-# the message to send, locking out the device driver. If the device
-# driver hasn't beaten us with an ABORT or RESET message, then tack
-# on an SDTR negotiation if required.
-#
-# Messages are stored in scratch RAM starting with a flag byte (high bit
-# set means active message), one length byte, and then the message itself.
-#
-
- test SCBARRAY+11,0xff jnz identify # 0 Length Command?
-
-# The kernel has sent us an SCB with no command attached. This implies
-# that the kernel wants to send a message of some sort to this target,
-# so we interrupt the driver, allow it to fill the message buffer, and
-# then go back into the arbitration loop
+start_scb2:
+ and SINDEX,0xf7,SBLKCTL /* Clear the channel select bit */
+ and A,0x08,SCB_TCL /* Get new channel bit */
+ or SINDEX,A
+ mov SBLKCTL,SINDEX /* select channel */
+ mov SCB_TCL call initialize_scsiid
+
+/*
+ * Enable selection phase as an initiator, and do automatic ATN
+ * after the selection. We do this now so that we can overlap the
+ * rest of our work to set up this target with the arbitration and
+ * selection bus phases.
+ */
+start_selection:
+ mvi SCSISEQ,0x58 /* ENSELO|ENAUTOATNO|ENRSELI */
+
+/*
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted. Prepare
+ * the message to send.
+ *
+ * Messages are stored in scratch RAM starting with a length byte
+ * followed by the message itself.
+ */
+ test SCB_CMDLEN,0xff jnz mk_identify /* 0 Length Command? */
+
+/*
+ * The kernel has sent us an SCB with no command attached. This implies
+ * that the kernel wants to send a message of some sort to this target,
+ * so we interrupt the driver, allow it to fill the message buffer, and
+ * then go back into the arbitration loop
+ */
mvi INTSTAT,AWAITING_MSG
jmp wait_for_selection
-identify:
- and A,DISCENB,SCBARRAY+0 # mask off disconnect privledge
-
- and SINDEX,0x7,SCBARRAY+1 # lun
- or SINDEX,A # or in disconnect privledge
- or SINDEX,0x80 call mk_mesg # IDENTIFY message
+mk_identify:
+ and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */
- mov A,SINDEX
- test SCBARRAY+0,0xb0 jz !message # WDTR, SDTR or TAG??
- cmp MSG_START+0,A jne !message # did driver beat us?
+ and MSG0,0x7,SCB_TCL /* lun */
+ or MSG0,A /* or in disconnect privledge */
+ or MSG0,MSG_IDENTIFY
+ mvi MSG_LEN, 1
-# Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag
-# value
+ test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */
+/*
+ * Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag
+ * value
+ */
mk_tag:
- mvi DINDEX, MSG_START+1
- test SCBARRAY+0,TAG_ENB jz mk_tag_done
- and A,0x23,SCBARRAY+0
+ mvi DINDEX, MSG1
+ test SCB_CONTROL,TAG_ENB jz mk_tag_done
+ and A,0x23,SCB_CONTROL
mov DINDIR,A
mov DINDIR,SCBPTR
- add MSG_LEN,-MSG_START+0,DINDEX # update message length
+ add MSG_LEN,COMP_MSG0,DINDEX /* update message length */
mk_tag_done:
- mov DINDEX call mk_dtr # build DTR message if needed
+ test SCB_CONTROL,0x90 jz !message /* NEEDWDTR|NEEDSDTR */
+ mov DINDEX call mk_dtr /* build DTR message if needed */
!message:
wait_for_selection:
- test SSTAT0,SELDI jnz reselect
- test SSTAT0,SELDO jnz select
- jmp wait_for_selection
-
-# Reselection has been initiated by a target. Make a note that we've been
-# reselected, but haven't seen an IDENTIFY message from the target
-# yet.
-#
+ test SSTAT0,SELDO jnz select
+ test SSTAT0,SELDI jz wait_for_selection
+
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target
+ * yet.
+ */
reselect:
+ clr MSG_LEN /* Don't have anything in the mesg buffer */
mov SELID call initialize_scsiid
- and FLAGS,0x3f # reselected, no IDENTIFY
- or FLAGS,RESELECTED jmp select2
-
-# After the selection, remove this SCB from the "waiting for selection"
-# list. This is achieved by simply moving our "next" pointer into
-# WAITING_SCBH and setting our next pointer to null so that the next
-# time this SCB is used, we don't get confused.
-#
+ and FLAGS,0x03 /* clear target specific flags */
+ or FLAGS,RESELECTED
+ jmp select2
+
+/*
+ * After the selection, remove this SCB from the "waiting for selection"
+ * list. This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH. Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
select:
- or SCBARRAY+0,NEEDDMA
- mov WAITING_SCBH,SCBARRAY+30
- mvi SCBARRAY+30,SCB_LIST_NULL
+ and FLAGS,0x03 /* Clear target flags */
+ mov WAITING_SCBH,SCB_NEXT_WAITING
select2:
- call initialize_for_target
- mvi SCSISEQ,ENRSELI
- mvi CLRSINT0,0x60 # CLRSELDI|CLRSELDO
- mvi CLRSINT1,0x8 # CLRBUSFREE
-
-# Main loop for information transfer phases. If BSY is false, then
-# we have a bus free condition, expected or not. Otherwise, wait
-# for the target to assert REQ before checking MSG, C/D and I/O
-# for the bus phase.
-#
-# We can't simply look at the values of SCSISIGI here (if we want
-# to do synchronous data transfer), because the target won't assert
-# REQ if it's already sent us some data that we haven't acknowledged
-# yet.
-#
+/*
+ * Set CLRCHN here before the target has entered a data transfer mode -
+ * with synchronous SCSI, if you do it later, you blow away some
+ * data in the SCSI FIFO that the target has already sent to you.
+ */
+ or SXFRCTL0,CLRCHN
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ */
+ call ndx_dtr
+ mov SCSIRATE,SINDIR
+
+ mvi SCSISEQ,ENAUTOATNP /*
+ * ATN on parity errors
+ * for "in" phases
+ */
+ mvi CLRSINT1,CLRBUSFREE
+ mvi CLRSINT0,0x60 /* CLRSELDI|CLRSELDO */
+
+/*
+ * Main loop for information transfer phases. If BSY is false, then
+ * we have a bus free condition, expected or not. Otherwise, wait
+ * for the target to assert REQ before checking MSG, C/D and I/O
+ * for the bus phase.
+ *
+ */
ITloop:
- test SSTAT1,0x8 jnz p_busfree # BUSFREE
- test SSTAT1,0x1 jz ITloop # REQINIT
+ test SSTAT1,BUSFREE jnz p_busfree
+ test SSTAT1,REQINIT jz ITloop
+
+/*
+ * If we've had a parity error, let the driver know before
+ * we overwrite LASTPHASE.
+ */
+ test SSTAT1, SCSIPERR jz parity_okay
+ or CLRSINT1, CLRSCSIPERR
+ mvi INTSTAT, PARITY_ERROR
+
+parity_okay:
+ and A,PHASE_MASK,SCSISIGI
+ mov LASTPHASE,A
+ mov SCSISIGO,A
- and A,0xe0,SCSISIGI # CDI|IOI|MSGI
-
- mov A call scsisig
cmp ALLZEROS,A je p_dataout
- cmp A,0x40 je p_datain
- cmp A,0x80 je p_command
- cmp A,0xc0 je p_status
- cmp A,0xa0 je p_mesgout
- cmp A,0xe0 je p_mesgin
+ cmp A,P_DATAIN je p_datain
+ cmp A,P_COMMAND je p_command
+ cmp A,P_MESGOUT je p_mesgout
+ cmp A,P_STATUS je p_status
+ cmp A,P_MESGIN je p_mesgin
- mvi INTSTAT,BAD_PHASE # unknown - signal driver
+ mvi INTSTAT,BAD_PHASE /* unknown phase - signal driver */
p_dataout:
- mvi DMAPARAMS,0x7d # WIDEODD|SCSIEN|SDMAEN|HDMAEN|
- # DIRECTION|FIFORESET
+ mvi DMAPARAMS,0x7d /*
+ * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+ * DIRECTION|FIFORESET
+ */
jmp data_phase_init
-# If we re-enter the data phase after going through another phase, the
-# STCNT may have been cleared, so restore it from the residual field.
+/*
+ * If we re-enter the data phase after going through another phase, the
+ * STCNT may have been cleared, so restore it from the residual field.
+ */
data_phase_reinit:
- mvi DINDEX, STCNT
- mvi SCBARRAY+15 call bcopy_3
+ mov STCNT0,SCB_RESID_DCNT0
+ mov STCNT1,SCB_RESID_DCNT1
+ mov STCNT2,SCB_RESID_DCNT2
jmp data_phase_loop
-# Reads should not use WIDEODD since it may make the last byte for a SG segment
-# go to the next segment.
p_datain:
- mvi DMAPARAMS,0x79 # WIDEODD|SCSIEN|SDMAEN|HDMAEN|
- # !DIRECTION|FIFORESET
+ mvi DMAPARAMS,0x79 /*
+ * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
+ * !DIRECTION|FIFORESET
+ */
data_phase_init:
call assert
test FLAGS, DPHASE jnz data_phase_reinit
call sg_scb2ram
- or FLAGS, DPHASE # We have seen a data phase
+ or FLAGS, DPHASE /* We have seen a data phase */
data_phase_loop:
-# If we are the last SG block, don't set wideodd.
+/* If we are the last SG block, don't set wideodd. */
cmp SG_COUNT,0x01 jne data_phase_wideodd
- and DMAPARAMS, 0xbf # Turn off WIDEODD
+ and DMAPARAMS, 0xbf /* Turn off WIDEODD */
data_phase_wideodd:
mov DMAPARAMS call dma
-# Exit if we had an underrun
- test SSTAT0,0x04 jz data_phase_finish # underrun STCNT != 0
+/* Exit if we had an underrun */
+ test SSTAT0,SDONE jz data_phase_finish /* underrun STCNT != 0 */
-# Advance the scatter-gather pointers if needed
-#
+/*
+ * Advance the scatter-gather pointers if needed
+ */
sg_advance:
- dec SG_COUNT # one less segment to go
-
- test SG_COUNT, 0xff jz data_phase_finish #Are we done?
-
- clr A # add sizeof(struct scatter)
- add SG_NEXT+0,SG_SIZEOF,SG_NEXT+0
- adc SG_NEXT+1,A,SG_NEXT+1
- adc SG_NEXT+2,A,SG_NEXT+2
- adc SG_NEXT+3,A,SG_NEXT+3
-
-# Load a struct scatter and set up the data address and length.
-# If the working value of the SG count is nonzero, then
-# we need to load a new set of values.
-#
-# This, like all DMA's, assumes a little-endian host data storage.
-#
+ dec SG_COUNT /* one less segment to go */
+
+ test SG_COUNT, 0xff jz data_phase_finish /* Are we done? */
+
+ clr A /* add sizeof(struct scatter) */
+ add SG_NEXT0,SG_SIZEOF,SG_NEXT0
+ adc SG_NEXT1,A,SG_NEXT1
+
+/*
+ * Load a struct scatter and set up the data address and length.
+ * If the working value of the SG count is nonzero, then
+ * we need to load a new set of values.
+ *
+ * This, like all DMA's, assumes a little-endian host data storage.
+ */
sg_load:
- clr HCNT+2
- clr HCNT+1
- mvi HCNT+0,SG_SIZEOF
-
- mvi DINDEX,HADDR
- mvi SG_NEXT call bcopy_4
-
- mvi DFCNTRL,0xd # HDMAEN|DIRECTION|FIFORESET
-
-# Wait for DMA from host memory to data FIFO to complete, then disable
-# DMA and wait for it to acknowledge that it's off.
-#
- call dma_finish
-
-# Copy data from FIFO into SCB data pointer and data count. This assumes
-# that the struct scatterlist has this structure (this and sizeof(struct
-# scatterlist) == 12 are asserted in aic7xxx.c):
-#
-# struct scatterlist {
-# char *address; /* four bytes, little-endian order */
-# ... /* four bytes, ignored */
-# unsigned short length; /* two bytes, little-endian order */
-# }
-#
-
-# Not in FreeBSD. the scatter list entry is only 8 bytes.
-#
-# struct ahc_dma_seg {
-# physaddr addr; /* four bytes, little-endian order */
-# long len; /* four bytes, little endian order */
-# };
-#
-
- mvi DINDEX,HADDR
-# call bcopy_7_dfdat
-
-# For Linux, we must throw away four bytes since there is a 32bit gap
-# in the middle of a struct scatterlist
- call bcopy_4_dfdat
+ clr HCNT2
+ clr HCNT1
+ mvi HCNT0,SG_SIZEOF
+
+ mov HADDR0,SG_NEXT0
+ mov HADDR1,SG_NEXT1
+ mov HADDR2,SG_NEXT2
+ mov HADDR3,SG_NEXT3
+
+ or DFCNTRL,0xd /* HDMAEN|DIRECTION|FIFORESET */
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+ test DFSTATUS,HDONE jz dma_finish
+ /* Turn off DMA preserving WIDEODD */
+ and DFCNTRL,WIDEODD
+dma_finish2:
+ test DFCNTRL,HDMAENACK jnz dma_finish2
+
+/*
+ * Copy data from FIFO into SCB data pointer and data count. This assumes
+ * that the struct scatterlist has this structure (this and sizeof(struct
+ * scatterlist) == 12 are asserted in aic7xxx.c):
+ *
+ * struct scatterlist {
+ * char *address; four bytes, little-endian order
+ * ... four bytes, ignored
+ * unsigned short length; two bytes, little-endian order
+ * }
+ *
+ *
+ * Not in FreeBSD. the scatter list entry is only 8 bytes.
+ *
+ * struct ahc_dma_seg {
+ * physaddr addr; four bytes, little-endian order
+ * long len; four bytes, little endian order
+ * };
+ */
+
+/*
+ * For Linux, we must throw away four bytes since there is a 32bit gap
+ * in the middle of a struct scatterlist
+ */
+#ifdef linux
+ mov HADDR0,DFDAT
+ mov HADDR1,DFDAT
+ mov HADDR2,DFDAT
+ mov HADDR3,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
mov NONE,DFDAT
- call bcopy_3_dfdat #Only support 24 bit length.
-
-# Load STCNT as well. It is a mirror of HCNT
- mvi DINDEX,STCNT
- mvi HCNT call bcopy_3
+ mov HCNT0,DFDAT
+ mov HCNT1,DFDAT
+ mov HCNT2,DFDAT
+#else
+/*
+ * For FreeBSD, just copy it wholesale
+ */
+ mov HADDR0,DFDAT
+ mov HADDR1,DFDAT
+ mov HADDR2,DFDAT
+ mov HADDR3,DFDAT
+ mov HCNT0,DFDAT
+ mov HCNT1,DFDAT
+ mov HCNT2,DFDAT
+#endif
+
+/* Load STCNT as well. It is a mirror of HCNT */
+ mov STCNT0,HCNT0
+ mov STCNT1,HCNT1
+ mov STCNT2,HCNT2
test SSTAT1,PHASEMIS jz data_phase_loop
data_phase_finish:
-# After a DMA finishes, save the SG and STCNT residuals back into the SCB
-# We use STCNT instead of HCNT, since it's a reflection of how many bytes
-# were transferred on the SCSI (as opposed to the host) bus.
-#
- mvi DINDEX,SCBARRAY+15
- mvi STCNT call bcopy_3
- mov SCBARRAY+18, SG_COUNT
+/*
+ * After a DMA finishes, save the SG and STCNT residuals back into the SCB
+ * We use STCNT instead of HCNT, since it's a reflection of how many bytes
+ * were transferred on the SCSI (as opposed to the host) bus.
+ */
+ mov SCB_RESID_DCNT0,STCNT0
+ mov SCB_RESID_DCNT1,STCNT1
+ mov SCB_RESID_DCNT2,STCNT2
+ mov SCB_RESID_SGCNT, SG_COUNT
jmp ITloop
-# Command phase. Set up the DMA registers and let 'er rip - the
-# two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
-# so we can copy those three bytes directly into HCNT.
-#
+/*
+ * Command phase. Set up the DMA registers and let 'er rip - the
+ * two bytes after the SCB SCSI_cmd_length are zeroed by the driver,
+ * so we can copy those three bytes directly into HCNT.
+ */
p_command:
call assert
-# Load HADDR and HCNT. We can do this in one bcopy since they are neighbors
- mvi DINDEX,HADDR
- mvi SCBARRAY+7 call bcopy_7
-
- mvi DINDEX,STCNT
- mvi SCBARRAY+11 call bcopy_3
+/*
+ * Load HADDR and HCNT. We can do this in one bcopy since they are neighbors
+ */
+ mov HADDR0, SCB_CMDPTR0
+ mov HADDR1, SCB_CMDPTR1
+ mov HADDR2, SCB_CMDPTR2
+ mov HADDR3, SCB_CMDPTR3
+ mov HCNT0, SCB_CMDLEN
+ clr HCNT1
+ clr HCNT2
+
+ mov STCNT0, HCNT0
+ mov STCNT1, HCNT1
+ mov STCNT2, HCNT2
mvi 0x3d call dma # SCSIEN|SDMAEN|HDMAEN|
# DIRECTION|FIFORESET
jmp ITloop
-# Status phase. Wait for the data byte to appear, then read it
-# and store it into the SCB.
-#
+/*
+ * Status phase. Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
p_status:
-
- mvi SCBARRAY+14 call inb_first
+ mvi SCB_TARGET_STATUS call inb_first
jmp mesgin_done
-# Message out phase. If there is no active message, but the target
-# took us into this phase anyway, build a no-op message and send it.
-#
+/*
+ * Message out phase. If there is no active message, but the target
+ * took us into this phase anyway, build a no-op message and send it.
+ */
p_mesgout:
- mvi 0x8 call mk_mesg # build NOP message
-
- clr STCNT+2
- clr STCNT+1
-
-# Set up automatic PIO transfer from MSG_START. Bit 3 in
-# SXFRCTL0 (SPIOEN) is already on.
-#
- mvi SINDEX,MSG_START+0
+ test MSG_LEN, 0xff jnz p_mesgout_start
+ mvi MSG_NOP call mk_mesg /* build NOP message */
+
+p_mesgout_start:
+/*
+ * Set up automatic PIO transfer from MSG0. Bit 3 in
+ * SXFRCTL0 (SPIOEN) is already on.
+ */
+ mvi SINDEX,MSG0
mov DINDEX,MSG_LEN
-# When target asks for a byte, drop ATN if it's the last one in
-# the message. Otherwise, keep going until the message is exhausted.
-# (We can't use outb for this since it wants the input in SINDEX.)
-#
-# Keep an eye out for a phase change, in case the target issues
-# a MESSAGE REJECT.
-#
-p_mesgout2:
- test SSTAT0,0x2 jz p_mesgout2 # SPIORDY
- test SSTAT1,0x10 jnz p_mesgout6 # PHASEMIS
-
- cmp DINDEX,1 jne p_mesgout3 # last byte?
- mvi CLRSINT1,0x40 # CLRATNO - drop ATN
-
-# Write a byte to the SCSI bus. The AIC-7770 refuses to automatically
-# send ACKs in automatic PIO or DMA mode unless you make sure that the
-# "expected" bus phase in SCSISIGO matches the actual bus phase. This
-# behaviour is completely undocumented and caused me several days of
-# grief.
-#
-# After plugging in different drives to test with and using a longer
-# SCSI cable, I found that I/O in Automatic PIO mode ceased to function,
-# especially when transferring >1 byte. It seems to be much more stable
-# if STCNT is set to one before the transfer, and SDONE (in SSTAT0) is
-# polled for transfer completion - for both output _and_ input. The
-# only theory I have is that SPIORDY doesn't drop right away when SCSIDATL
-# is accessed (like the documentation says it does), and that on a longer
-# cable run, the sequencer code was fast enough to loop back and see
-# an SPIORDY that hadn't dropped yet.
-#
-p_mesgout3:
- mvi STCNT+0, 0x01
+/*
+ * When target asks for a byte, drop ATN if it's the last one in
+ * the message. Otherwise, keep going until the message is exhausted.
+ *
+ * Keep an eye out for a phase change, in case the target issues
+ * a MESSAGE REJECT.
+ */
+p_mesgout_loop:
+ test SSTAT1,PHASEMIS jnz p_mesgout_phasemis
+ test SSTAT0,SPIORDY jz p_mesgout_loop
+ cmp DINDEX,1 jne p_mesgout_outb /* last byte? */
+ mvi CLRSINT1,CLRATNO /* drop ATN */
+p_mesgout_outb:
+ dec DINDEX
+ or CLRSINT0, CLRSPIORDY
mov SCSIDATL,SINDIR
-
+
p_mesgout4:
- test SSTAT0,0x4 jz p_mesgout4 # SDONE
- dec DINDEX
- test DINDEX,0xff jnz p_mesgout2
+ test DINDEX,0xff jnz p_mesgout_loop
-# If the next bus phase after ATN drops is a message out, it means
-# that the target is requesting that the last message(s) be resent.
-#
-p_mesgout5:
- test SSTAT1,0x8 jnz p_mesgout6 # BUSFREE
- test SSTAT1,0x1 jz p_mesgout5 # REQINIT
+/*
+ * If the next bus phase after ATN drops is a message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+p_mesgout_snoop:
+ test SSTAT1,BUSFREE jnz p_mesgout_done
+ test SSTAT1,REQINIT jz p_mesgout_snoop
- and A,0xe0,SCSISIGI # CDI|IOI|MSGI
- cmp A,0xa0 jne p_mesgout6
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig # ATNO - re-assert ATN
+ test SSTAT1,PHASEMIS jnz p_mesgout_done
+
+ or SCSISIGO,ATNO /* turn on ATNO */
jmp ITloop
-p_mesgout6:
- mvi CLRSINT1,0x40 # CLRATNO - in case of PHASEMIS
- and FLAGS,0xdf # no active msg
+p_mesgout_phasemis:
+ mvi CLRSINT1,CLRATNO /* Be sure turn ATNO off */
+p_mesgout_done:
+ clr MSG_LEN /* no active msg */
jmp ITloop
-# Message in phase. Bytes are read using Automatic PIO mode, but not
-# using inb. This alleviates a race condition, namely that if ATN had
-# to be asserted under Automatic PIO mode, it had to beat the SCSI
-# circuitry sending an ACK to the target. This showed up under heavy
-# loads and really confused things, since ABORT commands wouldn't be
-# seen by the drive after an IDENTIFY message in until it had changed
-# to a data I/O phase.
-#
+/*
+ * Message in phase. Bytes are read using Automatic PIO mode.
+ */
p_mesgin:
- mvi A call inb_first # read the 1st message byte
- mvi REJBYTE,A # save it for the driver
+ mvi A call inb_first /* read the 1st message byte */
+ mov REJBYTE,A /* save it for the driver */
- test A,0x80 jnz mesgin_identify # identify message?
- cmp A,4 je mesgin_disconnect # disconnect?
- cmp A,2 je mesgin_sdptrs # save data pointers?
- cmp ALLZEROS,A je mesgin_complete # command complete?
- cmp A,3 je mesgin_rdptrs # restore pointers code?
- cmp A,1 je mesgin_extended # extended message?
- cmp A,7 je mesgin_reject # message reject code?
+ test A,MSG_IDENTIFY jnz mesgin_identify
+ cmp A,MSG_DISCONNECT je mesgin_disconnect
+ cmp A,MSG_SDPTRS je mesgin_sdptrs
+ cmp ALLZEROS,A je mesgin_complete
+ cmp A,MSG_RDPTRS je mesgin_rdptrs
+ cmp A,MSG_EXTENDED je mesgin_extended
+ cmp A,MSG_REJECT je mesgin_reject
rej_mesgin:
-# We have no idea what this message in is, and there's no way
-# to pass it up to the kernel, so we issue a message reject and
-# hope for the best. Since we're now using manual PIO mode to
-# read in the message, there should no longer be a race condition
-# present when we assert ATN. In any case, rejection should be a
-# rare occurrence - signal the driver when it happens.
-#
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig
- mvi INTSTAT,SEND_REJECT # let driver know
-
- mvi 0x7 call mk_mesg # MESSAGE REJECT message
+/*
+ * We have no idea what this message in is, and there's no way
+ * to pass it up to the kernel, so we issue a message reject and
+ * hope for the best. Since we're now using manual PIO mode to
+ * read in the message, there should no longer be a race condition
+ * present when we assert ATN. In any case, rejection should be a
+ * rare occurrence - signal the driver when it happens.
+ */
+ or SCSISIGO,ATNO /* turn on ATNO */
+ mvi INTSTAT,SEND_REJECT /* let driver know */
+
+ mvi MSG_REJECT call mk_mesg
mesgin_done:
- call inb_last # ack & turn auto PIO back on
+ call inb_last /*ack & turn auto PIO back on*/
jmp ITloop
mesgin_complete:
-# We got a "command complete" message, so put the SCB pointer
-# into the Queue Out, and trigger a completion interrupt.
-# Check status for non zero return and interrupt driver if needed
-# This allows the driver to interpret errors only when they occur
-# instead of always uploading the scb. If the status is SCSI_CHECK,
-# the driver will download a new scb requesting sense to replace
-# the old one, modify the "waiting for selection" SCB list and set
-# RETURN_1 to 0x80. If RETURN_1 is set to 0x80 the sequencer imediately
-# jumps to main loop where it will run down the waiting SCB list.
-# If the kernel driver does not wish to request sense, it need
-# only clear RETURN_1, and the command is allowed to complete. We don't
-# bother to post to the QOUTFIFO in the error case since it would require
-# extra work in the kernel driver to ensure that the entry was removed
-# before the command complete code tried processing it.
-
-# First check for residuals
- test SCBARRAY+18,0xff jnz resid
+/*
+ * We got a "command complete" message, so put the SCB pointer
+ * into QUEUEOUT, and trigger a completion interrupt.
+ * Check status for non zero return and interrupt driver if needed
+ * This allows the driver to interpret errors only when they occur
+ * instead of always uploading the scb. If the status is SCSI_CHECK,
+ * the driver will download a new scb requesting sense to replace
+ * the old one, modify the "waiting for selection" SCB list and set
+ * RETURN_1 to 0x80. If RETURN_1 is set to 0x80 the sequencer imediately
+ * jumps to main loop where it will run down the waiting SCB list.
+ * If the kernel driver does not wish to request sense, it need
+ * only clear RETURN_1, and the command is allowed to complete. We don't
+ * bother to post to the QOUTFIFO in the error case since it would require
+ * extra work in the kernel driver to ensure that the entry was removed
+ * before the command complete code tried processing it.
+ *
+ * First check for residuals
+ */
+ test SCB_RESID_SGCNT,0xff jz check_status
+/*
+ * If we have a residual count, interrupt and tell the host. Other
+ * alternatives are to pause the sequencer on all command completes (yuck),
+ * dma the resid directly to the host (slick, we may have space to do it now)
+ * or have the sequencer pause itself when it encounters a non-zero resid
+ * (unecessary pause just to flag the command -yuck-, but takes one instruction
+ * and since it shouldn't happen that often is good enough for our purposes).
+ */
+resid:
+ mvi INTSTAT,RESIDUAL
check_status:
- test SCBARRAY+14,0xff jz status_ok # 0 Status?
- mvi INTSTAT,BAD_STATUS # let driver know
- test RETURN_1, 0x80 jz status_ok
+ test SCB_TARGET_STATUS,0xff jz status_ok /* Good Status? */
+ mvi INTSTAT,BAD_STATUS /* let driver know */
+ cmp RETURN_1, SEND_SENSE jne status_ok
jmp mesgin_done
status_ok:
-# First, mark this target as free.
- test SCBARRAY+0,TAG_ENB jnz complete # Tagged command
- and FUNCTION1,0x70,SCBARRAY+1
+/* First, mark this target as free. */
+ test SCB_CONTROL,TAG_ENB jnz test_immediate /*
+ * Tagged commands
+ * don't busy the
+ * target.
+ */
+ mov FUNCTION1,SCB_TCL
mov A,FUNCTION1
- test SCBARRAY+1,0x88 jz clear_a
+ test SCB_TCL,0x88 jz clear_a
xor ACTIVE_B,A
- jmp immediate
+ jmp test_immediate
clear_a:
xor ACTIVE_A,A
-immediate:
- test SCBARRAY+11,0xff jnz complete # Immediate message complete
-# Pause the sequencer until the driver gets around to handling the command
-# complete. This is so that any action that might require carefull timing
-# with the completion of this command can occur.
+test_immediate:
+ test SCB_CMDLEN,0xff jnz complete /* Immediate message complete */
+/*
+ * Pause the sequencer until the driver gets around to handling the command
+ * complete. This is so that any action that might require carefull timing
+ * with the completion of this command can occur.
+ */
mvi INTSTAT,IMMEDDONE
- jmp poll_for_work
+ jmp start
complete:
mov QOUTFIFO,SCBPTR
mvi INTSTAT,CMDCMPLT
jmp mesgin_done
-# If we have a residual count, interrupt and tell the host. Other
-# alternatives are to pause the sequencer on all command completes (yuck),
-# dma the resid directly to the host (slick, but a ton of instructions), or
-# have the sequencer pause itself when it encounters a non-zero resid
-# (unecessary pause just to flag the command -- yuck, but takes few instructions
-# and since it shouldn't happen that often is good enough for our purposes).
-resid:
- mvi INTSTAT,RESIDUAL
- jmp check_status
-
-# Is it an extended message? We only support the synchronous and wide data
-# transfer request messages, which will probably be in response to
-# WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it -
-# apparently this can be done after any message in byte, according
-# to the SCSI-2 spec.
-#
+/*
+ * Is it an extended message? We only support the synchronous and wide data
+ * transfer request messages, which will probably be in response to
+ * WDTR or SDTR message outs from us. If it's not SDTR or WDTR, reject it -
+ * apparently this can be done after any message in byte, according
+ * to the SCSI-2 spec.
+ */
mesgin_extended:
- mvi ARG_1 call inb_next # extended message length
- mvi A call inb_next # extended message code
+ mvi ARG_1 call inb_next /* extended message length */
+ mvi A call inb_next /* extended message code */
- cmp A,1 je p_mesginSDTR # Syncronous negotiation message
- cmp A,3 je p_mesginWDTR # Wide negotiation message
+ cmp A,MSG_SDTR je p_mesginSDTR
+ cmp A,MSG_WDTR je p_mesginWDTR
jmp rej_mesgin
p_mesginWDTR:
- cmp ARG_1,2 jne rej_mesgin # extended mesg length=2
- mvi A call inb_next # Width of bus
- mvi INTSTAT,MSG_WDTR # let driver know
- test RETURN_1,0x80 jz mesgin_done# Do we need to send WDTR?
-
-# We didn't initiate the wide negotiation, so we must respond to the request
- and RETURN_1,0x7f # Clear the SEND_WDTR Flag
- or FLAGS,ACTIVE_MSG
- mvi DINDEX,MSG_START+0
- mvi MSG_START+0 call mk_wdtr # build WDTR message
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig
+ cmp ARG_1,2 jne rej_mesgin /* extended mesg length=2 */
+ mvi ARG_1 call inb_next /* Width of bus */
+ mvi INTSTAT,WDTR_MSG /* let driver know */
+ test RETURN_1,0xff jz mesgin_done /* Do we need to send WDTR? */
+ cmp RETURN_1,SEND_REJ je rej_mesgin /*
+ * Bus width was too large
+ * Reject it.
+ */
+
+/* We didn't initiate the wide negotiation, so we must respond to the request */
+ and RETURN_1,0x7f /* Clear the SEND_WDTR Flag */
+ mvi DINDEX,MSG0
+ mvi MSG0 call mk_wdtr /* build WDTR message */
+ or SCSISIGO,ATNO /* turn on ATNO */
jmp mesgin_done
p_mesginSDTR:
- cmp ARG_1,3 jne rej_mesgin # extended mesg length=3
- mvi ARG_1 call inb_next # xfer period
- mvi A call inb_next # REQ/ACK offset
- mvi INTSTAT,MSG_SDTR # call driver to convert
-
- test RETURN_1,0xc0 jz mesgin_done# Do we need to mk_sdtr or rej?
- test RETURN_1,0x40 jnz rej_mesgin # Requested SDTR too small - rej
- or FLAGS,ACTIVE_MSG
- mvi DINDEX, MSG_START+0
- mvi MSG_START+0 call mk_sdtr
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig
+ cmp ARG_1,3 jne rej_mesgin /* extended mesg length=3 */
+ mvi ARG_1 call inb_next /* xfer period */
+ mvi A call inb_next /* REQ/ACK offset */
+ mvi INTSTAT,SDTR_MSG /* call driver to convert */
+
+ test RETURN_1,0xff jz mesgin_done /* Do we need to mk_sdtr/rej */
+ cmp RETURN_1,SEND_REJ je rej_mesgin /*
+ * Requested SDTR too small
+ * Reject it.
+ */
+ mvi DINDEX, MSG0
+ mvi MSG0 call mk_sdtr
+ or SCSISIGO,ATNO /* turn on ATNO */
jmp mesgin_done
-# Is it a disconnect message? Set a flag in the SCB to remind us
-# and await the bus going free.
-#
+/*
+ * Is it a disconnect message? Set a flag in the SCB to remind us
+ * and await the bus going free.
+ */
mesgin_disconnect:
- or SCBARRAY+0,DISCONNECTED
+ or SCB_CONTROL,DISCONNECTED
jmp mesgin_done
-# Save data pointers message? Copy working values into the SCB,
-# usually in preparation for a disconnect.
-#
+/*
+ * Save data pointers message? Copy working values into the SCB,
+ * usually in preparation for a disconnect.
+ */
mesgin_sdptrs:
call sg_ram2scb
jmp mesgin_done
-# Restore pointers message? Data pointers are recopied from the
-# SCB anytime we enter a data phase for the first time, so all
-# we need to do is clear the DPHASE flag and let the data phase
-# code do the rest.
-#
+/*
+ * Restore pointers message? Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
mesgin_rdptrs:
- and FLAGS,0xfb # !DPHASE we'll reload them
- # the next time through
+ and FLAGS,0xfb /*
+ * !DPHASE we'll reload them
+ * the next time through
+ */
jmp mesgin_done
-# Identify message? For a reconnecting target, this tells us the lun
-# that the reconnection is for - find the correct SCB and switch to it,
-# clearing the "disconnected" bit so we don't "find" it by accident later.
-#
+/*
+ * Identify message? For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
mesgin_identify:
- test A,0x78 jnz rej_mesgin # !DiscPriv|!LUNTAR|!Reserved
+ test A,0x78 jnz rej_mesgin /*!DiscPriv|!LUNTAR|!Reserved*/
- and A,0x07 # lun in lower three bits
+ and A,0x07 /* lun in lower three bits */
or SAVED_TCL,A,SELID
and SAVED_TCL,0xf7
- and A,0x08,SBLKCTL # B Channel??
+ and A,SELBUSB,SBLKCTL /* B Channel?? */
or SAVED_TCL,A
- call inb_last # ACK
- mov ALLZEROS call findSCB
+ call inb_last /* ACK */
+
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to switch to the proper
+ * SCB. Otherwise, we just use the findSCB method.
+ */
+snoop_tag_loop:
+ test SSTAT1,BUSFREE jnz use_findSCB
+ test SSTAT1,REQINIT jz snoop_tag_loop
+ test SSTAT1,PHASEMIS jnz use_findSCB
+ mvi A call inb_first
+ cmp A,MSG_SIMPLE_TAG je get_tag
+use_findSCB:
+ mov ALLZEROS call findSCB /* Have to search */
setup_SCB:
- and SCBARRAY+0,0xfb # clear disconnect bit in SCB
- or FLAGS,IDENTIFY_SEEN # make note of IDENTIFY
-
+ and SCB_CONTROL,0xfb /* clear disconnect bit in SCB */
+ or FLAGS,IDENTIFY_SEEN /* make note of IDENTIFY */
jmp ITloop
get_tag:
- mvi A call inb_first
- cmp A,0x20 jne return # Simple Tag message?
- mvi A call inb_next
- call inb_last
- test A,0xf0 jnz abort_tag # Tag in range?
- mov SCBPTR,A
+ mvi ARG_1 call inb_next /* tag value */
+/*
+ * See if the tag is in range. The tag is < SCBCOUNT if we add
+ * the complement of SCBCOUNT to the incomming tag and there is
+ * no carry.
+ */
+ mov A,COMP_SCBCOUNT
+ add SINDEX,A,ARG_1
+ jc abort_tag
+
+/*
+ * Ensure that the SCB the tag points to is for a SCB transaction
+ * to the reconnecting target.
+ */
+ mov SCBPTR,ARG_1
mov A,SAVED_TCL
- cmp SCBARRAY+1,A jne abort_tag
- test SCBARRAY+0,TAG_ENB jz abort_tag
- ret
+ cmp SCB_TCL,A jne abort_tag
+ test SCB_CONTROL,TAG_ENB jz abort_tag
+ call inb_last /* Ack Successful tag */
+ jmp setup_SCB
abort_tag:
- or SINDEX,0x10,SIGSTATE # turn on ATNO
- call scsisig
- mvi INTSTAT,ABORT_TAG # let driver know
- mvi 0xd call mk_mesg # ABORT TAG message
- ret
+ or SCSISIGO,ATNO /* turn on ATNO */
+ mvi INTSTAT,ABORT_TAG /* let driver know */
+ mvi 0xd call mk_mesg /* ABORT TAG message */
+ jmp mesgin_done
-# Message reject? Let the kernel driver handle this. If we have an
-# outstanding WDTR or SDTR negotiation, assume that it's a response from
-# the target selecting 8bit or asynchronous transfer, otherwise just ignore
-# it since we have no clue what it pertains to.
-#
+/*
+ * Message reject? Let the kernel driver handle this. If we have an
+ * outstanding WDTR or SDTR negotiation, assume that it's a response from
+ * the target selecting 8bit or asynchronous transfer, otherwise just ignore
+ * it since we have no clue what it pertains to.
+ */
mesgin_reject:
- mvi INTSTAT, MSG_REJECT
+ mvi INTSTAT, REJECT_MSG
jmp mesgin_done
-# [ ADD MORE MESSAGE HANDLING HERE ]
-#
+/*
+ * [ ADD MORE MESSAGE HANDLING HERE ]
+ */
-# Bus free phase. It might be useful to interrupt the device
-# driver if we aren't expecting this. For now, make sure that
-# ATN isn't being asserted and look for a new command.
-#
+/*
+ * Bus free phase. It might be useful to interrupt the device
+ * driver if we aren't expecting this. For now, make sure that
+ * ATN isn't being asserted and look for a new command.
+ */
p_busfree:
- mvi CLRSINT1,0x40 # CLRATNO
- clr SIGSTATE
-
-# if this is an immediate command, perform a psuedo command complete to
-# notify the driver.
- test SCBARRAY+11,0xff jz status_ok
- jmp poll_for_work
-
-# Instead of a generic bcopy routine that requires an argument, we unroll
-# the cases that are actually used, and call them explicitly. This
-# not only reduces the overhead of doing a bcopy, but ends up saving space
-# in the program since you don't have to put the argument into the accumulator
-# before the call. Both functions expect DINDEX to contain the destination
-# address and SINDEX to contain the source address.
+ mvi CLRSINT1,CLRATNO
+
+/*
+ * if this is an immediate command, perform a psuedo command complete to
+ * notify the driver.
+ */
+ test SCB_CMDLEN,0xff jz status_ok
+ jmp start
+
+#if 0
+/*
+ * Instead of a generic bcopy routine that requires an argument, we unroll
+ * the cases that are actually used, and call them explicitly. This
+ * not only reduces the overhead of doing a bcopy, but ends up saving space
+ * in the program since you don't have to put the argument into the accumulator
+ * before the call. Both functions expect DINDEX to contain the destination
+ * address and SINDEX to contain the source address.
+ */
bcopy_7:
mov DINDIR,SINDIR
mov DINDIR,SINDIR
mov DINDIR,SINDIR
mov DINDIR,SINDIR
mov DINDIR,SINDIR ret
+#endif
-bcopy_7_dfdat:
- mov DINDIR,DFDAT
- mov DINDIR,DFDAT
-bcopy_5_dfdat:
- mov DINDIR,DFDAT
-bcopy_4_dfdat:
- mov DINDIR,DFDAT
-bcopy_3_dfdat:
- mov DINDIR,DFDAT
- mov DINDIR,DFDAT
- mov DINDIR,DFDAT ret
-
-# Locking the driver out, build a one-byte message passed in SINDEX
-# if there is no active message already. SINDEX is returned intact.
-#
+/*
+ * Locking the driver out, build a one-byte message passed in SINDEX
+ * if there is no active message already. SINDEX is returned intact.
+ */
mk_mesg:
- mvi SEQCTL,0x50 # PAUSEDIS|FASTMODE
- test FLAGS,ACTIVE_MSG jnz mk_mesg1 # active message?
-
- or FLAGS,ACTIVE_MSG # if not, there is now
- mvi MSG_LEN,1 # length = 1
- mov MSG_START+0,SINDEX # 1-byte message
+ mvi SEQCTL,0x50 /* PAUSEDIS|FASTMODE */
+ test MSG_LEN,0xff jz mk_mesg1 /* Should always succeed */
+
+ /*
+ * Hmmm. For some reason the mesg buffer is in use.
+ * Tell the driver. It should look at SINDEX to find
+ * out what we wanted to use the buffer for and resolve
+ * the conflict.
+ */
+ mvi SEQCTL,0x10 /* !PAUSEDIS|FASTMODE */
+ mvi INTSTAT,MSG_BUFFER_BUSY
mk_mesg1:
- mvi SEQCTL,0x10 ret # !PAUSEDIS|FASTMODE
-
-# Carefully read data in Automatic PIO mode. I first tried this using
-# Manual PIO mode, but it gave me continual underrun errors, probably
-# indicating that I did something wrong, but I feel more secure leaving
-# Automatic PIO on all the time.
-#
-# According to Adaptec's documentation, an ACK is not sent on input from
-# the target until SCSIDATL is read from. So we wait until SCSIDATL is
-# latched (the usual way), then read the data byte directly off the bus
-# using SCSIBUSL. When we have pulled the ATN line, or we just want to
-# acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
-# spec guarantees that the target will hold the data byte on the bus until
-# we send our ACK.
-#
-# The assumption here is that these are called in a particular sequence,
-# and that REQ is already set when inb_first is called. inb_{first,next}
-# use the same calling convention as inb.
-#
-inb_first:
- clr STCNT+2
- clr STCNT+1
- mov DINDEX,SINDEX
- mov DINDIR,SCSIBUSL ret # read byte directly from bus
+ mvi MSG_LEN,1 /* length = 1 */
+ mov MSG0,SINDEX /* 1-byte message */
+ mvi SEQCTL,0x10 ret /* !PAUSEDIS|FASTMODE */
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from. So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL. When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL. The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called. inb_{first,next}
+ * use the same calling convention as inb.
+ */
inb_next:
- mov DINDEX,SINDEX # save SINDEX
-
- mvi STCNT+0,1 # xfer one byte
- mov NONE,SCSIDATL # dummy read from latch to ACK
-inb_next1:
- test SSTAT0,0x4 jz inb_next1 # SDONE
-inb_next2:
- test SSTAT0,0x2 jz inb_next2 # SPIORDY - wait for next byte
- mov DINDIR,SCSIBUSL ret # read byte directly from bus
-
+ or CLRSINT0, CLRSPIORDY
+ mov NONE,SCSIDATL /*dummy read from latch to ACK*/
+inb_next_wait:
+ test SSTAT1,PHASEMIS jnz mesgin_phasemis
+ test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */
+inb_first:
+ mov DINDEX,SINDEX
+ mov DINDIR,SCSIBUSL ret /*read byte directly from bus*/
inb_last:
- mvi STCNT+0,1 # ACK with dummy read
- mov NONE,SCSIDATL
-inb_last1:
- test SSTAT0,0x4 jz inb_last1 # wait for completion
- ret
+ mov NONE,SCSIDATL ret /*dummy read from latch to ACK*/
+
+mesgin_phasemis:
+/*
+ * We expected to receive another byte, but the target changed phase
+ */
+ mvi INTSTAT, MSGIN_PHASEMIS
+ jmp ITloop
-# DMA data transfer. HADDR and HCNT must be loaded first, and
-# SINDEX should contain the value to load DFCNTRL with - 0x3d for
-# host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
-# during initialization.
-#
+/*
+ * DMA data transfer. HADDR and HCNT must be loaded first, and
+ * SINDEX should contain the value to load DFCNTRL with - 0x3d for
+ * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared
+ * during initialization.
+ */
dma:
mov DFCNTRL,SINDEX
dma1:
- test SSTAT0,0x1 jnz dma3 # DMADONE
- test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun
-
-# We will be "done" DMAing when the transfer count goes to zero, or
-# the target changes the phase (in light of this, it makes sense that
-# the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
-# doing a SCSI->Host transfer, the data FIFO should be flushed auto-
-# magically on STCNT=0 or a phase change, so just wait for FIFO empty
-# status.
-#
+ test SSTAT0,DMADONE jnz dma3
+ test SSTAT1,PHASEMIS jz dma1 /* ie. underrun */
+
+/*
+ * We will be "done" DMAing when the transfer count goes to zero, or
+ * the target changes the phase (in light of this, it makes sense that
+ * the DMA circuitry doesn't ACK when PHASEMIS is active). If we are
+ * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
+ * magically on STCNT=0 or a phase change, so just wait for FIFO empty
+ * status.
+ */
dma3:
- test SINDEX,0x4 jnz dma5 # DIRECTION
+ test SINDEX,DIRECTION jnz dma5
dma4:
- test DFSTATUS,0x1 jz dma4 # !FIFOEMP
+ test DFSTATUS,FIFOEMP jz dma4
-# Now shut the DMA enables off and make sure that the DMA enables are
-# actually off first lest we get an ILLSADDR.
-#
+/*
+ * Now shut the DMA enables off and make sure that the DMA enables are
+ * actually off first lest we get an ILLSADDR.
+ */
dma5:
- and DFCNTRL, 0x40, SINDEX # disable DMA, but maintain
- # WIDEODD
+ /* disable DMA, but maintain WIDEODD */
+ and DFCNTRL,WIDEODD
dma6:
- test DFCNTRL,0x38 jnz dma6 # SCSIENACK|SDMAENACK|HDMAENACK
-
- ret
-
-dma_finish:
- test DFSTATUS,0x8 jz dma_finish # HDONE
+ test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */
- clr DFCNTRL # disable DMA
-dma_finish2:
- test DFCNTRL,0x8 jnz dma_finish2 # HDMAENACK
ret
-# Common SCSI initialization for selection and reselection. Expects
-# the target SCSI ID to be in the upper four bits of SINDEX, and A's
-# contents are stomped on return.
-#
+/*
+ * Common SCSI initialization for selection and reselection. Expects
+ * the target SCSI ID to be in the upper four bits of SINDEX, and A's
+ * contents are stomped on return.
+ */
initialize_scsiid:
- and SINDEX,0xf0 # Get target ID
+ and SINDEX,0xf0 /* Get target ID */
and A,0x0f,SCSIID
or SINDEX,A
mov SCSIID,SINDEX ret
-initialize_for_target:
-# Turn on Automatic PIO mode now, before we expect to see a REQ
-# from the target. It shouldn't hurt anything to leave it on. Set
-# CLRCHN here before the target has entered a data transfer mode -
-# with synchronous SCSI, if you do it later, you blow away some
-# data in the SCSI FIFO that the target has already sent to you.
-#
- clr SIGSTATE
-
- or SXFRCTL0,0x8a # DFON|SPIOEN|CLRCHN
-
-# Make sure that the system knows we have not been through a DATA
-# phase.
- and FLAGS, 0xfb # !DPHASE
-
-# Initialize SCSIRATE with the appropriate value for this target.
-#
- call ndx_dtr
- mov SCSIRATE,SINDIR ret
-
-# Assert that if we've been reselected, then we've seen an IDENTIFY
-# message.
-#
+/*
+ * Assert that if we've been reselected, then we've seen an IDENTIFY
+ * message.
+ */
assert:
- test FLAGS,RESELECTED jz return # reselected?
- test FLAGS,IDENTIFY_SEEN jnz return # seen IDENTIFY?
+ test FLAGS,RESELECTED jz return /* reselected? */
+ test FLAGS,IDENTIFY_SEEN jnz return /* seen IDENTIFY? */
- mvi INTSTAT,NO_IDENT ret # no - cause a kernel panic
+ mvi INTSTAT,NO_IDENT ret /* no - cause a kernel panic */
-# Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch
-# the SCB to it. Have the kernel print a warning message if it can't be
-# found, and generate an ABORT message to the target. SINDEX should be
-# cleared on call.
-#
+/*
+ * Locate the SCB matching the target ID/channel/lun in SAVED_TCL and switch
+ * the SCB to it. Have the kernel print a warning message if it can't be
+ * found, and generate an ABORT message to the target. SINDEX should be
+ * cleared on call.
+ */
findSCB:
mov A,SAVED_TCL
- mov SCBPTR,SINDEX # switch to new SCB
- cmp SCBARRAY+1,A jne findSCB1 # target ID/channel/lun match?
- test SCBARRAY+0,DISCONNECTED jz findSCB1 # should be disconnected
- test SCBARRAY+0,TAG_ENB jnz get_tag
+ mov SCBPTR,SINDEX /* switch to new SCB */
+ cmp SCB_TCL,A jne findSCB1 /* target ID/channel/lun match? */
+ test SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
ret
findSCB1:
mov A,SCBCOUNT
cmp SINDEX,A jne findSCB
- mvi INTSTAT,NO_MATCH # not found - signal kernel
- mvi 0x6 call mk_mesg # ABORT message
+ mvi INTSTAT,NO_MATCH /* not found - signal kernel */
+ mvi MSG_ABORT call mk_mesg /* ABORT message */
- or SINDEX,0x10,SIGSTATE # assert ATNO
- call scsisig
- ret
+ or SCSISIGO,ATNO ret /* assert ATNO */
-# Make a working copy of the scatter-gather parameters from the SCB.
-#
+/*
+ * Make a working copy of the scatter-gather parameters from the SCB.
+ */
sg_scb2ram:
- mvi DINDEX,HADDR
- mvi SCBARRAY+19 call bcopy_7
-
- mvi DINDEX,STCNT
- mvi SCBARRAY+23 call bcopy_3
-
- mov SG_COUNT,SCBARRAY+2
-
- mvi DINDEX,SG_NEXT
- mvi SCBARRAY+3 call bcopy_4
- ret
-
-# Copying RAM values back to SCB, for Save Data Pointers message, but
-# only if we've actually been into a data phase to change them. This
-# protects against bogus data in scratch ram and the residual counts
-# since they are only initialized when we go into data_in or data_out.
-#
+ mov HADDR0, SCB_DATAPTR0
+ mov HADDR1, SCB_DATAPTR1
+ mov HADDR2, SCB_DATAPTR2
+ mov HADDR3, SCB_DATAPTR3
+ mov HCNT0, SCB_DATACNT0
+ mov HCNT1, SCB_DATACNT1
+ mov HCNT2, SCB_DATACNT2
+
+ mov STCNT0, HCNT0
+ mov STCNT1, HCNT1
+ mov STCNT2, HCNT2
+
+ mov SG_COUNT,SCB_SGCOUNT
+
+ mov SG_NEXT0, SCB_SGPTR0
+ mov SG_NEXT1, SCB_SGPTR1
+ mov SG_NEXT2, SCB_SGPTR2
+ mov SG_NEXT3, SCB_SGPTR3 ret
+
+/*
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them. This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ */
sg_ram2scb:
test FLAGS, DPHASE jz return
- mov SCBARRAY+2,SG_COUNT
+ mov SCB_SGCOUNT,SG_COUNT
- mvi DINDEX,SCBARRAY+3
- mvi SG_NEXT call bcopy_4
+ mov SCB_SGPTR0,SG_NEXT0
+ mov SCB_SGPTR1,SG_NEXT1
+ mov SCB_SGPTR2,SG_NEXT2
+ mov SCB_SGPTR3,SG_NEXT3
- mvi DINDEX,SCBARRAY+19
- mvi SHADDR call bcopy_4
-
-# Use the residual number since STCNT is corrupted by any message transfer
- mvi SCBARRAY+15 call bcopy_3
- ret
-
-# Add the array base SYNCNEG to the target offset (the target address
-# is in SCSIID), and return the result in SINDEX. The accumulator
-# contains the 3->8 decoding of the target ID on return.
-#
+ mov SCB_DATAPTR0,SHADDR0
+ mov SCB_DATAPTR1,SHADDR1
+ mov SCB_DATAPTR2,SHADDR2
+ mov SCB_DATAPTR3,SHADDR3
+
+/*
+ * Use the residual number since STCNT is corrupted by any message transfer
+ */
+ mov SCB_DATACNT0,SCB_RESID_DCNT0
+ mov SCB_DATACNT1,SCB_RESID_DCNT1
+ mov SCB_DATACNT2,SCB_RESID_DCNT2 ret
+
+/*
+ * Add the array base TARG_SCRATCH to the target offset (the target address
+ * is in SCSIID), and return the result in SINDEX. The accumulator
+ * contains the 3->8 decoding of the target ID on return.
+ */
ndx_dtr:
shr A,SCSIID,4
- test SBLKCTL,0x08 jz ndx_dtr_2
- or A,0x08 # Channel B entries add 8
+ test SBLKCTL,SELBUSB jz ndx_dtr_2
+ or A,0x08 /* Channel B entries add 8 */
ndx_dtr_2:
- add SINDEX,SYNCNEG,A
-
- and FUNCTION1,0x70,SCSIID # 3-bit target address decode
- mov A,FUNCTION1 ret
-
-# If we need to negotiate transfer parameters, build the WDTR or SDTR message
-# starting at the address passed in SINDEX. DINDEX is modified on return.
-# The SCSI-II spec requires that Wide negotiation occur first and you can
-# only negotiat one or the other at a time otherwise in the event of a message
-# reject, you wouldn't be able to tell which message was the culpret.
-#
+ add SINDEX,TARG_SCRATCH,A ret
+
+/*
+ * If we need to negotiate transfer parameters, build the WDTR or SDTR message
+ * starting at the address passed in SINDEX. DINDEX is modified on return.
+ * The SCSI-II spec requires that Wide negotiation occur first and you can
+ * only negotiat one or the other at a time otherwise in the event of a message
+ * reject, you wouldn't be able to tell which message was the culpret.
+ */
mk_dtr:
- test SCBARRAY+0,0x90 jz return # NEEDWDTR|NEEDSDTR
- test SCBARRAY+0,NEEDWDTR jnz mk_wdtr_16bit
- or FLAGS, MAX_OFFSET # Force an offset of 15 or 8 if WIDE
+ test SCB_CONTROL,NEEDWDTR jnz mk_wdtr_16bit
+ or FLAGS, MAXOFFSET /* Force an offset of 15 or 8 if WIDE */
mk_sdtr:
- mvi DINDIR,1 # extended message
- mvi DINDIR,3 # extended message length = 3
- mvi DINDIR,1 # SDTR code
+ mvi DINDIR,1 /* extended message */
+ mvi DINDIR,3 /* extended message length = 3 */
+ mvi DINDIR,1 /* SDTR code */
call sdtr_to_rate
- mov DINDIR,RETURN_1 # REQ/ACK transfer period
- test FLAGS, MAX_OFFSET jnz mk_sdtr_max_offset
- and DINDIR,0x0f,SINDIR # Sync Offset
+ mov DINDIR,RETURN_1 /* REQ/ACK transfer period */
+ test FLAGS, MAXOFFSET jnz mk_sdtr_max_offset
+ and DINDIR,0x0f,SINDIR /* Sync Offset */
mk_sdtr_done:
- add MSG_LEN,-MSG_START+0,DINDEX ret # update message length
+ add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */
mk_sdtr_max_offset:
-# We're initiating sync negotiation, so request the max offset we can (15 or 8)
- xor FLAGS, MAX_OFFSET
- test SCSIRATE, 0x80 jnz wmax_offset # Talking to a WIDE device?
+/*
+ * We're initiating sync negotiation, so request the max offset we can (15 or 8)
+ */
+ xor FLAGS, MAXOFFSET
+
+ /* Talking to a WIDE device? */
+ test SCSIRATE, WIDEXFER jnz wmax_offset
mvi DINDIR, MAX_OFFSET_8BIT
jmp mk_sdtr_done
wmax_offset:
- mvi DINDIR, MAX_OFFSET_WIDE
+ mvi DINDIR, MAX_OFFSET_16BIT
jmp mk_sdtr_done
mk_wdtr_16bit:
mvi ARG_1,BUS_16_BIT
mk_wdtr:
- mvi DINDIR,1 # extended message
- mvi DINDIR,2 # extended message length = 2
- mvi DINDIR,3 # WDTR code
- mov DINDIR,ARG_1 # bus width
+ mvi DINDIR,1 /* extended message */
+ mvi DINDIR,2 /* extended message length = 2 */
+ mvi DINDIR,3 /* WDTR code */
+ mov DINDIR,ARG_1 /* bus width */
- add MSG_LEN,-MSG_START+0,DINDEX ret # update message length
+ add MSG_LEN,COMP_MSG0,DINDEX ret /* update message length */
-# Set SCSI bus control signal state. This also saves the last-written
-# value into a location where the higher-level driver can read it - if
-# it has to send an ABORT or RESET message, then it needs to know this
-# so it can assert ATN without upsetting SCSISIGO. The new value is
-# expected in SINDEX. Change the actual state last to avoid contention
-# from the driver.
-#
-scsisig:
- mov SIGSTATE,SINDEX
- mov SCSISIGO,SINDEX ret
-
sdtr_to_rate:
- call ndx_dtr # index scratch space for target
+ call ndx_dtr /* index scratch space for target */
shr A,SINDIR,0x4
- dec SINDEX #Preserve SINDEX
+ dec SINDEX /* Preserve SINDEX */
and A,0x7
clr RETURN_1
sdtr_to_rate_loop:
test A,0x0f jz sdtr_to_rate_done
- add RETURN_1,0x18
+ add RETURN_1,0x19
dec A
jmp sdtr_to_rate_loop
sdtr_to_rate_done:
shr RETURN_1,0x2
- add RETURN_1,0x18
+ add RETURN_1,0x19
test SXFRCTL0,ULTRAEN jz return
shr RETURN_1,0x1
return:
* A <label> is an <undef-sym> ending in a colon. Spaces, tabs, and commas
* are token separators.
*-M*************************************************************************/
-static char id[] = "$Id: aic7xxx_asm.c,v 2.1 1995/08/23 04:31:40 deang Exp $";
+static const char id[] = "$Id: aic7xxx_asm.c,v 2.4 1996/01/30 07:17:29 deang Exp $";
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
+#include <fcntl.h>
#define MEMORY 448
#define MAXLINE 1024
#define ADOTOUT "a.out"
#define NOVALUE -1
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+#define MAX_ARGS 16
+static const char *cpp[] = {
+ "/lib/cpp -P - -",
+ "/usr/lib/cpp -P - -",
+ "/usr/bin/cpp -P - -",
+ "/usr/bin/gcc -E -P -",
+ "/usr/bin/cc -E -P -"
+};
+
+#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
+
/*
* AIC-7770/AIC-7870 register definitions
*/
int debug;
int lineno, LC;
char *filename;
-FILE *ifp, *ofp;
unsigned char M[MEMORY][4];
void
i = 0;
- while (fgets(buf, sizeof(buf), ifp)) {
+ while (fgets(buf, sizeof(buf), stdin)) {
lineno += 1;
#undef A
void
-assemble(void)
+assemble(FILE *ofile)
{
int n;
char **a;
continue;
if (n == 3 && !strcmp("VERSION", *a))
- fprintf(ofp, "#define %s \"%s\"\n", a[1], a[2]);
+ fprintf(ofile, "#define %s \"%s\"\n", a[1], a[2]);
else {
if (n == 3 && !strcmp("=", a[1]))
define(*a, strtol(a[2], NULL, 0));
}
backpatch();
- output(ofp);
+ output(ofile);
if (debug)
output(stderr);
main(int argc, char **argv)
{
int c;
-
+ int pid;
+ int ifile;
+ int status;
+ FILE *ofile;
+ char *ofilename;
+ int fd[2];
+
+ ofile = NULL;
+ ofilename = NULL;
while ((c = getopt(argc, argv, "dho:vD")) != EOF) {
switch (c) {
case 'd':
break;
}
case 'o':
- ofp = fopen(optarg, "w");
- if (!ofp) {
+ ofilename = optarg;
+ if ((ofile = fopen(ofilename, "w")) < 0) {
perror(optarg);
exit(EXIT_FAILURE);
}
}
filename = argv[optind];
- ifp = fopen(filename, "r");
- if (!ifp) {
+
+ if ((ifile = open(filename, O_RDONLY)) < 0) {
perror(filename);
exit(EXIT_FAILURE);
}
- if (!ofp) {
- ofp = fopen(ADOTOUT, "w");
- if (!ofp) {
- perror(ADOTOUT);
+ if (!ofilename) {
+ ofilename = ADOTOUT;
+ if ((ofile = fopen(ofilename, "w")) < 0) {
+ perror(ofilename);
exit(EXIT_FAILURE);
}
}
- assemble();
- exit(EXIT_SUCCESS);
+ if (pipe(fd) < 0) {
+ perror("pipe failed");
+ exit(1);
+ }
+
+ if ((pid = fork()) < 0 ) {
+ perror("fork failed");
+ exit(1);
+ }
+ else if (pid > 0) { /* Parent */
+ close(fd[1]); /* Close write end */
+ if (fd[0] != STDIN_FILENO) {
+ if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
+ perror("dup2 error on stdin");
+ exit(EXIT_FAILURE);
+ }
+ close(fd[0]);
+ }
+ assemble(ofile);
+ if (wait(&status) < 0) {
+ perror("wait error");
+ }
+
+ if (status != 0) {
+ unlink(ofilename);
+ }
+ exit(status);
+ } else { /* Child */
+ int i, arg_cnt, found;
+ char *args[MAX_ARGS];
+ char *buf;
+
+ arg_cnt = 0;
+ found = FALSE;
+ for (i = 0; (!found && (i < NUMBER(cpp))); i++) {
+ char *bp;
+
+ buf = strdup(cpp[i]);
+
+ for (bp = strtok(buf, " \t\n"), arg_cnt = 0;
+ bp != NULL;
+ bp = strtok(NULL, " \t\n"), arg_cnt++) {
+ if (arg_cnt == 0) {
+ if (access(bp, X_OK) == 0) {
+ found = TRUE;
+ }
+ }
+
+ args[arg_cnt] = bp;
+ }
+
+ if (!found) {
+ free(buf);
+ }
+ }
+ args[arg_cnt] = NULL;
+
+ if (found) {
+ close(fd[0]); /* Close Read end */
+ if (fd[1] != STDOUT_FILENO) {
+ if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
+ perror("dup2 error on stdout");
+ exit(EXIT_FAILURE);
+ }
+ close(fd[1]);
+ }
+ if (ifile != STDIN_FILENO) {
+ if (dup2(ifile, STDIN_FILENO) != STDIN_FILENO) {
+ perror("dup2 error on stdin");
+ exit(EXIT_FAILURE);
+ }
+ close(ifile);
+ }
+
+ if (execvp(args[0], args) < 0) {
+ perror("execvp() error");
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ fprintf(stderr, "%s: Cannot find CPP command.\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ return(EXIT_SUCCESS);
}
--- /dev/null
+/*+M*************************************************************************
+ * Adaptec AIC7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: aic7xxx_reg.h,v 1.2 1996/02/10 06:23:39 deang Exp $
+ *-M*************************************************************************/
+
+/*
+ * This header is shared by the sequencer code and the kernel level driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book availible from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+#define SCSISEQ 0x000
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+#define SXFRCTL0 0x001
+#define DFON 0x80
+#define DFPEXP 0x40
+#define ULTRAEN 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+/* UNUSED 0x01 */
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+#define SXFRCTL1 0x002
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01 /* Powered Termination */
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+#define SCSISIGI 0x003
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/*
+ * Possible phases in SCSISIGI
+ */
+#define PHASE_MASK 0xe0
+#define P_DATAOUT 0x00
+#define P_DATAIN 0x40
+#define P_COMMAND 0x80
+#define P_MESGOUT 0xa0
+#define P_STATUS 0xc0
+#define P_MESGIN 0xe0
+/*
+ * SCSI Contol Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+#define SCSISIGO 0x003
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/*
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers. Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+#define SCSIRATE 0x004
+#define WIDEXFER 0x80 /* Wide transfer control */
+#define SXFR 0x70 /* Sync transfer rate */
+#define SOFS 0x0f /* Sync offset */
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+#define SCSIID 0x005
+#define TID 0xf0 /* Target ID mask */
+#define OID 0x0f /* Our ID mask */
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latchs used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode. SCSIDATH can be used for the
+ * upper byte of a 16bit wide asyncronouse data phase transfer.
+ */
+#define SCSIDATL 0x006
+#define SCSIDATH 0x007
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transfered
+ * across the SCSI bus. The counter is decremented only once
+ * the data has been safely transfered. SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+#define STCNT 0x008
+#define STCNT0 0x008
+#define STCNT1 0x009
+#define STCNT2 0x00a
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+#define CLRSINT0 0x00b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+/* UNUSED 0x04 */
+#define CLRSPIORDY 0x02
+/* UNUSED 0x01 */
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+#define SSTAT0 0x00b
+#define TARGET 0x80 /* Board acting as target */
+#define SELDO 0x40 /* Selection Done */
+#define SELDI 0x20 /* Board has been selected */
+#define SELINGO 0x10 /* Selection In Progress */
+#define SWRAP 0x08 /* 24bit counter wrap */
+#define SDONE 0x04 /* STCNT = 0x000000 */
+#define SPIORDY 0x02 /* SCSI PIO Ready */
+#define DMADONE 0x01 /* DMA transfer completed */
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+#define CLRSINT1 0x00c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+/* UNUSED 0x10 */
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+#define SSTAT1 0x00c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+#define SIMODE1 0x011
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+#define SCSIBUSL 0x012
+#define SCSIBUSH 0x013
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transfered on the SCSI bus. They are counted up in the same
+ * manner as STCNT is counted down. SHADDR should always be used
+ * to determine the address of the last byte transfered since HADDR
+ * can be squewed by write ahead.
+ */
+#define SHADDR 0x014
+#define SHADDR0 0x014
+#define SHADDR1 0x015
+#define SHADDR2 0x016
+#define SHADDR3 0x017
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+#define SELID 0x019
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+/* UNUSED 0x07 */
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+#define SBLKCTL 0x01f
+#define DIAGLEDEN 0x80 /* Aic78X0 only */
+#define DIAGLEDON 0x40 /* Aic78X0 only */
+#define AUTOFLUSHDIS 0x20
+/* UNUSED 0x10 */
+#define SELBUS_MASK 0x0a
+#define SELBUSB 0x08
+/* UNUSED 0x04 */
+#define SELWIDE 0x02
+/* UNUSED 0x01 */
+#define SELNARROW 0x00
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+#define SEQCTL 0x060
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in sucessesion. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+#define SEQRAM 0x061
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+#define SEQADDR0 0x062
+#define SEQADDR1 0x063
+#define SEQADDR1_MASK 0x01
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+#define ACCUM 0x064
+
+#define SINDEX 0x065
+#define DINDEX 0x066
+#define ALLZEROS 0x06a
+#define NONE 0x06a
+#define SINDIR 0x06c
+#define DINDIR 0x06d
+#define FUNCTION1 0x06e
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transfered across the host bus.
+ */
+#define HADDR 0x088
+#define HADDR0 0x088
+#define HADDR1 0x089
+#define HADDR2 0x08a
+#define HADDR3 0x08b
+
+#define HCNT 0x08c
+#define HCNT0 0x08c
+#define HCNT1 0x08d
+#define HCNT2 0x08e
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+#define SCBPTR 0x090
+
+/*
+ * Board Control (p. 3-43)
+ */
+#define BCTL 0x084
+/* RSVD 0xf0 */
+#define ACE 0x08 /* Support for external processors */
+/* RSVD 0x06 */
+#define ENABLE 0x01
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+#define DSCOMMAND 0x084
+#define CACHETHEN 0x80 /* Cache Threshold enable */
+#define DPARCKEN 0x40 /* Data Parity Check Enable */
+#define MPARCKEN 0x20 /* Memory Parity Check Enable */
+#define EXTREQLCK 0x10 /* External Request Lock */
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+#define BUSTIME 0x085
+#define BOFF 0xf0
+#define BON 0x0f
+#define BOFF_60BCLKS 0xf0
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+#define BUSSPD 0x086
+#define DFTHRSH 0xc0
+#define STBOFF 0x38
+#define STBON 0x07
+#define DFTHRSH_100 0xc0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overal host control of the device.
+ */
+#define HCNTRL 0x087
+/* UNUSED 0x80 */
+#define POWRDN 0x40
+/* UNUSED 0x20 */
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+#define INTSTAT 0x091
+#define SEQINT_MASK 0xf1 /* SEQINT Status Codes */
+#define BAD_PHASE 0x01 /* unknown scsi bus phase */
+#define SEND_REJECT 0x11 /* sending a message reject */
+#define NO_IDENT 0x21 /* no IDENTIFY after reconnect*/
+#define NO_MATCH 0x31 /* no cmd match for reconnect */
+#define SDTR_MSG 0x41 /* SDTR message recieved */
+#define WDTR_MSG 0x51 /* WDTR message recieved */
+#define REJECT_MSG 0x61 /* Reject message recieved */
+#define BAD_STATUS 0x71 /* Bad status from target */
+#define RESIDUAL 0x81 /* Residual byte count != 0 */
+#define ABORT_TAG 0x91 /* Sent an ABORT_TAG message */
+#define AWAITING_MSG 0xa1 /*
+ * Kernel requested to specify
+ * a message to this target
+ * (command was null), so tell
+ * it that it can fill the
+ * message buffer.
+ */
+#define IMMEDDONE 0xb1 /*
+ * An immediate command has
+ * completed
+ */
+#define MSG_BUFFER_BUSY 0xc1 /*
+ * Sequencer wants to use the
+ * message buffer, but it
+ * already contains a message
+ */
+#define MSGIN_PHASEMIS 0xd1 /*
+ * Target changed phase on us
+ * when we were expecting
+ * another msgin byte.
+ */
+#define PARITY_ERROR 0xe1 /*
+ * Sequencer detected a parity
+ * error.
+ */
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define SEQINT 0x01
+#define INT_PEND (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
+ */
+#define ERROR 0x092
+/* UNUSED 0xf0 */
+#define PARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+#define CLRINT 0x092
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define DFCNTRL 0x093
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define DFSTATUS 0x094
+#define HDONE 0x08
+#define FIFOEMP 0x01
+
+#define DFDAT 0x099
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+#define SCBCNT 0x09a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+#define QINFIFO 0x09b
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+#define QINCNT 0x09c
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+#define QOUTFIFO 0x09d
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+#define QOUTCNT 0x09e
+
+/*
+ * SCB Definition (p. 5-4)
+ * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
+ * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
+ * whether or not to DMA an SCB from host ram. This flag prevents the
+ * "re-fetching" of transactions that are requed because the target is
+ * busy with another command. We also use bits 6 & 7 to indicate whether
+ * or not to initiate SDTR or WDTR repectively when starting this command.
+ */
+#define SCBARRAY 0x0a0
+#define SCB_CONTROL 0x0a0
+#define NEEDWDTR 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define NEEDSDTR 0x10
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+#define SCB_TCL 0x0a1
+#define SCB_TARGET_STATUS 0x0a2
+#define SCB_SGCOUNT 0x0a3
+#define SCB_SGPTR 0x0a4
+#define SCB_SGPTR0 0x0a4
+#define SCB_SGPTR1 0x0a5
+#define SCB_SGPTR2 0x0a6
+#define SCB_SGPTR3 0x0a7
+#define SCB_RESID_SGCNT 0x0a8
+#define SCB_RESID_DCNT 0x0a9
+#define SCB_RESID_DCNT0 0x0a9
+#define SCB_RESID_DCNT1 0x0aa
+#define SCB_RESID_DCNT2 0x0ab
+#define SCB_DATAPTR 0x0ac
+#define SCB_DATAPTR0 0x0ac
+#define SCB_DATAPTR1 0x0ad
+#define SCB_DATAPTR2 0x0ae
+#define SCB_DATAPTR3 0x0af
+#define SCB_DATACNT 0x0b0
+#define SCB_DATACNT0 0x0b0
+#define SCB_DATACNT1 0x0b1
+#define SCB_DATACNT2 0x0b2
+/* UNUSED - QUAD PADDING 0x0b3 */
+#define SCB_CMDPTR 0x0b4
+#define SCB_CMDPTR0 0x0b4
+#define SCB_CMDPTR1 0x0b5
+#define SCB_CMDPTR2 0x0b6
+#define SCB_CMDPTR3 0x0b7
+#define SCB_CMDLEN 0x0b8
+#define SCB_NEXT_WAITING 0x0b9
+
+#ifdef linux
+#define SG_SIZEOF 0x0c /* sizeof(struct scatterlist) */
+#else
+#define SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
+#endif
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+#define SEECTL_2840 0x0c0
+/* UNUSED 0xf8 */
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0x0c1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+#define DSPCISTATUS 0x086
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device. In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device. When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM. When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.
+ *
+ * After successful arbitration for the memory port, the SEECS bit of
+ * the SEECTL register is connected to the chip select. The SEECK,
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in
+ * lines respectively. The SEERDY bit of SEECTL is useful in that it
+ * gives us an 800 nsec timer. After a write to the SEECTL register,
+ * the SEERDY goes high 800 nsec later. The one exception to this is
+ * when we first request access to the memory port. The SEERDY goes
+ * high to signify that access has been granted and, for this case, has
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to
+ * read the serial EEPROM.
+ */
+#define SEECTL 0x01e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE). The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space. This should work regardless of
+ * whether the bios has been installed.
+ */
+
+/*
+ * 1 byte per target starting at this address for configuration values
+ */
+#define TARG_SCRATCH 0x020
+
+/*
+ * The sequencer will stick the frist byte of any rejected message here so
+ * we can see what is getting thrown away.
+ */
+#define REJBYTE 0x031
+
+/*
+ * Bit vector of targets that have disconnection disabled.
+ */
+#define DISC_DSB 0x032
+#define DISC_DSB_A 0x032
+#define DISC_DSB_B 0x033
+
+/*
+ * Length of pending message
+ */
+#define MSG_LEN 0x034
+
+#define MSG0 0x035
+#define COMP_MSG0 0xcb /* 2's complement of MSG0 */
+#define MSG1 0x036
+#define MSG2 0x037
+#define MSG3 0x038
+#define MSG4 0x039
+#define MSG5 0x03a
+
+/*
+ * These are offsets into the card's scratch ram. Some of the values are
+ * specified in the AHA2742 technical reference manual and are initialized
+ * by the BIOS at boot time.
+ */
+#define LASTPHASE 0x049
+#define ARG_1 0x04a
+#define RETURN_1 0x04a
+#define SEND_SENSE 0x80
+#define SEND_WDTR 0x80
+#define SEND_SDTR 0x80
+#define SEND_REJ 0x40
+
+#define SIGSTATE 0x04b
+
+#define DMAPARAMS 0x04c /* Parameters for DMA Logic */
+
+#define SG_COUNT 0x04d
+#define SG_NEXT 0x04e /* working value of SG pointer */
+#define SG_NEXT0 0x04e
+#define SG_NEXT1 0x04f
+#define SG_NEXT2 0x050
+#define SG_NEXT3 0x051
+
+#define SCBCOUNT 0x052 /*
+ * Number of SCBs supported by
+ * this card.
+ */
+#define FLAGS 0x053
+#define SINGLE_BUS 0x00
+#define TWIN_BUS 0x01
+#define WIDE_BUS 0x02
+#define DPHASE 0x04
+#define MAXOFFSET 0x08
+#define IDENTIFY_SEEN 0x40
+#define RESELECTED 0x80
+
+#define ACTIVE_A 0x054
+#define ACTIVE_B 0x055
+#define SAVED_TCL 0x056 /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+#define WAITING_SCBH 0x057 /*
+ * head of list of SCBs awaiting
+ * selection
+ */
+#define WAITING_SCBT 0x058 /*
+ * tail of list of SCBs awaiting
+ * selection
+ */
+#define COMP_SCBCOUNT 0x059
+#define SCB_LIST_NULL 0xff
+
+#define SCSICONF 0x05a
+#define HOSTCONF 0x05d
+
+#define HA_274_BIOSCTRL 0x05f
+#define BIOSMODE 0x30
+#define BIOSDISABLED 0x30
+
+/* Message codes */
+#define MSG_EXTENDED 0x01
+#define MSG_SDTR 0x01
+#define MSG_WDTR 0x03
+#define MSG_SDPTRS 0x02
+#define MSG_RDPTRS 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_DET_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOP 0x08
+#define MSG_MSG_PARITY_ERROR 0x09
+#define MSG_BUS_DEVICE_RESET 0x0c
+#define MSG_SIMPLE_TAG 0x20
+#define MSG_IDENTIFY 0x80
+
+/* WDTR Message values */
+#define BUS_8_BIT 0x00
+#define BUS_16_BIT 0x01
+#define BUS_32_BIT 0x02
+
+#define MAX_OFFSET_8BIT 0x0f
+#define MAX_OFFSET_16BIT 0x08
+
X(need_isa_buffer),
X(request_queueable),
X(print_Scsi_Cmnd),
+ X(scsi_mark_host_bus_reset),
#if defined(CONFIG_PROC_FS)
X(proc_print_scsidevice),
#endif
# msdos filesystems
tristate 'DOS FAT fs support' CONFIG_FAT_FS
-dep_tristate 'Old MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
-dep_tristate 'vfat (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
bool '/proc filesystem support' CONFIG_PROC_FS
trim back the buffers */
} b_un;
unsigned int data[N_PARAM];
-} bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
+} bdf_prm = {{25, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
/* The lav constant is set for 1 minute, as long as the update process runs
every 5 seconds. If you change the frequency of update, the time
memcpy(next->b_data, tmp->b_data, size);
else {
memcpy(tmp->b_data, next->b_data, size);
- set_bit(BH_Dirty, &tmp->b_state);
+ mark_buffer_dirty(tmp, 0);
}
brelse(tmp);
next->b_count--;
*/
#include <linux/module.h>
+#include <linux/mm.h>
#include <linux/msdos_fs.h>
#include "msbuffer.h"
#include <linux/module.h>
+#include <linux/mm.h>
#include <linux/msdos_fs.h>
static struct symbol_table msdos_syms = {
#endif
extern int get_device_list(char *);
extern int get_filesystem_list(char *);
+extern int get_filesystem_info( char * );
extern int get_irq_list(char *);
extern int get_dma_list(char *);
extern int get_cpuinfo(char *);
#endif
case PROC_CMDLINE:
return get_cmdline(page);
+
+ case PROC_MTAB:
+ return get_filesystem_info( page );
}
return -EBADF;
}
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
});
+
+ proc_register( &proc_root, &(struct proc_dir_entry)
+ { PROC_MTAB, 6, "mounts", S_IFREG | S_IRUGO, 1, 0, 0, } );
if (prof_shift) {
proc_register(&proc_root, &(struct proc_dir_entry) {
return retval;
}
+int get_filesystem_info( char *buf )
+{
+ struct vfsmount *tmp = vfsmntlist;
+ int len = 0;
+
+ while ( tmp && len < PAGE_SIZE - 80 )
+ {
+ len += sprintf( buf + len, "%s %s %s %s",
+ tmp->mnt_devname, tmp->mnt_dirname, tmp->mnt_sb->s_type->name,
+ tmp->mnt_flags & MS_RDONLY ? "ro" : "rw" );
+ len += sprintf( buf + len, " 0 0\n" );
+ tmp = tmp->mnt_next;
+ }
+
+ return len;
+}
+
int get_filesystem_list(char * buf)
{
int len = 0;
extlen = 0;
}
ext[extlen] = '\0';
+ base[baselen] = '\0';
count = 0;
strcpy(msdos_name, base);
unsigned long int res, t1, t2;
__asm__(
- "# bswap input: %0 (aabbccdd)
- # output: %0, used %1 %2
- extlh %0,5,%1 # %1 = dd000000
- zap %0,0xfd,%2 # %2 = 0000cc00
- sll %2,5,%2 # %2 = 00198000
- s8addq %2,%1,%1 # %1 = ddcc0000
- zap %0,0xfb,%2 # %2 = 00bb0000
- srl %2,8,%2 # %2 = 0000bb00
- extbl %0,3,%0 # %0 = 000000aa
- or %1,%0,%0 # %0 = ddcc00aa
- or %2,%0,%0 # %0 = ddccbbaa"
+ "# bswap input: %0 (aabbccdd)\n\t"
+ "# output: %0, used %1 %2\n\t"
+ "extlh %0,5,%1 # %1 = dd000000\n\t"
+ "zap %0,0xfd,%2 # %2 = 0000cc00\n\t"
+ "sll %2,5,%2 # %2 = 00198000\n\t"
+ "s8addq %2,%1,%1 # %1 = ddcc0000\n\t"
+ "zap %0,0xfb,%2 # %2 = 00bb0000\n\t"
+ "srl %2,8,%2 # %2 = 0000bb00\n\t"
+ "extbl %0,3,%0 # %0 = 000000aa\n\t"
+ "or %1,%0,%0 # %0 = ddcc00aa\n\t"
+ "or %2,%0,%0 # %0 = ddccbbaa\n"
: "r="(res), "r="(t1), "r="(t2)
: "0" (x & 0xffffffffUL));
return res;
{
unsigned long int res, t1;
- __asm__
- ("bis %2,%2,%0 # v0 is result; swap in-place. v0=aabb
- extwh %0,7,%1 # t1 = bb00
- extbl %0,1,%0 # v0 = 00aa
- bis %0,%1,%0 # v0 = bbaa"
- : "r="(res), "r="(t1) : "r"(x));
+ __asm__(
+ "# v0 is result; swap in-place.\n\t"
+ "bis %2,%2,%0 # v0 = aabb\n\t"
+ "extwh %0,7,%1 # t1 = bb00\n\t"
+ "extbl %0,1,%0 # v0 = 00aa\n\t"
+ "bis %0,%1,%0 # v0 = bbaa\n"
+ : "r="(res), "r="(t1) : "r"(x));
return res;
}
/*
* Give prototypes to shut up gcc.
*/
-extern inline unsigned long xchg_u32 (volatile int * m, unsigned long val);
-extern inline unsigned long xchg_u64 (volatile long * m, unsigned long val);
+extern __inline__ unsigned long xchg_u32 (volatile int * m, unsigned long val);
+extern __inline__ unsigned long xchg_u64 (volatile long * m, unsigned long val);
-extern inline unsigned long xchg_u32(volatile int * m, unsigned long val)
+extern __inline__ unsigned long xchg_u32(volatile int * m, unsigned long val)
{
unsigned long dummy, dummy2;
return val;
}
-extern inline unsigned long xchg_u64(volatile long * m, unsigned long val)
+extern __inline__ unsigned long xchg_u64(volatile long * m, unsigned long val)
{
unsigned long dummy, dummy2;
*/
extern void __xchg_called_with_bad_pointer(void);
-static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+static __inline__ unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
{
switch (size) {
case 4:
/*
* cdk.h -- CDK interface definitions.
*
- * Copyright (C) 1994,1995 Greg Ungerer (gerg@stallion.oz.au).
+ * Copyright (C) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
*
* 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
#define STL_BSTOP _IO('s',22)
#define STL_BRESET _IO('s',23)
+/*
+ * Define a set of ioctl extensions, used to get at special stuff.
+ */
+#define STL_GETPFLAG _IO('s',80)
+#define STL_SETPFLAG _IO('s',81)
+
/*****************************************************************************/
#endif
#endif
PROC_PROFILE, /* whether enabled or not */
PROC_CMDLINE,
- PROC_SYS
+ PROC_SYS,
+ PROC_MTAB
};
enum pid_directory_inos {
extern void free_dma(unsigned int dmanr);
extern int (*rarp_ioctl_hook)(int,void*);
-extern void (* iABI_hook)(struct pt_regs * regs);
-
-#ifdef CONFIG_BINFMT_ELF
-#include <linux/elfcore.h>
-extern int dump_fpu(elf_fpregset_t *);
-#endif
-
-extern void dump_thread(struct pt_regs *, struct user *);
-
struct symbol_table symbol_table = {
#include <linux/symtab_begin.h>
#ifdef MODVERSIONS
X(tr_type_trans),
#endif
-#ifdef CONFIG_BINFMT_ELF
- X(dump_fpu),
-#endif
/* bimfm_aout */
- X(dump_thread),
X(get_write_access),
X(put_write_access),
#ifdef CONFIG_IP_ALIAS
#include <net/ip_alias.h>
#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
#define min(a,b) ((a)<(b)?(a):(b))
case SIOCDRARP:
case SIOCGRARP:
case SIOCSRARP:
+#ifdef CONFIG_KERNELD
+ if (rarp_ioctl_hook == NULL)
+ request_module("rarp");
+#endif
if (rarp_ioctl_hook != NULL)
return(rarp_ioctl_hook(cmd,(void *) arg));
case SIOCGIFCONF:
static struct inet_protocol ipip_protocol = {
ipip_rcv, /* IPIP handler */
+#if 0
NULL, /* Will be UDP fraglist handler */
+#endif
NULL, /* TUNNEL error control */
0, /* next */
IPPROTO_IPIP, /* protocol ID */
static inline void rarp_release_entry(struct rarp_table *entry)
{
kfree_s(entry, sizeof(struct rarp_table));
+ MOD_DEC_USE_COUNT;
return;
}
memcpy(&entry->ha, &r.arp_ha.sa_data, hlen);
entry->dev = dev;
+ /* Don't unlink if we have entries to serve. */
+ MOD_INC_USE_COUNT;
+
sti();
return 0;
}
t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
- memcpy(t1, th, sizeof(*t1));
+ memset(t1, 0, sizeof(*t1));
/*
* Swap the send and the receive.
t1->dest = th->source;
t1->source = th->dest;
- t1->syn = 0;
- t1->fin = 0;
- t1->urg = 0;
+ t1->doff = sizeof(*t1)/4;
t1->rst = 1;
- t1->psh = 0;
if(th->ack)
{
- t1->ack = 0;
t1->seq = th->ack_seq;
}
else
{
+ t1->ack = 1;
if(!th->syn)
t1->ack_seq = th->seq;
else