|
|
1.1 ! root 1: Win32 applications can communicate directly with SCSI devices via ! 2: IOCtls. The Win32 API that is used to implement an IOCtl request is ! 3: DeviceIoControl. In order to make the DeviceIoControl call, a valid ! 4: handle must be obtained to the device. The handle is obtained using ! 5: the Win32 API, CreateFile. ! 6: ! 7: There are five IOCtls's that the SCSI port driver supports. These ! 8: are IOCTL_SCSI_GET_INQUIRY_DATA, IOCTL_SCSI_GET_CAPABILITIES, ! 9: IOCTL_SCSI_PASS_THROUGH, IOCTL_SCSI_PASS_THROUGH_DIRECT and ! 10: IOCTL_SCSI_MINIPORT. The first four are demonstrated in this sample. ! 11: ! 12: IOCTL_SCSI_GET_INQUIRY_DATA ! 13: ! 14: The first IOCtl, IOCTL_SCSI_GET_INQUIRY_DATA, fills out a ! 15: SCSI_ADAPTER_BUS_INFO structure for all devices that are on the SCSI ! 16: bus. The structure member, BusData, is a structure of type ! 17: SCSI_BUS_DATA. It contains an offset to the inquiry data which is ! 18: also stored as a structure, SCSI_INQUIRY_DATA. See NTDDSCSI.H for ! 19: more details about these structures. One interesting ! 20: SCSI_INQUIRY_DATA structure member is DeviceClaimed. This tells the ! 21: caller whether or not this device has been claimed by a class driver. ! 22: ! 23: IOCTL_SCSI_GET_CAPABILITIES ! 24: ! 25: IOCTL_SCSI_GET_CAPABILITES fills out an IO_SCSI_CAPABILITES structure ! 26: (also found in NTDDSCSI.H). This structure contains valuable ! 27: information. Two items of note are the MaximumTransferLength, which ! 28: is the largest data block that can be handled in a single SRB as ! 29: defined by the miniport driver, and the AlignmentMask which defines ! 30: the alignment requirements of the current CPU platform. For an x86 ! 31: system, the AlignmentMask is 0 as there is no alignment requirement. ! 32: ! 33: IOCTL_SCSI_PASS_THROUGH, IOCTL_SCSI_PASS_THROUGH_DIRECT ! 34: ! 35: The two remaining IOCtls, IOCTL_SCSI_PASS_THROUGH and ! 36: IOCTL_SCSI_PASS_THROUGH_DIRECT, allow SCSI CDBs (Command Descriptor ! 37: Blocks) to be sent from a Win32 application to a SCSI device. ! 38: Depending on which IOCtl is used, a corresponding pass through ! 39: structure is filled out by the Win32 application. For ! 40: IOCTL_SCSI_PASS_THROUGH, the structure is SCSI_PASS_THROUGH. For ! 41: IOCTL_SCSI_PASS_THROUGH_DIRECT, the structure is ! 42: SCSI_PASS_THROUGH_DIRECT. See NTDDSCSI.H for more details about ! 43: these structures. ! 44: ! 45: The two structures SCSI_PASS_THROUGH and SCSI_PASS_THROUGH_DIRECT are ! 46: virtually identical. The only difference is that the data buffer for ! 47: the SCSI_PASS_THROUGH structure must be contiguous with the ! 48: structure. This structure member is called DataBufferOffset and is ! 49: of type ULONG. The data buffer for the SCSI_PASS_THROUGH_DIRECT ! 50: structure does not have to be contiguous with the structure. This ! 51: structure member is called DataBuffer and is of type PVOID. This is ! 52: the only difference between the two structures. ! 53: ! 54: IOCTL_SCSI_PASS_THROUGH and IOCTL_SCSI_PASS_THROUGH_DIRECT are not ! 55: substitues for a SCSI class driver and should only be used as a last ! 56: resort. In situations in which CDBs need to be sent to a SCSI ! 57: device, the best solution is to write a SCSI class driver that will ! 58: send these CDBs to the respective devices. ! 59: ! 60: Obtaining a handle to a device ! 61: ! 62: Before any IOCtls can be sent to a SCSI device, a handle for the ! 63: device must be obtained. The Win32 API, CreateFile, is used to ! 64: obtain this handle and to define the sharing mode and the access ! 65: mode. The key is to supply the proper filename for the device that ! 66: is to be opened. It is possible to obtain a handle to the device via ! 67: either the SCSI port driver or the appropriate SCSI class driver. If ! 68: the handle is obtained via the class driver, then the filename to be ! 69: opened is defined by the class driver. In the case of fixed disks ! 70: and optical storage devices, the filename is the corresponding drive ! 71: letter. "\\.\I:" can be used to obtain a handle to a CD-ROM drive ! 72: that has been mapped to I:. In the case of SCSI printers, the ! 73: filename is "LPTn", where n = 1, 2, etc. For all remaining SCSI ! 74: devices, the appropriate name is defined by the SCSI class driver. ! 75: Typically the name is related to the device (e.g. - "\\.\Scanner0", ! 76: "\\.\Tape1"). Except for COMn and LPTn, all device filenames must be ! 77: prepended with a \\.\ to inform the I/O Manager that this is a ! 78: device. ! 79: ! 80: If the device is unclaimed by a SCSI class driver, then a handle to ! 81: the SCSI port driver is required. The filename in this case is ! 82: "\\.\ScsiN:", where N = 0, 1, 2, etc. The number N corresponds to ! 83: the SCSI host adapter card number that controls the desired SCSI ! 84: device. If the device is claimed by a class driver and one of the ! 85: pass through IOCtls is to be used, then the filename opened must be ! 86: the one that is defined by the class driver. While a handle to the ! 87: SCSI port driver can be obtained, subsequent calls to DeviceIoControl ! 88: with a control code of IOCTL_SCSI_PASS_THROUGH (or ! 89: IOCTL_SCSI_PASS_THROUGH_DIRECT) that reference a claimed device will ! 90: always fail with ERROR_INVALID_PARAMETER. ! 91: ! 92: Initializing the structures ! 93: ! 94: Once a valid handle is obtained to a SCSI device, then appropriate ! 95: input/output buffers for the requested IOCtl must be allocated and ! 96: defined in the DeviceIoControl parameters. In the case of ! 97: IOCTL_SCSI_GET_INQUIRY_DATA and IOCTL_SCSI_GET_CAPABILITIES, no data ! 98: is sent to the device. Data is only read from the device. Therefore, ! 99: the pointer to the input buffer is NULL and it's length is 0. As for ! 100: the IOCTL_SCSI_GET_CAPABILITES output buffer, the buffer size must be ! 101: at least as large as the IO_SCSI_CAPABILITES structure. The output ! 102: buffer for IOCTL_SCSI_GET_INQUIRY_DATA should be quite large as each ! 103: SCSI device on the bus will provide data that will fill three ! 104: structures for each device, SCSI_ADAPTER_BUS_INFO, SCSI_BUS_DATA and ! 105: SCSI_INQUIRY_DATA. For the two pass through IOCtls, ! 106: IOCTL_SCSI_PASS_THROUGH and IOCTL_SCSI_PASS_THROUGH_DIRECT, both the ! 107: input and output buffers can vary in size depending on the sense info ! 108: buffer size and the data buffer size. In all cases, the input and ! 109: output buffer sizes must be at least the size of the ! 110: SCSI_PASS_THROUGH (or SCSI_PASS_THROUGH_DIRECT) structure. If the ! 111: SCSI port driver detects that one of the two buffers is too small, ! 112: then the DeviceIoControl API will fail with ERROR_INVALID_PARAMETER. ! 113: ! 114: Once the appropriate input/output buffers have been allocated, then, ! 115: in the case of IOCTL_SCSI_PASS_THROUGH and ! 116: IOCTL_SCSI_PASS_THROUGH_DIRECT, the appropriate structure must be ! 117: initialized. The SCSI_PASS_THROUGH structure is defined in ! 118: NTDDSCSI.H as follows : ! 119: ! 120: typedef struct _SCSI_PASS_THROUGH { ! 121: USHORT Length; ! 122: UCHAR ScsiStatus; ! 123: UCHAR PathId; ! 124: UCHAR TargetId; ! 125: UCHAR Lun; ! 126: UCHAR CdbLength; ! 127: UCHAR SenseInfoLength; ! 128: UCHAR DataIn; ! 129: ULONG DataTransferLength; ! 130: ULONG TimeOutValue; ! 131: ULONG DataBufferOffset; ! 132: ULONG SenseInfoOffset; ! 133: UCHAR Cdb[16]; ! 134: }SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; ! 135: ! 136: The Length is the size of the the SCSI_PASS_THROUGH structure. The ! 137: ScsiStatus should be initialized to 0. The status of the requested ! 138: SCSI operation is returned in this structure member. The possible ! 139: SCSI statuses are defined in SCSI.H and are of the form SCSISTAT_xxx. ! 140: The PathId is the bus number for the SCSI host adapter that controls ! 141: the SCSI device in question. Typically, this value will be 0, but ! 142: there are SCSI host adapters available that contain more than one ! 143: SCSI bus. The TargetId and Lun are the SCSI ID number and logical ! 144: unit number for the device. If the handle was obtained for a claimed ! 145: device, then the PathId, TargetId and Lun as defined in this ! 146: structure will be ignored and the appropriate class driver will ! 147: provide these three items. If the handle was obtained via the SCSI ! 148: port driver, then the PathId, TargetId and Lun must be correct for ! 149: the device intended. The CdbLength is the length of the CDB. Typical ! 150: values are 6, 10, 12 up to the maximum of 16. The SenseInfoLength is ! 151: the length of the SenseInfo buffer. DataIn has three possible values ! 152: which are defined in NTDDSCSI.H; SCSI_IOCTL_DATA_OUT, ! 153: SCSI_IOCTL_DATA_IN and SCSI_IOCTL_DATA_UNSPECIFIED. ! 154: SCSI_IOCTL_DATA_UNSPECIFIED should be used only if the appropriate ! 155: SCSI miniport driver supports its usage. The DataTransferLength is ! 156: the size of the data buffer. The TimeOutValue is the length of time, ! 157: in milliseconds, until a time out error should occur. This can range ! 158: from 0 to a maximum of 30 minutes (108000 seconds). The ! 159: DataBufferOffset is the offset of the data buffer from the beginning ! 160: of the pass through structure. For the SCSI_PASS_THROUGH_DIRECT ! 161: structure, this value is no longer an offset, but rather is a pointer ! 162: to a data buffer. The SenseInfoOffset is similarly an offset to the ! 163: SenseInfo buffer from the beginning of the pass through structure. ! 164: Finally, the sixteen remaining bytes are for the CDB data. The ! 165: format of this data must conform to the SCSI-2 standard as defined by ! 166: ANSI. ! 167: ! 168: Buffer Alignment ! 169: ! 170: The issue of buffer alignment is handled using two possible methods ! 171: for assuring that data buffers are aligned on the appropriate ! 172: boundaries. The first method uses the compiler to align the buffer ! 173: on the correct boundary. The structure ! 174: SCSI_PASS_THROUGH_WITH_BUFFERS contains a member, Filler, that is of ! 175: type ULONG. The compiler aligns Filler on a ULONG (double word) ! 176: boundary. The structure member that follows Filler, ucSenseBuf, is ! 177: now also on a double word boundary. The ucSenseBuf array is of a ! 178: size that is a multiple of a double word, and so this makes the last ! 179: structure member, ucDataBuf, also begin on a double word boundary. ! 180: ! 181: The other method to ensure buffer alignment is demonstrated in the ! 182: AllocateAlignedBuffer procedure. This works on the fact that a ! 183: buffer that is aligned on a certain boundary will have 0's in it's ! 184: least significant bits depending on the aligment requirements. A ! 185: buffer allocation request is made using the C runtime call, malloc, ! 186: that is the size of the requested buffer plus the alignment mask ! 187: value as returned by IOCTL_SCSI_GET_CAPABILITIES. A pointer is ! 188: manipulated so that it is pointing to the first possible address in ! 189: the buffer that meets the alignment requirements. ! 190: ! 191: METHOD_BUFFERED ! 192: ! 193: The various IOCtls that are demonstrated in this sample are defined ! 194: by the SCSI port driver (see NTDDSCSI.H for details). The memory ! 195: buffering is METHOD_BUFFERED. What this means if the I/O manager ! 196: examines the length of the input buffer and the length of the output ! 197: buffer as defined by the DeviceIoControl parameters and allocates a ! 198: contiguous buffer that is the size of the larger of the two buffers. ! 199: On entry, the contents of the Win32 application's input buffer are ! 200: copied to the buffer that was allocated by the I/O manager. The ! 201: amount of data copied is controlled by the input buffer lenght. The ! 202: I/O manager then makes calls the appropriate SCSI class driver's (or ! 203: port driver's) DeviceIoControl routine passing it the new buffer. On ! 204: exit, the reverse process occurs and the data in the new buffer is ! 205: copied back into the Win32 application's output buffer as defined by ! 206: the output buffer length. This is not user configurable. ! 207: ! 208: Running the SPTI.EXE sample ! 209: ! 210: Two command line parameters can be used with SPTI.EXE. The first ! 211: parameter is mandatory. It is the name of the device to be opened. ! 212: Typical values for this are drive letters such as C:, or device ! 213: names as defined by a class driver such as Scanner0, or the SCSI port ! 214: driver name, ScsiN:, where N = 0, 1, 2, etc. ! 215: ! 216: The second parameter is optional and is used to set the access flags ! 217: and the sector size. The default is READ/WRITE mode and a sector ! 218: size of 512. A value of 'r' makes the mode read-only. A value of ! 219: 'w' makes the mode write-only. A value of 'c' makes the mode ! 220: read-only and makes the sector size 2048. Only optical storage ! 221: devices would use the 'c' parameter.
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.