diff -u --recursive --new-file v1.3.63/linux/Documentation/Configure.help linux/Documentation/Configure.help --- v1.3.63/linux/Documentation/Configure.help Wed Feb 14 14:37:06 1996 +++ linux/Documentation/Configure.help Thu Feb 15 09:20:41 1996 @@ -2041,6 +2041,20 @@ 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 @@ -2056,14 +2070,30 @@ 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 diff -u --recursive --new-file v1.3.63/linux/Documentation/networking/arcnet-hardware.txt linux/Documentation/networking/arcnet-hardware.txt --- v1.3.63/linux/Documentation/networking/arcnet-hardware.txt Sun Jan 14 16:30:09 1996 +++ linux/Documentation/networking/arcnet-hardware.txt Thu Feb 15 09:21:21 1996 @@ -1,4 +1,4 @@ - + ----------------------------------------------------------------------------- 1) This file is a supplement to arcnet.txt. Please read that for general driver configuration help. @@ -26,7 +26,8 @@ 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. @@ -46,6 +47,10 @@ 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 @@ -60,9 +65,14 @@ 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 ----------------------- @@ -260,7 +270,7 @@ 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 : + Pavlik : ("Not on bus" means there is no way for a card to generate this interrupt) IRQ 0 - Timer 0 (Not on bus) @@ -268,9 +278,9 @@ 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 @@ -279,6 +289,19 @@ 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. @@ -324,7 +347,7 @@ network. Also, on many cards (not mine, though) there are red and green LED's. -Vojtech Pavlik tells me this is what they +Vojtech Pavlik tells me this is what they mean: GREEN RED Status ----- --- ------ @@ -368,7 +391,7 @@ 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 @@ -974,9 +997,12 @@ ***************************************************************************** ** Possibly SMC ** -LCS-8830-T (16-bit card) ------------------------- +LCS-8830(-T) (8 and 16-bit cards) +--------------------------------- - from Mathias Katzer + - Marek Michalkiewicz 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). @@ -1709,7 +1735,7 @@ ** Acer ** 8-bit card, Model 5210-003 -------------------------- - - from Vojtech Pavlik using portions of + - from Vojtech Pavlik using portions of the existing arcnet-hardware file. This is a 90C26 based card. Its configuration seems similar to @@ -1877,7 +1903,7 @@ ** Datapoint? ** LAN-ARC-8, an 8-bit card ------------------------ - - from Vojtech Pavlik + - from Vojtech Pavlik This is another SMC 90C65 based arcnet card. I couldn't identify the manufacturer, but it might be DataPoint, becsuse the card has the @@ -2017,7 +2043,7 @@ ** Topware ** 8-bit card, TA-ARC/10 ------------------------- - - from Vojtech Pavlik + - from Vojtech Pavlik This is another very similar 90C65 card. Most of the switches and jumpers are the same as on other clones. @@ -2726,7 +2752,7 @@ ** No Name ** 8-bit cards ("Made in Taiwan R.O.C.") ----------- - - from Vojtech Pavlik + - from Vojtech Pavlik 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 diff -u --recursive --new-file v1.3.63/linux/Documentation/networking/arcnet.txt linux/Documentation/networking/arcnet.txt --- v1.3.63/linux/Documentation/networking/arcnet.txt Sun Jan 14 16:30:09 1996 +++ linux/Documentation/networking/arcnet.txt Thu Feb 15 09:21:15 1996 @@ -7,8 +7,8 @@ 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. @@ -46,16 +46,16 @@ 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. @@ -65,12 +65,9 @@ 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. @@ -264,10 +261,14 @@ 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. @@ -308,7 +309,7 @@ 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 @@ -330,7 +331,7 @@ 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, @@ -374,16 +375,15 @@ 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: @@ -397,15 +397,13 @@ | | * | | | +-Freedom-*-Gatekeeper-+ | | | | * | | - \-------+-------/ | \-------+-------/ + \-------+-------/ | * \-------+-------/ | | | Insight | Patience (Internet) - - It works: what now? ------------------- @@ -424,31 +422,37 @@ -------------------------- 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 @@ -456,10 +460,10 @@ 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. diff -u --recursive --new-file v1.3.63/linux/Makefile linux/Makefile --- v1.3.63/linux/Makefile Wed Feb 14 14:37:06 1996 +++ linux/Makefile Thu Feb 15 14:55:36 1996 @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 3 -SUBLEVEL = 63 +SUBLEVEL = 64 ARCH = i386 diff -u --recursive --new-file v1.3.63/linux/arch/alpha/kernel/bios32.c linux/arch/alpha/kernel/bios32.c --- v1.3.63/linux/arch/alpha/kernel/bios32.c Thu Jan 4 21:54:53 1996 +++ linux/arch/alpha/kernel/bios32.c Thu Feb 15 09:20:41 1996 @@ -581,7 +581,7 @@ { 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 */ }; /* diff -u --recursive --new-file v1.3.63/linux/arch/alpha/kernel/irq.c linux/arch/alpha/kernel/irq.c --- v1.3.63/linux/arch/alpha/kernel/irq.c Sun Jan 14 16:30:09 1996 +++ linux/arch/alpha/kernel/irq.c Thu Feb 15 09:20:41 1996 @@ -622,6 +622,8 @@ 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: diff -u --recursive --new-file v1.3.63/linux/arch/i386/kernel/ksyms.c linux/arch/i386/kernel/ksyms.c --- v1.3.63/linux/arch/i386/kernel/ksyms.c Fri Feb 9 17:52:57 1996 +++ linux/arch/i386/kernel/ksyms.c Thu Feb 15 07:13:13 1996 @@ -1,10 +1,16 @@ #include #include +#include +#include +extern void dump_thread(struct pt_regs *, struct user *); +extern int dump_fpu(elf_fpregset_t *); static struct symbol_table arch_symbol_table = { #include /* platform dependent support */ + X(dump_thread), + X(dump_fpu), #ifdef __SMP__ X(apic_reg), /* Needed internally for the I386 inlines */ X(cpu_data), diff -u --recursive --new-file v1.3.63/linux/drivers/block/floppy.c linux/drivers/block/floppy.c --- v1.3.63/linux/drivers/block/floppy.c Fri Feb 9 17:53:00 1996 +++ linux/drivers/block/floppy.c Thu Feb 15 09:20:58 1996 @@ -107,7 +107,7 @@ /* do print messages for unexpected interrupts */ static int print_unex=1; - +#include #include /* the following is the mask of allowed drives. By default units 2 and @@ -3118,30 +3118,15 @@ 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; } @@ -3152,14 +3137,18 @@ 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; diff -u --recursive --new-file v1.3.63/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- v1.3.63/linux/drivers/block/ll_rw_blk.c Wed Feb 14 14:37:09 1996 +++ linux/drivers/block/ll_rw_blk.c Thu Feb 15 12:09:57 1996 @@ -29,7 +29,7 @@ * 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]; /* diff -u --recursive --new-file v1.3.63/linux/drivers/block/rd.c linux/drivers/block/rd.c --- v1.3.63/linux/drivers/block/rd.c Sat Jan 6 19:10:39 1996 +++ linux/drivers/block/rd.c Thu Feb 15 09:20:51 1996 @@ -431,7 +431,7 @@ 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++; } } diff -u --recursive --new-file v1.3.63/linux/drivers/char/README.stallion linux/drivers/char/README.stallion --- v1.3.63/linux/drivers/char/README.stallion Thu Jan 1 02:00:00 1970 +++ linux/drivers/char/README.stallion Thu Feb 15 12:07:35 1996 @@ -0,0 +1,320 @@ + +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 " 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. + diff -u --recursive --new-file v1.3.63/linux/drivers/char/istallion.c linux/drivers/char/istallion.c --- v1.3.63/linux/drivers/char/istallion.c Wed Nov 8 07:11:31 1995 +++ linux/drivers/char/istallion.c Thu Feb 15 12:07:35 1996 @@ -3,7 +3,7 @@ /* * 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. @@ -26,7 +26,6 @@ /*****************************************************************************/ #include - #include #include #include @@ -141,6 +140,13 @@ #include #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 + /*****************************************************************************/ /* @@ -173,7 +179,7 @@ * 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"; @@ -267,9 +273,10 @@ 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; @@ -287,6 +294,7 @@ int nrports; int nrdevs; unsigned int iobase; + unsigned long memaddr; void *membase; int memsize; int pagesize; @@ -304,7 +312,7 @@ stliport_t *ports[STL_MAXPORTS]; } stlibrd_t; -static stlibrd_t *stli_brds; +static stlibrd_t *stli_brds[STL_MAXBRDS]; static int stli_shared = 0; @@ -369,6 +377,30 @@ "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; + /*****************************************************************************/ /* @@ -382,6 +414,8 @@ #define ECP_EIPAGESIZE (64 * 1024) #define ECP_MCPAGESIZE (4 * 1024) +#define STL_EISAID 0x8c4e + /* * Important defines for the ISA class of ECP board. */ @@ -414,6 +448,8 @@ #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.) @@ -471,6 +507,8 @@ #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. @@ -535,7 +573,7 @@ (* 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 @@ -576,7 +614,6 @@ 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); @@ -595,9 +632,12 @@ 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); @@ -621,6 +661,7 @@ 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); @@ -695,19 +736,18 @@ 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; @@ -767,7 +807,9 @@ 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) { @@ -785,8 +827,9 @@ 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); } @@ -796,17 +839,12 @@ /*****************************************************************************/ /* - * 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)); } /*****************************************************************************/ @@ -826,9 +864,9 @@ 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); @@ -877,7 +915,7 @@ 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); } @@ -985,11 +1023,14 @@ } 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); @@ -1284,7 +1325,9 @@ 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)); @@ -1400,7 +1443,9 @@ 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; /* @@ -1562,7 +1607,9 @@ 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(); @@ -1645,7 +1692,9 @@ 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(); @@ -1698,7 +1747,9 @@ 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(); @@ -1726,6 +1777,7 @@ 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); @@ -1734,8 +1786,7 @@ 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; @@ -1743,6 +1794,11 @@ 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)); } @@ -1802,7 +1858,9 @@ 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; @@ -1870,6 +1928,16 @@ 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: @@ -1910,7 +1978,9 @@ 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)) @@ -2004,7 +2074,9 @@ 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; @@ -2036,7 +2108,9 @@ 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; @@ -2098,7 +2172,9 @@ 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; @@ -2154,7 +2230,9 @@ 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(); @@ -2188,8 +2266,8 @@ * 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) @@ -2203,13 +2281,15 @@ 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) { @@ -2520,7 +2600,9 @@ * 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; @@ -2691,6 +2773,11 @@ pp->iflag |= FI_1MARKRXERRS; if (tiosp->c_iflag & BRKINT) portp->rxmarkmsk |= BRKINT; + +/* + * Transfer any persistent flags into the asyport structure. + */ + pp->pflag = portp->pflag; } /*****************************************************************************/ @@ -2806,7 +2893,7 @@ 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)); } @@ -2897,9 +2984,9 @@ 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)); } @@ -3018,10 +3105,10 @@ 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); @@ -3079,7 +3166,7 @@ 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); } @@ -3102,12 +3189,12 @@ 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); @@ -3172,7 +3259,7 @@ 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); } @@ -3193,7 +3280,7 @@ 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); @@ -3235,7 +3322,7 @@ 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); } @@ -3254,7 +3341,7 @@ #endif outb(0x1, brdp->iobase); - for (i = 0; (i < 100); i++) + for (i = 0; (i < 1000); i++) udelay(1000); } @@ -3291,7 +3378,7 @@ 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); } @@ -3336,7 +3423,7 @@ * 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; @@ -3344,18 +3431,23 @@ 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 * as well. */ 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; @@ -3368,8 +3460,7 @@ 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; @@ -3382,10 +3473,9 @@ 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; @@ -3408,8 +3498,8 @@ 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); } @@ -3418,7 +3508,7 @@ /* * 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); @@ -3468,17 +3558,23 @@ * 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 * as well. @@ -3489,8 +3585,7 @@ 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; @@ -3503,8 +3598,7 @@ 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; @@ -3519,8 +3613,7 @@ 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; @@ -3533,8 +3626,7 @@ 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; @@ -3559,8 +3651,8 @@ 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); } @@ -3720,75 +3812,297 @@ /*****************************************************************************/ /* + * 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 * leave them all enabled. This is in fact the usual case. @@ -3796,10 +4110,14 @@ 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; } @@ -3809,7 +4127,9 @@ 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; @@ -3843,7 +4163,9 @@ 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) @@ -3891,7 +4213,9 @@ 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) @@ -3937,7 +4261,9 @@ 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); @@ -3971,11 +4297,11 @@ /*****************************************************************************/ -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. @@ -4039,7 +4365,7 @@ if (tty_register_driver(&stli_callout)) printk("STALLION: failed to register callout driver\n"); - return 0; + return(0); } /*****************************************************************************/ diff -u --recursive --new-file v1.3.63/linux/drivers/char/stallion.c linux/drivers/char/stallion.c --- v1.3.63/linux/drivers/char/stallion.c Thu Nov 9 11:23:48 1995 +++ linux/drivers/char/stallion.c Thu Feb 15 12:07:36 1996 @@ -3,7 +3,7 @@ /* * 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. @@ -26,7 +26,6 @@ /*****************************************************************************/ #include - #include #include #include @@ -42,7 +41,6 @@ #include #include #include /* for CONFIG_PCI */ - #include #include #include @@ -143,7 +141,7 @@ * 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"; @@ -452,7 +450,6 @@ 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); @@ -494,6 +491,7 @@ 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); @@ -504,8 +502,9 @@ #ifdef MODULE /* - * Use the kernel version number for modules. + * Loadable module initialization stuff. */ + int init_module() { unsigned long flags; @@ -605,15 +604,12 @@ /*****************************************************************************/ /* - * 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)); } /*****************************************************************************/ @@ -2782,18 +2778,6 @@ } 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); } } @@ -2919,7 +2903,7 @@ if (tty_register_driver(&stl_callout)) printk("STALLION: failed to register callout driver\n"); - return 0; + return(0); } /*****************************************************************************/ diff -u --recursive --new-file v1.3.63/linux/drivers/net/arcnet.c linux/drivers/net/arcnet.c --- v1.3.63/linux/drivers/net/arcnet.c Wed Feb 7 15:11:25 1996 +++ linux/drivers/net/arcnet.c Thu Feb 15 09:24:24 1996 @@ -1,4 +1,4 @@ -/* arcnet.c +/* arcnet.c: Written 1994-1996 by Avery Pennarun, derived from skeleton.c by Donald Becker. @@ -19,6 +19,45 @@ 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 . 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 @@ -82,27 +121,27 @@ 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) @@ -125,7 +164,7 @@ */ static const char *version = - "arcnet.c: v2.30 ALPHA 96/01/10 Avery Pennarun \n"; + "arcnet.c: v2.41 ALPHA 96/02/10 Avery Pennarun \n"; @@ -160,6 +199,36 @@ /**************************************************************************/ +/* 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 @@ -189,24 +258,34 @@ */ #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 @@ -224,55 +303,66 @@ * 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); +#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 -/* 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 - /* The number of low I/O ports used by the ethercard. */ #define ARCNET_TOTAL_SIZE 16 @@ -287,9 +377,11 @@ #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) @@ -309,8 +401,8 @@ #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: @@ -455,12 +547,9 @@ 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 */ @@ -473,29 +562,29 @@ 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); @@ -507,26 +596,35 @@ 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); @@ -535,8 +633,7 @@ #define tx_done(dev) 1 -#define JIFFER(time) for (delayval=0; delayval<(time*10); delayval++) \ - udelay(1000); +#define JIFFER(time) for (delayval=jiffies+time; jiffiesbase_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). - */ -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 */ - 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; + * (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) +{ + 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 @@ -609,80 +697,367 @@ 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-ports8) + { + 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-ports8) + { + 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-shmems8) + { + 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-shmems8) + { + 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-ports8) + { + 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-shmemsbase_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; + + /* find the real shared memory start/end points, including mirrors */ - BUGMSG(D_NORMAL,"ARCnet card found at %03lXh, IRQ %d, ShMem at %lXh.\n", - dev->base_addr, dev->irq, dev->mem_start); + #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; + + while (*last_mirror==TESTvalue) last_mirror+=MIRROR_SIZE; + last_mirror-=MIRROR_SIZE; - /* Initialize the device structure. */ + 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; @@ -697,261 +1072,87 @@ /* 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 - - /* if status port is FF, there's certainly no arcnet... give up. */ - if (inb(STATUS)==0xFF) - { - BUGMSG(D_INIT," probe failed. Status port empty.\n"); - return -ENODEV; - } + 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 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. - */ + if (reset_delay) { - 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; - } + /* reset the card */ + inb(RESET); + JIFFER(RESETtime); } -#endif - BUGMSG(D_INIT," status2=%Xh\n",inb(STATUS)); + outb(CFLAGScmd|RESETclear, COMMAND); /* clear flags & end reset */ + outb(CFLAGScmd|CONFIGclear,COMMAND); - /* 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 */ + /* verify that the ARCnet signature byte is present */ + cardmem = (u_char *) dev->mem_start; + if (cardmem[0] != TESTvalue) { - BUGMSG(D_INIT," probe failed. eternal reset flag1...(status=%Xh)\n", - inb(STATUS)); - return -ENODEV; + BUGMSG(D_NORMAL,"reset failed: TESTvalue not present.\n"); + return 1; } - - 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"); - return 1; - } - lp->stationid=cardmem[1]; /* save address for later use */ - - /* clear out status variables */ - recbuf=lp->recbuf=0; - lp->txbuf=2; + + /* clear out status variables */ + recbuf=lp->recbuf=0; + lp->txbuf=2; /* 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(); @@ -968,8 +1169,7 @@ } -/* - * 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... * * Actually, the whole idea of having all this kernel-dependent stuff (ie. @@ -977,17 +1177,18 @@ * * Intelligent defaults?! Nah. */ - void arcnet_setup(struct device *dev) { int i; for (i=0; ibuffs[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; @@ -999,49 +1200,6 @@ } -/* 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 * @@ -1069,13 +1227,20 @@ 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; @@ -1085,7 +1250,8 @@ /* 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) @@ -1100,7 +1266,11 @@ 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)); @@ -1108,6 +1278,9 @@ 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; @@ -1140,11 +1313,15 @@ 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; @@ -1152,6 +1329,7 @@ /* 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); @@ -1159,7 +1337,9 @@ 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); @@ -1167,6 +1347,7 @@ kfree(lp->sdev->name); kfree(lp->sdev); lp->sdev=NULL; +#endif /* Update the statistics here. (not necessary in ARCnet) */ @@ -1229,16 +1410,14 @@ 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++; @@ -1418,289 +1597,99 @@ } -/* 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 (lengthhardheader.offset1=0; - arcpacket->hardheader.offset2=offset; + arcpacket->hardheader.offset1=offset=256-length; + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset]); } - else /* short packet */ + else if (length>=MinTU) /* Extended (512-byte) 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; ilen; 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]); - } - else if (length>=MinTU) /* Extended (512-byte) Packet */ - { - arcpacket->hardheader.offset1=0; - arcpacket->hardheader.offset2=offset=512-length; - - arcsoft=(struct ClientData *) - (&arcpacket->raw[offset]); + arcpacket->hardheader.offset1=0; + arcpacket->hardheader.offset2=offset=512-length; + + arcsoft=(struct ClientData *) + (&arcpacket->raw[offset]); } else if (exceptA) /* RFC1201 Exception Packet */ { @@ -1735,8 +1724,17 @@ */ memcpy((u_char*)arcsoft, (u_char*)hdr,hdrlen); +#ifdef SLOW_XMIT_COPY + for (iptr=data,iend=iptr+length-hdrlen,optr=(char *)arcsoft+hdrlen; + iptrlastload_dest=daddr; -#endif lp->txready=lp->txbuf; /* packet is ready for sending */ } @@ -1792,7 +1788,7 @@ } return 0; } - + /* start sending */ outb(TXcmd|(lp->txready<<3),COMMAND); @@ -1800,10 +1796,8 @@ lp->txready=0; lp->sending++; -#ifdef VERIFY_ACK lp->lasttrans_dest=lp->lastload_dest; lp->lastload_dest=0; -#endif lp->intmask |= TXFREEflag; @@ -1829,18 +1823,29 @@ 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); } @@ -1871,93 +1876,27 @@ 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 */ @@ -1983,12 +1922,11 @@ 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++; @@ -2000,7 +1938,7 @@ lp->lasttrans_dest); } } -#endif + /* send packet if there is one */ arcnet_go_tx(dev,0); didsomething++; @@ -2063,6 +2001,78 @@ 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", @@ -2138,13 +2148,17 @@ 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", @@ -2172,9 +2186,11 @@ } +#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) @@ -2328,7 +2344,7 @@ 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); @@ -2478,49 +2494,510 @@ } -/* 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 (lengthhardheader.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; ilen; 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; ilen; 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; ilen; 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 @@ -2578,122 +3055,6 @@ } - -/**************************************************************************** - * * - * 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) @@ -2751,49 +3112,10 @@ } - /* 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) { @@ -2830,47 +3152,6 @@ * * 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; @@ -2907,7 +3188,7 @@ return htons(ETH_P_IP); } - +#endif /* CONFIG_ARCNET_1051 */ /**************************************************************************** * * diff -u --recursive --new-file v1.3.63/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c --- v1.3.63/linux/drivers/scsi/aic7xxx.c Thu Dec 21 08:53:33 1995 +++ linux/drivers/scsi/aic7xxx.c Thu Feb 15 06:57:02 1996 @@ -1,5 +1,5 @@ /*+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. @@ -41,7 +41,7 @@ * * -- 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 @@ -64,6 +64,7 @@ #include "scsi.h" #include "hosts.h" #include "aic7xxx.h" +#include "aic7xxx_reg.h" #include #include /* for CONFIG_PCI */ @@ -73,7 +74,7 @@ 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) @@ -92,10 +93,9 @@ * * 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. * @@ -130,7 +130,17 @@ * 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. */ @@ -211,8 +221,7 @@ typedef enum { LIST_HEAD, - LIST_SECOND, - LIST_TAIL + LIST_SECOND } insert_type; typedef enum { @@ -268,523 +277,52 @@ /* * 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 +#define MINREG 0xC00 +#define MAXREG 0xCBF -/* - * 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 +#define INTDEF 0x5C /* Interrupt Definition Register */ /* - * Host Address (p. 3-48) - * This register contains the address of the byte about - * to be transfered across the host bus. + * Some defines for the HCNTRL register. */ -#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 - */ - -/* - * 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) - -/* - * Bit vector of targets that have disconnection disabled. - */ -#define HA_DISC_DSB(x) ((x) + 0xC32ul) - -/* - * Length of pending message - */ -#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 REQ_PAUSE IRQMS | INTEN | PAUSE +#define UNPAUSE_274X IRQMS | INTEN +#define UNPAUSE_284X INTEN +#define UNPAUSE_294X IRQMS | INTEN + +/* + * AIC-78X0 PCI registers + */ +#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 */ /* * @@ -853,7 +391,6 @@ unsigned short res_1[11]; /* words 20-30 */ unsigned short checksum; /* word 31 */ - }; /* @@ -862,8 +399,8 @@ * 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) \ ; \ /* @@ -871,16 +408,16 @@ * 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); /* @@ -958,48 +495,31 @@ /* * 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 */ @@ -1016,8 +536,6 @@ 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; @@ -1142,10 +660,11 @@ 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, ...) { @@ -1169,8 +688,8 @@ 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 @@ -1282,13 +801,11 @@ 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); @@ -1385,14 +902,9 @@ * 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 @@ -1410,8 +922,8 @@ * 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************************************************************************* @@ -1551,7 +1063,7 @@ *-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; @@ -1568,8 +1080,8 @@ { 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. */ } @@ -1591,7 +1103,7 @@ } } *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; @@ -1613,43 +1125,35 @@ * 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************************************************************************* @@ -1659,22 +1163,17 @@ * 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************************************************************************* @@ -1709,6 +1208,32 @@ /*+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 * * Description: @@ -1718,9 +1243,9 @@ 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 @@ -1728,13 +1253,13 @@ { /* * 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************************************************************************* @@ -1767,7 +1292,7 @@ */ 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); @@ -1787,63 +1312,40 @@ 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; + /* 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); - } - } + 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************************************************************************* @@ -1867,15 +1369,15 @@ * 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); /* @@ -1886,22 +1388,22 @@ /* * 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); } /* @@ -1909,7 +1411,7 @@ * 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); @@ -1942,7 +1444,7 @@ /* * 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, " @@ -1953,11 +1455,11 @@ */ { 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)) { @@ -1967,8 +1469,8 @@ 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++; } @@ -1978,7 +1480,7 @@ */ for (queued = 0; queued < i; queued++) { - outb(saved_queue[queued], QINFIFO(base)); + outb(saved_queue[queued], QINFIFO + base); } } @@ -1988,7 +1490,7 @@ { 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) @@ -2004,9 +1506,9 @@ } else { - outb(scb->position, SCBPTR(base)); + outb(scb->position, SCBPTR + base); prev = next; - next = inb(SCBARRAY(base) + 30); + next = inb(SCB_NEXT_WAITING + base); } } } @@ -2026,8 +1528,8 @@ * 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); @@ -2035,7 +1537,7 @@ } } - outb(active_scb, SCBPTR(base)); + outb(active_scb, SCBPTR + base); return (found); } @@ -2052,9 +1554,9 @@ #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************************************************************************* @@ -2088,9 +1590,9 @@ { 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 { @@ -2098,20 +1600,20 @@ { 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) @@ -2134,7 +1636,7 @@ /* * 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) { @@ -2145,9 +1647,9 @@ /* * 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); } @@ -2183,11 +1685,12 @@ 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; @@ -2200,7 +1703,7 @@ * 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) { @@ -2235,7 +1738,7 @@ */ 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 @@ -2252,12 +1755,12 @@ * 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++) @@ -2269,7 +1772,7 @@ } 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) @@ -2281,10 +1784,10 @@ */ 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; @@ -2298,50 +1801,50 @@ 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. @@ -2354,13 +1857,14 @@ { 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 @@ -2368,7 +1872,7 @@ * 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 { @@ -2380,7 +1884,7 @@ /* * Don't send an SDTR back to the target. */ - outb(0, HA_RETURN_1(base)); + outb(0, RETURN_1 + base); } else { @@ -2388,7 +1892,7 @@ * Send our own SDTR in reply. */ printk("aic7xxx: Sending SDTR!!\n"); - outb(SEND_SDTR, HA_RETURN_1(base)); + outb(SEND_SDTR, RETURN_1 + base); } } /* @@ -2396,27 +1900,21 @@ */ 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: @@ -2428,6 +1926,12 @@ "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 @@ -2455,16 +1959,16 @@ 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 @@ -2473,7 +1977,7 @@ * 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) { @@ -2483,7 +1987,6 @@ 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); } @@ -2497,7 +2000,6 @@ 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); @@ -2506,16 +2008,16 @@ * 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", @@ -2524,7 +2026,7 @@ else { cmd = scb->cmd; - aic7xxx_getscb(base, scb); + aic7xxx_getscb(p, scb); aic7xxx_status(cmd) = scb->target_status; cmd->result |= scb->target_status; @@ -2543,6 +2045,7 @@ void *req_buf; tcl = scb->target_channel_lun; + /* * Send a sense command to the requesting target. */ @@ -2558,8 +2061,9 @@ 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]); @@ -2568,41 +2072,26 @@ 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: @@ -2634,9 +2123,9 @@ 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", @@ -2659,16 +2148,16 @@ */ 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; } @@ -2677,9 +2166,9 @@ 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", @@ -2702,9 +2191,9 @@ 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", @@ -2723,8 +2212,8 @@ 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 { @@ -2735,7 +2224,7 @@ 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", @@ -2753,9 +2242,9 @@ 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 @@ -2764,22 +2253,112 @@ } 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"); /* @@ -2787,10 +2366,9 @@ * 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 @@ -2806,36 +2384,23 @@ */ 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 @@ -2852,17 +2417,17 @@ * 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); @@ -2873,40 +2438,17 @@ } 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 */ } @@ -2921,21 +2463,20 @@ * 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)) { /* @@ -2957,7 +2498,7 @@ * 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]) @@ -2974,7 +2515,7 @@ * 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; @@ -3010,7 +2551,7 @@ } #endif /* AIC7XXX_PROC_STATS */ - } while (inb(QOUTCNT(base))); + } while (inb(QOUTCNT + base)); } } @@ -3118,11 +2659,11 @@ 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, @@ -3135,7 +2676,7 @@ /* * 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); /* @@ -3145,10 +2686,10 @@ 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); } /* @@ -3159,10 +2700,10 @@ 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); } @@ -3175,11 +2716,11 @@ 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); } /* @@ -3196,11 +2737,11 @@ /* * 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); } @@ -3292,7 +2833,7 @@ 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 */ \ } @@ -3304,15 +2845,15 @@ * 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); } @@ -3327,7 +2868,7 @@ /* * Send chip select for one clock cycle. */ - outb(SEEMS | SEECK | SEECS, SEECTL(base)); + outb(SEEMS | SEECK | SEECS, SEECTL + base); CLOCK_PULSE(base); /* @@ -3337,10 +2878,10 @@ 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); } /* @@ -3351,10 +2892,10 @@ 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); } @@ -3367,11 +2908,11 @@ 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); } @@ -3389,18 +2930,18 @@ /* * 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); @@ -3451,22 +2992,22 @@ * 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; @@ -3501,6 +3042,7 @@ */ break; } + if (walk_scbs) { /* @@ -3510,16 +3052,22 @@ 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); @@ -3540,10 +3088,10 @@ 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; @@ -3553,7 +3101,7 @@ /* * 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) { @@ -3569,33 +3117,33 @@ * 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. @@ -3605,19 +3153,19 @@ 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); @@ -3647,8 +3195,8 @@ } /* 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"); @@ -3665,11 +3213,11 @@ 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; @@ -3704,14 +3252,17 @@ /* * 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"); @@ -3739,43 +3290,43 @@ * 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); } @@ -3784,7 +3335,7 @@ * 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 @@ -3878,8 +3429,8 @@ } 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; @@ -3942,11 +3493,11 @@ /* * 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"); @@ -3959,29 +3510,37 @@ /* * 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); } /* @@ -3992,10 +3551,10 @@ * 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; @@ -4010,7 +3569,7 @@ */ if (have_seeprom) { - p->discenable = 0; + p->discenable = 0x0; } else { @@ -4022,8 +3581,8 @@ } 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)); } } @@ -4036,7 +3595,7 @@ { 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); } @@ -4047,29 +3606,42 @@ } 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 @@ -4083,27 +3655,33 @@ */ 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? @@ -4126,21 +3704,21 @@ /* * 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); @@ -4196,9 +3774,9 @@ */ 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 @@ -4207,7 +3785,7 @@ continue; } - config.type = aic7xxx_probe(slot, HID0(base)); + config.type = aic7xxx_probe(slot, HID0 + base); if (config.type != AIC_NONE) { /* @@ -4269,7 +3847,9 @@ 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++) @@ -4287,10 +3867,13 @@ { 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) @@ -4313,69 +3896,67 @@ 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; @@ -4383,20 +3964,39 @@ 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. */ } } @@ -4440,18 +4040,18 @@ } 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 @@ -4461,7 +4061,7 @@ 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 @@ -4493,18 +4093,16 @@ 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 { @@ -4522,7 +4120,7 @@ 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 { @@ -4531,9 +4129,7 @@ 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)); } } @@ -4552,7 +4148,6 @@ long flags; struct aic7xxx_host *p; struct aic7xxx_scb *scb; - unsigned char curscb; p = (struct aic7xxx_host *) cmd->host->hostdata; @@ -4562,19 +4157,19 @@ 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 @@ -4632,16 +4227,6 @@ 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; } } @@ -4681,7 +4266,8 @@ * 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 @@ -4720,7 +4306,7 @@ 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'; @@ -4730,7 +4316,7 @@ */ 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 @@ -4765,15 +4351,15 @@ * 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); @@ -4782,15 +4368,8 @@ 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); @@ -4801,14 +4380,14 @@ /* * 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 @@ -4822,15 +4401,14 @@ { #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) { /* @@ -4897,7 +4475,7 @@ */ /* cmd->retries = 0; */ aic7xxx_error(cmd) = errcode; - aic7xxx_done (p, scb); + aic7xxx_done(p, scb); } else { @@ -5060,3 +4638,4 @@ * tab-width: 8 * End: */ + diff -u --recursive --new-file v1.3.63/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq --- v1.3.63/linux/drivers/scsi/aic7xxx.seq Tue Jan 23 21:15:43 1996 +++ linux/drivers/scsi/aic7xxx.seq Thu Feb 15 06:57:02 1996 @@ -1,907 +1,797 @@ -##+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 + 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, then set SCSI options and set the initiator and -# target SCSI IDs. -# +/* + * 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 +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 -# 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 +/* + * 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 - - mov A,SINDEX - test SCBARRAY+0,0xb0 jz !message # WDTR, SDTR or TAG?? - cmp MSG_START+0,A jne !message # did driver beat us? +mk_identify: + and A,DISCENB,SCB_CONTROL /* mask off disconnect privledge */ -# Tag Message if Tag enabled in SCB control block. Use SCBPTR as the tag -# value + and MSG0,0x7,SCB_TCL /* lun */ + or MSG0,A /* or in disconnect privledge */ + or MSG0,MSG_IDENTIFY + mvi MSG_LEN, 1 + + test SCB_CONTROL,0xb0 jz !message /* WDTR, SDTR or TAG?? */ +/* + * 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 + 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. -# +/* + * 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 - and A,0xe0,SCSISIGI # CDI|IOI|MSGI +/* + * 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 - 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 + dec SG_COUNT /* one less segment to go */ - test SG_COUNT, 0xff jz data_phase_finish #Are we done? + 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. -# + 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 + test MSG_LEN, 0xff jnz p_mesgout_start + mvi MSG_NOP 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 +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 - - 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 +/* + * If the next bus phase after ATN drops is a message out, it means + * that the target is requesting that the last message(s) be resent. + */ +p_mesgout_snoop: + test SSTAT1,BUSFREE jnz p_mesgout_done + test SSTAT1,REQINIT jz p_mesgout_snoop + + test SSTAT1,PHASEMIS jnz p_mesgout_done + + or SCSISIGO,ATNO /* turn on ATNO */ jmp ITloop -p_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 +/* + * 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 0x7 call mk_mesg # MESSAGE REJECT message + 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 -setup_SCB: - and SCBARRAY+0,0xfb # clear disconnect bit in SCB - or FLAGS,IDENTIFY_SEEN # make note of IDENTIFY + 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 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 ] -# - -# 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. -# +/* + * [ ADD MORE MESSAGE HANDLING HERE ] + */ + +/* + * Bus free phase. It might be useful to interrupt the device + * driver if we aren't expecting this. For now, make sure that + * ATN isn't being asserted and look for a new command. + */ p_busfree: - mvi CLRSINT1,0x40 # CLRATNO - clr SIGSTATE + mvi CLRSINT1,CLRATNO -# 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. +/* + * 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 @@ -913,166 +803,134 @@ 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 + 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. + */ -# 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_next: + or CLRSINT0, CLRSPIORDY + mov NONE,SCSIDATL /*dummy read from latch to ACK*/ +inb_next_wait: + test SSTAT1,PHASEMIS jnz mesgin_phasemis + test SSTAT0,SPIORDY jz inb_next_wait /* wait for next byte */ inb_first: - clr STCNT+2 - clr STCNT+1 mov DINDEX,SINDEX - mov DINDIR,SCSIBUSL ret # read byte directly from bus - -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 - + 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*/ -# 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. -# +mesgin_phasemis: +/* + * We expected to receive another byte, but the target changed phase + */ + mvi INTSTAT, MSGIN_PHASEMIS + jmp ITloop + +/* + * DMA data transfer. HADDR and HCNT must be loaded first, and + * SINDEX should contain the value to load DFCNTRL with - 0x3d for + * host->scsi, or 0x39 for scsi->host. The SCSI channel is cleared + * during initialization. + */ dma: mov DFCNTRL,SINDEX dma1: - test SSTAT0,0x1 jnz dma3 # DMADONE - test SSTAT1,0x10 jz dma1 # PHASEMIS, ie. underrun + 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. -# +/* + * 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 + test DFCNTRL,0x38 jnz dma6 /* SCSIENACK|SDMAENACK|HDMAENACK */ -dma_finish: - test DFSTATUS,0x8 jz dma_finish # HDONE - - 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: @@ -1080,130 +938,135 @@ 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 + 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. -# +/* + * 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: diff -u --recursive --new-file v1.3.63/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c --- v1.3.63/linux/drivers/scsi/aic7xxx_asm.c Mon Oct 23 18:02:08 1995 +++ linux/drivers/scsi/aic7xxx_asm.c Thu Feb 15 06:57:02 1996 @@ -27,12 +27,13 @@ * A