Writing Device Drivers
  Search only this book
Download this book in PDF

SCSI Target Drivers

10

This chapter describes how to write a SCSI target driver using the interfaces provided by the Sun Common SCSI Architecture (SCSA). Overviews of SCSI and SCSA are presented, followed by the details of implementing a target driver.

Note - Target driver developers may be interested in SCSI HBA driver information. A SCSI HBA chapter in progress (SCSIHBA.PS) is included as a .ps file in the DDK. It is located in this path: opt/SUNWddk/doc

Overview

The Solaris 2.4 DDI/DKI divides the software interface to SCSI devices into two major parts: target drivers and host bus adapter (HBA) drivers. Target refers to a driver for a device on a SCSI bus, such as a disk or a tape drive. host bus adapter refers to the driver for the SCSI controller on the host machine, such as the "esp" driver on a SPARCstation. SCSA defines the interface between these two components. This chapter discusses target drivers only. See SCSI HBA Drivers for information on host bus adapter drivers.

Note - The terms "host bus adapter" or "HBA" used in this manual are equivalent to the phrase "host adapter" as defined in SCSI specifications.

Target drivers can be either character or block device drivers, depending on the device. Drivers for tape drives are usually character device drivers, while disks are handled by block device drivers. This chapter describes how to write a SCSI target driver and discusses the additional requirements that SCSA places on block and character drivers for SCSI target devices.

Reference Documents

The following reference documents provide supplemental information needed by the designers of target drivers and host bus adapter drivers.
Small Computer System Interface (SCSI) Standard, ANSI X3.131-1986 American National Standards Institute
Sales Department 1430 Broadway, New York, NY 10018 Phone (212) 642-4900)
Small Computer System Interface 2 (SCSI-2) Standard, document X3.131-1994 Global Engineering Documents
15 Inverness Way, East Englewood, CO 80112-5704 Phone: 800-854-7179 or 303-792-2181
FAX: 303-792-2192
Basics of SCSI
ANCOT Corporation Menlo Park, California 94025 Phone (415) 322-5322
FAX: (415) 322-0455
Also, refer to the SCSI command specification for the target device, provided by the hardware vendor.
For information on setting global SCSI options see Appendix B, "Advanced Topics."
For a pointer to SCSI driver sample code see Appendix D, "Sample Driver Source Code Listings".

Sun Common SCSI Architecture Overview

The Sun Common SCSI Architecture (SCSA) is the Solaris 2.4 DDI/DKI programming interface for the transmission of SCSI commands from a target driver to a host bus adapter driver. This interface is independent of the type of host bus adapter hardware, the platform, the processor architecture, and the SCSI command that is being transported across the interface.
By conforming to the SCSA, the target driver can pass any SCSI command to a target device without knowledge of the hardware implementation of the host bus adapter.
The SCSA conceptually separates building the SCSI command (by the target driver) from transporting the SCSI command and data across the SCSI bus.
The architecture defines the software interface between high-level and low-level software components. The higher level software component consists of one or more SCSI target drivers, which translate I/O requests into SCSI commands appropriate for the peripheral device.

Graphic

Figure 10-1

The lower-level software component consists of a SCSA interface layer and one or more host bus adapter drivers. The host bus adapter driver has several responsibilities. It must:
  • Manage host bus adapter hardware
  • Accept SCSI commands from the SCSI target driver
  • Transport the commands to the specified SCSI target device
  • Perform any data transfers that the command requires
  • Collect status
  • Handle auto-request sense (optional)
  • Inform the target driver of command completion (or failure)
The target driver is completely responsible for the generation of the proper SCSI commands required to execute the desired function.

General Flow of Control

When transferring data to or from a user address space (using the read(9E) or write(9E) entry points) SCSI target character device drivers must use physio(9F) to encode the request into a buf(9S) structure and call the driver's strategy(9E) routine.
physio(9F) locks down the user buffer into memory before issuing a SCSI command. The file system locks down memory for block device drivers. See Chapter 9, "Drivers for Block Devices", for more information on writing a strategy(9E) entry point and Chapter 8, "Drivers for Character Devices", for more information on using physio(9F).
Assuming no transport errors occur, the following steps describe the general flow of control for a read or write request starting from the call to the target driver's strategy routine:
  1. The target driver's strategy(9E) routine checks the request and allocates a scsi_pkt(9S) using scsi_init_pkt(9F). The target driver initializes the packet and sets the SCSI CDB using the makecom(9F) function. The target driver also specifies a timeout and provides a pointer to a callback function, which is called by the host bus adapter driver on completion of the command. The buf(9S) pointer should be saved in the scsi packet's target-private space.

  1. The target driver submits the packet to the host bus adapter driver using scsi_transport(9F). The target driver is then free to accept other requests. The target driver should not access the packet while it is in transport. If either the host bus adapter driver or the target support queueing, new requests can be submitted while the packet is in transport.

  2. As soon as the SCSI bus is free and the target not busy, the host bus adapter driver selects the target and passes the CDB. The target executes the command and performs the requested data transfers. The target controls the SCSI bus phase transitions. The host bus adapter just responds to these transitions until the command completes.

  3. After the target sends completion status and disconnects, the host bus adapter driver notifies the target driver by calling the completion function which was specified in the scsi packet. At this time the host bus adapter driver is no longer responsible for the packet, and the target driver has regained ownership of the packet.

  4. The SCSI packet's completion routine analyzes the returned information and determines whether the SCSI operation was successful. If a failure has occurred, the target driver may retry the command by calling scsi_transport(9F) again. If the host bus adapter driver does not support auto request sense, the target driver must submit a request sense packet in order to retrieve the sense data in the event of a check condition.

  5. If either the command completed successfully or cannot be retried, the target driver calls scsi_destroy_pkt(9F) which synchronizes the data and frees the packet. If the target driver needs to access the data before freeing the packet, it may call scsi_sync_pkt(9F)

  6. Finally, the target driver notifies the application program that originally requested the read or write that the transaction is complete, either by returning from the read(9E) entry point in the driver (for a character device), or indirectly through biodone(9F).

The SCSA allows the execution of many of such operations, both overlapped and queued at various points in the process. The model places the management of system resources on the host bus adapter driver. The software interface allows the execution of target driver functions on host bus adapter drivers using SCSI bus adapters of varying degrees of intelligence.

SCSA Functions

SCSA defines a number of functions, listed in Table 10-1, which manage the allocation and freeing of resources, the sensing and setting of control states, and the transport of SCSI commands:
Table 10-1
Function NameCategory
scsi_init_pkt(9F)
scsi_sync_pkt(9F)
scsi_dmafree(9F)
scsi_destroy_pkt(9F)
scsi_alloc_consistent_buf(9F)
scsi_free_consistent_buf(9F)
Resource management
scsi_transport(9F)Command transport
scsi_ifgetcap(9F)
scsi_ifsetcap(9F)
Transport information and control
scsi_abort(9F)
scsi_reset(9F)
Error handling
scsi_poll(9F)Polled I/O
scsi_probe(9F)
scsi_unprobe(9F)
Probe functions
makecom_g0(9F)
makecom_g1(9F)
makecom_g0_s(9F)
makecom_g5(9F)
CDB initialization functions

SCSA Compatibility Functions

The functions listed in Table 10-2 are maintained for both source and binary compatibility with previous releases. However, new drivers should use the new functions listed in Table 10-1.
Table 10-2
Function NameCategory
scsi_resalloc(9F)
scsi_resfree(9F)
scsi_pktalloc(9F)
scsi_pktfree(9F)
scsi_dmaget(9F)
get_pktiopb(9F)
free_pktiopb(9F)
Resource management
scsi_slave(9F)
scsi_unslave(9F)
Probe functions

SCSI Target Drivers

Hardware Configuration File

Since SCSI devices are not self-identifying, a hardware configuration file is required for a target driver (see driver.conf(4) and scsi(4) for details). A typical configuration file looks like this:
name="xx" class="scsi" target=2 lun=0;

The system reads the file during autoconfiguration and uses the class property to identify the driver's possible parent. The system then attempts to attach the driver to any parent driver that is of class "scsi". All host bus adapter drivers are of this class. Using the class property rather than the parent property allows the target driver to be attached to any host bus adapter driver that finds the expected device at the specified target and lun ids. The target driver is responsible for verifying this in its probe(9E) routine.

Declarations and Data Structures

Target drivers must include the header file <sys/scsi/scsi.h>.
SCSI target drivers must also include this declaration:
char _depends_on[] = "misc/scsi";

scsi_device Structure

The host bus adapter driver allocates and initializes a scsi_device(9S) structure for the target driver before either the probe(9E) or attach(9E) routine is called. This structure stores information about each SCSI logical unit, including pointers to information areas that contain both generic and device specific information. There is one scsi_device(9S) structure for each logical unit attached to the system. The target driver can retrieve a pointer to this structure by calling ddi_get_driver_private(9F).

Caution - Because the host bus adapter driver uses the private field in the target device's dev_info structure, target drivers should not use ddi_set_driver_private(9F).

The scsi_device(9S) structure contains the following fields:
struct scsi_address                sd_address;
dev_info_t                         *sd_dev;
kmutex_t                           sd_mutex;
struct scsi_inquiry                *sd_inq;
struct scsi_extended_sense         *sd_sense;
caddr_t                            sd_private;

sd_address is a data structure that is passed to the SCSI resource allocation routines.
sd_dev is a pointer to the target's dev_info structure.
sd_mutex is a mutex for use by the target driver. This is initialized by the host bus adapter driver and can be used by the target driver as a per-device mutex. Do not hold this mutex across a call to scsi_transport(9F) or scsi_poll(9F). See Chapter 4, "Multithreading," for more information on mutexes.
sd_inq is a pointer for the target device's SCSI Inquiry data. The scsi_probe(9F) routine allocates a buffer, fills it in, and attaches it to this field.
sd_sense is a pointer to a buffer to contain SCSI Request Sense data from the device. The target driver must allocate and manage this buffer itself; see "attach( )" on page 202.
sd_private is a pointer field for use by the target driver. It is commonly used to store a pointer to a private target driver state structure.

scsi_pkt Structure

This structure contains the following fields:
struct scsi_address       pkt_address;
opaque_t     pkt_private;
void         (*pkt_comp)(struct scsi_pkt *pkt);
long         pkt_flags;
u_long       pkt_time;
u_char       *pkt_scbp;
u_char       *pkt_cdbp;
long         pkt_resid;
u_long       pkt_state;
u_long       pkt_statistics;
u_char       pkt_reason;

pkt_address is the target device's address set by scsi_init_pkt(9F)
pkt_private is a place to store private data for the target driver. It is commonly used to save the buf(9S) pointer for the command.
pkt_comp is the address of the completion routine. The host bus adapter driver calls this routine when it has transported the command. This does not mean that the command succeeded; the target might have been busy or may not have responded before the time-out time elapsed (see the description for pkt_time field). The target driver must supply a valid value in this field, though it can be NULL if the driver does not want to be notified.

Note - There are two different SCSI callback routines. The pkt_comp field identifies a completion callback routine, called when the host bus adapter completes its processing. There is also a resource callback routine, called when currently unavailable resources are likely to be available (as in scsi_init_pkt(9F)).

pkt_flags provides additional control information, for example, to transport the command without disconnect privileges (FLAG_NODISCON) or to disable parity (FLAG_NOPARITY). See scsi_pkt(9S) for details.
pkt_time is a timeout value (in seconds). If the command does not complete within this time, the host bus adapter calls the completion routine with pkt_reason set to CMD_TIMEOUT. The target driver should set this field to longer than the maximum time the command might take. If the timeout is zero, no timeout is requested. Timeout starts when the command is transmitted on the SCSI bus.
pkt_scbp is a pointer to the SCSI Status completion block; this is filled in by the host bus adapter driver.
pkt_cdbp is a pointer to the SCSI Command Descriptor Block, the actual command to be sent to the target device. The host bus adapter driver does not interpret this field. The target driver must fill it in with a command that the target device understands.
pkt_resid is the residual of the operation. When allocating DMA resources for a command (scsi_init_pkt(9F), pkt_resid indicates the number of bytes for which DMA resources could not be allocated due to DMA hardware scatter/gather or other device limitations. After command transport, pkt_resid indicates the number of data bytes not transferred; this is filled in by the host bus adapter driver before the completion routine is called.
pkt_state indicates the state of the command. The host bus adapter driver fills in this field as the command progresses. One bit is set in this field for each of the five following command states:
  • STATE_GOT_BUS - Acquired the bus
  • STATE_GOT_TARGET - Selected the target
  • STATE_SENT_CMD - Sent the command
  • STATE_XFERRED_DATA - Transferred data (if appropriate)
  • STATE_GOT_STATUS - Received status from the device
pkt_statistics contains transport-related statistics, set by the host bus adapter driver.
pkt_reason gives the reason the completion routine was called. The main function of the completion routine is to decode this field and take the appropriate action. If the command completed--in other words, if there were no transport errors--this field is set to CMD_CMPLT; other values in this field indicate an error. After a command completes, the target driver should examine the pkt_scbp field for a check condition status. See scsi_pkt(9S) for more information.

State Structure

This section adds the following fields to the state structure. See "State Structure" on page 57 for more information.
struct scsi_pkt       *rqs;        /* Request Sense packet */
struct buf            *rqsbuf;     /* buf for Request Sense */
struct scsi_pkt       *pkt;        /* packet for current command */
struct scsi_device *sdp;           /* pointer to device's */
                                   /* scsi_device(9S) structure. */

rqs is a pointer to a SCSI Request Sense command scsi_pkt(9S) structure, allocated in the attach(9E) routine. This packet is preallocated because the Request Sense command is small and may be used in time-critical areas of the driver (such as when handling errors).

Autoconfiguration

SCSI target drivers must implement the standard autoconfiguration routines _init(9E), _fini(9E), _info(9E), and identify(9E). See Chapter 5, "Autoconfiguration," for more information.
probe(9E), attach(9E), and getinfo(9E) are also required, but they must perform SCSI (and SCSA) specific processing.

probe( )

SCSI target devices are not self-identifying, so target drivers must have a probe(9E) routine. This routine must determine whether or not the expected type of device is present and responding.
The general structure and return codes of the probe(9E) routine are the same as those of other device drivers. See probe() on page 87 for more information. SCSI target drivers must use the scsi_probe(9F) routine in their probe(9E) entry point. scsi_probe(9F) sends a SCSI Inquiry command to the device and returns a code indicating the result. If the SCSI Inquiry command is successful, scsi_probe(9F) allocates a scsi_inquiry(9S) structure and fills it in with the device's Inquiry data. Upon return from scsi_probe(9F), the sd_inq field of the scsi_device(9S) structure points to this scsi_inquiry(9S) structure.
Since probe(9E) must be stateless, the target driver must call scsi_unprobe(9F) before probe(9E) returns, even if scsi_probe(9F) fails.
Code Example 10-1 shows a typical probe(9E) routine. It uses the ddi_getprop(9F) routine to retrieve the device's SCSI target and logical unit numbers so that it can print them in messages. It also retrieves its scsi_device(9S) structure from the private field of its dev_info structure. The probe(9E) routine then calls scsi_probe(9F) to verify that the expected device (a printer in this case) is present.
If scsi_probe(9F) succeeds, it has attached the device's SCSI Inquiry data in a scsi_inquiry(9S) structure, to the sd_inq field of the scsi_device(9S) structure. The driver can then check to see if the device type is a printer (reported in the inq_dtype field). If it is, the type is reported with scsi_log(9F), using scsi_dname(9F) to convert the device type into a string.
Code Example 10-1 SCSI target driver probe(9E) routine
static int
xxprobe(dev_info_t *dip)
{
    struct scsi_device *sdp;
    int rval, target, lun;
    /*
     * Get the SCSI target/lun properties. DDI_PROP_DONTPASS
     * prevents ddi_getprop from looking beyond this node for the
     * properties.
     */
    target = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
         "target", -1);
    lun = ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,

    "lun", -1);
if ((target == -1) || (lun == -1))
    return (DDI_FAILURE);
/*
 * Get a pointer to the scsi_device(9S) structure
 */
sdp = (struct scsi_device *) ddi_get_driver_private(dip);
/*
 * Call scsi_probe(9F) to send the Inquiry command. It will
 * fill in the sd_inq field of the scsi_device structure.
 */
switch (scsi_probe(sdp, NULL_FUNC)) {
case SCSIPROBE_FAILURE:
case SCSIPROBE_NORESP:
case SCSIPROBE_NOMEM:
    /*
     * In these cases, device may be powered off,
     * in which case we may be able to successfully
     * probe it at some future time - referred to
     * as 'deferred attach'.
     */
    rval = DDI_PROBE_PARTIAL;
    break;
case SCSIPROBE_NONCCS:
default:
    /*
     * Device isn't of the type we can deal with,
     * and/or it will never be useable.
     */
    rval = DDI_PROBE_FAILURE;
    break;
case SCSIPROBE_EXISTS:
    /*
     * There is a device at the target/lun address. Check
     * inq_dtype to make sure that it is the right device
     * type. See scsi_inquiry(9S)for possible device types.
     */
    switch (sdp->sd_inq->inq_dtype) {
    case DTYPE_PRINTER:
        scsi_log(sdp, "xx", SCSI_DEBUG,
             "found %s device at target%d, lun%d\n",
             scsi_dname((int)sdp->sd_inq->inq_dtype),
             target, lun);
        rval = DDI_PROBE_SUCCESS;
        break;

        case DTYPE_NOTPRESENT:
        default:
             rval = DDI_PROBE_FAILURE;
             break;
        }
    }
    scsi_unprobe(sdp);
    return (rval);
}

A more thorough probe(9E) routine could also check other fields of the scsi_inquiry(9S) structure as necessary to make sure that the device is of the type expected by a particular driver.

attach( )

After the probe(9E) routine has verified that the expected device is present, attach(9E) is called. This routine allocates and initializes any per-instance data and creates minor device node information. See "attach( )" on page 95 for details of this. In addition to these steps, a SCSI target driver again calls scsi_probe(9F) to retrieve the device's Inquiry data and also creates a SCSI Request Sense packet. If the attach is successful, the attach function should not call scsi_unprobe.
Three routines are used to create the Request Sense packet: scsi_alloc_consistent_buf(9F), scsi_init_pkt(9F), and makecom_g0(9F). scsi_alloc_consistent_buf(9F) allocates a buffer suitable for consistent DMA and returns a pointer to a buf(9S) structure. The advantage of a consistent buffer is that no explicit syncing of the data is required. In other words, the target driver can access the data after the callback. The sd_sense element of the device's scsi_device(9S) structure must be initialized with the address of the sense buffer. scsi_init_pkt(9F) creates and partially initializes a scsi_pkt(9S) structure. makecom_g0(9F) creates a SCSI Command Descriptor Block (CDB), in this case creating a SCSI Request Sense command.
Code Example 10-2 SCSI target driver attach(9E) routine.
static int
xxattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
    struct xxstate        *xsp;

struct scsi_pkt       *rqpkt = NULL;
struct scsi_device *sdp;
struct buf            *bp = NULL;
int     instance;
instance = ddi_get_instance(dip);

allocate a state structure and initialize it
...
xsp = ddi_get_soft_state(statep, instance);
sdp = (struct scsi_device *) ddi_get_driver_private(dip);
/*
 * Cross-link the state and scsi_device(9S) structures.
 */
sdp->sd_private = (caddr_t) xsp;
xsp->sdp = sdp;

call scsi_probe(9F) again to get and validate inquiry data
/*
 * Allocate a request sense buffer. The buf(9S) structure
 * is set to NULL to tell the routine to allocate a new
 * one. The callback function is set to NULL_FUNC to tell
 * the routine to return failure immediately if no
 * resources are available.
 */
bp = scsi_alloc_consistent_buf(&sdp->sd_address, NULL,
    SENSE_LENGTH, B_READ, NULL_FUNC, NULL);
if (bp == NULL)
    goto failed;
/*
 * Create a Request Sense scsi_pkt(9S) structure.
 */
rqpkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
    CDB_GROUP0, 1, 0, PKT_CONSISTENT, NULL_FUNC, NULL);
if (rqpkt == NULL)
    goto failed;
/*
 * scsi_alloc_consistent_buf(9F) returned a buf(9S) structure.
 * The actual buffer address is in b_un.b_addr.
 */
sdp->sd_sense = (struct scsi_extended_sense *) bp->b_un.b_addr;

/*
 * Create a Group0 CDB for the Request Sense command
 */
makecom_g0(rqpkt, devp, FLAG_NOPARITY, SCMD_REQUEST_SENSE,
    0, SENSE_LENGTH);
/*
 * Fill in the rest of the scsi_pkt structure.
 * xxcallback() is the private command completion routine.
 */
rqpkt->pkt_comp = xxcallback;
rqpkt->pkt_time = 30; /* 30 second command timeout */
rqpkt->pkt_flags |= FLAG_SENSING;
xsp->rqs = rqpkt;
xsp->rqsbuf = bp;

create minor nodes, report device, and do any other initialization
    xsp->open = 0;
    return (DDI_SUCCESS);
failed:
    if (bp)
        scsi_free_consistent_buf(bp);
    if (rqpkt)
        scsi_destroy_pkt(rqpkt);
    sdp->sd_private = (caddr_t) NULL;
    sdp->sd_sense = NULL;
    scsi_unprobe(sdp);

free any other resources, such as the state structure
    return (DDI_FAILURE);
}

detach( )

The detach(9E) entry point is the inverse of attach(9E); it must free all resources that were allocated in attach(9E). If successful, the detach should call scsi_unprobe.
Code Example 10-3 SCSI target driver detach(9E) routine
static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
    struct xxstate *xsp;

normal detach(9E) operations, such as getting a pointer to the state structure
...
scsi_free_consistent_buf(xsp->rqsbuf);
scsi_destroy_pkt(xsp->rqs);
xsp->sdp->sd_private = (caddr_t) NULL;
xsp->sdp->sd_sense = NULL;
scsi_unprobe(xsp->sdp);

remove minor nodes free resources, such as the state structure
    return (DDI_SUCCESS);
}

getinfo ( )

The getinfo(9E) routine for SCSI target drivers is much the same as for other drivers; see "getinfo( )" on page 102 for more information on DDI_INFO_DEVT2INSTANCE case. However, in the DDI_INFO_DEVT2DEVINFO case of the getinfo(9E) routine, the target driver must return a pointer to its dev_info node. This can be saved in the driver state structure or can be retrieved from the sd_dev field of the scsi_device(9S) structure.
Code Example 10-4 Alternative SCSI target driver getinfo(9E) code fragment
...
case DDI_INFO_DEVT2DEVINFO:
    dev = (dev_t) arg;
    instance = getminor(dev);
    xsp = ddi_get_soft_state(statep, instance);
    if (xsp == NULL)
        return (DDI_FAILURE);
    *result = (void *) xsp->sdp->sd_dev;
    return (DDI_SUCCESS);
...

Resource Allocation

To send a SCSI command to the device, the target driver must create and initialize a scsi_pkt(9S) structure and pass it to the host bus adapter driver.

scsi_init_pkt( )

The scsi_init_pkt(9F) routine allocates and zeros a scsi_pkt(9S) structure; it also sets pointers to pkt_private, *pkt_scbp, *pkt_cdbp. Additionally, it provides a callback mechanism to handle the case where resources are not available. This structure contains the following fields:
struct scsi_pkt *scsi_init_pkt(struct scsi_address *ap,
    struct scsi_pkt *pktp, struct buf *bp, int cmdlen,
    int statuslen, int privatelen, int flags,
    int (*callback)(caddr_t), caddr_t arg)

ap is a pointer to a scsi_address structure. This is the sd_address field of the device's scsi_device(9S) structure.
pktp is a pointer to the scsi_pkt(9S) structure to be initialized. If this is set to NULL, a new packet is allocated.
bp is a pointer to a buf(9S) structure. If this is non-NULL and contains a valid byte count, DMA resources are allocated.
cmdlen is the length of the SCSI Command Descriptor Block (CDB) in bytes.
statuslen is the required length of the SCSI status completion block, in bytes.
privatelen is the number of bytes to allocate for the pkt_private field. To store a pointer, specify the size of the pointer here (such as sizeof(struct xxstate *) when storing a pointer to the state structure).
flags is a set of flags. Possible bits include:
· PKT_CONSISTENT

This must be set if the DMA buffer was allocated using scsi_alloc_consistent_buf(9F). In this case, the host bus adapter driver guarantees that the data transfer is properly synchronized before performing the target driver's command completion callback.
· PKT_DMA_PARTIAL

This may be set if the driver can accept a partial DMA mapping. If set, scsi_init_pkt(9F) allocates DMA resources with the DDI_DMA_PARTIAL dmar_flag set. The pkt_resid(9E) field of the scsi_pkt(9S) structure may be returned with a non-zero residual, indicating the number of bytes for which scsi_init_pkt() was unable to allocate DMA resources.
callback specifies the action to take if resources are not available. If set to NULL_FUNC, scsi_init_pkt(9F) returns immediately (returning NULL). If set to SLEEP_FUNC, it does not return until resources are available. Any other valid kernel address is interpreted as the address of a function to be called when resources are likely to be available.
arg is the parameter to pass to the callback function.
The scsi_sync_pkt(9F) routine can be used to synchronize any cached data after a transfer, when a target driver wants to reuse a scsi_pkt for another command. This may be done either in the command completion routine or before calling scsi_transport(9F) for the command a second time.
The scsi_destroy_pkt(9F) routine synchronizes any remaining cached data associated with the packet, if necessary, and then frees the packet and associated command, status, and target driver private data areas. This routine should be called in the command completion routine (see scsi_pkt structure on page 193).

scsi_alloc_consistent_buf( )

For most I/O requests, the data buffer passed to the driver entry points is not accessed directly by the driver, it is just passed on to scsi_init_pkt(9F). If a driver sends SCSI commands which operate on buffers the driver examines itself (such as the SCSI Request Sense command), the buffers should be DMA consistent. The scsi_alloc_consistent_buf(9F) routine allocates a buf(9S) structure and a data buffer suitable for DMA consistent operations. The HBA will perform any necessary synchronization of the buffer before performing the command completion callback.

Caution - scsi_alloc_consistent_buf(9F) uses scarce system resources; it should be used sparingly.

scsi_free_consistent_buf(9F) releases a buf(9S) structure and the associated data buffer allocated with scsi_alloc_consistent_buf(9F). See "attach( )" on page 202 and "detach( )" on page 204 for examples.

Building and Transporting a Command

The host bus adapter driver is responsible for transmitting the command to the device and taking care of the low-level SCSI protocol. The scsi_transport(9F) routine hands a packet to the host bus adapter driver for transmission. It is the target driver's responsibility to create a valid scsi_pkt(9S) structure.

Building a Command

The routine scsi_init_pkt(9F) allocates space for a SCSI CDB, allocates DMA resources if necessary, and sets the pkt_flags field:
pkt = scsi_init_pkt(&sdp->sd_address, NULL, bp,
    CDB_GROUP0, 1, 0, 0, SLEEP_FUNC, NULL);

This example creates a new packet and allocates DMA resources as specified in the passed buf(9S) structure pointer. A SCSI CDB is allocated for a Group 0 (6 byte) command, the pkt_flags field is set to zero, but no space is allocated for the pkt_private field. This call to scsi_init_pkt(9F), because of the SLEEP_FUNC parameter, waits indefinitely for resources if none are currently available.
The next step is to initialize the SCSI CDB, using the makecom(9F) family of functions:
makecom_g0(pkt, sdp, flags, SCMD_READ, bp->b_blkno,
    bp->b_bcount >> DEV_BSHIFT);

This example builds a Group 0 Command Descriptor Block and fills in the pkt_cdbp field as follows:
  • The command itself (byte 0) is set from the fourth parameter (SCMD_READ).
  • The target device's logical unit number (bits 5-7 of byte 1) is set using sd_address field of sdp.
  • The pkt_flags field is set from the flags parameter.
  • The address field (bits 0-4 of byte 1 and bytes 2 and 3) is set from bp->b_blkno.
  • The count field (byte 4) is set from the last parameter. In this case it is set to bp->b_bcount >> DEV_BSHIFT, where DEV_BSHIFT is the byte count of the transfer converted to the number of blocks.
After initializing the SCSI CDB, initialize three other fields in the packet and store as a pointer to the packet in the state structure.
pkt->pkt_private = (opaque_t) bp;
pkt->pkt_comp = xxcallback;
pkt->pkt_time = 30;
xsp->pkt = pkt;

The buf(9S) pointer is saved in the pkt_private field for later use in the completion routine.

Transporting a Command

After creating and filling in the scsi_pkt(9S) structure, the final step is to hand it to the host bus adapter driver:
if (scsi_transport(pkt) != TRAN_ACCEPT) {
    bp->b_resid = bp->b_bcount;
    bioerror(bp, EIO);
    biodone(bp);
}

The other return values from scsi_transport(9F) are:
  • TRAN_BUSY - There is already a command in progress for the specified target.
  • TRAN_BADPKT - The DMA count in the packet was too large.
  • TRAN_BADPKT - The host adapter driver rejected this packet.
  • TRAN_FATAL_ERROR - The host adapter driver is unable to accept this packet.

Warning - The mutex sd_mutex in the scsi_device(9S) structure must not be held across a call to scsi_transport(9F).

If scsi_transport(9F) returns TRAN_ACCEPT, the packet is the responsibility of the host bus adapter driver and should not be accessed by the target driver until the command completion routine is called.

Command Completion

Once the host bus adapter driver has done all it can with the command, it invokes the packet's completion callback routine, passing a pointer to the scsi_pkt(9S) structure as a parameter. The completion routine decodes the packet and takes the appropriate action. A simple completion routine is given in Code Example 10-5.
Code Example 10-5 SCSI driver completion routine
static void
xxcallback(struct scsi_pkt *pkt)
{
    struct buf       *bp;
    struct xxstate *xsp;
    int              instance;
    struct scsi_status *ssp;
    /*
     * Get a pointer to the buf(9S) structure for the command
     * and to the per-instance data structure.
     */
    bp = (struct buf *) pkt->pkt_private;
    instance = getminor(bp->b_edev);
    xsp = ddi_get_soft_state(statep, instance);
    /*
     * Figure out why this callback routine was called
     */
    if (pkt->pkt_reason != CMP_CMPLT) {
        bp->b_resid = bp->b_bcount;
        bioerror(bp, EIO);
        scsi_destroy_pkt(pkt); /* release resources */
        biodone(bp);              /* notify waiting threads */ ;
    } else {
        /*
         * Command completed, check status.
         * See scsi_status(9S)
         */
        ssp = (struct scsi_status *) pkt->pkt_scbp;
        if (ssp->sts_busy) {
             error, target busy or reserved
        } else if (ssp->sts_chk) {
             send a request sense command
        } else {
             bp->b_resid = pkt->pkt_resid; /*packet completed OK */

             scsi_destroy_pkt(pkt);
             biodone(bp);
        }
    }
}

This is a very simple completion callback routine. It checks to see whether the command completed and if it did not, gives up immediately. If the target was busy it gives up, or if it returned a "check condition" status, it sends a Request Sense command.
Otherwise, the command succeeded. If this is the end of processing for the command, it destroys the packet and calls biodone(9F).
This example does not attempt to retry incomplete commands. See Appendix D, "Sample Driver Source Code Listings" for information about sample SCSI drivers. Also see Appendix B, "Advanced Topics" for further information.