Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
14.  SCSI Target Drivers Autoconfiguration for SCSI Target Drivers detach() Entry Point (SCSI Target Drivers)  Previous   Contents   Next 
   
 

getinfo() Entry Point (SCSI Target Drivers)

The getinfo(9E) routine for SCSI target drivers is much the same as for other drivers (see "The getinfo() Entry Point" for more information on DDI_INFO_DEVT2INSTANCE case). However, in the DDI_INFO_DEVT2DEVINFO case of the getinfo() routine, the target driver must return a pointer to its dev_info node. This pointer can be saved in the driver state structure or can be retrieved from the sd_dev field of the scsi_device(9S) structure. Example 14-4 shows an alternative SCSI target driver getinfo() code fragment.


Example 14-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() Function

The scsi_init_pkt(9F) routine allocates and zeros a scsi_pkt(9S) structure; it also sets pointers to pkt_private, *pkt_scbp, and *pkt_cdbp. Additionally, it provides a callback mechanism to handle the case where resources are not available. This function has the following syntax:

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

Pointer to a scsi_address structure. This is the sd_address field of the device's scsi_device(9S) structure.

pktp

Pointer to the scsi_pkt(9S) structure to be initialized. If this is set to NULL, a new packet is allocated.

bp

Pointer to a buf(9S) structure. If this is non-NULL and contains a valid byte count, DMA resources are allocated.

cmdlen

Length of the SCSI command descriptor block in bytes.

statuslen

Required length of the SCSI status completion block in bytes.

privatelen

Number of bytes to allocate for the pkt_private field.

flags

Set of flags. Possible bits include:

  • PKT_CONSISTENT - This bit 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 bit can be set if the driver accepts a partial DMA mapping. If set, scsi_init_pkt(9F) allocates DMA resources with the DDI_DMA_PARTIAL flag set. The pkt_resid field of the scsi_pkt(9S) structure can be returned with a nonzero residual, indicating the number of bytes for which scsi_init_pkt(9F) 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

Parameter to pass to the callback function.

The scsi_init_pkt() routine synchronizes the data prior to transport. If the driver needs to access the data after transport, it should call scsi_sync_pkt(9F) to flush any intermediate caches. The scsi_sync_pkt() routine can be used to synchronize any cached data.

scsi_sync_pkt() Function

If the target driver needs to resubmit the packet after changing the data, scsi_sync_pkt(9F) must be called before calling scsi_transport(9F). However, if the target driver does not need to access the data, there is no need to call scsi_sync_pkt() after the transport.

scsi_destroy_pkt() Function

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.

scsi_alloc_consistent_buf() Function

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 that operate on buffers that the driver itself examines (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; use it sparingly.


scsi_free_consistent_buf() Function

scsi_free_consistent_buf(9F) releases a buf(9S) structure and the associated data buffer allocated with scsi_alloc_consistent_buf(9F). See "attach() Entry Point (SCSI Target Drivers)" and "detach() Entry Point (SCSI Target Drivers)" for examples.

Building and Transporting a Command

The host bus adapter driver is responsible for transmitting the command to the device and handling the low-level SCSI protocol. The scsi_transport(9F) routine hands a packet to the host bus adapter driver for transmission. The target driver has the 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, as shown in this example:

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 scsi_setup_cdb(9F) function:

    if (scsi_setup_cdb((union scsi_cdb *)pkt->pkt_cdbp,
         SCMD_READ, bp->b_blkno, bp->b_bcount >> DEV_BSHIFT, 0) == 0)
         goto failed;

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 parameter (SCMD_READ).

  • 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.


Note - scsi_setup_cdb(9F) does not support setting a target device's logical unit number (LUN) in bits 5-7 of byte 1 of the SCSI command block, as defined by SCSI-1. For SCSI-1 devices requiring the LUN bits set in the command block, use makecom_g0(9F) (or equivalent) rather than scsi_setup_cdb(9F).


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.

Setting Target Capabilities

The target drivers use scsi_ifsetcap(9F) to set the capabilities of the host adapter driver. A cap is a name-value pair whose name is a null terminated character string and whose value is an integer. The current value of a capability can be retrieved using scsi_ifgetcap(9F). scsi_ifsetcap(9F) allows capabilities to be set for all targets on the bus.

In general, however, setting capabilities of targets that are not owned by the target driver is not recommended and is not universally supported by HBA drivers. Some capabilities (such as disconnect and synchronous) can be set by default by the HBA driver but others might need to be set explicitly by the target driver (wide-xfer or tagged-queueing, for example).

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 using scsi_transport(9F):

    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, or the host adapter driver rejected this packet for other reasons.

  • TRAN_FATAL_ERROR - The host adapter driver is unable to accept this packet.


Caution - 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.

 
 
 
  Previous   Contents   Next