summaryrefslogtreecommitdiff
path: root/scsi/adapters/README
diff options
context:
space:
mode:
Diffstat (limited to 'scsi/adapters/README')
-rw-r--r--scsi/adapters/README290
1 files changed, 0 insertions, 290 deletions
diff --git a/scsi/adapters/README b/scsi/adapters/README
deleted file mode 100644
index 1bc7e7c..0000000
--- a/scsi/adapters/README
+++ /dev/null
@@ -1,290 +0,0 @@
-
-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.
-