summaryrefslogtreecommitdiff
path: root/scsi/adapters/README
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /scsi/adapters/README
Initial source
Diffstat (limited to 'scsi/adapters/README')
-rw-r--r--scsi/adapters/README290
1 files changed, 290 insertions, 0 deletions
diff --git a/scsi/adapters/README b/scsi/adapters/README
new file mode 100644
index 0000000..1bc7e7c
--- /dev/null
+++ b/scsi/adapters/README
@@ -0,0 +1,290 @@
+
+This directory contains various examples of HBA support code,
+among them:
+
+ Chip/Board File Machine tested By
+
+ NCR 53C94 scsi_53C94 DecStation 5000 af@cmu
+ DEC 7061 scsi_7061 DecStation 3100/2100 af@cmu
+ NCR 5380 scsi_5380 VaxStation 3100 af@cmu
+ Fujitsu 89352 scsi_89352 Omron Luna88k danner@cmu
+ Adaptec 1542B scsi_aha15 AT/PC af@cmu
+
+It should be trivial to modify them for some other machine that uses
+the same SCSI chips, hopefully by properly conditionalizing and macroizing
+the existing code.
+
+There are various rules and assumptions to keep in mind when designing/coding
+the support code for a new HBA, here is a list. Pls extend this with
+anything you find is not openly stated here and made your life miserable
+during the port, and send it back to CMU.
+
+
+AUTOCONF
+
+We assume the structures and procedures defined in chips/busses.*,
+e.g. someone will call configure_bus_master to get to our foo_probe()
+routine. This should make up its mind on how many targets it sees
+[see later for dynamic reconfig], allocate a descriptor for each
+one and leave the driver ready to accept commands (via foo_go()).
+
+ On raw chips you should use a test_unit_ready command,
+ selecting without ATN, and timing out on non-existant
+ devices. Use LUN 0.
+ On boards, there probably is a command to let the board do
+ it (see Adaptec), if not do as above.
+
+The typical autoconf descriptor might look like
+
+ caddr_t foo_std[NFOO] = { 0 };
+ struct bus_device *foo_dinfo[NFOO*8];
+ struct bus_ctlr *foo_minfo[NFOO];
+ struct bus_driver foo_driver =
+ { foo_probe, scsi_slave, scsi_attach, foo_go, foo_std, "rz",
+ foo_dinfo, "foo", foo_minfo, BUS_INTR_B4_PROBE};
+
+which indicates that foo_probe() and foo_go() are our interface functions,
+and we use the generic scsi_slave() and scsi_attach() for the rest.
+Their definition is
+
+ foo_probe(reg, ui)
+ vm_offset_t reg;
+ struct bus_ctlr *ui;
+
+[the "reg" argument might actually be something else on architectures that
+ do not use memory mapped I/O]
+
+ aha_go(tgt, cmd_count, in_count, cmd_only)
+ target_info_t *tgt;
+ boolean_t cmd_only;
+
+The foo_go() routine is fairly common across chips, look at any example to
+see how to structure it. Basically, the arguments tell us how much data
+to expect in either direction, and whether (cmd_only) we think we should
+be selecting with ATN (cmd_only==FALSE) or not. The only gotcha is cmd_count
+actually includes the size of any parameters.
+
+The "go" field of the scsi_softc structure describing your HBA should be
+set to your foo_go() routine, by the foo_probe() routine.
+
+DATA DEPENDENCIES
+
+The upper layer assumes that tgt->cmd_ptr is a pointer to good memory
+[e.g. no funny padding] where it places the scsi command control blocks
+AND small (less than 255 bytes) parameters. It also expects small results
+in there (things like read_capacity, for instance). I think I cleaned
+up all the places that used to assume tgt->cmd_ptr was aligned, but do not
+be surprised if I missed one.
+
+It does NOT use the dma_ptr, or any of the transient_state fields.
+
+WATCHDOG
+
+There is an optional MI watchdog facility, which can be used quite simply by
+filling in the "watchdog" field of the scsi_softc structure describing
+your HBA. To disable it, leave the field zero (or, dynamically, zero the
+timeout value). You can use a watchdog of your own if you like, or more
+likely set this field to point to the MI scsi_watchdog().
+This requires that your foo_softc descriptor starts off with a watchdog_t
+structure, with the "reset" field pointing to a function that will
+reset the SCSI bus should the watchdog expire.
+
+When a new SCSI command is initiated you should
+ if (foo->wd.nactive++ == 0)
+ foo->wd.watchdog_state = SCSI_WD_ACTIVE;
+to activate the watchdog, on each interrupt [or other signal that all
+is proceeding well for the command and it is making progress] you should
+ if (foo->wd.nactive)
+ foo->wd.watchdog_state = SCSI_WD_ACTIVE;
+bump down the watchdog 'trigger level', and when the command terminates
+ if (aha->wd.nactive-- == 1)
+ aha->wd.watchdog_state = SCSI_WD_INACTIVE;
+
+When you detect a SCSI bus reset (possibly we initiated it) you should
+ aha->wd.nactive = 0;
+and after cleaning up properly invoke
+ scsi_bus_was_reset(sc)
+ scsi_softc_t sc;
+
+The functiona that is invoked on watchdog expiry is
+ foo_reset_scsibus(foo)
+ register foo_softc_t foo;
+
+Note that this can be used for dumb chips that do not support select timeouts
+in hardware [see the 5380 or 7061 code], but its primary use is to detect
+instances where a target is holding on the SCSI bus way too long.
+
+The one drawback of resetting the bus is that some devices (like tapes)
+lose status in case of a reset, and the MI code does not (yet?) try to
+keep enough information around to be able to recover. If you want to
+add something very useful you might change the rz_tape.c code to do just
+that, e.g. on SCSI_RET_ABORTs wait a while for the tape to do whatever,
+then rewind, and seek forward where the tape should have been positioned at
+the beginning of the command that failed, then reissue the command.
+None of the examples so far tries to be 'smart' like making an attempt
+to get the bus unstuck without resetting it, send us ideas if you have
+some.
+
+
+DYNAMIC RECONFIG
+
+Your code should be ready to add/remove targets on the fly. To do so,
+notify the upper layer that a target went offline returning
+SCSI_RET_DEVICE_DOWN when e.g. the select timed out, and clear out
+the tgt->flags field.
+To find new devices, define a function
+
+ boolean_t
+ foo_probe_target(tgt, ior)
+ target_info_t *tgt;
+ io_req_t ior;
+
+and install it in the "probe" field of the scsi_softc_t structure describing
+the HBA to the upper layer. This function should finalize all HBA-specific
+info in the target_info structure, then do a scsi_inquiry and check the
+return code. If this is not SCSI_RET_DEVICE_DOWN the target should be
+marked TGT_ALIVE.
+
+
+COMMAND TERMINATION
+
+Generally, command termination should be reported to the upper layers
+by setting the tgt->done field to the proper value [it should remain
+SCSI_RET_IN_PROGRESS while the command is executing] and invoking the
+target's completion routine, like:
+ if (tgt->ior) {
+ LOG(0xA,"ops->restart");
+ (*tgt->dev_ops->restart)( tgt, TRUE);
+ }
+Note that early on some commands will actually wait for completion
+by spinning on the tgt->done value, because autoconf happens when
+threads and the scheduler are not working.
+
+Return SCSI_RET_RETRY if the target was busy, the command will be retried
+as appropriate.
+
+Check the completion routines [in rz_disk.c and rz_tape.c for instance]
+if you are not sure what to return in a troubled case.
+
+HBA CHIPS GOTCHAS
+
+All of the examples so far use the idea of 'scripts': the interrupt routine
+matches the chip state with what is expected and if this is ok (it is
+in the common important case) it just calls a prepackaged function.
+We have found this to be _extremely_ simpler than using a state machine
+of various ridiculous and erroneous sorts, and much more handy for debugging
+also. Not to mention the saving on code.
+Nonetheless, there really are no restrictions on how to structure the HBA
+code, if you prefer state machines go ahead and use them!
+
+Scheduling of the bus among competing targets is one of the major missing
+pieces for simple HBAs. A winning strategy used so far is as follows.
+Allocate a queue_head_t of waiting_targets in your foo_softc, and two
+target_info_t pointers next_target and active_target. A three-valued
+state field is also needed. If you enter the foo_go() routine
+and find the state&BUSY simply enqueue_tail() your tgt on the waiting_targets
+queue. Otherwise mark the bus BUSY, set next_target to tgt, and proceed
+to a selection attempt.
+Note that the attempt might fail and a reselection win over it, therefore
+the attempt_selection() routine should just retrieve the next_target
+and install it in active_target, start the selection and let the interrupt
+routine take care of the rest [see scsi_5380 for a different setup].
+If a reselection wins we should note that we had a COLLISION in the state
+field, install the reconecting target and proceed to completion.
+When either a command is complete or a target disconnects you should invoke
+a foo_release_bus() routine, which might look like:
+
+boolean_t
+foo_release_bus(foo)
+ register foo_softc_t foo;
+{
+ boolean_t ret = TRUE;
+
+ LOG(9,"release");
+ if (foo->state & FOO_STATE_COLLISION) {
+
+ LOG(0xB,"collided");
+ foo->state &= ~FOO_STATE_COLLISION;
+ foo_attempt_selection(foo);
+
+ } else if (queue_empty(&foo->waiting_targets)) {
+
+ foo->state &= ~FOO_STATE_BUSY;
+ foo->active_target = 0;
+ foo->script = 0;
+ ret = FALSE;
+
+ } else {
+
+ LOG(0xC,"dequeue");
+ foo->next_target = (target_info_t *)
+ dequeue_head(&foo->waiting_targets);
+ foo_attempt_selection(foo);
+ }
+ return ret;
+}
+
+which indicates whether the bus has been handed off to a new target or not.
+This provides the desired FIFO scheduling of the bus and gives maximum
+parallelism when targets are allowed to (and infact do) disconnect.
+
+An area where there are problems most times is how to minimize the
+interaction of selections and reselections in, e.g. foo_attempt_selection().
+This is very much chip specific, but sneaking on the SCSI bus should
+be a viable alternative in most cases. Check in the specs what happens
+if you send a command while there is already a reselection pending:
+a well behaved chip would ignore the command and not screwup its status.
+[Keep in mind that even if _now_ there is no reselection indication
+ on the next cycle there might be and you won't see it!]
+
+RANDOM GOTCHAS
+
+A number of workstations do not provide real DMA support [do not ask me why]
+but rather a special 'buffer' more or less wide where you have to copy
+data to and from. This has been handled, see esp the 52C94 code which has
+even the extreme optimization of issuing the send command even before
+the data has been copied into the buffer! We have handled even machines
+where no DMA at all was provided.
+
+Speaking of DMA.. many of these chips 'prefetch' data, or have a FIFO
+on board (they have to if they do synch xfers), and when the target
+disconnects it is always a pain to find out how many bytes exactly did we
+xfer. Be advised that this hurdle exists, and that the best way to
+debug your code here is to use a tape. A safe way is to initially
+disable disconnects [so that you can get the system up from disk]
+and enable them only on the tape unit that you are using for testing.
+Later on enable disks but make sure you have some way to recover from
+a zapped disk !
+
+MOVING TO USER SPACE
+
+All examples have hooks for user-space versions of the driver, the
+ones for 54C94 and 7061 actually do work. Look in mapped_scsi.c
+for how this is done, it is fairly simple as far as the kernel is
+concerned. To keep the option of mapping to user space open you
+should structure your interrupt routine such that it does all the
+state gathering and clearing of the interrupt right away. This
+scheme gives you some assurance that your code will keep on working
+when the interrupt processing is actually delayed and you recover
+the interrupt state from the saved structure in the mapped area.
+
+
+IMPROVEMENTS
+
+There are a variety of things to be done still, for instance:
+
+- rewrite scsi_slave() and scsi_attach() to be fully SCSI-II compliant.
+ There are only comments right now as to how that should be done.
+
+- add enough machinery to the tape code to be able to recover from
+ bus resets. Do so in such a way that other devices might use the ideas.
+
+- add more devices, like printers scanners modems etc that are currently
+ missing
+
+- add a 'generic' set_status flavor which simply executes a scsi command
+ passed in from user. This seems the way many vendors and other people
+ have strutured their drivers, it would make it possible to have a common
+ user-level program to do special maintainance work like, for instance,
+ reformatting of disks.
+