This chapter describes the SCSI/CAM special I/O interface. The S/CA software includes an interface developed to process special SCSI I/O control commands that the existing Digital SCSI subsystem uses and to aid in porting new or existing SCSI device drivers from other vendors to the S/CA.
Application programs issue I/O control commands by using the ioctl system call to send special SCSI I/O commands to a peripheral device. The term ``special'' refers to commands that are not usually issued to the device through the standard driver entry points. SCSI device drivers usually require the special I/O control commands in addition to the standard read and write system calls. With the SCSI/CAM special I/O interface, SCSI/CAM peripheral driver writers do not need detailed knowledge of either the system-specific or the CAM-specific structures and routines used to issue a SCSI command to the CAM I/O subsystem.
Application programs access the SCSI/CAM special I/O interface by making requests to peripheral drivers that use the ioctl system call. This system call is processed by system kernel support routines that invoke the device driver's I/O control command entry point in the character device switch table, which is defined in the /usr/sys/io/common/conf.c file. The device driver's I/O control routine accesses the special I/O interface by using either the supplied SCSI/CAM peripheral common routine, ccmn_DoSpecialCmd, or a driver-specific routine. Figure 12-1 shows the flow of application program requests through the operating system to the SCSI/CAM special I/O interface and the CAM I/O subsystem.
SCSI/CAM peripheral device drivers access the SCSI/CAM special I/O interface by using either the supplied SCSI/CAM peripheral common routine, ccmn_SysSpecialCmd, or a driver-specific routine. Figure 12-2 shows the flow of system requests from device drivers through the SCSI/CAM special I/O interface and the CAM I/O subsystem.
The SCSI/CAM special I/O interface includes default command tables that provide backwards compatibility with existing SCSI I/O control commands. The following predefined SCSI/CAM special command tables are included:
The interface also allows commands to be added to the existing command tables and new command tables to be added. The SCSI/CAM special I/O interface includes routines that manipulate the tables so programmers can write device drivers to easily add and remove command tables.
The command table header structure, SPECIAL_HEADER, provides a bit mask of device types that can be used with a command table. The Special Command Header structure is defined as follows:
/* * Special Command Header structure: */typedef struct special_header { struct special_header *sph_flink; /* Forward link to next table */ struct special_header *sph_blink; /* Backward link to prev table */ struct special_cmd *sph_cmd_table; /* Pointer to command table */ U32 sph_device_type; /* The device types supported */ U32 sph_table_flags; /* Flags to control cmd lookup */ caddr_t sph_table_name; /* Name of this command table */ } SPECIAL_HEADER;
The sph_flink and sph_blink members are table-linkage members that allow command tables to be dynamically added or removed from the list of tables searched by the SCSI/CAM special I/O interface when processing commands.
The sph_cmd_table member specifies a pointer to the Special Command Entry structure.
The sph_device_type member specifies the device types supported by this SCSI/CAM special command table.
The sph_table_flags member specifies flags to control command lookup. The SPH_SUB_COMMAND bit indicates that the command table contains subcommands.
The sph_table_name member specifies the name of this SCSI/CAM special command table.
Each SCSI/CAM special command table contains multiple entries. Each entry provides enough information to process the command associated with that entry. The command tables can be dynamically added, but the entries within the command tables are not dynamic. Each command table's entries are statically defined so that individual entries cannot be appended to the table. The Special Command Entry structure structure is defined as follows:
/* * Special Command Entry structure: */ typedef struct special_cmd { u_int spc_ioctl_cmd; /* The I/O control command code */ u_int spc_sub_command; /* The I/O control sub-command */ u_char spc_cmd_flags; /* The special command flags */ u_char spc_cmd_code; /* The special command code */ u_short : 16; /* Unused ... align next field */ U32 spc_device_type; /* The device types supported */ U32 spc_cmd_parameter; /* Command parameter (if any) */ U32 spc_cam_flags; /* The CAM flags field for CCB */ U32 spc_file_flags; /* File control flags (fcntl) */ int spc_data_length; /* Kernel data buffer length */ int spc_timeout; /* Timeout for this command */ int (*spc_docmd)(); /* Function to do the command */ int (*spc_mkcdb)(); /* Function to make SCSI CDB */ int (*spc_setup)(); /* Setup parameters routine */ caddr_t spc_cdbp; /* Pointer to prototype CDB */ caddr_t spc_cmdp; /* Pointer to the command name */ } SPECIAL_CMD;
The spc_ioctl_cmd and spc_sub_command members contain the SCSI I/O control command code and subcommand used to locate the appropriate table entry. The subcommand is checked only if flags are set that indicate a subcommand exists.
The spc_cmd_flags member contains flags to control the action of the SCSI/CAM special I/O interface routines. The flag definitions are described in the following table:
Flag Name | Description |
SPC_SUSER | Restricted to superuser. |
SPC_COPYIN | User buffer to copy from. |
SPC_COPYOUT | User buffer to copy to. |
SPC_NOINTR | Do not allow sleep interrupts. |
SPC_DATA_IN | Data direction is from device. |
SPC_DATA_OUT | Data direction is to device. |
SPC_DATA_NONE | No data movement for command. |
SPC_SUB_COMMAND | Entry contains subcommand. |
SPC_INOUT | Copy in and out. |
SPC_DATA_INOUT | Copy data in and out. |
The spc_command_code member contains the special SCSI opcode used to execute this command. This member is used during the creation of the CDB.
The spc_device_type member defines the specific device types with which this command is used. For example, direct-access and read-only direct-access devices share many of the same commands. Therefore, rather than duplicating command table entries, both device types can use the same command table. The values that are valid for this member are those defined in the Inquiry data device type member of the inquiry_info structure, which is defined in the /usr/sys/include/io/cam/scsi_all.h file.
The spc_cmd_parameter member is used to define any special parameters that the command uses. For example, the SCSI START CDB command, which is defined in the /usr/sys/include/io/cam/scsi_direct.h file, is used for stopping, starting, and ejecting a CDROM caddy. The parameter member can be defined as the subcommand code so a common routine can be used to create the CDB.
The spc_cam_flags member contains the CAM flags necessary for processing the command. The CAM flags are defined in the file /usr/sys/include/io/cam/cam.h.
The spc_file_flags member contains the file access bits required for accessing the command. For example, the command can be restricted to device files opened for read and write access. The file flags are defined in the file /usr/sys/include/sys/file.h.
The spc_data_length member describes the length of the buffer to hold additional kernel data that is required to process the command. Usually this member is set to 0 (zero) because the data buffer lengths are normally decoded from the I/O command code or taken from a member in the I/O parameter buffer.
The spc_timeout member defines the default timeout for this command. This value is used for the SCSI I/O CCB timeout member, unless it is overridden by the timeout member in the Special I/O Argument structure.
The spc_docmd member specifies the routine to invoke to execute the command. A routine is required by I/O commands that need special servicing. For example, if the I/O command does not return all the data read by the SCSI command, then a routine is needed to handle this special servicing.
The spc_mkcdb member specifies the routine that is invoked to create the CDB for the command. A routine is not necessary for simple commands, such as TEST UNIT READY. However, any command that requires additional members to be set up in the CDB prior to issuing the SCSI command must define this routine.
The spc_setup member is required by any command that has special setup requirements. For example, commands that pass a user buffer and length as part of the I/O parameters buffer structure must have a setup routine to copy these members to the Special I/O Argument structure. This applies to all previously defined commands, but does not apply to commands implemented using the new SCSI_SPECIAL I/O control command code.
The spc_cdbp member specifies a pointer to the prototype CDB. This member is used by commands that can be implemented using a prototype CDB. A prototype CDB is a SCSI command that can be implemented using a statically defined SCSI CDB. Fields within the CDB do not change. Usually, simple SCSI commands, such as SCSI_START_UNIT, can be implemented with a prototype CDB so that the make CDB routine is not required.
The spc_cmdp member points to a string that describes the name of the command. This string is used during error reporting and during debugging.
The example that follows shows a sample SCSI/CAM special command table with one entry defined:
#include "<cdrom.h" #include "<mtio.h" #include "<rzdisk.h"#include <cam.h> #include <cam_special.h> #include <dec_cam.h> #include <scsi_all.h> #include <scsi_direct.h> #include <scsi_rodirect.h> #include <scsi_sequential.h> #include <scsi_special.h>
extern int scmn_MakeFormatUnit(), scmn_SetupFormatUnit();
/* * Command Header for Direct-Access Command Table: */ struct special_header cam_DirectCmdsHdr = { (struct special_header *) 0, /* sph_flink */ (struct special_header *) 0, /* sph_blink */ cam_DirectCmds, /* sph_cmd_table */ (BITMASK(ALL_DTYPE_DIRECT) | BITMASK(ALL_DTYPE_RODIRECT)), /* sph_device_type */ 0, /* sph_table_flags */ "Direct Access Commands" /* sph_table_name */ };
/********************************************************************** * * * Special Direct Access Command Table * * * **********************************************************************/ struct special_cmd cam_DirectCmds[] = { { SCSI_FORMAT_UNIT, /* spc_ioctl_cmd */ 0, /* spc_sub_command */ (SPC_COPYIN | SPC_DATA_OUT), /* spc_cmd_flags */ DIR_FORMAT_OP, /* spc_cmd_code */ BITMASK(ALL_DTYPE_DIRECT), /* spc_device_type */ 0, /* spc_cmd_parameter */ CAM_DIR_OUT, /* spc_cam_flags */ FWRITE, /* spc_file_flags */ -1, /* spc_data_length */ (120 * ONE_MINUTE), /* spc_timeout */ (int (*)()) 0, /* spc_docmd */ scmn_MakeFormatUnit, /* spc_mkcdb */ scmn_SetupFormatUnit, /* spc_setup */ (caddr_t) 0, /* spc_cdbp */ "format unit" /* spc_cmdp */ }, . . . { END_OF_CMD_TABLE } /* End of cam_DirectCmds[] Table. */ };
/* * Define Special Commands Header & Table for Initialization Routine. */ struct special_header *cam_SpecialCmds = &cam_SpecialCmdsHdr;
struct special_header *cam_SpecialHdrs[] = { &cam_GenericCmdsHdr, &cam_DirectCmdsHdr, &cam_AudioCmdsHdr, &cam_SequentialCmdsHdr, &cam_MtCmdsHdr, 0 };
A Special I/O Argument structure is passed to the SCSI/CAM special I/O interface to control processing of the I/O control command being executed. The structure members provide information to process a special command for different SCSI subsystems. The calling routine can override default settings and routines invoked by the SCSI/CAM special I/O interface. Table 12-1 shows the members that are mandatory for the calling routine to set up, the members that are optional, and the members that are used or filled in by the SCSI/CAM special I/O interface.
Member Name | Type | Description |
U32 sa_flags; | M | Flags to control command |
dev_t sa_dev; | M | Device major/minor number |
u_char sa_unit; | U | Device logical unit number |
u_char sa_bus; | M | SCSI host adapter bus number |
u_char sa_target; | M | SCSI device target number |
u_char sa_lun; | M | SCSI logical unit number |
u_int sa_ioctl_cmd; | M | The I/O control command |
u_int sa_ioctl_scmd; | C | The subcommand, if any |
caddr_t sa_ioctl_data; | C | The command data pointer |
caddr_t sa_device_name; | M | Pointer to the device name |
int sa_device_type; | M | The peripheral device type |
int sa_iop_length; | I | Parameters' buffer length |
caddr_t sa_iop_buffer; | I | Parameters' buffer address |
int sa_file_flags; | M | The file control flags |
u_char sa_sense_length; | O | Sense data buffer length |
u_char sa_sense_resid; | I | Sense data residual count |
caddr_t sa_sense_buffer; | O | Sense data buffer address |
u_char sa_user_length; | I | User data buffer length |
caddr_t sa_user_buffer; | I | User data buffer address |
struct buf *sa_bp; | O | Kernel-only I/O request buffer |
CCB_SCSIIO *sa_ccb; | O | CAM control block buffer |
struct special_cmd *sa_spc; | I | Special command table entry |
struct special_header *sa_sph; | O | Special command table header |
U32 sa_cmd_parameter; | I | Command parameter, if any |
int (*sa_error)(); | O | The error report routine |
int (*sa_start)(); | O | The driver start routine |
int sa_data_length; | I | Kernel data buffer length |
caddr_t sa_data_buffer; | I | Kernel data buffer address |
caddr_t sa_cdb_pointer; | I | Pointer to the CDB buffer |
u_char sa_cdb_length; | I | Length of the CDB buffer |
u_char sa_cmd_flags; | I | The special command flags |
u_char sa_retry_count; | I | The current retry count |
u_char sa_retry_limit; | O | Times to retry this command |
int sa_timeout; | O | Timeout for this command |
int sa_xfer_resid; | I | Transfer residual count |
caddr_t sa_specific; | O | Driver-specific information |
Legend: | M = Mandatory. Must be set up by the caller. |
C = Command dependent. Depends on special command. | |
O = Optional. Optionally overrides defaults. | |
I = Interface. Used or filled in by SCSI/CAM special I/O interface. | |
U = Unused. Not used by SCSI/CAM special I/O interface. |
Several of the members marked as mandatory in Table 12-1 are set up initially by the routine that allocates the Special I/O Argument structure. The following members are initialized by the allocation routine: sa_bus, sa_target, sa_lun, sa_unit (same as target), sa_retry_limit (set to 30), and sa_start (set to the xpt_action routine).
Fields that are identified as optional in Table 12-1 can be defined by the caller to override some of the defaults that the SCSI/CAM special I/O interface uses. The following table describes these defaults:
Member Name | Default |
sa_sense_length | Set to DEC_AUTO_SENSE_SIZE, which is defined in /usr/sys/include/io/cam/dec_cam.h. |
sa_sense_buffer | Sense buffer in SCSI/CAM Peripheral Device Driver Working Set structure. |
sa_bp | Allocated as needed for data movement commands. |
sa_ccb | Allocated by the CAM xpt_ccb_alloc routine. |
sa_error() | Special interface error report routine. |
sa_start() | Uses the CAM xpt_action routine. |
sa_timeout | Uses the timeout value from the SCSI/CAM special command table entry. |
sa_specific | Is not set up or used by SCSI/CAM special I/O interface. |
The sa_flags member is used to control the actions of the SCSI/CAM special I/O interface. The calling routine can set the low-order 5 bits of this member. All other bits in this member are reserved. The following table shows the control flags that the calling routine can set:
Flag Name | Description |
SA_NO_ERROR_RECOVERY | Do not perform error recovery. |
SA_NO_ERROR_LOGGING | Do not log error messages. |
SA_NO_SLEEP_INTR | Do not allow sleep interrupts. |
SA_NO_SIMQ_THAW | Leave SIM queue frozen on errors. |
SA_NO_WAIT_FOR_IO | Do not wait for I/O to complete. |
The sa_dev member contains the device major/minor number pair passed into the device driver routines. It is used to fill in the bp_dev member of the system I/O request member.
The sa_unit, sa_bus, sa_target, and sa_lun members are used to address the SCSI device to which the command is being sent. The sa_unit member is not used, but has been included for device drivers that implement logical device mapping.
The sa_ioctl_cmd member contains the I/O control command to be processed. This command usually maps directly to a SCSI I/O command, but that is not necessary. For example, the Digital-specific SCSI_GET_SENSE command returns the sense data from the last failing command. A REQUEST SENSE command is not issued to the device, because autosense is assumed to have been enabled on the failing command, and the sense data is part of the common Peripheral Device structure.
The sa_ioctl_scmd member specifies the subcommand (if any). This member must be filled in for special commands implemented with a subcommand code. For example, magnetic tape I/O control commands have both an I/O control command code and a subroutine command code.
The sa_ioctl_data member specifies that an I/O parameters buffer is required if the I/O control command transfers data to and from the kernel. If the request comes from an application program, this buffer is normally passed into the driver ioctl routine.
The sa_device_name member contains a pointer to the device name string that is used when reporting device errors.
The sa_device_type member contains the device type member from the Inquiry data. This member controls the SCSI/CAM special command tables and the entries within each command table that are searched for the SCSI/CAM special I/O command being issued.
The sa_iop_length member specifies the parameter's buffer length. The sa_iop_buffer member specifies the parameter's buffer address. The SCSI/CAM special I/O interface uses these members internally when processing a command. If I/O would normally be performed directly to the I/O parameters buffer because no other buffer was set up, then a kernel buffer is allocated and set up in these members.
The sa_file_flags member contains the file flags passed into the device driver routines. The flags describe access control bits associated with the device. The file access flags are defined in the /usr/sys/include/io/cam/fcntl.h file.
The sa_sense_length member specifies the sense data buffer length. The sa_sense_buffer member specifies the sense data buffer address. These members set up the sense buffer and expected sense data length that autosense uses when device errors occur. If these members are not set up by the calling routine, then the SCSI/CAM special I/O interface uses the sense buffer allocated in the SCSI/CAM Peripheral Device Driver Working Set structure that is pointed to by the SCSI I/O CCB.
The sa_user_length member specifies the user's data buffer length. The sa_user_buffer member specifies the user's data buffer address. These members are set up by command setup routines to describe the user buffer and user data length required by a command. Requests from application programs that pass a user buffer and length in the I/O parameter buffers require a setup routine to copy this information into those members . The SCSI/CAM special I/O interface checks access and locking on this address range and sets up the address and length in the SCSI I/O CCB for the command.
The sa_bp member contains a pointer to a system I/O request buffer for commands that perform data movement directly to user address space. A system buffer is not required if a kernel data buffer is used for I/O. If the calling routine does not pass a previously allocated request buffer in this member, and the SCSI/CAM special I/O interface determines that the I/O requires one based on the I/O buffer address, then a request buffer is allocated and deallocated automatically by the SCSI/CAM special I/O interface.
The sa_ccb member contains a pointer to the SCSI I/O CCB for a command. If the calling routine does not specify a SCSI I/O CCB in this member, then the SCSI/CAM special I/O interface automatically allocates and deallocates a SCSI I/O CCB for the command.
The special_cmd member specifies a special command table entry. The SCSI/CAM special I/O interface uses this member internally to save the SPECIAL_CMD after a command is located.
The special_header member specifies a special command table header. The calling routine can use this member to specify the SCSI/CAM special command table to search for the special command. This lets device drivers restrict the SCSI/CAM special command tables that are searched. If this member is not used, then all the SCSI/CAM special command tables in the list are searched for an entry that matches the special command being processed.
The sa_cmd_parameter member is used to store the command parameter, if any, from the command entry associated with this special command. Special support routines use this member when setting up members for a particular CDB.
The sa_error member contains the routine to be invoked when an error condition is detected. If not specified, a SCSI/CAM special I/O interface support routine handles the error condition. Otherwise, the routine is called as follows:
status = (*sap->sa_error)(ccb, sense);
This member can be specified for drivers requiring specialized error handling and for specific error logging. The SCSI/CAM special I/O interface's error logging uses the mprintf facility to report errors. Both sense key and CAM status members are logged.
The sa_start member contains the routine that starts processing the SCSI I/O CCB. If not specified, the CAM xpt_action routine is used. The routine is invoked as follows:
(void) ((sap->sa_start)(ccb);
The sa_data_length member specifies the kernel data buffer length. The sa_data_buffer member specifies the kernel data buffer address. The SCSI/CAM special I/O interface uses these members internally to store the address and length of an additional kernel buffer required for a command. These members are usually initialized by the resulting value of the Special Command Entry structure member, spc_data_length, but SCSI/CAM special I/O command developers can use them if needed.
The sa_cdb_pointer member specifies a pointer to the CDB buffer. The SCSI/CAM special I/O interface uses this member internally to save a pointer to the CDB for this special command. This member may point to a prototype CDB; to a driver-allocated CDB buffer, if the CAM_CDB_POINTER flag is set in CCB header; or to the CDB buffer allocated within the SCSI I/O CCB. This member is set up with the CDB buffer address before the Special Command Header structure make CDB routine is invoked as follows:
status = (*spc->spc_mkcdb)(sap, cdbp);
The sa_cdb_length member specifies the size (in bytes) of the CDB required by a SCSI command. If the Special Command Header structure make CDB routine does not set up this member, then the SCSI Group Code is decoded to determine the length.
The sa_cmd_flags member specifies the special command flags. This member is initialized from the Special Command Header structure spc_cmd_flags member so SCSI/CAM special I/O command support routines have easy and quick access to the flags.
The sa_retry_count member contains the number of retrys that were required to successfully complete the request. It is filled in by the SCSI/CAM special I/O interface after processing the command.
The sa_retry_limit member contains the maximum number of times a command is retried. The only retries automatically handled by the SCSI/CAM special I/O interface are a sense key of Unit Attention or a SCSI bus status of Bus Busy or Reservation Conflict. All other error conditions must be handled by the calling routine.
The sa_timeout member contains the timeout value, in seconds, to use with the command being processed. The calling routine can specify this member. If it is not specified, the timeout value is taken from the Special Command Entry structure. This member is used to initialize the cam_timeout member of the SCSI I/O CCB before issuing the command.
The sa_xfer_resid member contains the residual byte count of data movement commands. This member is copied from the cam_resid member of the SCSI I/O CCB before returning to the caller.
The sa_specific member specifies driver-specfic information. This member is not set up or used by the SCSI/CAM special I/O interface. It provides a mechanism for device driver code to pass driver-dependent information to SCSI/CAM special I/O command support routines. The SCSI/CAM peripheral driver common routine ccmn_DoSpecialCmd passes the pointer to the Peripheral Device structure in this member.
The following sample function shows how to use the SCSI/CAM special I/O interface to create a CDB for a SCSI FORMAT_UNIT command:
/********************************************************************* * * * scmn_MakeFormatUnit() - Make Format Unit Command Descriptor Block.* * * * Inputs: sap = Special command argument block pointer. * * cdbp = Pointer to command descriptor block. * * * * Return Value: * * Returns 0 for SUCCESS, or error code on failures. * * * *********************************************************************/ int scmn_MakeFormatUnit (sap, cdbp) register struct special_args *sap; [1] register struct dir_format_cdb6 *cdbp; [2] { register struct special_cmd *spc = sap->sa_spc; [3] register struct format_params *fp; [4]
fp = (struct format_params *) sap->sa_iop_buffer; cdbp->opcode = (u_char) spc->spc_cmd_code; if (fp->fp_defects == VENDOR_DEFECTS) { [5] cdbp->fmt_data = 1; cdbp->cmp_list = 1; } else if (fp->fp_defects == KNOWN_DEFECTS) { cdbp->fmt_data = 1; cdbp->cmp_list = 0; } else if (fp->fp_defects == NO_DEFECTS) { cdbp->fmt_data = 0; cdbp->cmp_list = 0; } cdbp->defect_list_fmt = fp->fp_format; [6] cdbp->vendor_specific = fp->fp_pattern; cdbp->interleave1 = 0; cdbp->interleave0 = fp->fp_interleave; return (SUCCESS); }
The following sample function shows how to use the SCSI/CAM special I/O interface to set up parameters for a SCSI FORMAT_UNIT command:
/********************************************************************* * * * scmn_SetupFormatUnit() - Set up Format Unit Parameters. * * * * Inputs: sap = Special command argument block pointer. * * data = The address of input/output arguments. * * * * Return Value: * * Returns 0 for SUCCESS, or error code on failures. * * * *********************************************************************/ int scmn_SetupFormatUnit (sap, data) register struct special_args *sap; [1] caddr_t data; { struct form2_defect_list_header defect_header; [2] register struct form2_defect_list_header *ddh = &defect_header; register struct format_params *fp; [3]
fp = (struct format_params *) data; sap->sa_user_buffer = (caddr_t) fp->fp_addr; [4]
/* * For diskettes, there are no defect lists. */ if ( ((sap->sa_user_length = fp->fp_length) == 0) && (fp->fp_defects == NO_DEFECTS) ) { sap->sa_cmd_flags &= ~(SPC_INOUT | SPC_DATA_INOUT); return (SUCCESS); }
#ifdef KERNEL /* * Ensure the defect list address is valid (user address). */ if ( ((sap->sa_flags & SA_SYSTEM_REQUEST) == 0) && !CAM_IS_KUSEG(fp->fp_addr) ) { return (EINVAL); }
/* * The format parameters structure is not set up with the length * of the defect lists as it should be. Therefore, we must copy * in the defect list header then calculate the defect list length. */ if (copyin ((caddr_t)fp->fp_addr, (caddr_t)ddh, sizeof(*ddh)) != 0) { return (EFAULT); #else (void) bcopy ((caddr_t)fp-> fp_addr, (caddr_t)ddh, sizeof(* ddh)) #endif } sap->sa_user_length = (int) ( (ddh->defect_len1 << 8) + ddh->defect_len0 + sizeof(*ddh) );
return (SUCCESS); }
A SCSI/CAM special I/O control command has been defined to provide a single standard method of implementing new SCSI/CAM special I/O commands. A subcommand member is used to determine the specific SCSI command being issued.
The SCSI/CAM special I/O control command structure can be used both in porting applications that use existing SCSI I/O control commands and when implementing new SCSI commands. Applications can be modified to use this structure to gain control over subsystem processing. For example, the SCSI/CAM special I/O command flags can be set to control error recovery and error reporting, sense data can be returned automatically by specifying a sense buffer address and length, and the command timeout and retry limit can be specified.
A member in the Special I/O Control Commands structure must be initialized to zero if a default value is desired. A nonzero member is used to override the default value.
The SCSI I/O control command and its associated structure and definitions are included in the file /usr/sys/include/io/cam/scsi_special.h. The scsi_special structure is defined as follows:
/* * Structure for Processing Special I/O Control Commands. */ struct scsi_special { U32 sp_flags; /* The special command flags */ dev_t sp_dev; /* Device major/minor number */ u_char sp_unit; /* Device logical unit number */ u_char sp_bus; /* SCSI host adapter bus number */ u_char sp_target; /* SCSI device target number */ u_char sp_lun; /* SCSI logical unit number */ u_int sp_sub_command; /* The subcommand */ U32 sp_cmd_parameter; /* Command parameter (if any) */ int sp_iop_length; /* Parameters buffer length */ caddr_t sp_iop_buffer; /* Parameters buffer address */ u_char sp_sense_length; /* Sense data buffer length */ u_char sp_sense_resid; /* Sense data residual count */ caddr_t sp_sense_buffer; /* Sense data buffer address */ int sp_user_length; /* User data buffer length */ caddr_t sp_user_buffer; /* User data buffer address */ int sp_timeout; /* Timeout for this command */ u_char sp_retry_count; /* Retrys performed on command */ u_char sp_retry_limit; /* Times to retry this command */ int sp_xfer_resid; /* Transfer residual count */ };
This structure is used with the following SCSI special I/O control command:
#define SCSI_SPECIAL _IOWR('p', 100, struct scsi_special)
The
sp_flags
member controls the actions of the SCSI/CAM special I/O interface.
The calling routine can set the
low-order 3 bits.
The other bits are reserved for use by SCSI/CAM peripheral drivers and
the SCSI/CAM special I/O interface routines.
The bits that the calling routine can set are described as
follows:
Flag Name | Description |
SA_NO_ERROR_RECOVERY | Do not perform error recovery. |
SA_NO_ERROR_LOGGING | Do not log error messages. |
SA_NO_SLEEP_INTR | Do not allow sleep interrupts. |
The sp_dev member specifies the device major/minor number pair. The sp_unit member specifies the device logical unit number. The sp_bus member specifies the SCSI host adapter bus number. The sp_target member specifies the SCSI device target number. The sp_lun member specifies the SCSI logical unit number.
These members pass the device major/minor number pair and the device bus, target, LUN, and unit information to the SCSI/CAM special I/O interface when the I/O control command is not being issued to a SCSI/CAM peripheral device driver. These members provide the necessary hooks to allow software pseudodevice drivers, such as the User Agent driver, to send requests to the SCSI/CAM special I/O interface.
The sp_sub_command member contains the SCSI/CAM special I/O subcommand code of the SCSI command to execute. This member can also be defined as an I/O control command to support backwards compatibility with preexisting SCSI I/O control commands. The SCSI/CAM special I/O interface detects an I/O control command, as opposed to a subcommand code, and coerces the arguments into the appropriate format for processing by the support routines associated with that I/O control command. The predefined subcommand codes are listed in the file /usr/sys/include/io/cam/scsi_special.h.
The sp_cmd_parameter member contains the command parameter, if any, for the SCSI special I/O command being issued. This parameter is specific to the special command processing routines and is not used directly by the SCSI/CAM special I/O interface routines.
The sp_iop_length member contains the I/O parameters buffer length and the sp_iop_buffer member contains the I/O parameters buffer address. These members contain the I/O parameters buffer address and length for those commands that require additional parameters. Special command processing routines use these members to obtain and set up additional information prior to issuing the SCSI command. For example, the SCSI FORMAT_UNIT I/O control command passes a format_params structure that describes the format, length, pattern, and interleave information for the defect list. The scmn_MakeFormatUnit support routine uses this information when creating the CDB for this command.
The sp_sense_length member contains the sense data buffer length. The sp_sense_resid member contains the sense data residual count. The sp_sense_buffer member contains the sense data buffer address.
These members contain the buffer address, length, and residual byte count for the sense data that is returned when device errors occur. If these members are specified, then the last sense data is saved in the Peripheral Device structure from which it can be obtained by the Digital-specific SCSI_GET_SENSE I/O control command.
The sp_user_length member contains the user data buffer length and the sp_user_buffer contains the user data buffer address.
These members contain the user data buffer length and address for those commands that require them. The SCSI/CAM special I/O interface performs verification, locking, and unlocking of the user pages when processing the command.
The sp_timeout member specifies the timeout for this command. This member can be specified to override the default timeout (in seconds), which is usually taken from the Special Command Entry structure.
The sp_retry_count member contains the number of retrys that were required to successfully complete the request. It is filled in by the SCSI/CAM special I/O interface after processing the command.
The sp_retry_limit member contains the maximum number of times a command is retried. The only retries automatically handled by the SCSI/CAM special I/O interface are a sense key of Unit Attention, or a SCSI bus status of Bus Busy or Reservation Conflict. All other error conditions must be handled by the calling routine.
The sp_xfer_resid member is filled in with the transfer residual byte count when a command has completed. The SCSI/CAM special I/O interface copies the cam_resid member of the SCSI I/O CCB to this member before completing the request.
The following sample function shows how to use the SCSI/CAM special I/O interface to create an I/O control command:
/*********************************************************************** * * * DoIoctl() Do An I/O Control Command. * * * * Description: * * This routine issues the specified I/O control command to the * * file descriptor associated with the CD-ROM device driver. * * * * Inputs: cmd = The I/O control command. * * argp = The command argument to pass. * * msgp = The message to display on errors. * * * * Return Value: * * Returns 0 / -1 = SUCCESS / FAILURE. * * * ***********************************************************************/ int DoIoctl (cmd, argp, msgp) int cmd; caddr_t argp; caddr_t msgp; { int status; #if defined(CAM) struct scsi_special special_cmd; [1] register struct scsi_special *sp = &special_cmd; register struct extended_sense *es; [2]
es = (struct extended_sense *)SenseBufPtr; bzero ((char *) sp, sizeof(*sp)); bzero ((char *) es, sizeof(*es)); sp->sp_sub_command = cmd; [3] sp->sp_sense_length = sizeof(*es); sp->sp_sense_buffer = (caddr_t) es; sp->sp_iop_length = ((cmd & ~(_IOC_INOUT|_IOC_VOID)) >> 16); sp->sp_iop_buffer = argp; if ((status = ioctl (CdrFd, SCSI_SPECIAL, sp)) < 0) { [4] perror (msgp); if (es->snskey) { cdbg_DumpSenseData (es); } } #else /* !defined(CAM) */ if ((status = ioctl (CdrFd, cmd, argp)) < 0) { perror (msgp); } #endif /* defined(CAM) */ return (status); }
This section contains other driver code samples that use the SCSI/CAM special I/O interface.
The following sample code shows how to use the SCSI/CAM special I/O interface to open a CDROM device from a device driver:
/********************************************************************* * * * cdrom_open() - Driver Entry Point to Open CD-ROM Device. * * * * Inputs: dev = The device major/minor number pair. * * flags = The file open flags (read/write/nodelay). * * * * Outputs: Returns 0 for Success or error code on Failure. * * * *********************************************************************/ cdrom_open (dev, flags) dev_t dev; int flags; { register PDRV_DEVICE *pd; [1] DIR_READ_CAP_DATA read_capacity; [2] DIR_READ_CAP_DATA *capacity = &read_capacity; . . . pd = GET_PDRV_PTR(dev); [3] status = cdrom_read_capacity (pd, capacity, flags); . . . return (status); } /********************************************************************** * * * cdrom_read_capacity() - Obtain Disk Capacity Information. * * * * Inputs: pd = Pointer to peripheral driver structure. * * capacity = Pointer to read capacity data buffer. * * flags = The file open flags. * * * * Outputs: Returns 0 for Success or error code on Failure. * * * **********************************************************************/ int cdrom_read_capacity (pd, capacity, flags) PDRV_DEVICE *pd; DIR_READ_CAP_DATA *capacity; int flags; { int status;
PRINTD(DEV_BUS_ID(pd->pd_dev), DEV_TARGET(pd->pd_dev), DEV_LUN(pd->pd_dev), CAMD_CDROM, [4] ("[%d/%d/%d] cdrom_read_capacity: ENTRY - pd = 0x%x, \ capacity = 0x%x, flags = 0x%x\n", DEV_BUS_ID(pd->pd_dev), DEV_TARGET(pd->pd_dev), DEV_LUN(pd->pd_dev), pd, capacity, flags));
bzero ((char *)capacity, sizeof(*capacity));
status = ccmn_SysSpecialCmd (pd->pd_dev, SCSI_READ_CAPACITY, [5] (caddr_t) capacity, flags, (CCB_SCSIIO *) 0, SA_NO_ERROR_LOGGING);
PRINTD(DEV_BUS_ID(pd->pd_dev), DEV_TARGET(pd->pd_dev), DEV_LUN(pd->pd_dev), CAMD_CDROM, ("[%d/%d/%d] cdrom_read_capacity: EXIT - status = %d (%s)\n", DEV_BUS_ID(pd->pd_dev), DEV_TARGET(pd->pd_dev), DEV_LUN(pd->pd_dev), status, cdbg_SystemStatus(status))); [6]
return (status); }
The following sample code shows how to use the SCSI/CAM special I/O interface to create a driver entry point for I/O control commands:
/********************************************************************* * * * cdrom_ioctl() - Driver Entry Point for I/O Control Commands. * * * * Inputs: dev = The device major/minor number pair. * * cmd = The I/O control command code. * * data = The I/O parameters data buffer. * * flags = The file open flags (read/write/nodelay). * * * * Outputs: Returns 0 for Success or error code on Failure. * * * *********************************************************************/ int cdrom_ioctl (dev, cmd, data, flags) dev_t dev; register int cmd; caddr_t data; int flags; { register PDRV_DEVICE *pd; [1] register DISK_SPECIFIC *cdisk; register DEV_DESC *dd; int status;
pd = GET_PDRV_PTR(dev); [2] dd = pd->pd_dev_desc; cdisk = (DISK_SPECIFIC *)pd->pd_specific;
switch (cmd) { . . /* Process Expected I/O Control Commands */ . . default: /* * Process Special I/O Control Commands. */ status = ccmn_DoSpecialCmd (dev, cmd, data, flags, [3] (CCB_SCSIIO *) 0, 0); break; } return (status); }