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