mirror of
https://github.com/Wack0/entii-for-workcubes.git
synced 2025-12-23 21:05:07 -05:00
2254 lines
71 KiB
C
2254 lines
71 KiB
C
// USB mass storage support via usblow
|
|
// Despite USB mass storage being SCSI, we will not be using NT scsi drivers.
|
|
#define DEVL 1
|
|
#include <ntddk.h>
|
|
#include <hal.h>
|
|
#include <ntstatus.h>
|
|
#include <ntddcdrm.h>
|
|
#include <ntdddisk.h>
|
|
#include <ntddscsi.h>
|
|
#include <stdio.h>
|
|
#include "usblowms.h"
|
|
#include "usblow.h"
|
|
#include "usbms.h"
|
|
#include "iosapi.h"
|
|
#include "zwdd.h"
|
|
#include "runtime.h"
|
|
|
|
#define SIGNATURE_PARTITION 'UPRT'
|
|
#define SIGNATURE_DISK 'UDSK'
|
|
|
|
typedef enum {
|
|
USBMS_DISK_UNKNOWN,
|
|
USBMS_DISK_FLOPPY,
|
|
USBMS_DISK_CDROM,
|
|
USBMS_DISK_OTHER_FIXED,
|
|
USBMS_DISK_OTHER_REMOVABLE
|
|
} USBMS_DISK_TYPE, *PUSBMS_DISK_TYPE;
|
|
|
|
typedef struct _USBMS_EXTENSION USBMS_EXTENSION, *PUSBMS_EXTENSION;
|
|
|
|
typedef struct _USBMS_PARTITION {
|
|
ULONG Signature;
|
|
struct _USBMS_PARTITION * NextPartition;
|
|
PUSBMS_EXTENSION PhysicalDisk;
|
|
LARGE_INTEGER PartitionLength;
|
|
LARGE_INTEGER StartingOffset;
|
|
// Extra stuff needed for PARTITION_INFORMATION
|
|
ULONG HiddenSectors;
|
|
ULONG PartitionNumber;
|
|
UCHAR PartitionType;
|
|
} USBMS_PARTITION, *PUSBMS_PARTITION;
|
|
|
|
enum {
|
|
COUNT_EMERGENCY_BLOCKS = 32
|
|
};
|
|
|
|
struct _USBMS_CONTROLLER;
|
|
|
|
typedef struct _USBMS_LOCK_CONTEXT {
|
|
NTSTATUS Status;
|
|
KEVENT Event;
|
|
#if 0
|
|
UCHAR Lun;
|
|
PUCHAR Buffer;
|
|
ULONG Length;
|
|
PUCHAR Cb;
|
|
UCHAR CbLength;
|
|
union {
|
|
BOOLEAN Write;
|
|
PULONG TransferredLength;
|
|
};
|
|
PUCHAR CswStatus;
|
|
PULONG CswDataResidue;
|
|
ULONG SecondsTimeout;
|
|
#endif
|
|
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIRP Irp;
|
|
BOOLEAN InUse;
|
|
} USBMS_LOCK_CONTEXT, *PUSBMS_LOCK_CONTEXT;
|
|
|
|
typedef void (*USBMS_LOCK_CALLBACK)(struct _USBMS_CONTROLLER* Controller, PUSBMS_LOCK_CONTEXT Context);
|
|
|
|
typedef struct _USBMS_WAIT_CONTROL_BLOCK {
|
|
KDEVICE_QUEUE_ENTRY DeviceQueueEntry;
|
|
USBMS_LOCK_CALLBACK Callback;
|
|
PUSBMS_LOCK_CONTEXT Context;
|
|
} USBMS_WAIT_CONTROL_BLOCK, *PUSBMS_WAIT_CONTROL_BLOCK;
|
|
|
|
typedef struct _USBMS_CONTROLLER {
|
|
IOS_USB_HANDLE DeviceHandle;
|
|
PVOID Buffer;
|
|
ULONG MaxSize;
|
|
UCHAR EndpointIn;
|
|
UCHAR EndpointOut;
|
|
UCHAR Interface;
|
|
UCHAR CbwTag;
|
|
KDEVICE_QUEUE LockQueue;
|
|
USBMS_WAIT_CONTROL_BLOCK EmergencyBlocks[COUNT_EMERGENCY_BLOCKS];
|
|
USBMS_LOCK_CONTEXT StateContext[COUNT_EMERGENCY_BLOCKS];
|
|
} USBMS_CONTROLLER, *PUSBMS_CONTROLLER;
|
|
|
|
struct _USBMS_EXTENSION {
|
|
ULONG Signature;
|
|
// Workaround for changer.sys bug and probably some other stupid drivers too:
|
|
// changer.sys expects to see a scsiclass DEVICE_EXTENSION on any FILE_DEVICE_CD_ROM device.
|
|
// and expects to see a device object at ->PortDeviceObject (offset 4) there.
|
|
// so, put our device object pointer here, so it doesn't try to use something else as a device object!
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PCONTROLLER_OBJECT Controller;
|
|
ULONG SectorSize;
|
|
ULONG SectorShift;
|
|
ULONG SectorCount;
|
|
BOOLEAN WriteProtected;
|
|
BOOLEAN DriveNotReady;
|
|
UCHAR Lun;
|
|
ULONG DiskNumber;
|
|
PUSBMS_PARTITION NextPartition;
|
|
KDPC FinishDpc;
|
|
};
|
|
|
|
typedef struct _USBMS_WORK_ITEM {
|
|
WORK_QUEUE_ITEM WorkItem;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIRP Irp;
|
|
PUSBMS_EXTENSION ExtDisk;
|
|
} USBMS_WORK_ITEM, *PUSBMS_WORK_ITEM;
|
|
|
|
// PARTITION_INFORMATION should be 64 bits aligned
|
|
_Static_assert(sizeof(PARTITION_INFORMATION) == 0x20);
|
|
|
|
// USB timeout stuff:
|
|
// assumption: as a mass storage device, nothing's spinlooping waiting on us
|
|
// BUGBUG: any timeouting IPC request *will* leak memory :(
|
|
// but it'll also leak memory on the IOS side, so...
|
|
//static ULONG s_SecondsTimeout = USBSTORAGE_TIMEOUT;
|
|
#define MS_TO_TIMEOUT(ms) ((ms) * 10000)
|
|
|
|
// USB controller ARC path.
|
|
// Must be synchronised with arcfw\source\arcdisk.c
|
|
static char s_ControllerPath[] = "scsi(0)";
|
|
|
|
static PUSBMS_LOCK_CONTEXT MspGetStateContext(PUSBMS_CONTROLLER Controller) {
|
|
for (ULONG i = 0; i < COUNT_EMERGENCY_BLOCKS; i++) {
|
|
PUSBMS_LOCK_CONTEXT ctx = &Controller->StateContext[i];
|
|
if (!ctx->InUse) {
|
|
KeInitializeEvent(&ctx->Event, SynchronizationEvent, FALSE);
|
|
ctx->Status = STATUS_SUCCESS;
|
|
ctx->InUse = 1;
|
|
return ctx;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void MspReleaseStateContext(PUSBMS_LOCK_CONTEXT ctx) {
|
|
ctx->InUse = 0;
|
|
//ctx->TransferredLength = NULL;
|
|
}
|
|
|
|
static NTSTATUS MspLockController(USBMS_LOCK_CALLBACK callback, PUSBMS_CONTROLLER controller, PUSBMS_LOCK_CONTEXT context) {
|
|
// Allocate a control block to store the callback.
|
|
PUSBMS_WAIT_CONTROL_BLOCK Block = (PUSBMS_WAIT_CONTROL_BLOCK) ExAllocatePool(NonPagedPool, sizeof(USBMS_WAIT_CONTROL_BLOCK));
|
|
if (Block == NULL) {
|
|
// Pick out the first emergency block with a null callback.
|
|
for (ULONG i = 0; i < COUNT_EMERGENCY_BLOCKS; i++) {
|
|
if (controller->EmergencyBlocks[i].Callback != NULL) continue;
|
|
Block = &controller->EmergencyBlocks[i];
|
|
break;
|
|
}
|
|
// If none were found, return insufficient resources.
|
|
if (Block == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(Block, sizeof(*Block));
|
|
Block->Callback = callback;
|
|
Block->Context = context;
|
|
|
|
|
|
// Go to DISPATCH_LEVEL when calling device queue related functions
|
|
KIRQL CurrentIrql;
|
|
KeRaiseIrql(DISPATCH_LEVEL, &CurrentIrql);
|
|
BOOLEAN Result = KeInsertDeviceQueue(&controller->LockQueue, &Block->DeviceQueueEntry);
|
|
KeLowerIrql(CurrentIrql);
|
|
|
|
if (!Result) {
|
|
// The controller is not busy.
|
|
// While a lock callback is present, call it and free the block.
|
|
do {
|
|
Block->Callback(controller, Block->Context);
|
|
|
|
if (Block >= &controller->EmergencyBlocks[0] && Block < &controller->EmergencyBlocks[COUNT_EMERGENCY_BLOCKS]) {
|
|
Block->Callback = NULL;
|
|
} else ExFreePool(Block);
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &CurrentIrql);
|
|
Block = (PUSBMS_WAIT_CONTROL_BLOCK) KeRemoveDeviceQueue(&controller->LockQueue);
|
|
KeLowerIrql(CurrentIrql);
|
|
} while (Block != NULL);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
static void MspUnlockController(PUSBMS_CONTROLLER controller) {
|
|
while (TRUE) {
|
|
KIRQL CurrentIrql;
|
|
KeRaiseIrql(DISPATCH_LEVEL, &CurrentIrql);
|
|
PUSBMS_WAIT_CONTROL_BLOCK Block = (PUSBMS_WAIT_CONTROL_BLOCK) KeRemoveDeviceQueue(&controller->LockQueue);
|
|
KeLowerIrql(CurrentIrql);
|
|
|
|
if (Block == NULL) break;
|
|
|
|
Block->Callback(controller, Block->Context);
|
|
|
|
if (Block >= &controller->EmergencyBlocks[0] && Block < &controller->EmergencyBlocks[COUNT_EMERGENCY_BLOCKS]) {
|
|
Block->Callback = NULL;
|
|
} else ExFreePool(Block);
|
|
}
|
|
}
|
|
|
|
NTSTATUS MspClearHalt(IOS_USB_HANDLE DeviceHandle, UCHAR Endpoint) {
|
|
return UlTransferControlMessage(
|
|
DeviceHandle,
|
|
(USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_ENDPOINT),
|
|
USB_REQ_CLEARFEATURE,
|
|
USB_FEATURE_ENDPOINT_HALT,
|
|
Endpoint,
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
NTSTATUS MspCtrlMsgTimeout(IOS_USB_HANDLE DeviceHandle, UCHAR RequestType, UCHAR Request, USHORT Value, USHORT Index, USHORT Length, PVOID Data, ULONG SecondsTimeout) {
|
|
PIOS_USB_ASYNC_RESULT Async = ExAllocatePool(NonPagedPool, sizeof(IOS_USB_ASYNC_RESULT));
|
|
if (Async == NULL) return STATUS_NO_MEMORY;
|
|
|
|
NTSTATUS Status = UlTransferControlMessageAsync(DeviceHandle, RequestType, Request, Value, Index, Length, Data, Async, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(Async);
|
|
return Status;
|
|
}
|
|
|
|
// Wait on the event with timeout.
|
|
LARGE_INTEGER Timeout = {.QuadPart = 0};
|
|
Timeout.QuadPart = SecondsTimeout * 1000;
|
|
Timeout.QuadPart = -MS_TO_TIMEOUT(Timeout.QuadPart);
|
|
Status = KeWaitForSingleObject(&Async->Event, Executive, KernelMode, FALSE, &Timeout);
|
|
|
|
if (Status == STATUS_TIMEOUT) {
|
|
// Cancel the control message.
|
|
UlCancelEndpoint(DeviceHandle, USB_CANCEL_CONTROL);
|
|
// And wait for the failure IPC response.
|
|
KeWaitForSingleObject(&Async->Event, Executive, KernelMode, FALSE, NULL);
|
|
// Turn it into a failing status.
|
|
Status = STATUS_IO_TIMEOUT;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Got an IPC response in time.
|
|
Status = Async->Status;
|
|
}
|
|
|
|
// Ensure the usblow context gets freed.
|
|
UlGetPassedAsyncContext( Async->Buffer );
|
|
|
|
// Free the async context.
|
|
ExFreePool( Async );
|
|
|
|
// And return.
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspBulkMsgTimeout(IOS_USB_HANDLE DeviceHandle, UCHAR Endpoint, USHORT Length, PVOID Data, ULONG SecondsTimeout) {
|
|
PIOS_USB_ASYNC_RESULT Async = ExAllocatePool(NonPagedPool, sizeof(IOS_USB_ASYNC_RESULT));
|
|
if (Async == NULL) return STATUS_NO_MEMORY;
|
|
|
|
NTSTATUS Status = UlTransferBulkMessageAsync(DeviceHandle, Endpoint, Length, Data, Async, NULL);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ExFreePool(Async);
|
|
return Status;
|
|
}
|
|
|
|
// Wait on the event with timeout.
|
|
LARGE_INTEGER Timeout = {.QuadPart = 0};
|
|
Timeout.QuadPart = SecondsTimeout * 1000;
|
|
Timeout.QuadPart = -MS_TO_TIMEOUT(Timeout.QuadPart);
|
|
Status = KeWaitForSingleObject(&Async->Event, Executive, KernelMode, FALSE, &Timeout);
|
|
|
|
if (Status == STATUS_TIMEOUT) {
|
|
// Cancel the endpoint message.
|
|
UlCancelEndpoint(DeviceHandle, Endpoint);
|
|
// And wait for the failure IPC response.
|
|
KeWaitForSingleObject(&Async->Event, Executive, KernelMode, FALSE, NULL);
|
|
// Turn it into a failing status.
|
|
Status = STATUS_IO_TIMEOUT;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// Got an IPC response in time.
|
|
Status = Async->Status;
|
|
}
|
|
|
|
// Ensure the usblow context gets freed.
|
|
UlGetPassedAsyncContext( Async->Buffer );
|
|
|
|
// Free the async context.
|
|
ExFreePool( Async );
|
|
|
|
if (Status == 0xC0101BBE || Status == 0xC0101B5C) {
|
|
MspClearHalt(DeviceHandle, Endpoint);
|
|
}
|
|
|
|
// And return.
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspReset(IOS_USB_HANDLE DeviceHandle, UCHAR Interface, UCHAR EndpointIn, UCHAR EndpointOut) {
|
|
NTSTATUS Status = UlTransferControlMessage(
|
|
DeviceHandle,
|
|
(USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE),
|
|
USBSTORAGE_RESET,
|
|
0,
|
|
Interface,
|
|
0,
|
|
NULL
|
|
);
|
|
LARGE_INTEGER Timeout = {.QuadPart = 0};
|
|
Timeout.QuadPart = -MS_TO_TIMEOUT(60);
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
|
|
|
|
UlCancelEndpoint(DeviceHandle, EndpointIn);
|
|
Timeout.QuadPart = -MS_TO_TIMEOUT(10);
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
|
|
UlCancelEndpoint(DeviceHandle, EndpointOut);
|
|
Timeout.QuadPart = -MS_TO_TIMEOUT(10);
|
|
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
|
|
//MspClearHalt(DeviceHandle, EndpointIn);
|
|
//MspClearHalt(DeviceHandle, EndpointOut);
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspSendCbw(
|
|
PUSBMS_CONTROLLER Controller,
|
|
UCHAR Lun,
|
|
ULONG Length,
|
|
UCHAR Flags,
|
|
PUCHAR Cb,
|
|
UCHAR CbLength,
|
|
ULONG SecondsTimeout,
|
|
PULONG pTag
|
|
) {
|
|
if (CbLength == 0 || CbLength > 16) return STATUS_INVALID_PARAMETER;
|
|
|
|
USBMS_CBW Cbw;
|
|
RtlZeroMemory(&Cbw, sizeof(Cbw));
|
|
PUSBMS_CBW pCbw = HalIopAlloc( sizeof(USBMS_CBW) );
|
|
if (pCbw == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
Cbw.Signature = CBW_SIGNATURE;
|
|
Controller->CbwTag++;
|
|
*pTag = Controller->CbwTag;
|
|
Cbw.Tag = Controller->CbwTag;
|
|
Cbw.Length = Length;
|
|
Cbw.Flags = Flags;
|
|
Cbw.Lun = Lun;
|
|
Cbw.CbLength = (CbLength > 6 ? 10 : 6);
|
|
RtlCopyMemory(Cbw.Cb, Cb, CbLength);
|
|
RtlCopyMemory(pCbw, &Cbw, sizeof(Cbw));
|
|
|
|
NTSTATUS Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Controller->EndpointOut,
|
|
CBW_SIZE,
|
|
pCbw,
|
|
SecondsTimeout
|
|
);
|
|
|
|
if (Status == 0xC0101BBE || Status == 0xC0101B5C) {
|
|
Status = MspReset(
|
|
Controller->DeviceHandle,
|
|
Controller->Interface,
|
|
Controller->EndpointIn,
|
|
Controller->EndpointOut
|
|
);
|
|
//DbgPrint("ResetStatus(SendCbw) %08x\n", Status);
|
|
|
|
Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Controller->EndpointOut,
|
|
CBW_SIZE,
|
|
pCbw,
|
|
SecondsTimeout
|
|
);
|
|
}
|
|
|
|
HalIopFree(pCbw);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
LONG IopResult = STATUS_TO_IOP(Status);
|
|
if (IopResult == CBW_SIZE) return Status;
|
|
return STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
NTSTATUS MspReadCsw(
|
|
PUSBMS_CONTROLLER Controller,
|
|
ULONG Tag,
|
|
PUCHAR CswStatus,
|
|
PULONG DataResidue,
|
|
ULONG SecondsTimeout
|
|
) {
|
|
PUSBMS_CSW Csw = HalIopAlloc(sizeof(USBMS_CSW));
|
|
if (Csw == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
RtlZeroMemory(Csw, sizeof(*Csw));
|
|
NTSTATUS Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Controller->EndpointIn,
|
|
CSW_SIZE,
|
|
Csw,
|
|
SecondsTimeout
|
|
);
|
|
|
|
do {
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == 0xC0101BBE || Status == 0xC0101B5C) {
|
|
//MspClearHalt(Controller->DeviceHandle, Controller->EndpointIn);
|
|
Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Controller->EndpointIn,
|
|
CSW_SIZE,
|
|
Csw,
|
|
SecondsTimeout
|
|
);
|
|
}
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
LONG IopResult = STATUS_TO_IOP(Status);
|
|
if (IopResult != CSW_SIZE) {
|
|
Status = STATUS_IO_DEVICE_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (Csw->Signature != CSW_SIGNATURE) {
|
|
//DbgPrint("USBMS: Bad csw, signature=%08x expected %08x\n", Csw->Signature, CSW_SIGNATURE);
|
|
Status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
break;
|
|
}
|
|
//Status = STATUS_SUCCESS;
|
|
if (CswStatus != NULL) {
|
|
*CswStatus = Csw->Status;
|
|
}
|
|
if (DataResidue != NULL) {
|
|
*DataResidue = Csw->DataResidue;
|
|
}
|
|
|
|
if (Csw->Tag != Tag) {
|
|
//DbgPrint("USBMS: Bad csw, tag=%08x expected %08x\n", Csw->Tag, Tag);
|
|
Status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
}
|
|
} while (FALSE);
|
|
|
|
HalIopFree(Csw);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspRunScsi(
|
|
PUSBMS_CONTROLLER Controller,
|
|
UCHAR Lun,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
PUCHAR Cb,
|
|
UCHAR CbLength,
|
|
BOOLEAN Write,
|
|
PUCHAR CswStatus,
|
|
PULONG CswDataResidue,
|
|
ULONG SecondsTimeout
|
|
) {
|
|
// Toggle the disc LED gpio.
|
|
MmioWrite32((PVOID)0x8d8000c0, MmioRead32((PVOID)0x8d8000c0) ^ 0x20);
|
|
USHORT MaxSize = Controller->MaxSize;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Endpoint = (Write ? Controller->EndpointOut : Controller->EndpointIn );
|
|
UCHAR LocalCswStatus = 0;
|
|
ULONG LocalDataResidue = 0;
|
|
for (UCHAR Retry = 0; Retry < USBSTORAGE_CYCLE_RETRIES; Retry++) {
|
|
ULONG CurrLength = Length;
|
|
PUCHAR Pointer = Buffer;
|
|
if (Status == STATUS_IO_TIMEOUT) break;
|
|
ULONG Tag;
|
|
|
|
//KeStallExecutionProcessor(100);
|
|
|
|
Status = MspSendCbw(
|
|
Controller, Lun,
|
|
CurrLength,
|
|
( Write ? CBW_OUT : CBW_IN ),
|
|
Cb,
|
|
CbLength,
|
|
SecondsTimeout,
|
|
&Tag
|
|
);
|
|
//DbgPrint("SendCbw(%s,%08x) %08x\n", Write ? "Write" : "Read", CurrLength, Status);
|
|
|
|
while (CurrLength > 0 && NT_SUCCESS(Status)) {
|
|
ULONG RoundLength = CurrLength > MaxSize ? MaxSize : CurrLength;
|
|
|
|
// Always go through the map buffer.
|
|
if (Write && Buffer != NULL) RtlCopyMemory( Controller->Buffer, Pointer, RoundLength );
|
|
//KeStallExecutionProcessor(100);
|
|
Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Endpoint,
|
|
RoundLength,
|
|
Controller->Buffer,
|
|
SecondsTimeout
|
|
);
|
|
//DbgPrint("BulkMsgTimeout(%s,%x) %08x\n", Write ? "Write" : "Read", RoundLength, Status);
|
|
ULONG BytesTransferred = 0;
|
|
if (NT_SUCCESS(Status)) {
|
|
BytesTransferred = STATUS_TO_IOP( Status );
|
|
if (!Write && Buffer != NULL) {
|
|
RtlCopyMemory( Pointer, Controller->Buffer, BytesTransferred );
|
|
HalSweepDcacheRange(Pointer, BytesTransferred);
|
|
}
|
|
if (BytesTransferred == RoundLength) {
|
|
CurrLength -= BytesTransferred;
|
|
if (Buffer != NULL) Pointer += BytesTransferred;
|
|
}
|
|
else Status = STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//KeStallExecutionProcessor(100);
|
|
Status = MspReadCsw(Controller, Tag, &LocalCswStatus, &LocalDataResidue, SecondsTimeout);
|
|
//DbgPrint("ReadCsw(%s) %08x\n", Write ? "Write" : "Read", Status);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NTSTATUS ResetStatus = MspReset(
|
|
Controller->DeviceHandle,
|
|
Controller->Interface,
|
|
Controller->EndpointIn,
|
|
Controller->EndpointOut
|
|
);
|
|
//DbgPrint("ResetStatus(%s) %08x\n", Write ? "Write" : "Read", Status);
|
|
if (ResetStatus == STATUS_IO_TIMEOUT) Status = ResetStatus;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CswStatus != NULL) *CswStatus = LocalCswStatus;
|
|
if (CswDataResidue != NULL) *CswDataResidue = LocalDataResidue;
|
|
// Toggle the disc LED gpio.
|
|
MmioWrite32((PVOID)0x8d8000c0, MmioRead32((PVOID)0x8d8000c0) ^ 0x20);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspRunScsiMeta(
|
|
PUSBMS_CONTROLLER Controller,
|
|
UCHAR Lun,
|
|
PUCHAR Buffer,
|
|
ULONG Length,
|
|
PUCHAR Cb,
|
|
UCHAR CbLength,
|
|
PULONG TransferredLength,
|
|
PUCHAR CswStatus,
|
|
PULONG CswDataResidue,
|
|
ULONG SecondsTimeout
|
|
) {
|
|
USHORT MaxSize = Controller->MaxSize;
|
|
if (Length > MaxSize) return STATUS_INVALID_PARAMETER;
|
|
// This is a metadata request.
|
|
// Instead of using the buffer from the controller;
|
|
// allocate a map buffer from the IPC area.
|
|
// This is so ioctls can be performed synchronously.
|
|
PUCHAR MapBuffer = NULL;
|
|
if (Length != 0) {
|
|
MapBuffer = (PUCHAR) HalIopAlloc(Length);
|
|
if (MapBuffer == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG Endpoint = Controller->EndpointIn;
|
|
UCHAR LocalCswStatus = 0;
|
|
ULONG LocalDataResidue = 0;
|
|
for (UCHAR Retry = 0; Retry < USBSTORAGE_CYCLE_RETRIES; Retry++) {
|
|
//if (Status == STATUS_IO_TIMEOUT) break;
|
|
ULONG Tag;
|
|
|
|
//KeStallExecutionProcessor(100);
|
|
|
|
Status = MspSendCbw(
|
|
Controller, Lun,
|
|
Length,
|
|
CBW_IN,
|
|
Cb,
|
|
CbLength,
|
|
SecondsTimeout,
|
|
&Tag
|
|
);
|
|
//DbgPrint("SendCbw(Meta,%02x) %08x\n", Cb[0], Status);
|
|
|
|
if (NT_SUCCESS(Status) && Length != 0) {
|
|
//KeStallExecutionProcessor(100);
|
|
// Always go through the map buffer.
|
|
Status = MspBulkMsgTimeout(
|
|
Controller->DeviceHandle,
|
|
Endpoint,
|
|
Length,
|
|
MapBuffer,
|
|
SecondsTimeout
|
|
);
|
|
//DbgPrint("BulkMsgTimeout(Meta,%x) %08x\n", Length, Status);
|
|
ULONG BytesTransferred = 0;
|
|
if (NT_SUCCESS(Status)) {
|
|
if (Buffer != NULL) {
|
|
BytesTransferred = STATUS_TO_IOP( Status );
|
|
RtlCopyMemory( Buffer, MapBuffer, BytesTransferred );
|
|
HalSweepDcacheRange(Buffer, BytesTransferred);
|
|
}
|
|
if (TransferredLength) *TransferredLength = BytesTransferred;
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//KeStallExecutionProcessor(100);
|
|
Status = MspReadCsw(Controller, Tag, &LocalCswStatus, &LocalDataResidue, SecondsTimeout);
|
|
//DbgPrint("ReadCsw(Meta) %08x\n", Status);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NTSTATUS ResetStatus = MspReset(
|
|
Controller->DeviceHandle,
|
|
Controller->Interface,
|
|
Controller->EndpointIn,
|
|
Controller->EndpointOut
|
|
);
|
|
//DbgPrint("ResetStatus(Meta) %08x\n", Status);
|
|
if (ResetStatus == STATUS_IO_TIMEOUT) Status = ResetStatus;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CswStatus != NULL) *CswStatus = LocalCswStatus;
|
|
if (CswDataResidue != NULL) *CswDataResidue = LocalDataResidue;
|
|
if (MapBuffer != NULL) HalIopFree(MapBuffer);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspLowReinit(PUSBMS_CONTROLLER Controller, UCHAR Lun);
|
|
|
|
NTSTATUS MspLowRead10(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SectorStart, USHORT SectorCount, ULONG SectorShift, PVOID Buffer) {
|
|
NTSTATUS Status;
|
|
UCHAR CswStatus;
|
|
UCHAR ScsiCdb[] = {
|
|
SCSI_READ_10,
|
|
Lun << 5,
|
|
SectorStart >> 24,
|
|
SectorStart >> 16,
|
|
SectorStart >> 8,
|
|
SectorStart >> 0,
|
|
0,
|
|
SectorCount >> 8,
|
|
SectorCount >> 0,
|
|
0
|
|
};
|
|
ULONG Length = SectorCount << SectorShift;
|
|
|
|
// Use a 10 seconds timeout to allow for drive to wake up if needed
|
|
Status = MspRunScsi(
|
|
Controller,
|
|
Lun,
|
|
Buffer,
|
|
Length,
|
|
ScsiCdb,
|
|
sizeof(ScsiCdb),
|
|
FALSE,
|
|
&CswStatus,
|
|
NULL,
|
|
10
|
|
);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
if (CswStatus != 0) return STATUS_IO_DEVICE_ERROR;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspLowWrite10(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SectorStart, USHORT SectorCount, ULONG SectorShift, PVOID Buffer) {
|
|
NTSTATUS Status;
|
|
UCHAR CswStatus;
|
|
UCHAR ScsiCdb[] = {
|
|
SCSI_WRITE_10,
|
|
Lun << 5,
|
|
SectorStart >> 24,
|
|
SectorStart >> 16,
|
|
SectorStart >> 8,
|
|
SectorStart >> 0,
|
|
0,
|
|
SectorCount >> 8,
|
|
SectorCount >> 0,
|
|
0
|
|
};
|
|
ULONG Length = SectorCount << SectorShift;
|
|
|
|
// Use a 10 seconds timeout to allow for drive to wake up if needed
|
|
Status = MspRunScsi(
|
|
Controller,
|
|
Lun,
|
|
Buffer,
|
|
Length,
|
|
ScsiCdb,
|
|
sizeof(ScsiCdb),
|
|
TRUE,
|
|
&CswStatus,
|
|
NULL,
|
|
10
|
|
);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
if (CswStatus != 0) return STATUS_IO_DEVICE_ERROR;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspLowRead(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SectorStart, ULONG SectorCount, ULONG SectorShift, PUCHAR Buffer) {
|
|
USHORT CurrentRound;
|
|
NTSTATUS Status;
|
|
while (SectorCount != 0) {
|
|
if (SectorCount > 0xFFFF) CurrentRound = 0xFFFF;
|
|
else CurrentRound = (USHORT)SectorCount;
|
|
|
|
Status = MspLowRead10(Controller, Lun, SectorStart, CurrentRound, SectorShift, Buffer);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
ULONG Length = CurrentRound << SectorShift;
|
|
SectorStart += CurrentRound;
|
|
SectorCount -= CurrentRound;
|
|
Buffer += Length;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspLowVerify(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SectorStart, ULONG SectorCount, ULONG SectorShift) {
|
|
USHORT CurrentRound;
|
|
NTSTATUS Status;
|
|
while (SectorCount != 0) {
|
|
if (SectorCount > 0xFFFF) CurrentRound = 0xFFFF;
|
|
else CurrentRound = (USHORT)SectorCount;
|
|
|
|
Status = MspLowRead10(Controller, Lun, SectorStart, CurrentRound, SectorShift, NULL);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
ULONG Length = CurrentRound << SectorShift;
|
|
SectorStart += CurrentRound;
|
|
SectorCount -= CurrentRound;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspLowWrite(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SectorStart, ULONG SectorCount, ULONG SectorShift, PUCHAR Buffer) {
|
|
USHORT CurrentRound;
|
|
NTSTATUS Status;
|
|
while (SectorCount != 0) {
|
|
if (SectorCount > 0xFFFF) CurrentRound = 0xFFFF;
|
|
else CurrentRound = (USHORT)SectorCount;
|
|
|
|
Status = MspLowWrite10(Controller, Lun, SectorStart, CurrentRound, SectorShift, Buffer);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
ULONG Length = CurrentRound << SectorShift;
|
|
SectorStart += CurrentRound;
|
|
SectorCount -= CurrentRound;
|
|
Buffer += Length;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS MspClearErrors(PUSBMS_CONTROLLER Controller, UCHAR Lun, ULONG SecondsTimeout) {
|
|
NTSTATUS Status;
|
|
UCHAR ScsiCdb[6];
|
|
UCHAR Sense[SCSI_SENSE_REPLY_SIZE];
|
|
UCHAR CswStatus = 0;
|
|
|
|
RtlZeroMemory(&ScsiCdb, sizeof(ScsiCdb));
|
|
ScsiCdb[0] = SCSI_TEST_UNIT_READY;
|
|
|
|
Status = MspRunScsiMeta(Controller, Lun, NULL, 0, ScsiCdb, sizeof(ScsiCdb), NULL, &CswStatus, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (CswStatus) {
|
|
ScsiCdb[0] = SCSI_REQUEST_SENSE;
|
|
ScsiCdb[1] = Lun << 5;
|
|
ScsiCdb[4] = sizeof(Sense);
|
|
ULONG Transferred = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, Sense, sizeof(Sense), ScsiCdb, sizeof(ScsiCdb), &Transferred, NULL, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
if (Transferred < sizeof(Sense)) return STATUS_DEVICE_PROTOCOL_ERROR;
|
|
|
|
UCHAR SenseErr = Sense[2] & 0xF;
|
|
if (SenseErr == SCSI_SENSE_NOT_READY) return STATUS_DEVICE_NOT_READY;
|
|
if (SenseErr == SCSI_SENSE_MEDIUM_ERROR) return STATUS_DEVICE_DATA_ERROR;
|
|
if (SenseErr == SCSI_SENSE_HARDWARE_ERROR) return STATUS_IO_DEVICE_ERROR;
|
|
if (SenseErr == SCSI_SENSE_UNIT_ATTENTION) return STATUS_VERIFY_REQUIRED;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
BOOLEAN MspCapacityIsFloppy(PSCSI_FORMAT_CAPACITY_ENTRY Entries, UCHAR Count) {
|
|
for (ULONG i = 0; i < Count; i++) {
|
|
ULONG NumberOfBlocks = Entries[i].NumberOfBlocks;
|
|
ULONG BlockLength = (Entries[i].BlockLengthHigh << 16) |
|
|
Entries[i].BlockLengthLow;
|
|
if (BlockLength == 1024) {
|
|
if (NumberOfBlocks == (77 * 2 * 8)) {
|
|
// 1.25MB (PC-98, etc)
|
|
return TRUE;
|
|
}
|
|
continue;
|
|
}
|
|
if (BlockLength != 512) continue;
|
|
// for 512-byte sectors, allow everything that NT5 usbmass.sys does;
|
|
// assume that usbmass.sys supports it because some USB floppy drive does.
|
|
switch (NumberOfBlocks) {
|
|
case 80 * 2 * 8: // 640 KB (5.25"?!)
|
|
case 80 * 2 * 9: // 720 KB
|
|
case 80 * 2 * 15: // 1.2 MB
|
|
case 80 * 2 * 18: // 1.44MB
|
|
|
|
case 963 * 8 * 32: // 120MB, SuperDisk LS-120
|
|
|
|
case 890 * 13 * 34: // 200MB, Sony HiFD
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
NTSTATUS MspGetDiskType(PUSBMS_CONTROLLER Controller, UCHAR Lun, PUSBMS_DISK_TYPE DiskType, ULONG SecondsTimeout) {
|
|
// Do SCSI Inquiry, get 36 bytes
|
|
NTSTATUS Status;
|
|
UCHAR Inquiry[36];
|
|
UCHAR ScsiCdb[6] = {0};
|
|
ScsiCdb[0] = SCSI_INQUIRY;
|
|
ScsiCdb[1] = Lun << 5;
|
|
ScsiCdb[4] = sizeof(Inquiry);
|
|
for (int Retry = 0; Retry < 2; Retry++) {
|
|
RtlZeroMemory(Inquiry, sizeof(Inquiry));
|
|
ULONG Transferred = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, Inquiry, sizeof(Inquiry), ScsiCdb, sizeof(ScsiCdb), &Transferred, NULL, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) continue;
|
|
if (Transferred < sizeof(Inquiry)) {
|
|
Status = STATUS_DEVICE_PROTOCOL_ERROR;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
BOOLEAN RemovableDisk = (Inquiry[1] & 0x80) != 0;
|
|
UCHAR DeviceType = Inquiry[0] & 0x1f;
|
|
if (DeviceType == 5) {
|
|
*DiskType = USBMS_DISK_CDROM;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
if (DeviceType != 0) {
|
|
*DiskType = USBMS_DISK_UNKNOWN;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
// is this a floppy drive?
|
|
// assume non-floppy until it is known to be otherwise
|
|
*DiskType = RemovableDisk ? USBMS_DISK_OTHER_REMOVABLE : USBMS_DISK_OTHER_FIXED;
|
|
// send SCSI Read Format Capacities to find out
|
|
// if any entry matches a known floppy size;
|
|
// then this is a floppy disk
|
|
UCHAR FormatCapsBuf2[
|
|
sizeof(SCSI_FORMAT_CAPACITY_LIST) +
|
|
(31 * sizeof(SCSI_FORMAT_CAPACITY_ENTRY))
|
|
];
|
|
UCHAR FormatCapsBuf[
|
|
sizeof(SCSI_FORMAT_CAPACITY_LIST) +
|
|
(1 * sizeof(SCSI_FORMAT_CAPACITY_ENTRY))
|
|
];
|
|
UCHAR ScsiCdb12[12];
|
|
RtlZeroMemory(FormatCapsBuf, sizeof(FormatCapsBuf));
|
|
RtlZeroMemory(ScsiCdb12, sizeof(ScsiCdb12));
|
|
ScsiCdb12[0] = SCSI_READ_FORMAT_CAPACITY;
|
|
ScsiCdb12[1] = Lun << 5;
|
|
ScsiCdb12[8] = sizeof(FormatCapsBuf);
|
|
ULONG TransferredBytes = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, FormatCapsBuf, sizeof(FormatCapsBuf), ScsiCdb12, sizeof(ScsiCdb12), &TransferredBytes, NULL, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) return STATUS_SUCCESS;
|
|
if (TransferredBytes < sizeof(SCSI_FORMAT_CAPACITY_LIST)) return STATUS_SUCCESS;
|
|
PSCSI_FORMAT_CAPACITY_LIST List = (PSCSI_FORMAT_CAPACITY_LIST)
|
|
FormatCapsBuf;
|
|
if (List->Length == 0) return STATUS_SUCCESS;
|
|
if ((List->Length % sizeof(SCSI_FORMAT_CAPACITY_ENTRY)) != 0) return STATUS_SUCCESS;
|
|
|
|
if (List->Length > sizeof(SCSI_FORMAT_CAPACITY_ENTRY)) {
|
|
// Device returned more than one entry. It's now known how many entries there are so ask for as many as possible
|
|
UCHAR CompleteSize = List->Length + sizeof(SCSI_FORMAT_CAPACITY_LIST);
|
|
if (CompleteSize > sizeof(FormatCapsBuf2)) CompleteSize = sizeof(FormatCapsBuf2);
|
|
ScsiCdb12[8] = CompleteSize;
|
|
TransferredBytes = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, FormatCapsBuf2, CompleteSize, ScsiCdb12, sizeof(ScsiCdb12), &TransferredBytes, NULL, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) return 0;
|
|
if (TransferredBytes < sizeof(SCSI_FORMAT_CAPACITY_LIST)) return 0;
|
|
List = (PSCSI_FORMAT_CAPACITY_LIST)FormatCapsBuf2;
|
|
if (List->Length == 0) return 0;
|
|
if ((List->Length % sizeof(SCSI_FORMAT_CAPACITY_ENTRY)) != 0) return 0;
|
|
}
|
|
|
|
ULONG Count = TransferredBytes - sizeof(SCSI_FORMAT_CAPACITY_ENTRY);
|
|
if (Count >= List->Length) Count = List->Length;
|
|
Count /= sizeof(SCSI_FORMAT_CAPACITY_ENTRY);
|
|
|
|
if (MspCapacityIsFloppy(List->Entries, Count)) {
|
|
*DiskType = USBMS_DISK_FLOPPY;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspIsWriteProtect(PUSBMS_CONTROLLER Controller, UCHAR Lun, PBOOLEAN WriteProtect, ULONG SecondsTimeout) {
|
|
NTSTATUS Status;
|
|
UCHAR ScsiCdb6[6] = {0};
|
|
ScsiCdb6[0] = SCSI_MODE_SENSE;
|
|
ScsiCdb6[2] = 0x3F;
|
|
ScsiCdb6[4] = 4;
|
|
UCHAR ModeSense[4];
|
|
|
|
ULONG Transferred = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, ModeSense, sizeof(ModeSense), ScsiCdb6, sizeof(ScsiCdb6), &Transferred, NULL, NULL, SecondsTimeout);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (Transferred < 4) {
|
|
// try again
|
|
Status = MspRunScsiMeta(Controller, Lun, ModeSense, sizeof(ModeSense), ScsiCdb6, sizeof(ScsiCdb6), &Transferred, NULL, NULL, SecondsTimeout);
|
|
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
if (Transferred < 4) return STATUS_IO_DEVICE_ERROR;
|
|
}
|
|
|
|
*WriteProtect = (ModeSense[2] & 0x80) != 0;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspReadCapacity(PUSBMS_CONTROLLER Controller, UCHAR Lun, PULONG SectorSize, PULONG SectorCount, ULONG SecondsTimeout) {
|
|
NTSTATUS Status;
|
|
UCHAR ScsiCdb10[10] = {0};
|
|
ScsiCdb10[0] = SCSI_READ_CAPACITY;
|
|
ScsiCdb10[1] = (Lun << 5);
|
|
SCSI_READ_CAPACITY_RES ReadCapacity;
|
|
|
|
ULONG Transferred = 0;
|
|
Status = MspRunScsiMeta(Controller, Lun, (PUCHAR)&ReadCapacity, sizeof(ReadCapacity), ScsiCdb10, sizeof(ScsiCdb10), &Transferred, NULL, NULL, SecondsTimeout);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
if (SectorCount != NULL) *SectorCount = ReadCapacity.SectorCount;
|
|
if (SectorSize != NULL) *SectorSize = ReadCapacity.SectorSize;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS MspCheckVerifyLockedImpl(PDEVICE_OBJECT DeviceObject, PUSBMS_CONTROLLER Controller, PUSBMS_EXTENSION Ext) {
|
|
// Do clear errors. Use 20 second timeout just in case.
|
|
UCHAR Lun = Ext->Lun;
|
|
NTSTATUS Status = MspClearErrors(Controller, Lun, 20);
|
|
NTSTATUS CapStatus;
|
|
BOOLEAN IsCdRom = DeviceObject->DeviceType == FILE_DEVICE_CD_ROM;
|
|
if (Status == STATUS_VERIFY_REQUIRED) {
|
|
// Medium changed. Go again, allow media to spin up, ignore result.
|
|
Status = MspClearErrors(Controller, Lun, 20);
|
|
// Read the drive capacity.
|
|
ULONG SectorCount = 0;
|
|
ULONG SectorSize = 0;
|
|
NTSTATUS CapStatus = MspReadCapacity(Controller, Lun, &SectorSize, &SectorCount, 20);
|
|
Ext->DriveNotReady = !NT_SUCCESS(CapStatus);
|
|
if (!NT_SUCCESS(CapStatus) || SectorSize == 0) {
|
|
if (IsCdRom) {
|
|
SectorCount = 0x7fffffff;
|
|
SectorSize = 2048;
|
|
} else {
|
|
SectorCount = 0;
|
|
SectorSize = 512;
|
|
}
|
|
}
|
|
// set the new sector-count/size
|
|
Ext->SectorSize = SectorSize;
|
|
Ext->SectorCount = SectorCount;
|
|
Ext->SectorShift = __builtin_ctz(SectorSize);
|
|
}
|
|
// Run mode sense to get write protect status.
|
|
BOOLEAN WriteProtect = IsCdRom;
|
|
if (!WriteProtect) {
|
|
CapStatus = MspIsWriteProtect(Controller, Lun, &WriteProtect, USBSTORAGE_TIMEOUT);
|
|
if (!NT_SUCCESS(CapStatus)) WriteProtect = FALSE;
|
|
}
|
|
Ext->WriteProtected = WriteProtect;
|
|
return Status;
|
|
}
|
|
|
|
static void MspCheckVerifyLocked(PUSBMS_CONTROLLER Controller, PUSBMS_LOCK_CONTEXT Context) {
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION)Context->DeviceObject->DeviceExtension;
|
|
PUSBMS_PARTITION Partition = NULL;
|
|
if (Ext->Signature == SIGNATURE_PARTITION) {
|
|
Partition = (PUSBMS_PARTITION)Ext;
|
|
Ext = Partition->PhysicalDisk;
|
|
}
|
|
Context->Status = MspCheckVerifyLockedImpl(Context->DeviceObject, Controller, Ext);
|
|
KeSetEvent(&Context->Event, (KPRIORITY)0, FALSE);
|
|
}
|
|
|
|
NTSTATUS MspCheckVerify(PDEVICE_OBJECT DeviceObject, PUSBMS_CONTROLLER Controller, PUSBMS_EXTENSION Ext) {
|
|
PUSBMS_LOCK_CONTEXT Context = MspGetStateContext(Controller);
|
|
if (Context == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
Context->DeviceObject = DeviceObject;
|
|
|
|
NTSTATUS Status = MspLockController(MspCheckVerifyLocked, Controller, Context);
|
|
if (!NT_SUCCESS(Status)) {
|
|
MspReleaseStateContext(Context);
|
|
return Status;
|
|
}
|
|
|
|
KeWaitForSingleObject( &Context->Event, Executive, KernelMode, FALSE, NULL);
|
|
Status = Context->Status;
|
|
MspReleaseStateContext(Context);
|
|
return Status;
|
|
}
|
|
|
|
static void MspFinishDpc(
|
|
PKDPC Dpc,
|
|
PVOID DeferredContext,
|
|
PVOID SystemArgument1,
|
|
PVOID SystemArgument2
|
|
) {
|
|
// Get the device object and the IRP.
|
|
PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)DeferredContext;
|
|
PIRP Irp = (PIRP)SystemArgument1;
|
|
PCONTROLLER_OBJECT Controller = (PCONTROLLER_OBJECT)SystemArgument2;
|
|
|
|
// Complete the request.
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
#if 0
|
|
// Release the controller object.
|
|
IoFreeController(Controller);
|
|
// Start next packet.
|
|
IoStartNextPacket(DeviceObject, FALSE);
|
|
#endif
|
|
}
|
|
|
|
static NTSTATUS MspDeviceCreateImpl(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PCONTROLLER_OBJECT Controller,
|
|
UCHAR Lun,
|
|
ULONG SectorSize,
|
|
ULONG SectorCount,
|
|
PULONG DeviceCount,
|
|
ULONG ArcKey,
|
|
DEVICE_TYPE DeviceType,
|
|
ULONG DeviceCharacteristics,
|
|
const char * DevicePath
|
|
) {
|
|
BOOLEAN IsHardDisk = DeviceType == FILE_DEVICE_DISK && ((DeviceCharacteristics & FILE_FLOPPY_DISKETTE) == 0);
|
|
// Generate the NT object path name for this device.
|
|
UCHAR NtName[256];
|
|
sprintf(NtName, "\\Device\\%s%d", DevicePath, *DeviceCount);
|
|
|
|
// Wrap an ANSI string around it.
|
|
STRING NtNameStr;
|
|
RtlInitString(&NtNameStr, NtName);
|
|
|
|
// Convert to unicode.
|
|
UNICODE_STRING NtNameUs;
|
|
NTSTATUS Status = RtlAnsiStringToUnicodeString(&NtNameUs, &NtNameStr, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
HANDLE hDir = NULL;
|
|
if (IsHardDisk) {
|
|
// For hard disks, we create a directory, and Partition%d objects inside that
|
|
// (where Partition0 means the entire disk)
|
|
// We also don't need to symlink the arc name (kernel will do that for us)
|
|
OBJECT_ATTRIBUTES Oa;
|
|
InitializeObjectAttributes(&Oa, &NtNameUs, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
|
|
Status = ZwCreateDirectoryObject(&hDir, DIRECTORY_ALL_ACCESS, &Oa);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
// Generate the NT object path name for the actual device.
|
|
sprintf(NtName, "\\Device\\%s%d\\Partition0", DevicePath, *DeviceCount);
|
|
RtlInitString(&NtNameStr, NtName);
|
|
Status = RtlAnsiStringToUnicodeString(&NtNameUs, &NtNameStr, TRUE);
|
|
// Let the following code create the device object.
|
|
}
|
|
|
|
PDEVICE_OBJECT DeviceObject = NULL;
|
|
do {
|
|
if (!NT_SUCCESS(Status)) break;
|
|
// Create the device object.
|
|
Status = IoCreateDevice(DriverObject, sizeof(USBMS_EXTENSION), &NtNameUs, DeviceType, DeviceCharacteristics, FALSE, &DeviceObject);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
// Symlink it to the arc name, if needed.
|
|
if (!IsHardDisk) {
|
|
UCHAR ArcName[256];
|
|
sprintf(ArcName,
|
|
"\\ArcName\\%s%s(%u)fdisk(0)",
|
|
s_ControllerPath,
|
|
DeviceType == FILE_DEVICE_CD_ROM ? "cdrom" : "disk",
|
|
ArcKey
|
|
);
|
|
STRING ArcNameStr;
|
|
RtlInitString(&ArcNameStr, ArcName);
|
|
UNICODE_STRING ArcNameUs;
|
|
Status = RtlAnsiStringToUnicodeString(&ArcNameUs, &ArcNameStr, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
break;
|
|
}
|
|
// This might fail if there were multiple LUNs.
|
|
// ARC firmware only uses the first one anyway, so don't bother checking.
|
|
IoAssignArcName(&ArcNameUs, &NtNameUs);
|
|
RtlFreeUnicodeString(&ArcNameUs);
|
|
} else {
|
|
// We don't need the directory handle any more.
|
|
ZwClose(hDir);
|
|
hDir = NULL;
|
|
}
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
|
|
// Initialise the new device object.
|
|
DeviceObject->Flags |= DO_DIRECT_IO;
|
|
DeviceObject->AlignmentRequirement = 32;
|
|
DeviceObject->StackSize = (DeviceType == FILE_DEVICE_CD_ROM ? 2 : 1);
|
|
|
|
// Initialise the device extension.
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION) DeviceObject->DeviceExtension;
|
|
Ext->Controller = Controller;
|
|
Ext->Lun = Lun;
|
|
Ext->WriteProtected = (DeviceCharacteristics & FILE_READ_ONLY_DEVICE) != 0;
|
|
Ext->Signature = SIGNATURE_DISK;
|
|
Ext->DeviceObject = DeviceObject;
|
|
Ext->DiskNumber = *DeviceCount;
|
|
Ext->SectorSize = SectorSize;
|
|
Ext->SectorCount = SectorCount;
|
|
Ext->SectorShift = __builtin_ctz(SectorSize);
|
|
Ext->DriveNotReady = FALSE;
|
|
KeInitializeDpc(&Ext->FinishDpc, MspFinishDpc, DeviceObject);
|
|
// TODO: more needed here?
|
|
|
|
// For a hard disk, create all partition objects.
|
|
if (IsHardDisk) {
|
|
PDRIVE_LAYOUT_INFORMATION PartitionList;
|
|
Status = IoReadPartitionTable(DeviceObject, SectorSize, TRUE, (PVOID)&PartitionList);
|
|
if (NT_SUCCESS(Status)) {
|
|
PUSBMS_PARTITION ExtPart = NULL;
|
|
for (ULONG Partition = 0; Partition < PartitionList->PartitionCount; Partition++) {
|
|
sprintf(NtName, "\\Device\\%s%d\\Partition%d", DevicePath, *DeviceCount, Partition + 1);
|
|
RtlInitString(&NtNameStr, NtName);
|
|
Status = RtlAnsiStringToUnicodeString(&NtNameUs, &NtNameStr, TRUE);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
Status = IoCreateDevice(DriverObject, sizeof(USBMS_PARTITION), &NtNameUs, DeviceType, DeviceCharacteristics, FALSE, &DeviceObject);
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
DeviceObject->Flags |= DO_DIRECT_IO;
|
|
DeviceObject->AlignmentRequirement = 32;
|
|
DeviceObject->StackSize = 1;
|
|
|
|
if (ExtPart == NULL) {
|
|
Ext->NextPartition = (PUSBMS_PARTITION) DeviceObject->DeviceExtension;
|
|
ExtPart = Ext->NextPartition;
|
|
} else {
|
|
ExtPart->NextPartition = (PUSBMS_PARTITION) DeviceObject->DeviceExtension;
|
|
ExtPart = ExtPart->NextPartition;
|
|
}
|
|
ExtPart->PhysicalDisk = Ext;
|
|
PPARTITION_INFORMATION PartitionInfo = &PartitionList->PartitionEntry[Partition];
|
|
ExtPart->PartitionLength.QuadPart = PartitionInfo->PartitionLength.QuadPart;
|
|
ExtPart->StartingOffset.QuadPart = PartitionInfo->StartingOffset.QuadPart;
|
|
ExtPart->PartitionType = PartitionInfo->PartitionType;
|
|
ExtPart->PartitionNumber = PartitionInfo->PartitionNumber;
|
|
ExtPart->HiddenSectors = PartitionInfo->HiddenSectors;
|
|
ExtPart->Signature = SIGNATURE_PARTITION;
|
|
}
|
|
ExFreePool(PartitionList);
|
|
}
|
|
}
|
|
|
|
// All done.
|
|
return Status;
|
|
} while (FALSE);
|
|
|
|
if (hDir != NULL) {
|
|
// Delete the directory.
|
|
ZwMakeTemporaryObject(hDir);
|
|
ZwClose(hDir);
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
}
|
|
if (DeviceObject != NULL) IoDeleteDevice(DeviceObject);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspDeviceCreate(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PCONTROLLER_OBJECT Controller,
|
|
UCHAR Lun,
|
|
ULONG SectorSize,
|
|
ULONG SectorCount,
|
|
PULONG DeviceCount,
|
|
ULONG ArcKey,
|
|
DEVICE_TYPE DeviceType,
|
|
ULONG DeviceCharacteristics,
|
|
const char * DevicePath
|
|
) {
|
|
NTSTATUS Status = MspDeviceCreateImpl(
|
|
DriverObject,
|
|
Controller,
|
|
Lun,
|
|
SectorSize,
|
|
SectorCount,
|
|
DeviceCount,
|
|
ArcKey,
|
|
DeviceType,
|
|
DeviceCharacteristics,
|
|
DevicePath
|
|
);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
*DeviceCount += 1;
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspDiskCreate(
|
|
PDRIVER_OBJECT DriverObject,
|
|
PCONTROLLER_OBJECT Controller,
|
|
UCHAR Lun,
|
|
ULONG SectorSize,
|
|
ULONG SectorCount,
|
|
PCONFIGURATION_INFORMATION Config,
|
|
ULONG ArcKey,
|
|
USBMS_DISK_TYPE DiskType
|
|
) {
|
|
if (DiskType == USBMS_DISK_UNKNOWN) return STATUS_UNSUCCESSFUL;
|
|
DEVICE_TYPE DeviceType;
|
|
ULONG DeviceCharacteristics;
|
|
const char * DevicePath = NULL;
|
|
PULONG DeviceCount = NULL;
|
|
|
|
if (DiskType == USBMS_DISK_FLOPPY) {
|
|
DeviceType = FILE_DEVICE_DISK;
|
|
DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_FLOPPY_DISKETTE;
|
|
DevicePath = "Floppy";
|
|
DeviceCount = &Config->FloppyCount;
|
|
} else if (DiskType == USBMS_DISK_CDROM) {
|
|
DeviceType = FILE_DEVICE_CD_ROM;
|
|
DeviceCharacteristics = FILE_REMOVABLE_MEDIA | FILE_READ_ONLY_DEVICE;
|
|
DevicePath = "CdRom";
|
|
DeviceCount = &Config->CdRomCount;
|
|
} else if (DiskType == USBMS_DISK_OTHER_FIXED || DiskType == USBMS_DISK_OTHER_REMOVABLE) {
|
|
DeviceType = FILE_DEVICE_DISK;
|
|
DeviceCharacteristics =
|
|
#if 0 // OTHER_REMOVABLE is probably flash storage so mount it as fixed
|
|
DiskType == USBMS_DISK_OTHER_REMOVABLE ?
|
|
FILE_REMOVABLE_MEDIA : 0;
|
|
#else
|
|
0;
|
|
#endif
|
|
DevicePath = "Harddisk";
|
|
DeviceCount = &Config->DiskCount;
|
|
} else {
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return MspDeviceCreate(
|
|
DriverObject,
|
|
Controller,
|
|
Lun,
|
|
SectorSize,
|
|
SectorCount,
|
|
DeviceCount,
|
|
ArcKey,
|
|
DeviceType,
|
|
DeviceCharacteristics,
|
|
DevicePath
|
|
);
|
|
}
|
|
|
|
void MsIoWorkImpl(PDEVICE_OBJECT DeviceObject, PIRP Irp);
|
|
|
|
// Read/write handler
|
|
NTSTATUS MsDiskRw(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
// Ensure the device extension is actually valid.
|
|
if (Ext == NULL || (
|
|
Ext->Signature != SIGNATURE_PARTITION &&
|
|
Ext->Signature != SIGNATURE_DISK
|
|
)) {
|
|
// this is probably keyboard or mouse driver
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
// If this is a partition, get the physical disk.
|
|
PUSBMS_PARTITION Partition = NULL;
|
|
if (Ext->Signature == SIGNATURE_PARTITION) {
|
|
Partition = (PUSBMS_PARTITION) Ext;
|
|
Ext = Partition->PhysicalDisk;
|
|
}
|
|
|
|
// ensure this is actually a disk
|
|
if (Ext->Signature != SIGNATURE_DISK) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
ULONG SectorSize = Ext->SectorSize;
|
|
ULONG SectorMask = SectorSize - 1;
|
|
|
|
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
ULONG Length = Stack->Parameters.Read.Length;
|
|
LARGE_INTEGER Offset = Stack->Parameters.Read.ByteOffset;
|
|
ULONG SectorOffset = Offset.LowPart & SectorMask;
|
|
|
|
// Ensure the read is aligned to sector size.
|
|
if ((Length & SectorMask) != 0) {
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
if (Ext->DriveNotReady) Status = STATUS_DEVICE_NOT_READY;
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
|
|
// If this is a partition, fix up the offsets to be based on the disk.
|
|
if (Partition != NULL) {
|
|
// Get the ending offset of the read.
|
|
LARGE_INTEGER OffsetEnd = Offset;
|
|
OffsetEnd.QuadPart += Length;
|
|
// Fix up the offset.
|
|
Offset.QuadPart += Partition->StartingOffset.QuadPart;
|
|
Stack->Parameters.Read.ByteOffset = Offset;
|
|
// Get the partition length.
|
|
LARGE_INTEGER PartitionLength = Partition->PartitionLength;
|
|
// Ensure r/w in bounds of the partition.
|
|
if (OffsetEnd.QuadPart >= PartitionLength.QuadPart) {
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
if (Ext->DriveNotReady) Status = STATUS_DEVICE_NOT_READY;
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
// Get the length of the disk.
|
|
LARGE_INTEGER DiskLength = RtlEnlargedUnsignedMultiply(Ext->SectorSize, Ext->SectorCount);
|
|
|
|
// Ensure offset + length is not beyond the end of the disk.
|
|
LARGE_INTEGER OffsetEnd = Offset;
|
|
OffsetEnd.QuadPart += Length;
|
|
if (
|
|
OffsetEnd.QuadPart > DiskLength.QuadPart
|
|
) {
|
|
NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
if (Ext->DriveNotReady) Status = STATUS_DEVICE_NOT_READY;
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
|
|
// Ensure sector number fits in 32 bits...
|
|
LARGE_INTEGER SectorStart;
|
|
SectorStart.QuadPart = (Stack->Parameters.Read.ByteOffset.QuadPart >> Ext->SectorShift);
|
|
if (SectorStart.HighPart != 0) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// All looks good, start the operation on the physical disk device.
|
|
Irp->IoStatus.Information = 0;
|
|
//IoMarkIrpPending(Irp);
|
|
//IoStartPacket(Ext->DeviceObject, Irp, &SectorOffset, NULL);
|
|
//return STATUS_PENDING;
|
|
MsIoWorkImpl(DeviceObject, Irp);
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
void MsIoWorkLockedImpl(PUSBMS_CONTROLLER Controller, PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PUSBMS_EXTENSION ExtDisk = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
|
|
if (ExtDisk->Signature == SIGNATURE_PARTITION) {
|
|
PUSBMS_PARTITION Partition = (PUSBMS_PARTITION)
|
|
DeviceObject->DeviceExtension;
|
|
ExtDisk = Partition->PhysicalDisk;
|
|
}
|
|
|
|
// Run a test unit ready and request sense to make sure everything's fine.
|
|
//PCONTROLLER_OBJECT CtrlObj = Ext->Controller;
|
|
//PUSBMS_CONTROLLER Controller = CtrlObj->ControllerExtension;
|
|
NTSTATUS Status = MspClearErrors(Controller, ExtDisk->Lun, 10);
|
|
if (!NT_SUCCESS(Status)) {
|
|
Irp->IoStatus.Status = Status;
|
|
//KeInsertQueueDpc(&ExtDisk->FinishDpc, Irp, CtrlObj);
|
|
//MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
|
|
// Perform the I/O operation on the work thread.
|
|
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
// Calculate the sector offset and length.
|
|
ULONG SectorShift = ExtDisk->SectorShift;
|
|
ULONG SectorStart = (ULONG)(Stack->Parameters.Read.ByteOffset.QuadPart >> SectorShift);
|
|
ULONG SectorCount = (ULONG)(Stack->Parameters.Read.Length >> SectorShift);
|
|
//NTSTATUS Status = STATUS_INVALID_PARAMETER;
|
|
if ((SectorCount << SectorShift) != Stack->Parameters.Read.Length) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
//KeInsertQueueDpc(&ExtDisk->FinishDpc, Irp, CtrlObj);
|
|
//MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
|
|
if (Stack->MajorFunction != IRP_MJ_DEVICE_CONTROL) {
|
|
PVOID Buffer = MmGetSystemAddressForMdl(Irp->MdlAddress);
|
|
if (Buffer == NULL) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
Irp->IoStatus.Information = 0;
|
|
//KeInsertQueueDpc(&ExtDisk->FinishDpc, Irp, CtrlObj);
|
|
//MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
ULONG Transferred;
|
|
ULONG Length = Stack->Parameters.Read.Length;
|
|
if (Stack->MajorFunction == IRP_MJ_READ) {
|
|
Status = MspLowRead(Controller, ExtDisk->Lun, SectorStart, SectorCount, SectorShift, Buffer);
|
|
} else if (Stack->MajorFunction == IRP_MJ_WRITE) {
|
|
Status = MspLowWrite(Controller, ExtDisk->Lun, SectorStart, SectorCount, SectorShift, Buffer);
|
|
}
|
|
Irp->IoStatus.Status = Status;
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
Irp->IoStatus.Information = Length;
|
|
}
|
|
//KeInsertQueueDpc(&ExtDisk->FinishDpc, Irp, CtrlObj);
|
|
//MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
|
|
// This is verify.
|
|
// Perform a READ(10) but throw the contents away, this should simulate verify.
|
|
// VERIFY(10) does not allow specific LUN to be used, and some USB devices might not support it, so...
|
|
Status = MspLowVerify(Controller, ExtDisk->Lun, SectorStart, SectorCount, SectorShift);
|
|
Irp->IoStatus.Status = Status;
|
|
//KeInsertQueueDpc(&ExtDisk->FinishDpc, Irp, CtrlObj);
|
|
//MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
}
|
|
|
|
static void MsIoWorkLocked(PUSBMS_CONTROLLER Controller, PUSBMS_LOCK_CONTEXT Context) {
|
|
MsIoWorkLockedImpl(
|
|
Controller,
|
|
Context->DeviceObject,
|
|
Context->Irp
|
|
);
|
|
KeSetEvent(&Context->Event, (KPRIORITY)0, FALSE);
|
|
}
|
|
|
|
void MsIoWorkImpl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PUSBMS_EXTENSION ExtDisk = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
if (ExtDisk->Signature == SIGNATURE_PARTITION) {
|
|
PUSBMS_PARTITION Partition = (PUSBMS_PARTITION)
|
|
DeviceObject->DeviceExtension;
|
|
ExtDisk = Partition->PhysicalDisk;
|
|
}
|
|
|
|
PCONTROLLER_OBJECT CtrlObj = ExtDisk->Controller;
|
|
PUSBMS_CONTROLLER Controller = CtrlObj->ControllerExtension;
|
|
|
|
PUSBMS_LOCK_CONTEXT Context = MspGetStateContext(Controller);
|
|
if (Context == NULL) {
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
|
|
Context->DeviceObject = DeviceObject;
|
|
Context->Irp = Irp;
|
|
|
|
NTSTATUS Status = MspLockController(MsIoWorkLocked, Controller, Context);
|
|
if (!NT_SUCCESS(Status)) {
|
|
MspReleaseStateContext(Context);
|
|
Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
return;
|
|
}
|
|
|
|
KeWaitForSingleObject( &Context->Event, Executive, KernelMode, FALSE, NULL);
|
|
MspReleaseStateContext(Context);
|
|
MspFinishDpc(NULL, DeviceObject, Irp, CtrlObj);
|
|
}
|
|
|
|
void MsIoWorkRoutine(PUSBMS_WORK_ITEM Parameter) {
|
|
// Grab the parameters.
|
|
PDEVICE_OBJECT DeviceObject = Parameter->DeviceObject;
|
|
PIRP Irp = Parameter->Irp;
|
|
PUSBMS_EXTENSION ExtDisk = Parameter->ExtDisk;
|
|
PCONTROLLER_OBJECT CtrlObj = ExtDisk->Controller;
|
|
PUSBMS_CONTROLLER Controller = CtrlObj->ControllerExtension;
|
|
|
|
// Free the work item.
|
|
ExFreePool(Parameter);
|
|
|
|
MsIoWorkImpl(DeviceObject, Irp);
|
|
}
|
|
|
|
IO_ALLOCATION_ACTION MsIoSeizedController(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID NullBase, PVOID Context) {
|
|
PUSBMS_EXTENSION ExtDisk = (PUSBMS_EXTENSION) Context;
|
|
|
|
// Touching disk needs to be done by a worker.
|
|
// This is because we need to wait on events, and we are currently at DISPATCH_LEVEL.
|
|
|
|
// Allocate the work item
|
|
PUSBMS_WORK_ITEM WorkItem = (PUSBMS_WORK_ITEM)
|
|
ExAllocatePool(NonPagedPool, sizeof(USBMS_WORK_ITEM));
|
|
if (WorkItem == NULL) {
|
|
// um.
|
|
Irp->IoStatus.Status = STATUS_NO_MEMORY;
|
|
// Complete the request.
|
|
IoCompleteRequest(Irp, IO_DISK_INCREMENT);
|
|
// Start next packet.
|
|
IoStartNextPacket(DeviceObject, FALSE);
|
|
// Release the controller on return so next packet can seize it.
|
|
return DeallocateObject;
|
|
}
|
|
|
|
// Fill in the parameters.
|
|
WorkItem->DeviceObject = DeviceObject;
|
|
WorkItem->Irp = Irp;
|
|
WorkItem->ExtDisk = ExtDisk;
|
|
|
|
// Initialise the ExWorkItem
|
|
ExInitializeWorkItem(
|
|
&WorkItem->WorkItem,
|
|
(PWORKER_THREAD_ROUTINE) MsIoWorkRoutine,
|
|
WorkItem
|
|
);
|
|
|
|
// Queue it, and keep the controller object held.
|
|
BOOLEAN IsForPaging = (Irp->Flags & IRP_PAGING_IO) != 0;
|
|
ExQueueWorkItem(&WorkItem->WorkItem, IsForPaging ? CriticalWorkQueue : DelayedWorkQueue);
|
|
return KeepObject;
|
|
}
|
|
|
|
void MsStartIo(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
// Ensure the device extension is actually valid.
|
|
if (Ext == NULL || (
|
|
Ext->Signature != SIGNATURE_PARTITION &&
|
|
Ext->Signature != SIGNATURE_DISK
|
|
) || Ext->Controller == NULL) {
|
|
// this is probably keyboard or mouse driver
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
IoStartNextPacket(DeviceObject, FALSE);
|
|
return;
|
|
}
|
|
// If this is a partition, get the physical disk.
|
|
PUSBMS_PARTITION Partition = NULL;
|
|
if (Ext->Signature == SIGNATURE_PARTITION) {
|
|
Partition = (PUSBMS_PARTITION) Ext;
|
|
Ext = Partition->PhysicalDisk;
|
|
}
|
|
|
|
// ensure this is actually a disk
|
|
if (Ext->Signature != SIGNATURE_DISK) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
IoStartNextPacket(DeviceObject, FALSE);
|
|
return;
|
|
}
|
|
|
|
// Seize the controller object to obtain exclusive access to its DDR buffer.
|
|
IoAllocateController(
|
|
Ext->Controller,
|
|
DeviceObject,
|
|
(PDRIVER_CONTROL)MsIoSeizedController,
|
|
Ext
|
|
);
|
|
}
|
|
|
|
void MspUpdateDevices(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
PUSBMS_PARTITION Partition = NULL;
|
|
if (Ext->Signature == SIGNATURE_PARTITION) {
|
|
Partition = (PUSBMS_PARTITION)DeviceObject->DeviceExtension;
|
|
Ext = Partition->PhysicalDisk;
|
|
}
|
|
if (Ext->Signature != SIGNATURE_DISK) {
|
|
return;
|
|
}
|
|
|
|
// Ensure that this device object describes a hard disk
|
|
if (DeviceObject->DeviceType == FILE_DEVICE_CD_ROM) return;
|
|
if ((DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) != 0) return;
|
|
|
|
PDRIVE_LAYOUT_INFORMATION PartitionList = (PDRIVE_LAYOUT_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ULONG PartitionCount = ((PartitionList->PartitionCount + 3) / 4) * 4;
|
|
|
|
// Zero all the partition numbers.
|
|
for (ULONG Partition = 0; Partition < PartitionCount; Partition++) {
|
|
PartitionList->PartitionEntry[Partition].PartitionNumber = 0;
|
|
}
|
|
|
|
// Walk through the partitions to determine if any existing partitions were changed or deleted.
|
|
ULONG LastPartitionNumber = 0;
|
|
PUSBMS_PARTITION LastPartition = NULL;
|
|
for (PUSBMS_PARTITION This = Ext->NextPartition; This != NULL; This = This->NextPartition) {
|
|
LastPartition = This;
|
|
|
|
if (This->PartitionNumber > LastPartitionNumber)
|
|
LastPartitionNumber = This->PartitionNumber;
|
|
|
|
// If the partition length is zero then it's unused.
|
|
if (This->PartitionLength.QuadPart == 0) continue;
|
|
|
|
// Look for a match in the partition list.
|
|
BOOLEAN Found = FALSE;
|
|
PPARTITION_INFORMATION PartitionEntry = NULL;
|
|
for (ULONG Partition = 0; Partition < PartitionCount; Partition++) {
|
|
PartitionEntry = &PartitionList->PartitionEntry[Partition];
|
|
|
|
// Skip this if it's empty.
|
|
if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED) continue;
|
|
|
|
// Skip this if it's an extended partition.
|
|
if (IsContainerPartition(PartitionEntry->PartitionType)) continue;
|
|
|
|
// Offset is equal?
|
|
if (PartitionEntry->StartingOffset.QuadPart != This->StartingOffset.QuadPart) continue;
|
|
|
|
// Length is equal?
|
|
if (PartitionEntry->PartitionLength.QuadPart != This->PartitionLength.QuadPart) continue;
|
|
|
|
// Found the correct partition.
|
|
Found = TRUE;
|
|
PartitionEntry->PartitionNumber = This->PartitionNumber;
|
|
break;
|
|
}
|
|
|
|
if (Found) {
|
|
// Change the partition type if needed.
|
|
if (PartitionEntry->RewritePartition)
|
|
This->PartitionType = PartitionEntry->PartitionType;
|
|
} else {
|
|
// Didn't find a match, thus this partition was deleted.
|
|
This->PartitionLength.QuadPart = 0;
|
|
}
|
|
}
|
|
|
|
// Walk through the partitions to determine if any new partitions were created.
|
|
for (ULONG Partition = 0; Partition < PartitionCount; Partition++) {
|
|
PPARTITION_INFORMATION PartitionEntry = &PartitionList->PartitionEntry[Partition];
|
|
|
|
// Skip this if it's empty.
|
|
if (PartitionEntry->PartitionType == PARTITION_ENTRY_UNUSED) continue;
|
|
|
|
// Skip this if it's an extended partition.
|
|
if (IsContainerPartition(PartitionEntry->PartitionType)) continue;
|
|
|
|
// Skip this if we know about it already
|
|
if (PartitionEntry->PartitionNumber != 0) continue;
|
|
|
|
// Find an existing, unused, partition.
|
|
ULONG PartitionNumber = 0;
|
|
PUSBMS_PARTITION ModifiedPartition = NULL;
|
|
for (PUSBMS_PARTITION This = Ext->NextPartition; This != NULL; This = This->NextPartition) {
|
|
if (This->PartitionLength.QuadPart != 0) continue;
|
|
PartitionNumber = This->PartitionNumber;
|
|
ModifiedPartition = This;
|
|
break;
|
|
}
|
|
|
|
// If none was found then one needs to be created.
|
|
if (PartitionNumber == 0) {
|
|
LastPartitionNumber++;
|
|
PartitionNumber = LastPartitionNumber;
|
|
|
|
// Initialise the pathname.
|
|
CCHAR NtName[MAXIMUM_FILENAME_LENGTH];
|
|
sprintf(NtName, "\\Device\\Harddisk%d\\Partition%d", Ext->DiskNumber, PartitionNumber);
|
|
STRING NtNameStr;
|
|
RtlInitString(&NtNameStr, NtName);
|
|
UNICODE_STRING NtNameUs;
|
|
NTSTATUS Status = RtlAnsiStringToUnicodeString(&NtNameUs, &NtNameStr, TRUE);
|
|
if (!NT_SUCCESS(Status)) continue;
|
|
|
|
// Create the device object.
|
|
PDEVICE_OBJECT NewDevice;
|
|
Status = IoCreateDevice(DeviceObject->DriverObject, sizeof(USBMS_PARTITION), &NtNameUs, FILE_DEVICE_DISK, 0, FALSE, &NewDevice);
|
|
RtlFreeUnicodeString(&NtNameUs);
|
|
|
|
if (!NT_SUCCESS(Status)) continue;
|
|
|
|
// Initialise the device object.
|
|
NewDevice->Flags |= DO_DIRECT_IO;
|
|
NewDevice->StackSize = DeviceObject->StackSize;
|
|
NewDevice->Flags &= ~DO_DEVICE_INITIALIZING;
|
|
|
|
// Initialise the device extension.
|
|
PUSBMS_PARTITION NewPartition = (PUSBMS_PARTITION) NewDevice->DeviceExtension;
|
|
NewPartition->Signature = SIGNATURE_PARTITION;
|
|
NewPartition->PhysicalDisk = Ext;
|
|
if (LastPartition != NULL) LastPartition->NextPartition = NewPartition;
|
|
else Ext->NextPartition = NewPartition;
|
|
NewPartition->NextPartition = NULL;
|
|
|
|
ModifiedPartition = NewPartition;
|
|
LastPartition = NewPartition;
|
|
}
|
|
// Update the partition information.
|
|
ModifiedPartition->PartitionNumber = PartitionNumber;
|
|
ModifiedPartition->PartitionType = PartitionEntry->PartitionType;
|
|
ModifiedPartition->StartingOffset = PartitionEntry->StartingOffset;
|
|
ModifiedPartition->PartitionLength = PartitionEntry->PartitionLength;
|
|
ModifiedPartition->HiddenSectors = PartitionEntry->HiddenSectors;
|
|
|
|
PartitionEntry->PartitionNumber = PartitionNumber;
|
|
}
|
|
}
|
|
|
|
NTSTATUS MsDiskDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
|
|
PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp);
|
|
PUSBMS_EXTENSION Ext = (PUSBMS_EXTENSION)DeviceObject->DeviceExtension;
|
|
PUSBMS_PARTITION Partition = NULL;
|
|
// Ensure the device extension is actually valid.
|
|
if (Ext == NULL || (
|
|
Ext->Signature != SIGNATURE_PARTITION &&
|
|
Ext->Signature != SIGNATURE_DISK
|
|
) || Ext->Controller == NULL) {
|
|
// this is probably keyboard or mouse driver
|
|
Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
if (Ext->Signature == SIGNATURE_PARTITION) {
|
|
Partition = (PUSBMS_PARTITION)DeviceObject->DeviceExtension;
|
|
Ext = Partition->PhysicalDisk;
|
|
}
|
|
if (Ext->Signature != SIGNATURE_DISK) {
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
PCONTROLLER_OBJECT CtrlObj = Ext->Controller;
|
|
PUSBMS_CONTROLLER Controller = (PUSBMS_CONTROLLER)CtrlObj->ControllerExtension;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
ULONG LenIn = Stack->Parameters.DeviceIoControl.InputBufferLength;
|
|
ULONG LenOut = Stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
BOOLEAN IsCdRom = DeviceObject->DeviceType == FILE_DEVICE_CD_ROM;
|
|
BOOLEAN IsFloppy = (DeviceObject->Characteristics & FILE_FLOPPY_DISKETTE) != 0;
|
|
BOOLEAN IsRemovable = DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA;
|
|
BOOLEAN FallThrough = FALSE;
|
|
|
|
if (IsCdRom) {
|
|
switch (Stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
FallThrough = TRUE;
|
|
break;
|
|
// Multisession / CD audio stuff : unimplemented.
|
|
// We only support a single data track as .ISO.
|
|
// If this stuff is unimplemented cdfs.sys does fall back to such anyways.
|
|
// case IOCTL_CDROM_GET_LAST_SESSION:
|
|
// case IOCTL_CDROM_READ_TOC:
|
|
// case IOCTL_CDROM_PLAY_AUDIO_MSF:
|
|
// case IOCTL_CDROM_SEEK_AUDIO_MSF:
|
|
// case IOCTL_CDROM_PAUSE_AUDIO:
|
|
// case IOCTL_CDROM_RESUME_AUDIO:
|
|
// case IOCTL_CDROM_READ_Q_CHANNEL:
|
|
// case IOCTL_CDROM_GET_CONTROL:
|
|
// case IOCTL_CDROM_GET_VOLUME:
|
|
// case IOCTL_CDROM_SET_VOLUME:
|
|
// case IOCTL_CDROM_STOP_AUDIO:
|
|
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
}
|
|
else if (IsFloppy) {
|
|
// we only have to implement this because things want to read from a floppy drive...
|
|
switch (Stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_DISK_FORMAT_TRACKS:
|
|
case IOCTL_DISK_FORMAT_TRACKS_EX:
|
|
case IOCTL_DISK_IS_WRITABLE:
|
|
Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
break;
|
|
case IOCTL_DISK_CHECK_VERIFY:
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
|
case IOCTL_DISK_GET_MEDIA_TYPES:
|
|
FallThrough = TRUE;
|
|
break;
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
FallThrough = TRUE;
|
|
}
|
|
|
|
if (!FallThrough) {
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
|
|
// not cdrom or floppy, or we fell through to common code
|
|
switch (Stack->Parameters.DeviceIoControl.IoControlCode) {
|
|
case IOCTL_DISK_IS_WRITABLE:
|
|
// Run check verify if removable media to update the WP bit.
|
|
if (IsRemovable) Status = MspCheckVerify(DeviceObject, Controller, Ext);
|
|
if (Ext->WriteProtected) Status = STATUS_MEDIA_WRITE_PROTECTED;
|
|
break;
|
|
case IOCTL_DISK_GET_MEDIA_TYPES:
|
|
if (!IsFloppy) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
// fall through, we only support the one geometry, which is the fatfs image we say is a floppy
|
|
case IOCTL_DISK_GET_DRIVE_GEOMETRY:
|
|
case IOCTL_CDROM_GET_DRIVE_GEOMETRY:
|
|
{
|
|
if (LenOut < sizeof(DISK_GEOMETRY)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
// Get the current information if needded.
|
|
if (IsRemovable) {
|
|
Status = MspCheckVerify(DeviceObject, Controller, Ext);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
}
|
|
PDISK_GEOMETRY Geometry = (PDISK_GEOMETRY) Irp->AssociatedIrp.SystemBuffer;
|
|
// Set up the disk geometry.
|
|
if (DeviceObject->Characteristics & FILE_REMOVABLE_MEDIA) {
|
|
Geometry->MediaType = RemovableMedia;
|
|
} else {
|
|
Geometry->MediaType = FixedMedia;
|
|
}
|
|
// Default sectors per track / tracks per head as per what SCSI devices do
|
|
Geometry->SectorsPerTrack = 32;
|
|
Geometry->TracksPerCylinder = 64;
|
|
// set BytesPerSector to the specified value.
|
|
// For an ISO use 2048 byte sectors as expected; it's a multiple of 512 anyway
|
|
// We will allow rws aligned to 512 no matter what.
|
|
LARGE_INTEGER DeviceLength;
|
|
if (Partition != NULL) DeviceLength = Partition->PartitionLength;
|
|
else {
|
|
DeviceLength.QuadPart = Ext->SectorCount;
|
|
DeviceLength.QuadPart <<= Ext->SectorShift;
|
|
}
|
|
Geometry->BytesPerSector = Ext->SectorSize;
|
|
Geometry->Cylinders.LowPart = Ext->SectorCount / (32 * 64);
|
|
Geometry->Cylinders.HighPart = 0;
|
|
// all done.
|
|
Irp->IoStatus.Information = sizeof(*Geometry);
|
|
}
|
|
break;
|
|
case IOCTL_DISK_CHECK_VERIFY:
|
|
case IOCTL_CDROM_CHECK_VERIFY:
|
|
{
|
|
// If device is not removable, just return success.
|
|
if (!IsRemovable) break;
|
|
// Run check verify.
|
|
Status = MspCheckVerify(DeviceObject, Controller, Ext);
|
|
}
|
|
break;
|
|
case IOCTL_DISK_VERIFY:
|
|
{
|
|
if (LenIn < sizeof(VERIFY_INFORMATION)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
Irp->IoStatus.Information = 0;
|
|
//IoMarkIrpPending(Irp);
|
|
// Make it look like a rw request for the async code.
|
|
PVERIFY_INFORMATION VerifyInfo = (PVERIFY_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
Stack->Parameters.Read.Length = VerifyInfo->Length;
|
|
Stack->Parameters.Read.ByteOffset.QuadPart = VerifyInfo->StartingOffset.QuadPart;
|
|
if (Partition != NULL)
|
|
Stack->Parameters.Read.ByteOffset.QuadPart += Partition->StartingOffset.QuadPart;
|
|
// make sure the end looks ok
|
|
LARGE_INTEGER DeviceLength;
|
|
DeviceLength.QuadPart = Ext->SectorSize;
|
|
DeviceLength.QuadPart <<= Ext->SectorShift;
|
|
LARGE_INTEGER OffsetEnd = Stack->Parameters.Read.ByteOffset;
|
|
OffsetEnd.QuadPart += Stack->Parameters.Read.Length;
|
|
if (OffsetEnd.QuadPart > DeviceLength.QuadPart || (Stack->Parameters.Read.Length & (Ext->SectorSize - 1) != 0)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
//ULONG SectorOffset = Stack->Parameters.Read.ByteOffset.QuadPart & (Ext->SectorSize - 1);
|
|
//IoStartPacket(Ext->DeviceObject, Irp, &SectorOffset, NULL);
|
|
//return STATUS_PENDING;
|
|
// Just return success here.
|
|
}
|
|
break;
|
|
case IOCTL_DISK_GET_PARTITION_INFO:
|
|
{
|
|
if (LenOut < sizeof(PARTITION_INFORMATION)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (Partition == NULL) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
PPARTITION_INFORMATION Info = (PPARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
Info->PartitionType = Partition->PartitionType;
|
|
Info->StartingOffset = Partition->StartingOffset;
|
|
Info->PartitionLength = Partition->PartitionLength;
|
|
Info->HiddenSectors = Partition->HiddenSectors;
|
|
Info->PartitionNumber = Partition->PartitionNumber;
|
|
|
|
Irp->IoStatus.Information = sizeof(*Info);
|
|
}
|
|
break;
|
|
case IOCTL_DISK_SET_PARTITION_INFO:
|
|
{
|
|
if (LenIn < sizeof(SET_PARTITION_INFORMATION)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (Partition == NULL) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
PSET_PARTITION_INFORMATION Info = (PSET_PARTITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer;
|
|
Status = IoSetPartitionInformation(Ext->DeviceObject, Ext->SectorSize, (ULONG) Partition->PartitionNumber, Info->PartitionType);
|
|
if (NT_SUCCESS(Status)) Partition->PartitionType = Info->PartitionType;
|
|
}
|
|
break;
|
|
case IOCTL_DISK_GET_DRIVE_LAYOUT:
|
|
{
|
|
if (LenOut < sizeof(DRIVE_LAYOUT_INFORMATION)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if (IsFloppy) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
PDRIVE_LAYOUT_INFORMATION PartitionList;
|
|
Status = IoReadPartitionTable(Ext->DeviceObject, Ext->SectorSize, FALSE, &PartitionList);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
// what's the size of the buffer we were given?
|
|
ULONG Size = __builtin_offsetof(DRIVE_LAYOUT_INFORMATION, PartitionEntry);
|
|
Size += PartitionList->PartitionCount * sizeof(PartitionList->PartitionEntry[0]);
|
|
if (LenOut < Size) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
Status = STATUS_SUCCESS;
|
|
RtlMoveMemory(Irp->AssociatedIrp.SystemBuffer, PartitionList, Size);
|
|
Irp->IoStatus.Information = Size;
|
|
MspUpdateDevices(DeviceObject, Irp);
|
|
ExFreePool(PartitionList);
|
|
}
|
|
break;
|
|
case IOCTL_DISK_SET_DRIVE_LAYOUT:
|
|
{
|
|
if (LenIn < sizeof(DRIVE_LAYOUT_INFORMATION)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
if (IsFloppy) {
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
PDRIVE_LAYOUT_INFORMATION PartitionList = (PDRIVE_LAYOUT_INFORMATION) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
MspUpdateDevices(DeviceObject, Irp);
|
|
|
|
Status = IoWritePartitionTable(Ext->DeviceObject, Ext->SectorSize, 32, 64, PartitionList);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
Irp->IoStatus.Information = Stack->Parameters.DeviceIoControl.OutputBufferLength;
|
|
}
|
|
break;
|
|
case IOCTL_DISK_INTERNAL_SET_VERIFY:
|
|
if (Irp->RequestorMode != KernelMode) break;
|
|
DeviceObject->Flags |= DO_VERIFY_VOLUME;
|
|
break;
|
|
case IOCTL_DISK_INTERNAL_CLEAR_VERIFY:
|
|
if (Irp->RequestorMode != KernelMode) break;
|
|
DeviceObject->Flags &= ~DO_VERIFY_VOLUME;
|
|
break;
|
|
case IOCTL_SCSI_GET_ADDRESS:
|
|
{
|
|
if (LenOut < sizeof(SCSI_ADDRESS)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
PSCSI_ADDRESS ScsiAddress = (PSCSI_ADDRESS) Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
ScsiAddress->Length = sizeof(ScsiAddress);
|
|
ScsiAddress->PortNumber = 0;
|
|
if (IsFloppy) ScsiAddress->PathId = 0;
|
|
else if (IsCdRom) ScsiAddress->PathId = 1;
|
|
else ScsiAddress->PathId = 2;
|
|
ScsiAddress->TargetId = Ext->DiskNumber;
|
|
ScsiAddress->Lun = Ext->Lun;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
|
|
Irp->IoStatus.Status = Status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS MspInitDevice(
|
|
PDRIVER_OBJECT DriverObject,
|
|
IOS_USB_HANDLE DeviceHandle,
|
|
ULONG ArcKey,
|
|
PVOID UsbBuffer,
|
|
PULONG UsbOffset,
|
|
PCONFIGURATION_INFORMATION IoConfig
|
|
) {
|
|
// Open device.
|
|
NTSTATUS Status = UlOpenDevice(DeviceHandle);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
do {
|
|
// Get device descriptors.
|
|
USB_DEVICE_DESC Descriptors;
|
|
Status = UlGetDescriptors(DeviceHandle, &Descriptors);
|
|
if (!NT_SUCCESS(Status)) break;
|
|
|
|
if (Descriptors.Device.bNumConfigurations == 0) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
|
|
PUSB_CONFIGURATION Config = &Descriptors.Config;
|
|
if (Config->bNumInterfaces == 0) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
PUSB_INTERFACE Interface = &Descriptors.Interface;
|
|
if (Interface->bInterfaceClass != USB_CLASS_MASS_STORAGE) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
if (Interface->bInterfaceProtocol != MASS_STORAGE_BULK_ONLY) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
if (Interface->bNumEndpoints < 2) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
|
|
BOOLEAN IsUsb2 = FALSE;
|
|
UCHAR EndpointIn = 0, EndpointOut = 0;
|
|
for (ULONG i = 0; i < Interface->bNumEndpoints; i++) {
|
|
PUSB_ENDPOINT Endpoint = &Descriptors.Endpoints[i];
|
|
if (Endpoint->bmAttributes != USB_ENDPOINT_BULK) continue;
|
|
if ((Endpoint->bEndpointAddress & USB_ENDPOINT_IN) != 0) {
|
|
EndpointIn = Endpoint->bEndpointAddress;
|
|
} else {
|
|
EndpointOut = Endpoint->bEndpointAddress;
|
|
IsUsb2 = Endpoint->wMaxPacketSize > 64;
|
|
}
|
|
}
|
|
|
|
if (EndpointIn == 0 || EndpointOut == 0) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
|
|
// Ignore failing status as some devices do not support these
|
|
UCHAR CurrConfig = 0;
|
|
UlGetConfiguration(DeviceHandle, &CurrConfig);
|
|
if (CurrConfig != Config->bConfigurationValue) {
|
|
UlSetConfiguration(DeviceHandle, Config->bConfigurationValue);
|
|
}
|
|
if (Interface->bAlternateSetting != 0) {
|
|
UlSetAlternativeInterface(DeviceHandle, Interface->bInterfaceNumber, Interface->bAlternateSetting);
|
|
}
|
|
// libogc implementation sends a reset here for usb1;
|
|
// however we're only using usbv5, so is that actually needed?
|
|
// update: turns out it is not, this breaks writes for some reason???
|
|
//MspReset(DeviceHandle, Interface->bInterfaceNumber, EndpointIn, EndpointOut);
|
|
|
|
PUCHAR pMaxLun = (PUCHAR)HalIopAlloc(sizeof(UCHAR));
|
|
if (pMaxLun == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
Status = MspCtrlMsgTimeout(DeviceHandle,
|
|
(USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE),
|
|
USBSTORAGE_GET_MAX_LUN,
|
|
0,
|
|
Interface->bInterfaceNumber,
|
|
1,
|
|
pMaxLun,
|
|
USBSTORAGE_TIMEOUT
|
|
);
|
|
ULONG MaxLun = *pMaxLun + 1;
|
|
HalIopFree(pMaxLun);
|
|
if (Status == STATUS_IO_TIMEOUT) {
|
|
Status = STATUS_NO_SUCH_DEVICE;
|
|
break;
|
|
}
|
|
if (!NT_SUCCESS(Status)) MaxLun = 1;
|
|
|
|
// This device is working USB mass storage device.
|
|
// We can now do SCSI inquiry and read capacity on all LUNs;
|
|
// and create disk device for all LUNs.
|
|
|
|
// First, create the controller for this device.
|
|
PCONTROLLER_OBJECT Controller = IoCreateController(sizeof(USBMS_CONTROLLER));
|
|
if (Controller == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
// Grab the extension.
|
|
PUSBMS_CONTROLLER Extension = (PUSBMS_CONTROLLER)
|
|
Controller->ControllerExtension;
|
|
|
|
// Fill in the parameters.
|
|
Extension->DeviceHandle = DeviceHandle;
|
|
Extension->EndpointIn = EndpointIn;
|
|
Extension->EndpointOut = EndpointOut;
|
|
Extension->Interface = Interface->bInterfaceNumber;
|
|
Extension->MaxSize = IsUsb2 ? MAX_TRANSFER_SIZE_V2 : MAX_TRANSFER_SIZE_V1;
|
|
Extension->Buffer = (PVOID) (*UsbOffset + (ULONG)UsbBuffer);
|
|
KeInitializeDeviceQueue(&Extension->LockQueue);
|
|
*UsbOffset += Extension->MaxSize;
|
|
|
|
BOOLEAN HasValidLun = FALSE;
|
|
BOOLEAN EntryPointsSet = DriverObject->DriverStartIo == MsStartIo;
|
|
CHAR Buffer[512];
|
|
for (int lun = 0; lun < MaxLun; lun++) {
|
|
|
|
KeStallExecutionProcessor(50);
|
|
Status = MspClearErrors(Extension, lun, USBSTORAGE_TIMEOUT);
|
|
if (Status < 0) {
|
|
MspReset(Extension->DeviceHandle, Extension->Interface, Extension->EndpointIn, Extension->EndpointOut);
|
|
Status = MspClearErrors(Extension, lun, USBSTORAGE_TIMEOUT);
|
|
}
|
|
|
|
USBMS_DISK_TYPE DiskType = USBMS_DISK_UNKNOWN;
|
|
Status = MspGetDiskType(Extension, lun, &DiskType, USBSTORAGE_TIMEOUT);
|
|
if (!NT_SUCCESS(Status)) {
|
|
_snprintf(Buffer, sizeof(Buffer), "MspGetDiskType returned %08x\n", Status);
|
|
HalDisplayString(Buffer);
|
|
continue;
|
|
}
|
|
|
|
// Get the drive capacity, if needed.
|
|
ULONG SectorSize = 0;
|
|
ULONG SectorCount = 0;
|
|
Status = MspReadCapacity(Extension, lun, &SectorSize, &SectorCount, USBSTORAGE_TIMEOUT);
|
|
if (!NT_SUCCESS(Status) && DiskType == USBMS_DISK_OTHER_FIXED) {
|
|
_snprintf(Buffer, sizeof(Buffer), "MspReadCapacity returned %08x\n", Status);
|
|
HalDisplayString(Buffer);
|
|
continue;
|
|
}
|
|
|
|
// Set up the usbms dispatch callbacks.
|
|
if (!EntryPointsSet) {
|
|
DriverObject->DriverStartIo = MsStartIo;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = MsDiskRw;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = MsDiskRw;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MsDiskDeviceControl;
|
|
EntryPointsSet = TRUE;
|
|
}
|
|
// Create the device.
|
|
Status = MspDiskCreate(DriverObject, Controller, lun, SectorSize, SectorCount, IoConfig, ArcKey & ~BIT(31), DiskType);
|
|
if (!NT_SUCCESS(Status)) {
|
|
_snprintf(Buffer, sizeof(Buffer), "MspDiskCreate returned %08x\n", Status);
|
|
HalDisplayString(Buffer);
|
|
continue;
|
|
}
|
|
|
|
HasValidLun = TRUE;
|
|
}
|
|
if (HasValidLun) Status = STATUS_SUCCESS;
|
|
} while (FALSE);
|
|
//if (!NT_SUCCESS(Status)) UlCloseDevice(DeviceHandle);
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS UlmsInit(PDRIVER_OBJECT DriverObject) {
|
|
// Map USB buffer uncached.
|
|
// (We will always be using 32-bit writes to it.)
|
|
// (...as sector size is always 32-bit aligned)
|
|
PHYSICAL_ADDRESS UsbBufferPhysical = {.QuadPart = 0};
|
|
UsbBufferPhysical.LowPart = USB_BUFFER_PHYS_START;
|
|
PVOID UsbBuffer = MmMapIoSpace( UsbBufferPhysical, USB_BUFFER_LENGTH, MmNonCached );
|
|
if (UsbBuffer == NULL) {
|
|
// well, we can't do anything!
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
// Get device list.
|
|
|
|
PIOS_USB_DEVICE_ENTRY Entries = (PIOS_USB_DEVICE_ENTRY)
|
|
HalIopAlloc(sizeof(IOS_USB_DEVICE_ENTRY_MAX));
|
|
if (Entries == NULL) return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
RtlZeroMemory(Entries, sizeof(IOS_USB_DEVICE_ENTRY_MAX));
|
|
|
|
UCHAR NumVen = 0;
|
|
UlGetDeviceList(Entries, USB_COUNT_DEVICES, USB_CLASS_MASS_STORAGE, &NumVen);
|
|
|
|
|
|
// Get the configuration information.
|
|
PCONFIGURATION_INFORMATION Config = IoGetConfigurationInformation();
|
|
|
|
ULONG UsbOffset = 0;
|
|
UCHAR NumMs = 0;
|
|
for (ULONG i = 0; i < NumVen; i++) {
|
|
USHORT Vid = Entries[i].VendorId;
|
|
USHORT Pid = Entries[i].ProductId;
|
|
if (Vid == 0 || Pid == 0) continue;
|
|
if (Vid == 0x0b95 && Pid == 0x7720) {
|
|
// this is actually a USB network adapter
|
|
continue;
|
|
}
|
|
|
|
// Create the ARC key, which is a 32-bit value:
|
|
// top 16 bits is vendor ID and lower 16 bits is product ID
|
|
ULONG ArcKey = Vid;
|
|
ArcKey <<= 16;
|
|
ArcKey |= Pid;
|
|
// Check if this is a USB mass storage device and create the disk device object if so.
|
|
NTSTATUS Status = MspInitDevice(DriverObject, Entries[i].DeviceHandle, ArcKey, UsbBuffer, &UsbOffset, Config);
|
|
if (NT_SUCCESS(Status)) {
|
|
// successful, device was created.
|
|
NumMs++;
|
|
}
|
|
}
|
|
|
|
// If no mass storage devices were found, then return as much.
|
|
if (NumMs == 0) return STATUS_NO_SUCH_DEVICE;
|
|
|
|
return STATUS_SUCCESS;
|
|
} |