/*++ Copyright (c) 1996-2000 Microsoft Corporation Module Name: MU.H Abstract: Header file for MU driver Environment: kernel mode Revision History: 06-13-2000 : started rewrite : georgioc 10-11-2000 : cleanup : mitchd --*/ //***************************************************************************** // Includes //***************************************************************************** extern "C" { #include } #include #include #include #include #include #include #include #define MODULE_POOL_TAG '__UM' #include //***************************************************************************** // Forward declaration //***************************************************************************** //***************************************************************************** // Useful Macros //***************************************************************************** #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) #define SET_FLAG(Flags, Bit) ((Flags) |= (Bit)) #define CLEAR_FLAG(Flags, Bit) ((Flags) &= ~(Bit)) #define TEST_FLAG(Flags, Bit) ((Flags) & (Bit)) #define TEST_ALL_FLAGS(Flags, Bits) ((Bits) == ((Flags) & (Bits))) #if DBG || DEBUG_LOG extern ULONG MU_DebugFlags; // DBGF_* Flags extern ULONG MU_DebugLevel; // Level of debug output #endif // A better WhichBit written as an inline template function. // It returns the lowest order bit set. If only one bit is set // template inline UCHAR WhichBit(T data) { UCHAR bit; T mask; for( bit=0, mask=1; bit < sizeof(T)*8; bit++, mask <<= 1) { if(mask&data) break; } ASSERT(bit != sizeof(T)*8); return bit; } template inline BOOL IsPowerOf2(T data) /* Routine Description: Returns TRUE if data is a power of 2. Implemented as inline template to work on any data type, with the type safety of a real function, but the efficiency of a MACRO. bitwise ANDing a value with itself minus one drops the lowest bit. If the result is zero then it is a power of 2, provided that it wasn't zero or one to begin with. Ex. 010110 => 010110 & 010101 == 010100 NOT Power of 2. Ex. 001000 => 001000 & 000111 == 000000 Power of 2. */ { return ((data > 1) && !(data&(data-1))) ? TRUE : FALSE; } // // ReverseEndian for ULONG and USHORT, could have // used a template instead of overloading, but // the loop would have been less efficient. // inline ULONG ReverseEndian(ULONG Data) { ULONG RetData; ((BYTE *)&RetData)[0]=((BYTE *)&Data)[3]; ((BYTE *)&RetData)[1]=((BYTE *)&Data)[2]; ((BYTE *)&RetData)[2]=((BYTE *)&Data)[1]; ((BYTE *)&RetData)[3]=((BYTE *)&Data)[0]; return RetData; } inline USHORT ReverseEndian(USHORT Data) { USHORT RetData; ((BYTE *)&RetData)[0]=((BYTE *)&Data)[1]; ((BYTE *)&RetData)[1]=((BYTE *)&Data)[0]; return RetData; } //***************************************************************************** // Defintions for a debug only IRP watchdog //***************************************************************************** #if DBG #define MU_DEBUG_DECLARE_WATCHDOG_PARAMETERS() \ KTIMER DbgIrpTimer;\ KDPC DbgIrpTimeoutDpc;\ LARGE_INTEGER DbgIrpTimeoutTime; #define MU_DEBUG_INIT_WATCHDOG_PARAMETERS(DeviceExtension) MUDebugInitWatchDogParameters(DeviceExtension) #define MU_DEBUG_SET_WATCHDOG(DeviceExtension) MuDebugSetWatchDogTimer(DeviceExtension) #define MU_DEBUG_PET_WATCHDOG(DeviceExtension) MuDebugPetWatchDogTimer(DeviceExtension) #define MU_COMPLETE_REQUEST(DeviceExtension, Irp, PriorityBoost) MuDebugCompleteRequest(DeviceExtension, Irp, PriorityBoost) #else #define MU_DEBUG_INIT_WATCHDOG_PARAMETERS(DeviceExtension) #define MU_DEBUG_DECLARE_WATCHDOG_PARAMETERS() #define MU_DEBUG_SET_WATCHDOG(DeviceExtension) #define MU_DEBUG_PET_WATCHDOG(DeviceExtension) #define MU_COMPLETE_REQUEST(DeviceExtension, Irp, PriorityBoost) IoCompleteRequest((Irp), (PriorityBoost)) #endif //***************************************************************************** // USB Definitions that are not elsewhere //***************************************************************************** // // Interface Descriptor values // #define MU_SUBCLASS_RBC 0x01 #define MU_SUBCLASS_TRANSPARENT 42 #define MU_PROTOCOL_BULK_ONLY 0x50 // // Bulk-Only class-specific bRequest codes // #define BULK_ONLY_MASS_STORAGE_RESET 0xFF //***************************************************************************** // Declarations for performing diagnostics on Memory Units (requires special build) //***************************************************************************** #ifdef MU_DIAGNOSTIC_IOCTLS // // VENDOR Specific Debug Commands bRequest // #define MU_VSC_GET_BAD_BLOCK_TABLE 0 #define MU_VSC_MEMORY_TEST 1 // // Definitions for VENDOR Specific Debug Commands // #define MU_VSC_BAD_BLOCK_TABLE_SIZE (sizeof(USHORT)*16) #define MU_VSC_BAD_BLOCK_COUNT_SIZE sizeof(USHORT) // // IOCTL to expose the vendor specific commands // (supported only in debug builds) // #define MU_IOCTL_GET_BAD_BLOCK_TABLE\ CTL_CODE(FILE_DEVICE_DISK, MU_VSC_GET_BAD_BLOCK_TABLE, METHOD_NEITHER, FILE_ANY_ACCESS) #define MU_IOCTL_MEMORY_TEST\ CTL_CODE(FILE_DEVICE_DISK, MU_VSC_MEMORY_TEST, METHOD_NEITHER, FILE_ANY_ACCESS) #endif //end of DIAGNOSTIC IOCTLS //***************************************************************************** // USB Bulk-Only Protocol Definitions //***************************************************************************** // // The MU driver requires that MU BULK endpoints // have a wMaxPacket of MU_MAX_BULK_PACKET_SIZE // (extra stringent Xbox requirement) #define MU_MAX_BULK_PACKET_SIZE 0x40 // // Command Block Wrapper Signature 'USBC' // #define CBW_SIGNATURE 0x43425355 #define CBW_FLAGS_DATA_IN 0x80 #define CBW_FLAGS_DATA_OUT 0x00 // // Command Status Wrapper Signature 'USBS' // #define CSW_SIGNATURE 0x53425355 #define CSW_STATUS_GOOD 0x00 #define CSW_STATUS_FAILED 0x01 #define CSW_STATUS_PHASE_ERROR 0x02 #pragma pack (push, 1) // // Command Block Wrapper // typedef struct _CBW { ULONG dCBWSignature; ULONG dCBWTag; ULONG dCBWDataTransferLength; UCHAR bCBWFlags; UCHAR bCBWLUN; UCHAR bCDBLength; union { UCHAR CBWCDB[16]; CDB Cdb; }; } CBW, *PCBW; // Command Status Wrapper // typedef struct _CSW { ULONG dCSWSignature; ULONG dCSWTag; ULONG dCSWDataResidue; UCHAR bCSWStatus; } CSW, *PCSW; #pragma pack (pop) //****************************************************************************** // Various Hard-coded sizes. // // EMULATED_SECTOR_SIZE - This is the sector size reported to the filesystem. // It is the smallest unit of data the filesystem will ever try to // read or write. // // MEDIA_BLOCK_SIZE - This is the size of a block on the media. A block on the // media is the smallest unit which can be erased. All writes must // start on a block boundary and end on a block boundary. The Xbox MU // protocol does NOT currently provide a mechanism to query the device. // This value is is hardcoded as 8192 bytes. Devices with smaller // media block sizes should work fine with this driver, devices with // larger media block sizes would not work. // // The other type of block size is the logical block size. This is reported // back as BytesPerBlock in the READ_CAPACITY_DATA structure received in // in response to a read capacity command. Any power of 2 value less than // 4096 is supported. This is typically the page size of the media, which // is the smallest unit which may be read or written (but not erased, // or rewritten). For the standard Xbox hawk this value is 512 bytes. // // WRITE_BUFFER_SIZE - This buffer is used store the original contents // of the portion of a media block that we need to write over. When // a write for a portion of a media block is requested, the original // contents are read into this block, and then the whole thing is // written back out. // //****************************************************************************** #define EMULATED_SECTOR_SIZE 4096 #define EMULATED_SECTOR_SHIFT 12 #define MAXIMUM_MEDIA_BLOCK_SIZE 16384 #define DEFAULT_MEDIA_BLOCK_SIZE 8192 #define WRITE_BUFFER_SIZE (MAXIMUM_MEDIA_BLOCK_SIZE - EMULATED_SECTOR_SIZE) //***************************************************************************** // Forward declarations of pointer to structure types //***************************************************************************** typedef struct _MU_DRIVER_EXTENSION *PMU_DRIVER_EXTENSION; typedef struct _MU_INSTANCE *PMU_INSTANCE; typedef struct _MU_DEVICE_EXTENSION *PMU_DEVICE_EXTENSION; //***************************************************************************** // Driver object and driver(not device) specific definitions //***************************************************************************** #define MU_CONTROLLERS_PER_PORT 2 #define MU_DEFAULT_MAX_MOUNTED 8 /* DEVICE_INDEX_FROM_PORT_AND_SLOT depends on MU_CONTROLLERS_PER_PORT = 2 */ #define NODE_INDEX_FROM_PORT_AND_SLOT(port, slot) ((port<<1)+slot) typedef struct MU_DRIVER_EXTENSION { PMU_DEVICE_EXTENSION DeviceObjectFreeList; // // Resources for handling writes to only a portion of // a media block. Not that there is one global WriteBuffer // and partial requests are queued. // LIST_ENTRY PartialRequestQueue; UCHAR WriteBuffer[WRITE_BUFFER_SIZE]; PMU_INSTANCE Nodes; } MU_DRIVER_EXTENSION; extern MU_DRIVER_EXTENSION MU_DriverExtension; //***************************************************************************** // MU Request Block (MRB) defintions //***************************************************************************** #define MRB_TIMEOUT_UNIT (-100000) //This constant represents 10 ms to the timer routines. #define MRB_STANDARD_TIMEOUT (8) //Standard Timeout is 80 ms (spec says 50 ms), this applies to CBW's #define MRB_DATA_TIMEOUT (15) //Time out is 150 ms for each 2k portion of a read or write. #define MRB_RESET_TIMEOUT (10) //Time out is 100 ms for each stage of the reset sequence. #define MRB_READ_CAPACITY_TIMEOUT (10) // a 100 ms delay for the CSW of read capacity. #define MRB_VERIFY_TIMEOUT_PER_64K (100) // 1 second per 64 kbytes verified for CSW of VERIFY. #define MRB_CALC_VERIFY_TIMEOUT(VerifyLength) ((USHORT)(MRB_VERIFY_TIMEOUT_PER_64K*((VerifyLength) >> 16))) #define MRB_MAXIMUM_RETRIES 2 //Maximum number of times an MRB is retried. #define MRB_FLAGS_NO_DATA_TRANSFER 0x00 #define MRB_FLAGS_DATA_IN 0x01 #define MRB_FLAGS_DATA_OUT 0x02 #define MRB_FLAGS_SPLIT_WRITE 0x04 #define MRB_FLAGS_UNSPECIFIED_DIRECTION (MRB_FLAGS_DATA_IN | MRB_FLAGS_DATA_OUT) #define MRB_TRANSFER_SIZE 1024 //size of each transfer #define MU_BULK_MIN_TD_QUOTA ((2*MRB_TRANSFER_SIZE)/MU_MAX_BULK_PACKET_SIZE) typedef VOID (*PFMU_IO_COMPLETION)(PMU_DEVICE_EXTENSION DeviceExtension, NTSTATUS Status); typedef struct _MU_REQUEST_BLOCK { // filled out by disk layer // (are guaranteed to remain intact) PUCHAR DataBuffer; ULONG TransferLength; PFMU_IO_COMPLETION CompletionRoutine; USHORT TimeOutValue; //in 10's of milliseconds UCHAR Retries; UCHAR Flags; ULONG UserStartOffset; //For partial writes (read\modify\write) ULONG UserEndOffset; //Specifies which portion of the read goes //into the user buffer. CBW Cbw; // Disk layer fills out only CDB // Used by the MRB state machine CSW Csw; union { NTSTATUS FailedStatus; //used to hold failure during reset. ULONG BytesSubmitted; }; KTIMER Timer; KDPC TimeoutDpcObject; } MU_REQUEST_BLOCK, *PMU_REQUEST_BLOCK; //***************************************************************************** // MU DeviceExtension and related defintions //***************************************************************************** // // PDEVICE_EXTENSION->DeviceFlags state flags // #define DF_PENDING_CLOSE 0x00000001 #define DF_PENDING_REMOVE 0x00000002 #define DF_REMOVED 0x00000004 // // Flags used by the MRB state machine, // these are internal to it. The flags // in the MRB is only a direction. // #define DF_MRB_TIMER_RUNNING 0x00000100 #define DF_PRIMARY_URB_PENDING 0x00000200 #define DF_SECONDARY_URB_PENDING 0x00000400 #define DF_ERROR_PENDING 0x00000800 #define DF_RESET_STEP1 0x00001000 #define DF_RESET_STEP2 0x00002000 #define DF_RESET_STEP3 0x00004000 #define DF_RESET_STEPS (DF_RESET_STEP1|DF_RESET_STEP2|DF_RESET_STEP3) #define DF_ANY_URB_PENDING (DF_PRIMARY_URB_PENDING|DF_SECONDARY_URB_PENDING) // // PDEVICE_EXTENSION->DeviceFlags state flags // related to the endpoint state machine. // #define DF_ENDPOINT_FLAGS 0x000F0000 #define DF_DEFAULT_ENDPOINT_OPEN 0x00010000 #define DF_BULK_IN_ENDPOINT_OPEN 0x00020000 #define DF_BULK_OUT_ENDPOINT_OPEN 0x00040000 #define DF_CLOSING_ENDPOINTS 0x00080000 #define DF_ENDPOINTS_READY 0x00070000 // // PDEVICE_EXTENSION->DeviceFlags state flags // related to the write state machine. // #define MU_WRITE_HAS_INITIAL_PORTION 0x02000000 #define MU_WRITE_HAS_FINAL_PORTION 0x04000000 #define MU_WRITE_HAS_MIDDLE_PORTION 0x08000000 #define MU_WRITE_HAS_FLAGS (MU_WRITE_HAS_INITIAL_PORTION|MU_WRITE_HAS_FINAL_PORTION|MU_WRITE_HAS_MIDDLE_PORTION) // // Write state machine flags for the normal // partial write state machine. // #define MU_WRITE_STATE_START 0x10000000 #define MU_WRITE_STATE_INITIAL_READ 0x20000000 #define MU_WRITE_STATE_INITIAL_WRITE 0x30000000 #define MU_WRITE_STATE_FINAL_READ 0x40000000 #define MU_WRITE_STATE_FINAL_WRITE 0x50000000 #define MU_WRITE_STATE_DONE 0x60000000 #define MU_WRITE_STATE_BITS 0xF0000000 #define MU_WRITE_STATE_INCREMENT 0x10000000 // // Write state machine flags for the "middle" partial // write state machine. // #define MU_WRITE_STATE_MIDDLE_START 0x10000000 #define MU_WRITE_STATE_MIDDLE_READ_BEFORE 0x20000000 #define MU_WRITE_STATE_MIDDLE_READ_AFTER 0x30000000 #define MU_WRITE_STATE_MIDDLE_WRITE 0x40000000 #define MU_WRITE_STATE_MIDDLE_DONE 0x50000000 // // Pattern that we use for marking corrupt sectors. // (Don't worry how long it is (except for code space;), // in the success case, we stop comparing on the first // mismatch. In the error case we have plenty of time // anyway.) The number pattern at the beginning is part // of GUID generated with GUIDGEN. // const char tagMU_CORRUPT_SECTOR_PATTERN[]="951F0EF630DC46d9_CORRUPT_SECTOR"; #define MU_CORRUPT_SECTOR_PATTERN ((PULONG)tagMU_CORRUPT_SECTOR_PATTERN) #define MU_CORRUPT_SECTOR_PATTERN_SIZE sizeof(tagMU_CORRUPT_SECTOR_PATTERN) #define MU_CORRUPT_SECTOR_PATTERN_ULONG_COUNT (MU_CORRUPT_SECTOR_PATTERN_SIZE/sizeof(ULONG)) // // MU_INSTANCE contains context data for each MU. // These are statically allocated at startup time. // typedef struct _MU_INSTANCE { // // Device class interface to USB core stack // IUsbDevice *Device; // // USB related data retrieved during // enumeration, or when opening endpoints // UCHAR InterfaceNumber; UCHAR BulkInEndpointAddress; UCHAR BulkOutEndpointAddress; UCHAR AddRemoveCount; //DEBUG ONLY, catches double removes and\or adds. // // Pointer to assigned device extension // PMU_DEVICE_EXTENSION DeviceExtension; } MU_INSTANCE; typedef struct _MU_DEVICE_EXTENSION { // // Points to the Device Object // PDEVICE_OBJECT DeviceObject; // // Number (used to regenerate the name) // ULONG DeviceObjectNumber; // // Points to our MU_INSTANCE // union { PMU_INSTANCE MuInstance; PMU_DEVICE_EXTENSION NextFree; }; // // Various DF_xxxx flags // ULONG DeviceFlags; //*** //*** Above here is maintained while the device object //*** is on the free list, below is always zeroed. //*** // // At any time, each device is processing // one IRP, this is it. // PIRP PendingIrp; ULONG IrpCount; //Count of outstanding IRPs // Variables to cache results of calculations // for partial writes ULONG InitialWriteByteCount; ULONG FinalWriteByteCount; PVOID BulkInEndpointHandle; PVOID BulkOutEndpointHandle; // // The Mrb for managing commands // MU_REQUEST_BLOCK Mrb; // // Each instance maitains three independent URBs // for communication with the device. // This is so that two bulk URBs can be outstanding // at once (which better utilizes bandwidth). // Furthermore, a dedicated close URB is provided // so that the close state machine does not need // to synchronize with the MRB state machine. // URB Urb; URB_BULK_OR_INTERRUPT_TRANSFER BulkUrbSecondary; URB_CLOSE_ENDPOINT CloseEndpointUrb; KEVENT CloseEvent; //signaled when a close completes // // Geomerty Information // DISK_GEOMETRY DiskGeometry; ULONG LogicalBlockShift; LARGE_INTEGER PartitionLength; ULONG MediaBlockSize; // // Watchdog timer parameters // MU_DEBUG_DECLARE_WATCHDOG_PARAMETERS() } MU_DEVICE_EXTENSION; //***************************************************************************** // F U N C T I O N P R O T O T Y P E S //***************************************************************************** // Only those between translation units. // // inline [acquire and release preallocate device objects. // __inline PMU_DEVICE_EXTENSION MU_AcquireDeviceObject() { PMU_DEVICE_EXTENSION retVal; retVal = MU_DriverExtension.DeviceObjectFreeList; if(retVal) { MU_DriverExtension.DeviceObjectFreeList = retVal->NextFree; retVal->DeviceFlags = 0; retVal->MuInstance = NULL; } return retVal; } __inline VOID MU_ReleaseDeviceObject(PMU_DEVICE_EXTENSION DeviceExtension) { RtlZeroMemory(&DeviceExtension->PendingIrp, sizeof(MU_DEVICE_EXTENSION)-FIELD_OFFSET(MU_DEVICE_EXTENSION, PendingIrp)); DeviceExtension->DeviceFlags = DF_REMOVED; DeviceExtension->NextFree = MU_DriverExtension.DeviceObjectFreeList; MU_DriverExtension.DeviceObjectFreeList = DeviceExtension; } // // mu.cpp [These are all called from outside the MU driver] // EXTERNUSB VOID MU_Init(IUsbInit *pUsbInit); EXTERNUSB VOID MU_AddDevice ( IN IUsbDevice *Device ); EXTERNUSB VOID MU_RemoveDevice ( IN IUsbDevice *Device ); extern "C" { NTSTATUS MU_CreateDeviceObject( IN ULONG Port, IN ULONG Slot, IN POBJECT_STRING DeviceName ); VOID MU_CloseDeviceObject( IN ULONG Port, IN ULONG Slot ); PDEVICE_OBJECT MU_GetExistingDeviceObject( IN ULONG Port, IN ULONG Slot ); } // // mrb.cpp // VOID FASTCALL MU_fStartMrb( IN PMU_DEVICE_EXTENSION DeviceExtension ); VOID MU_MrbTimeout ( IN PKDPC Dpc, IN PVOID fdoDeviceExtension, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); // // disk.cpp // NTSTATUS MU_InternalIo ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID MU_StartIo ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); // // Debug Only Methods, defined at the bottom of disk.cpp // #if DBG VOID MUDebugWatchdogDpcRoutine( PKDPC, PMU_DEVICE_EXTENSION DeviceExtension, PVOID, PVOID ); VOID MUDebugInitWatchDogParameters( PMU_DEVICE_EXTENSION DeviceExtension ); VOID MuDebugSetWatchDogTimer( PMU_DEVICE_EXTENSION DeviceExtension ); VOID MuDebugPetWatchDogTimer( PMU_DEVICE_EXTENSION DeviceExtension ); VOID MuDebugCompleteRequest( PMU_DEVICE_EXTENSION DeviceExtension, PIRP Irp, CCHAR PriorityBoost ); #endif