diff --git a/docs/nscsi.txt b/docs/nscsi.txt index e69de29bb2d..3eea1576344 100644 --- a/docs/nscsi.txt +++ b/docs/nscsi.txt @@ -0,0 +1,234 @@ +The new SCSI subsystem +---------------------- + + 1. Introduction + +The nscsi subsystem was created to allow an implementation to be +closer to the physical reality, making it easier (hopefully) to +implement new controller chips from the documentations. + + + 2. Global structure + +Parallel SCSI is built around a symmetric bus to which a number of +devices are connected. The bus is composed of 9 control lines (for +now, later scsi versions may have more) and up to 32 data lines (but +currently implemented chips only handle 8). All the lines are open +collector, which means that either one or multiple chip connect the +line to ground and the line, of course, goes to ground, or no chip +drives anything and the line stays at Vcc. Also, the bus uses +inverted logic, where ground means 1. SCSI chips traditionally work +in logical and not physical levels, so the nscsi subsystem also works +in logical levels and does a logical-or of all the outputs of the +devices. + +Structurally, the implementation is done around two main classes: +nscsi_bus_devices represents the bus, and nscsi_device represents an +individual device. A device only communicate with the bus, and the +bus takes care of transparently handling the device discovery and +communication. In addition the nscsi_full_device class proposes a +scsi device with the scsi protocol implemented making building generic +scsi devices like harddrives or cdrom readers easier. + + + 3. Plugging in a scsi bus in a driver + +The nscsi subsystem leverages the slot interfaces and the device +naming to allow for a configurable yet simple bus implementation. + +First you need to create a list of acceptable devices to plug on the +bus. This usually comprises of cdrom, harddisk and the controller +chip. For instance: + +static SLOT_INTERFACE_START( next_scsi_devices ) + SLOT_INTERFACE("cdrom", NSCSI_CDROM) + SLOT_INTERFACE("harddisk", NSCSI_HARDDISK) + SLOT_INTERFACE_INTERNAL("ncr5390", NCR5390) +SLOT_INTERFACE_END + +The _INTERNAL interface indicates a device that is not +user-selectable, which is useful for the controller. + +Then in the machine config (or in a fragment config) you need to first +add the bus, and then the (potential) devices as subdevices of the bus +with the scsi id as the name. For instance you can use: + + MCFG_NSCSI_BUS_ADD("scsibus") + MCFG_NSCSI_ADD("scsibus:0", next_scsi_devices, "cdrom", 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:1", next_scsi_devices, "harddisk", 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:2", next_scsi_devices, 0, 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:3", next_scsi_devices, 0, 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:4", next_scsi_devices, 0, 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:5", next_scsi_devices, 0, 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:6", next_scsi_devices, 0, 0, 0, 0, false) + MCFG_NSCSI_ADD("scsibus:7", next_scsi_devices, "ncr5390", 0, &next_ncr5390_interface, 10000000, true) + +That configuration puts as default a cdrom reader on scsi id 0 and a +hard drive on scsi id 1, and forces the controller on id 7. The +parameters of add are: +- device tag, comprised of bus-tag:scsi-id +- the list of acceptable devices +- the device name as per the list, if one is to be there by default +- the device input config, if any (and there usually isn't one) +- the device configuration structure, usually for the controller only +- the frequency, usually for the controller only +- "false" for a user-modifyable slot, "true" for a fixed slot + +The full device name, for mapping purposes, will be +bus-tag:scsi-id:device-type, i.e. "scsibus:7:ncr5390" for our +controller here. + + + 4. Creating a new scsi device using nscsi_device + +The base class "nscsi_device" is to be used for down-to-the-metal +devices, i.e. scsi controller chips. The class provides three +variables and one method. The first variable, scsi_bus, is a pointer +to the nscsi_bus_device. The second, scsi_refid, is an opaque +reference to pass to the bus on some operations. Finally, scsi_id +gives the scsi id as per the device tag. It's written once at startup +and never written or read afterwards, the device can do whatever it +wants with the value or the variable. + +The virtual method scsi_ctrl_changed is called when watched-for +control lines change. Which lines are watched is defined through the +bus. + +The bus proposes five methods to access the lines. The read methods +are ctrl_r() and data_r(). The meaning of the control bits are +defined in the S_* enum of nscsi_device. The bottom three bits (INP, +CTL and MSG) are setup so that masking with 7 (S_PHASE_MASK) gives the +traditional numbers for the phases, which are also available with the +S_PHASE_* enum. + +Writing the data lines is done with data_w(scsi_refid, value). +Writing the control lines is done with ctrl_w(scsi_refid, value, +mask-of-lines-to-change). To change all control lines in one call use +S_ALL as the mask. + +Of course, what is read is the logical-or of all of what is driven by +all devices. + +Finally, the method ctrl_wait_w(scsi_id, value, +mask-of-wait-lines-to-change) allows to select which control lines are +watched. The watch mask is per-device, and the device method +scsi_ctrl_changed is called whenever a control line in the mask +changes due to an action of another device (not itself, to avoid an +annoying and somewhat useless recursion). + +Implementing the controller is then just a matter of following the +state machines descriptions, at least if they're available. The only +part often not described is the arbitration/selection, which is +documented in the scsi standard though. For an initiator (which is +what the controller essentially always is), it goes like this: +- wait for the bus to be idle +- assert the data line which number is your scsi_id (1 << scsi_id) +- assert the busy line +- wait the arbitration time +- check that the of the active data lines the one with the highest number is yours + - if no, the arbitration was lost, stop driving anything and restart at the beginning +- assert the select line (at that point, the bus is yours) +- wait a short while +- keep your data line asserted, assert the data line which number is the scsi id of the target +- wait a short while +- assert the atn line if needed, deassert busy +- wait for busy to be asserted or timeout + - timeout means nobody is answering at that id, deassert everything and stop +- wait a short while for deskewing +- deassert the data bus and the select line +- wait a short while + +and then you're done, you're connected with the target until the +target deasserts the busy line, either because you asked it to or just +to annoy you. The deassert is called a disconnect. + +The ncr5390 is an example of how to use a two-level state machine to +handle all the events. + + + 5. Creating a new scsi device using nscsi_full_device + +The base class "nscsi_full_device" is used to create HLE-d scsi +devices intended for generic uses, like hard drives, cdroms, perhaps +scanners, etc. The class provides the scsi protocol handling, leaving +only the command handling and (optionally) the message handling to the +implementation. + +The class currently only support target devices. + +The first method to implement is scsi_command(). That method is +called when a command has fully arrived. The command is available in +scsi_cmdbuf[], and its length is in scsi_cmdsize (but the length is +generally useless, the command first byte giving it). The 4096-bytes +scsi_cmdbuf array is then freely modifiable. + +In scsi_command(), the device can either handle the command or pass it +up with nscsi_full_device::scsi_command(). + +To handle the command, a number of methods are available: + +- get_lun(lua-set-in-command) will give you the lun to work on (the + in-command one can be overriden by a message-level one). + +- bad_lun() replies to the host that the specific lun is unsupported. + +- scsi_data_in(buffer-id, size) sends size bytes from buffer buffer-id + +- scsi_data_out(buffer-id, size) recieves size bytes into buffer buffer-id + +- scsi_status_complete(status) ends the command with a given status. + +- sense(deferred, key) prepares the sense buffer for a subsequent + request-sense command, which is useful when returning a + check-condition status. + +The scsi_data_* and scsi_status_complete commands are queued, the +command handler should call them all without waiting. + +buffer-id identifies a buffer. 0, aka SBUF_MAIN, targets the +scsi_cmdbuf buffer. Other acceptable values are 2 or more. 2+ ids +are handled through the scsi_get_data method for read and +scsi_put_data for write. + +UINT8 device::scsi_get_data(int id, int pos) must return byte pos of +buffer id, upcalling in nscsi_full_device for id < 2. + +void device::scsi_put_data(int id, int pos, UINT8 data) must write +byte pos in buffer id, upcalling in nscsi_full_device for id < 2. + +scsi_get_data and scsi_put_data should do the external reads/writes +when needed. + +The device can also override scsi_message to handle scsi messages +other than the ones generically handled, and it can also override some +of the timings (but a lot of them aren't used, beware). + +A number of enums are defined to make things easier. The SS_* enum +gives status returns (with SS_GOOD for all's well). The SC_* enum +gives the scsi commands. The SM_* enum gives the scsi messages, with +the exception of identify (which is 80-ff, doesn't really fit in an +enum). + + + 6. What's missing + 6.1. What's missing in scsi_full_device + +Initiator support - we have no initiator device to HLE at that point. + +Delays - a scsi_delay command would help giving more realistic timings +to the cdrom reader in particular. + +Disconnected operation - would first require delays and in addition an +emulated OS that can handle it. + +16-bits wide operation - needs an OS and an initiator that can handle +it. + + + 6.2. What's missing in the ncr5390 (and probably future other controllers) + +Bus free detection. Right now the bus is considered free if the +controllers isn't using it, which is true. This may change once +disconnected operation is in. + +Target commands, we don't emulate (vs. HLE) any target yet.