2 * Copyright (C) 2009-2010, Shao Miller <shao.miller@yrdsb.edu.on.ca>.
3 * Copyright 2006-2008, V.
4 * For WinAoE contact information, see http://winaoe.org/
6 * This file is part of WinVBlock, derived from WinAoE.
8 * WinVBlock is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * WinVBlock is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with WinVBlock. If not, see <http://www.gnu.org/licenses/>.
25 * Disk SCSI IRP handling.
33 #include "winvblock.h"
41 * The prototype for a disk SCSI function.
43 * @v dev Points to the disk's device for the SCSI request.
44 * @v irp Points to the IRP.
45 * @v disk Points to the disk for the SCSI request.
46 * @v srb Points to the SCSI request block.
47 * @v cdb Points to the command descriptor block.
48 * @v completion Points to a boolean for whether the IRP has
49 * been completed or not.
50 * @ret NTSTATUS The status of the SCSI operation.
52 typedef NTSTATUS STDCALL disk_scsi__func(
56 IN PSCSI_REQUEST_BLOCK,
58 OUT winvblock__bool_ptr
61 /* Forward declarations. */
62 disk_scsi__func disk_scsi__read_write_;
63 disk_scsi__func disk_scsi__verify_;
64 disk_scsi__func disk_scsi__read_capacity_;
65 disk_scsi__func disk_scsi__read_capacity_16_;
66 disk_scsi__func disk_scsi__mode_sense_;
67 disk_scsi__func disk_scsi__read_toc_;
68 WV_F_DEV_SCSI disk_scsi__dispatch;
70 #if _WIN32_WINNT <= 0x0600
71 # if 0 /* FIXME: To build with WINDDK 6001.18001 */
75 typedef union _EIGHT_BYTE
79 winvblock__uint8 Byte0;
80 winvblock__uint8 Byte1;
81 winvblock__uint8 Byte2;
82 winvblock__uint8 Byte3;
83 winvblock__uint8 Byte4;
84 winvblock__uint8 Byte5;
85 winvblock__uint8 Byte6;
86 winvblock__uint8 Byte7;
88 ULONGLONG AsULongLong;
89 } __attribute__ ( ( __packed__ ) ) EIGHT_BYTE, *PEIGHT_BYTE;
93 # endif /* To build with WINDDK 6001.18001 */
95 # if _WIN32_WINNT < 0x0500
96 # if 0 /* FIXME: To build with WINDDK 6001.18001 */
100 typedef struct _READ_CAPACITY_DATA_EX
102 LARGE_INTEGER LogicalBlockAddress;
104 } __attribute__ ( ( __packed__ ) ) READ_CAPACITY_DATA_EX,
105 *PREAD_CAPACITY_DATA_EX;
109 # endif /* To build with WINDDK 6001.18001 */
110 # endif /* _WIN32_WINNT < 0x0500 */
115 typedef struct _DISK_CDB16
117 winvblock__uint8 OperationCode;
118 winvblock__uint8 Reserved1:3;
119 winvblock__uint8 ForceUnitAccess:1;
120 winvblock__uint8 DisablePageOut:1;
121 winvblock__uint8 Protection:3;
122 winvblock__uint8 LogicalBlock[8];
123 winvblock__uint8 TransferLength[4];
124 winvblock__uint8 Reserved2;
125 winvblock__uint8 Control;
126 } __attribute__ ( ( __packed__ ) ) DISK_CDB16, *PDISK_CDB16;
131 # define REVERSE_BYTES_QUAD(Destination, Source) { \
132 PEIGHT_BYTE d = (PEIGHT_BYTE)(Destination); \
133 PEIGHT_BYTE s = (PEIGHT_BYTE)(Source); \
134 d->Byte7 = s->Byte0; \
135 d->Byte6 = s->Byte1; \
136 d->Byte5 = s->Byte2; \
137 d->Byte4 = s->Byte3; \
138 d->Byte3 = s->Byte4; \
139 d->Byte2 = s->Byte5; \
140 d->Byte1 = s->Byte6; \
141 d->Byte0 = s->Byte7; \
143 #endif /* if _WIN32_WINNT <= 0x0600 */
145 static NTSTATUS STDCALL disk_scsi__read_write_(
148 IN disk__type_ptr disk,
149 IN PSCSI_REQUEST_BLOCK srb,
151 OUT winvblock__bool_ptr completion
153 ULONGLONG start_sector;
154 winvblock__uint32 sector_count;
155 NTSTATUS status = STATUS_SUCCESS;
157 if (cdb->AsByte[0] == SCSIOP_READ16 || cdb->AsByte[0] == SCSIOP_WRITE16) {
160 &(((PDISK_CDB16) cdb)->LogicalBlock[0])
164 &(((PDISK_CDB16) cdb )->TransferLength[0])
167 start_sector = (cdb->CDB10.LogicalBlockByte0 << 24) +
168 (cdb->CDB10.LogicalBlockByte1 << 16) +
169 (cdb->CDB10.LogicalBlockByte2 << 8 ) +
170 cdb->CDB10.LogicalBlockByte3;
171 sector_count = (cdb->CDB10.TransferBlocksMsb << 8) +
172 cdb->CDB10.TransferBlocksLsb;
174 if (start_sector >= disk->LBADiskSize) {
175 DBG("Fixed sector_count (start_sector off disk)!!\n");
179 (start_sector + sector_count > disk->LBADiskSize) &&
182 DBG("Fixed sector_count (start_sector + sector_count off disk)!!\n");
183 sector_count = (winvblock__uint32) (disk->LBADiskSize - start_sector);
185 if (sector_count * disk->SectorSize > srb->DataTransferLength) {
186 DBG("Fixed sector_count (DataTransferLength " "too small)!!\n");
187 sector_count = srb->DataTransferLength / disk->SectorSize;
189 if (srb->DataTransferLength % disk->SectorSize != 0)
190 DBG("DataTransferLength not aligned!!\n");
191 if (srb->DataTransferLength > sector_count * disk->SectorSize)
192 DBG("DataTransferLength too big!!\n");
194 srb->DataTransferLength = sector_count * disk->SectorSize;
195 srb->SrbStatus = SRB_STATUS_SUCCESS;
196 if (sector_count == 0) {
197 irp->IoStatus.Information = 0;
203 (winvblock__uint8_ptr) srb->DataBuffer -
204 (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)
205 ) + (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
209 status = STATUS_INSUFFICIENT_RESOURCES;
210 irp->IoStatus.Information = 0;
214 if (cdb->AsByte[0] == SCSIOP_READ || cdb->AsByte[0] == SCSIOP_READ16) {
220 ((winvblock__uint8_ptr) srb->DataBuffer -
221 (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)) +
222 (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
234 ((winvblock__uint8_ptr) srb->DataBuffer -
235 (winvblock__uint8_ptr) MmGetMdlVirtualAddress(irp->MdlAddress)) +
236 (winvblock__uint8_ptr) MmGetSystemAddressForMdlSafe(
243 if (status != STATUS_PENDING)
248 static NTSTATUS STDCALL disk_scsi__verify_(
251 IN disk__type_ptr disk,
252 IN PSCSI_REQUEST_BLOCK srb,
254 OUT winvblock__bool_ptr completion
256 LONGLONG start_sector;
257 winvblock__uint32 sector_count;
259 if (cdb->AsByte[0] == SCSIOP_VERIFY16) {
262 &(((PDISK_CDB16) cdb)->LogicalBlock[0])
266 &(((PDISK_CDB16) cdb)->TransferLength[0])
269 start_sector = (cdb->CDB10.LogicalBlockByte0 << 24) +
270 (cdb->CDB10.LogicalBlockByte1 << 16) +
271 (cdb->CDB10.LogicalBlockByte2 << 8) +
272 cdb->CDB10.LogicalBlockByte3;
273 sector_count = (cdb->CDB10.TransferBlocksMsb << 8) +
274 cdb->CDB10.TransferBlocksLsb;
277 srb->DataTransferLength = sector_count * SECTORSIZE;
279 srb->SrbStatus = SRB_STATUS_SUCCESS;
280 return STATUS_SUCCESS;
283 static NTSTATUS STDCALL disk_scsi__read_capacity_(
286 IN disk__type_ptr disk,
287 IN PSCSI_REQUEST_BLOCK srb,
289 OUT winvblock__bool_ptr completion
291 winvblock__uint32 temp = disk->SectorSize;
292 PREAD_CAPACITY_DATA data = (PREAD_CAPACITY_DATA) srb->DataBuffer;
294 REVERSE_BYTES(&data->BytesPerBlock, &temp);
295 if ((disk->LBADiskSize - 1) > 0xffffffff) {
296 data->LogicalBlockAddress = -1;
298 temp = (winvblock__uint32) (disk->LBADiskSize - 1);
299 REVERSE_BYTES(&data->LogicalBlockAddress, &temp);
301 irp->IoStatus.Information = sizeof (READ_CAPACITY_DATA);
302 srb->SrbStatus = SRB_STATUS_SUCCESS;
303 return STATUS_SUCCESS;
306 static NTSTATUS STDCALL disk_scsi__read_capacity_16_(
309 IN disk__type_ptr disk,
310 IN PSCSI_REQUEST_BLOCK srb,
312 OUT winvblock__bool_ptr completion
314 winvblock__uint32 temp;
317 temp = disk->SectorSize;
319 &(((PREAD_CAPACITY_DATA_EX) srb->DataBuffer)->BytesPerBlock),
322 big_temp = disk->LBADiskSize - 1;
324 &(((PREAD_CAPACITY_DATA_EX) srb->DataBuffer)->
325 LogicalBlockAddress.QuadPart),
328 irp->IoStatus.Information = sizeof (READ_CAPACITY_DATA_EX);
329 srb->SrbStatus = SRB_STATUS_SUCCESS;
330 return STATUS_SUCCESS;
333 static NTSTATUS STDCALL disk_scsi__mode_sense_(
336 IN disk__type_ptr disk,
337 IN PSCSI_REQUEST_BLOCK srb,
339 OUT winvblock__bool_ptr completion
341 PMODE_PARAMETER_HEADER mode_param_header;
342 static MEDIA_TYPE media_types[WvDiskMediaTypes] =
343 { RemovableMedia, FixedMedia, RemovableMedia };
345 if (srb->DataTransferLength < sizeof (MODE_PARAMETER_HEADER)) {
346 srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
347 return STATUS_SUCCESS;
349 mode_param_header = (PMODE_PARAMETER_HEADER) srb->DataBuffer;
350 RtlZeroMemory(mode_param_header, srb->DataTransferLength);
351 mode_param_header->ModeDataLength = sizeof (MODE_PARAMETER_HEADER);
352 mode_param_header->MediumType = media_types[disk->Media];
353 mode_param_header->BlockDescriptorLength = 0;
354 srb->DataTransferLength = sizeof (MODE_PARAMETER_HEADER);
355 irp->IoStatus.Information = sizeof (MODE_PARAMETER_HEADER);
356 srb->SrbStatus = SRB_STATUS_SUCCESS;
357 return STATUS_SUCCESS;
360 static NTSTATUS STDCALL disk_scsi__read_toc_(
363 IN disk__type_ptr disk,
364 IN PSCSI_REQUEST_BLOCK srb,
366 OUT winvblock__bool_ptr completion
368 /* With thanks to Olof Lagerkvist's ImDisk source. */
369 PCDROM_TOC table_of_contents = (PCDROM_TOC) srb->DataBuffer;
371 if (srb->DataTransferLength < sizeof (CDROM_TOC)) {
372 irp->IoStatus.Information = 0;
373 srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
374 return STATUS_BUFFER_TOO_SMALL;
376 RtlZeroMemory(table_of_contents, sizeof (CDROM_TOC));
378 table_of_contents->FirstTrack = 1;
379 table_of_contents->LastTrack = 1;
380 table_of_contents->TrackData[0].Control = 4;
381 irp->IoStatus.Information = sizeof (CDROM_TOC);
382 srb->SrbStatus = SRB_STATUS_SUCCESS;
383 return STATUS_SUCCESS;
386 NTSTATUS STDCALL disk_scsi__dispatch(
391 disk__type_ptr disk = disk__get_ptr(dev);
392 PIO_STACK_LOCATION io_stack_loc = IoGetCurrentIrpStackLocation(irp);
393 PSCSI_REQUEST_BLOCK srb = io_stack_loc->Parameters.Scsi.Srb;
394 NTSTATUS status = STATUS_SUCCESS;
396 winvblock__bool completion = FALSE;
398 srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
399 srb->ScsiStatus = SCSISTAT_GOOD;
401 cdb = (PCDB) srb->Cdb;
403 irp->IoStatus.Information = 0;
405 DBG("Invalid Lun!!\n");
409 case SRB_FUNCTION_EXECUTE_SCSI:
410 switch (cdb->AsByte[0]) {
411 case SCSIOP_TEST_UNIT_READY:
412 srb->SrbStatus = SRB_STATUS_SUCCESS;
419 status = disk_scsi__read_write_(
430 case SCSIOP_VERIFY16:
431 status = disk_scsi__verify_(
441 case SCSIOP_READ_CAPACITY:
442 status = disk_scsi__read_capacity_(
452 case SCSIOP_READ_CAPACITY16:
453 status = disk_scsi__read_capacity_16_(
463 case SCSIOP_MODE_SENSE:
464 status = disk_scsi__mode_sense_(
474 case SCSIOP_MEDIUM_REMOVAL:
475 irp->IoStatus.Information = 0;
476 srb->SrbStatus = SRB_STATUS_SUCCESS;
477 status = STATUS_SUCCESS;
480 case SCSIOP_READ_TOC:
481 status = disk_scsi__read_toc_(
492 DBG("Invalid SCSIOP (%02x)!!\n", cdb->AsByte[0]);
493 srb->SrbStatus = SRB_STATUS_ERROR;
494 status = STATUS_NOT_IMPLEMENTED;
496 break; /* SRB_FUNCTION_EXECUTE_SCSI */
498 case SRB_FUNCTION_IO_CONTROL:
499 srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
502 case SRB_FUNCTION_CLAIM_DEVICE:
503 srb->DataBuffer = dev->Self;
506 case SRB_FUNCTION_RELEASE_DEVICE:
507 ObDereferenceObject(dev->Self);
510 case SRB_FUNCTION_SHUTDOWN:
511 case SRB_FUNCTION_FLUSH:
512 srb->SrbStatus = SRB_STATUS_SUCCESS;
516 DBG("Invalid SRB FUNCTION (%08x)!!\n", code);
517 status = STATUS_NOT_IMPLEMENTED;
522 irp->IoStatus.Status = status;
523 if (status != STATUS_PENDING)
524 IoCompleteRequest(irp, IO_NO_INCREMENT);