|author||Olivier Galibert <email@example.com>||2012-08-05 09:09:22 +0000|
|committer||Olivier Galibert <firstname.lastname@example.org>||2012-08-05 09:09:22 +0000|
nscsi: documentation [O. Galibert]
Diffstat (limited to 'docs')
1 files changed, 234 insertions, 0 deletions
diff --git a/docs/nscsi.txt b/docs/nscsi.txt
index e69de29bb2d..3eea1576344 100644
@@ -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
+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)
+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_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
+ 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
+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
+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
+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
+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
+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
+ 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
+ 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.