|Last modified: 8 February 1999.|
Initialisation happens different, ie in PnP manner.
Miniport called by the system port driver to process SCSI Request Blocks (SRBs). These are translated by NT into Command Descriptor Blocks (CDBs). Non-SCSI drivers can fit into this scheme and pretend to be SCSI device.
A miniport has a DriverEntry and various HwScsi... routines. Various other call-backs used. Called by port driver.
A driver has a device extension for each HBA. Also has a
luextension for each logical unit. Can specify the size of
DriverEntryinitialises an HW_INITIALIZATION_DATA structure with pointers to its routines. It then calls ScsiPortInitialize for each bus it might be attached to.
In legacy drivers, ScsiPortInitialize calls back the driverís HwScsiFindAdapter routine. HwScsiFindAdapter sees if its hardware is present on the given bus and gets access to it. It allocates a DMA buffer if appropriate.
HwScsiInitializeis then called for each found HBA. This sets any registers and enables interrupts.
HwScsiStartIocalled to process SRBs. The miniport driver owns the request and must complete it. The miniport calls ScsiPortNotification with notification codes. For each SRB, a miniport usually issues a
CompleteRequestnotification and then a
Most SRBs are
A miniport can say that it is busy and ask that the SRB be queued again for processing later.
To perform DMA, call ScsiPortIoMapTransfer. When the DMA controller is ready, the miniportís HwScsiDmaStarted routine is called to set up the HBA for the DMA transfer. Call ScsiPortFlushDma to flush any cached data from DMA controllers.
The HwScsiInterrupt routine is to service a HBA interrupt. Note that normally there is no deferred procedure call. So service the interrupt and call ScsiPortNotification to complete the request and start the next one.
Some HwScsiInterrupt routines might need to run for a long time, eg more than 50 microseconds if doing polling programmed I/O. This is best handled by scheduling a DPC call-back using ScsiPortNotification
CallEnableInterrupts with a HwScsiEnableInterruptsCallback routine. HwScsiEnableInterruptsCallback can carry on interacting with the HBA safe from its own interrupts, although all other system interrupts are enabled. After completing the request, as per usual, it makes a ScsiPortNotification
CallDisableInterrupts with a HwScsiDisableInterruptsCallback routine; this should reenable interrupts from the HBA.
HwScsiResetBusis called to reset a bus. ScsiPortCompleteRequest can be used instead to complete all relevant requests after a bus or device reset.
A timer routine might be used to poll the HBA. To set up a one-shot timer, make a ScsiPortNotification
RequestTimer call with a HwScsiTimer routine and the required interval. The port driver ensures that HwScsiTimer does not run at the same time as a HwScsiInterrupt.
HwScsiStopAdaptoris called when the HBA is about to be stopped or removed. Disable all interrupts and release all resources.
Use ScsiPortLogError to report errors.
Use ScsiPortStallExecution to wait for small time intervals. Never delay for more than 1 millisecond in any miniport routine.
Miniport drivers of HBAs that are initialised in x86 real mode and that are sensitive to x86-specific processor mode transitions must have a HwScsiAdapterState routine.
The use can set values which disable certain features of an HBA: synchronous transfers, bus-disconnect operations, tagged queuing and internal queuing. How?
The PathId value
This identifies the SCSI bus on which the device is attached. It is a required value in SRBs.
The TargetId value
This identifies the TID of the controller or device. It is a required value in SRBs.
The Lun value
A SCSI driver is a standard driver, ie it processes IRPs, creates devices, etc. It maps IRPs to SRBs, limiting the size of transfers for underlying HBAs.
The generic port driver is found using a IoGetDeviceObjectPointer call. Note that this is not an HBA device object.
GetInquiryDatauses IOCTL_SCSI_GET_INQUIRY_DATA to get a SCSI_ADAPTER_BUS_INFO structure filled by the port driver. ClaimDevices makes the appropriate physical, logical or virtual devices. It makes a port driver IOCTL_SCSI_EXECUTE_NONE call with an SRB_FUNCTION_CLAIM_DEVICE SRB. It should store the returned pointer to the port driverís HBA specific device. The driver should use this device pointer to issue all subsequent requests to this device via the port driver.
A GetCapabilities routine then issues a IOCTL_SCSI_GET_CAPABILITIES to get a IO_SCSI_CAPABILITIES structure filled. This reveals various details of the HBA, such as the number of bytes that it can transfer in one operation.
Donít forget to dereference the generic port driver handle.
A class driverís IRP_MJ_CREATE and IRP_MJ_CLOSE handlers usually just return TRUE.
Donít forget to handle IRP_MJ_SHUTDOWN and IRP_MJ_FLUSH_BUFFERS if you buffer data internally.
You can pass IRP_MJ_INTERNAL_DEVICE_CONTROL and IOCTL_SCSI_PASS_THROUGH requests through to the port driver as long as you set up the PathId, TargetId and Lun fields in the SRB and set the minor function code to IRP_MN_SCSI_CLASS.
Usually class drivers do not have internal queues as the port driver has queues for each LU.
To process read and write requests, have a BuildSRV routine to set up an IRP with an appropriate SRB containing a CDB. The IRP has a IRP_MJ_SCSI function code (actually the same as IRP_MJ_INTERNAL_DEVICE_CONTROL) and Parameters.Scsi.Srb points to the SRB. Allocate nonpaged memory for the SRB either using ExAllocatePool or use lookaside lists.
For request-sense information requests or where retries might be needed, set an IRP completion routine.
Most class drivers need to have a SplitTransferRequest to ensure that read and write requests do not exceed the underlying HBA capabilities. SplitTransferRequest registers an completion routine for each driver-allocated IRP it sends down to the port driver. The completion routine maintains a count of completed partial transfer requests in the original IRP and protects the count with a spin lock.
The completion routine should translate error codes into NTSTATUS values. It should release the SRB memory if appropriate.
If the port driver freezes an LUís queue following an error, the completion routine calls a ReleaseQueue routine. It should determine the cause of the error and release the queue. Alternatively, if the device media has changed, ReleaseQueue might also clear the queue of any stacked up requests. Use auto-sense requests or SRBs with a SRB_FLAGS_BYPASS_FROZEN_QUEUE flag if you need to bypass a frozen queue. The release or flush IRP must be allocated with from
NonPagedPoolMustSucceed memory - this is one of the few times such memory can be requested.
The port driver can handle time-outs requests for the class driver.
A filter driver does any special handling of existing IRPs or implementing new IRP_MJ_DEVICE_CONTROL requests.
An SFD must support all the requests that its underlying class driver supports. Usually it just passes these straight through to the underlying driver. If appropriate, it can set a completion routine to do any post-processing.
An SFD can use IoGetConfigurationInformation to find how many devices to intercept. Otherwise grovelling around in the registry might be necessary. It then attaches to the underlying device and sees if it is one that it supports. If not, it should detach itself from the device.
A changer has transporters for moving media from storage slots to data transfer elements. It may also have an import/export element where media can be physically changed, and a door to access all media.
Write a changer miniclass driver to interface with the system changer class driver. It reports the above changer characteristics to the class driver.
See the DDK for full details of the required interface. It ought to support Plug and Play and Power Management requests as well.
User applications need to call the user-mode NT Media Services (NTMS) to change media.