summaryrefslogtreecommitdiffstats
path: root/private/ntos/miniport/mitsumi/mitsumi.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/miniport/mitsumi/mitsumi.c3365
1 files changed, 3365 insertions, 0 deletions
diff --git a/private/ntos/miniport/mitsumi/mitsumi.c b/private/ntos/miniport/mitsumi/mitsumi.c
new file mode 100644
index 000000000..3429a15b4
--- /dev/null
+++ b/private/ntos/miniport/mitsumi/mitsumi.c
@@ -0,0 +1,3365 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Mitsumi.c
+
+Abstract:
+
+ This is the miniport driver for the Panasonic MKE CR5xx Proprietary CDROM drive.
+
+Author:
+
+ Chuck Park (chuckp)
+
+
+Environment:
+
+ kernel mode only
+
+Notes:
+
+Revision History:
+
+--*/
+
+#include "miniport.h"
+#include "Mitsumi.h"
+
+#if DBG
+#define GATHER_STATS 1
+#endif
+
+//
+// Device extension
+//
+
+typedef struct _HW_DEVICE_EXTENSION {
+
+ //
+ // I/O port base address.
+ //
+
+ PREGISTERS BaseIoAddress;
+
+ //
+ // Srb being currently serviced.
+ //
+
+ PSCSI_REQUEST_BLOCK CurrentSrb;
+
+ //
+ // Pointer to data buffer
+ //
+
+ PUCHAR DataBuffer;
+
+ //
+ // Bytes left to transfer for current request.
+ //
+
+ ULONG ByteCount;
+
+ //
+ // Identifies the model.
+ //
+
+ DRIVE_TYPE DriveType;
+
+ //
+ // Current status of audio
+ //
+
+ ULONG AudioStatus;
+
+ //
+ // Saved position after pausing play.
+ //
+
+ ULONG SavedPosition;
+
+ //
+ // Ending LBA from last audio play.
+ //
+
+ ULONG EndPosition;
+
+ //
+ // Number of retries to get valid status.
+ //
+
+ ULONG StatusRetries;
+
+ //
+ // Number of microseconds to go away on CallBack requests
+ //
+
+ ULONG PollRate;
+ ULONG PollRateMultiplier;
+
+#ifdef GATHER_STATS
+
+ //
+ // Used to determine hit rate for various polling increments
+ //
+
+ ULONG Hits[4];
+ ULONG Misses[4];
+ ULONG Requests;
+ BOOLEAN FirstCall;
+ BOOLEAN FirstStatusTry;
+#endif
+
+ //
+ // Status from last NoOp
+ //
+
+ UCHAR DriveStatus;
+
+ //
+ // Determines whether MSF addressing is used.
+ //
+
+ BOOLEAN MSFAddress;
+
+} HW_DEVICE_EXTENSION, *PHW_DEVICE_EXTENSION;
+
+
+#define MAX_STATUS_RETRIES 512
+
+
+//
+// Function declarations
+//
+//
+
+ULONG
+DriverEntry(
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ );
+
+
+ULONG
+MitsumiFindAdapter(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ );
+
+BOOLEAN
+MitsumiHwInitialize(
+ IN PVOID DeviceExtension
+ );
+
+BOOLEAN
+MitsumiStartIo(
+ IN PVOID DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+VOID
+MitsumiCallBack(
+ IN PVOID HwDeviceExtension
+ );
+
+BOOLEAN
+MitsumiReset(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ );
+
+BOOLEAN
+FindMitsumi(
+ IN PHW_DEVICE_EXTENSION HwDeviceExtension
+ );
+
+BOOLEAN
+ReadAndMapError(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+CheckStatus(
+ IN PVOID HwDeviceExtension
+ );
+
+BOOLEAN
+MitsumiBuildCommand(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+BOOLEAN
+MitsumiSendCommand(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ );
+
+UCHAR
+WaitForSTEN(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+BOOLEAN
+UpdateCurrentPosition(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ );
+
+
+
+ULONG
+DriverEntry(
+ IN PVOID DriverObject,
+ IN PVOID Argument2
+ )
+
+/*++
+
+Routine Description:
+
+ Installable driver initialization entry point for system.
+
+Arguments:
+
+ Driver Object
+
+Return Value:
+
+ Status from ScsiPortInitialize()
+
+--*/
+
+{
+ HW_INITIALIZATION_DATA hwInitializationData;
+ ULONG i;
+ ULONG adapterCount;
+
+ DebugPrint((1,"\n\nMitsumi Proprietary CDROM Driver\n"));
+
+ //
+ // Zero out structure.
+ //
+
+ for (i = 0; i < sizeof(HW_INITIALIZATION_DATA); i++) {
+ ((PUCHAR)&hwInitializationData)[i] = 0;
+ }
+
+ //
+ // Set size of hwInitializationData.
+ //
+
+ hwInitializationData.HwInitializationDataSize = sizeof(HW_INITIALIZATION_DATA);
+
+ //
+ // Set entry points.
+ //
+
+ hwInitializationData.HwInitialize = MitsumiHwInitialize;
+ hwInitializationData.HwResetBus = MitsumiReset;
+ hwInitializationData.HwStartIo = MitsumiStartIo;
+ hwInitializationData.HwFindAdapter = MitsumiFindAdapter;
+
+ //
+ // Specify size of extensions.
+ //
+
+ hwInitializationData.DeviceExtensionSize = sizeof(HW_DEVICE_EXTENSION);
+ hwInitializationData.SrbExtensionSize = sizeof(CMD_PACKET);
+
+ //
+ // Specifiy the bus type and access ranges.
+ //
+
+ hwInitializationData.AdapterInterfaceType = Isa;
+ hwInitializationData.NumberOfAccessRanges = 1;
+
+ //
+ // Indicate PIO device.
+ //
+
+ hwInitializationData.MapBuffers = TRUE;
+
+ adapterCount = 0;
+
+ return (ScsiPortInitialize(DriverObject, Argument2, &hwInitializationData, &adapterCount));
+
+} // end MitsumiEntry()
+
+
+ULONG
+MitsumiFindAdapter(
+ IN PVOID HwDeviceExtension,
+ IN PVOID Context,
+ IN PVOID BusInformation,
+ IN PCHAR ArgumentString,
+ IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo,
+ OUT PBOOLEAN Again
+ )
+/*++
+
+Routine Description:
+
+ This function is called by the OS-specific port driver after
+ the necessary storage has been allocated, to gather information
+ about the adapter's configuration.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Context - Register base address
+ ConfigInfo - Configuration information structure describing HBA
+ This structure is defined in PORT.H.
+
+Return Value:
+
+ ULONG
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PULONG AdapterCount = Context;
+
+ CONST ULONG AdapterAddresses[] = {0x230, 0x250,0x300, 0x310, 0x320, 0x330, 0x340, 0x350, 0x360, 0x370, 0x380,
+ 0x390, 0x3A0, 0x3B0, 0x3C0, 0x3D0, 0x3E0, 0x3F0, 0};
+
+
+ while (AdapterAddresses[*AdapterCount] != 0) {
+
+ //
+ // Map I/O space.
+ //
+
+ deviceExtension->BaseIoAddress = (PREGISTERS)ScsiPortGetDeviceBase(HwDeviceExtension,
+ ConfigInfo->AdapterInterfaceType,
+ ConfigInfo->SystemIoBusNumber,
+ ScsiPortConvertUlongToPhysicalAddress(
+ AdapterAddresses[*AdapterCount]),
+ 0x4,
+ TRUE
+ );
+ if (!deviceExtension->BaseIoAddress) {
+ return SP_RETURN_ERROR;
+ }
+
+ (*AdapterCount)++;
+
+ if (!FindMitsumi(deviceExtension)) {
+
+ //
+ // CD is not at this address.
+ //
+
+ ScsiPortFreeDeviceBase(HwDeviceExtension,
+ deviceExtension->BaseIoAddress
+ );
+
+ continue;
+
+ } else {
+
+ //
+ // Indicate further searches are to be attempted.
+ // Why anyone would want more than one of these drives...
+ //
+
+ *Again = TRUE;
+
+ //
+ // Fill in the access ranges.
+ //
+
+ (*ConfigInfo->AccessRanges)[0].RangeStart =
+ ScsiPortConvertUlongToPhysicalAddress(AdapterAddresses[*AdapterCount - 1]);
+ (*ConfigInfo->AccessRanges)[0].RangeLength = 4;
+ (*ConfigInfo->AccessRanges)[0].RangeInMemory = FALSE;
+
+ ConfigInfo->NumberOfBuses = 1;
+ ConfigInfo->InitiatorBusId[0] = 1;
+
+ ConfigInfo->MaximumTransferLength = 0x8000;
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_NO_STATUS;
+ return SP_RETURN_FOUND;
+
+ }
+
+ }
+
+ *Again = FALSE;
+ *AdapterCount = 0;
+
+ return SP_RETURN_NOT_FOUND;
+
+} // end MitsumiFindAdapter()
+
+
+BOOLEAN
+FindMitsumi(
+ IN PHW_DEVICE_EXTENSION HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE if device found.
+
+--*/
+
+{
+
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PREGISTERS baseIoAddress = deviceExtension->BaseIoAddress;
+ ULONG i;
+ UCHAR status;
+ UCHAR driveId[2];
+
+ //
+ // If status is not 0xFF, something else is living at this location.
+ //
+
+ status = ScsiPortReadPortUchar(&baseIoAddress->Reset);
+ if (status != 0xFF) {
+ DebugPrint((1,
+ "FindMitsumi: Something else is living at %x\n",
+ baseIoAddress));
+
+ return FALSE;
+ }
+ status = ScsiPortReadPortUchar(&baseIoAddress->Data);
+ if (status != 0xFF) {
+ DebugPrint((1,
+ "FindMitsumi: Something else is living at %x\n",
+ baseIoAddress));
+
+ return FALSE;
+ }
+
+ //
+ // Reset the device.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Reset,0);
+ ScsiPortStallExecution(10);
+ ScsiPortWritePortUchar(&baseIoAddress->Status, 0);
+ ScsiPortStallExecution (10);
+
+ ScsiPortStallExecution(10000);
+ ReadStatus(deviceExtension,baseIoAddress,status);
+
+ //
+ // Issue read drive Id command.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data,OP_READ_DRIVE_ID);
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the drive id and version #.
+ //
+
+ for (i = 0; i < 2; i++) {
+ ReadStatus(deviceExtension,baseIoAddress,driveId[i]);
+ if (driveId[i] == 0xFF) {
+ return FALSE;
+ }
+ }
+
+ //
+ // Check the id for validity and drive type.
+ //
+
+ switch (driveId[0]) {
+ case 'M':
+
+ DebugPrint((1,
+ "FindMitsumi: Found LU005 at %x\n",
+ baseIoAddress));
+
+ deviceExtension->DriveType = LU005;
+ break;
+
+ case 'D':
+
+ DebugPrint((1,
+ "FindMitsumi: Found FX001D at %x\n",
+ baseIoAddress));
+
+ deviceExtension->DriveType = FX001D;
+ break;
+
+ case 'F':
+
+ DebugPrint((1,
+ "FindMitsumi: Found FX001 at %x\n",
+ baseIoAddress));
+
+ deviceExtension->DriveType = FX001;
+ break;
+
+ default:
+ DebugPrint((1,
+ "FindMitsumi: No drive found at %x\n",
+ baseIoAddress));
+
+ return FALSE;
+
+ }
+
+ } else {
+
+ DebugPrint((1,
+ "FindMitsumi: No drive found at %x\n",
+ baseIoAddress));
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+} // end FindMitsumi
+
+
+BOOLEAN
+MitsumiHwInitialize(
+ IN PVOID HwDeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from ScsiPortInitialize
+ to set up the adapter so that it is ready to service requests.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if initialization successful.
+ FALSE - if initialization unsuccessful.
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+
+ deviceExtension->PollRate = 1500;
+ deviceExtension->PollRateMultiplier = 15;
+ return TRUE;
+
+} // end MitsumiHwInitialize()
+
+
+BOOLEAN
+MitsumiStartIo(
+ IN PVOID HwDeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+ Srb - IO request packet
+
+Return Value:
+
+ TRUE
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PREGISTERS baseIoAddress = deviceExtension->BaseIoAddress;
+ UCHAR status;
+
+ //
+ // Determine which function.
+ //
+
+ switch (Srb->Function) {
+
+ case SRB_FUNCTION_EXECUTE_SCSI:
+
+ //
+ // Indicate that a request is active on the controller.
+ //
+
+ deviceExtension->CurrentSrb = Srb;
+
+ //
+ // Build the command packet.
+ //
+
+ if (!MitsumiBuildCommand(deviceExtension,Srb)) {
+ status = Srb->SrbStatus;
+ break;
+ }
+
+ //
+ // Send command to device.
+ //
+
+ if (MitsumiSendCommand(deviceExtension,Srb)) {
+
+ if ( Srb->SrbStatus == SRB_STATUS_PENDING) {
+
+ //
+ // Request a timer callback to finish request.
+ //
+
+ ScsiPortNotification(RequestTimerCall,
+ HwDeviceExtension,
+ MitsumiCallBack,
+ deviceExtension->PollRate * deviceExtension->PollRateMultiplier);
+ return TRUE;
+ }
+ }
+
+ status = Srb->SrbStatus;
+
+ break;
+
+ case SRB_FUNCTION_ABORT_COMMAND:
+
+ //
+ // Verify that SRB to abort is still outstanding.
+ //
+
+ if (!deviceExtension->CurrentSrb) {
+
+ //
+ // Complete abort SRB.
+ //
+
+ status = SRB_STATUS_ABORT_FAILED;
+
+ break;
+ }
+
+ //
+ // Fall through to reset
+ //
+
+ case SRB_FUNCTION_RESET_BUS:
+
+
+ //
+ // Reset the device.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Reset,0);
+ ScsiPortStallExecution(10);
+ ScsiPortWritePortUchar(&baseIoAddress->Status, 0);
+ ScsiPortStallExecution (10);
+
+ //
+ // Update drive status in device ext.
+ //
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+
+ //
+ // Port driver will give 5 sec. to recover.
+ //
+
+ status = SRB_STATUS_BUS_RESET;
+
+ break;
+
+ default:
+
+ //
+ // Indicate unsupported command.
+ //
+
+ status = SRB_STATUS_INVALID_REQUEST;
+
+ break;
+
+ } // end switch
+
+
+ if (status != SRB_STATUS_PENDING) {
+
+ //
+ // Clear current SRB.
+ //
+
+ deviceExtension->CurrentSrb = NULL;
+
+ //
+ // Map status to Srb status
+ //
+
+ Srb->SrbStatus = (UCHAR)status;
+
+ //
+ // Indicate command complete.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ //
+ // Indicate ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return TRUE;
+ }
+
+ return TRUE;
+
+} // end MitsumiStartIo()
+
+
+BOOLEAN
+MitsumiReadCapacity(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Extracts the 'TOC' data and manipulates it to determine
+ the size of the disc.
+
+Arguments:
+
+ DeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if command was successful.
+
+--*/
+
+{
+
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ PSCSI_REQUEST_BLOCK Srb = DeviceExtension->CurrentSrb;
+ PUCHAR data = DeviceExtension->DataBuffer;
+ ULONG dataLength,lba,i;
+ UCHAR minutes,seconds,frames;
+ UCHAR status;
+
+
+ dataLength = 8;
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_DISC_IN) || (status & STATUS_DOOR_OPEN) ) {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ if (Srb->SenseInfoBufferLength) {
+
+ PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_NOT_READY;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+
+ Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ }
+
+ return FALSE;
+ }
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the Toc data
+ //
+
+ for (i = 0; i < dataLength; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,*data);
+ if (*data == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "MitsumiReadCapacity: Error occurred on data read.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+ data++;
+ }
+
+ //
+ // All of the Toc data has been read. Now munge it into a form that is useable.
+ //
+
+ DeviceExtension->ByteCount = 0;
+
+ data = &DeviceExtension->DataBuffer[2];
+ minutes = *data++;
+ seconds = *data++;
+ frames = *data;
+
+ BCD_TO_DEC(minutes);
+ BCD_TO_DEC(seconds);
+ BCD_TO_DEC(frames);
+
+ lba = MSF_TO_LBA(minutes,seconds,frames);
+
+ DeviceExtension->DataBuffer[0] = ((PFOUR_BYTE)&lba)->Byte3;
+ DeviceExtension->DataBuffer[1] = ((PFOUR_BYTE)&lba)->Byte2;
+ DeviceExtension->DataBuffer[2] = ((PFOUR_BYTE)&lba)->Byte1;
+ DeviceExtension->DataBuffer[3] = ((PFOUR_BYTE)&lba)->Byte0;
+
+
+ *((ULONG *) &(DeviceExtension->DataBuffer[4])) = 0x00080000;
+
+ } else {
+
+ DebugPrint((1,
+ "MitsumiReadCapacity: Status %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ return TRUE;
+
+} //End MitsumiReadCapacity
+
+
+
+BOOLEAN
+MitsumiRead(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+ Carries out the read command. Each sector will be transferred to the
+ Srb's data buffer individually. Afterwards, a new timer call-back will
+ be requested.
+
+Arguments:
+
+ DeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if data transfer is complete.
+
+--*/
+
+{
+
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ ULONG dataLength = DeviceExtension->ByteCount;
+ PSCSI_REQUEST_BLOCK Srb = DeviceExtension->CurrentSrb;
+ PCMD_PACKET packet = (PCMD_PACKET)Srb->SrbExtension;
+ ULONG i,j;
+ UCHAR status;
+
+
+ while (DeviceExtension->ByteCount) {
+
+ //
+ // Check whether ready to transfer.
+ //
+
+ for (i = 0; i < 400; i++) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if (!(status & DTEN)) {
+
+ break;
+
+ } else if (!(status & STEN)){
+
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x04);
+ status = ScsiPortReadPortUchar(&baseIoAddress->Data);
+ DeviceExtension->DriveStatus = status;
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x0C);
+
+ if (status & STATUS_READ_ERROR) {
+
+ DebugPrint((1,
+ "MitsumiRead: Read error %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ }
+ }
+ ScsiPortStallExecution(10);
+ }
+
+ if (i == 400) {
+
+
+#ifdef GATHER_STATS
+ if (DeviceExtension->ByteCount == Srb->DataTransferLength) {
+ DeviceExtension->FirstCall = FALSE;
+ DeviceExtension->Misses[0]++;
+ } else {
+ DeviceExtension->Misses[1]++;
+ }
+#endif
+ if (DeviceExtension->StatusRetries >= MAX_STATUS_RETRIES) {
+
+ DebugPrint((1,
+ "MitsumiRead: Resetting due to timeout waiting for DTEN\n"));
+
+ DeviceExtension->StatusRetries = 0;
+
+ //
+ // Clear state fields.
+ //
+
+ DeviceExtension->CurrentSrb = NULL;
+ DeviceExtension->ByteCount = 0;
+ DeviceExtension->DataBuffer = NULL;
+
+ //
+ // Reset the device.
+ //
+
+ MitsumiReset((PVOID)DeviceExtension, Srb->PathId);
+
+ ScsiPortNotification(ResetDetected,
+ DeviceExtension,
+ NULL);
+
+ Srb->SrbStatus = SRB_STATUS_BUS_RESET;
+
+ return FALSE;
+
+ } else {
+
+ DeviceExtension->StatusRetries++;
+
+ //
+ // Schedule another callback.
+ //
+
+ DebugPrint((2,
+ "MitsumiRead: DTEN timed out waiting for seek. Scheduling another callback %x\n",
+ status));
+
+ ScsiPortNotification(RequestTimerCall,
+ (PVOID)DeviceExtension,
+ MitsumiCallBack,
+ DeviceExtension->PollRate);
+
+ return FALSE;
+ }
+ }
+#ifdef GATHER_STATS
+ else {
+ if (DeviceExtension->ByteCount == Srb->DataTransferLength) {
+ if (DeviceExtension->FirstCall) {
+ DeviceExtension->Hits[0]++;
+ DeviceExtension->FirstCall = FALSE;
+ }
+ } else {
+ DeviceExtension->Hits[1]++;
+ }
+ }
+#endif
+
+ //
+ // Some unknown check that seems to be essential.
+ //
+
+ if (DeviceExtension->DriveType != LU005) {
+
+ //
+ // TODO: Fix this loop. Don't want to spin forever.
+ //
+
+ while (TRUE) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if (status & 0x01) {
+ break;
+ } else {
+ ScsiPortStallExecution(20);
+ }
+ }
+ }
+
+ //
+ // Ready to transfer. Set the drive in 'data' mode.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x04);
+
+ ScsiPortReadPortBufferUchar(&baseIoAddress->Data,
+ DeviceExtension->DataBuffer,
+ 2048);
+
+
+ //
+ // Set the drive back to 'status' mode.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x0C);
+
+ //
+ // Adjust Bytes left and Buffer pointer
+ //
+
+ DeviceExtension->DataBuffer += 2048;
+ DeviceExtension->ByteCount -= 2048;
+ DeviceExtension->StatusRetries = 0;
+
+ if (DeviceExtension->ByteCount) {
+
+ //
+ // If ready to transfer another sector quick enough, go and
+ // do so.
+ //
+
+ for (j = 0; j < 20; j++) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if ((status & DTEN)) {
+ if (!(status & STEN)) {
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x04);
+ status = ScsiPortReadPortUchar(&baseIoAddress->Data);
+ DeviceExtension->DriveStatus = status;
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x0C);
+
+ if (status & STATUS_READ_ERROR) {
+
+ DebugPrint((1,
+ "MitsumiRead: Read error %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ }
+ }
+ } else {
+ break;
+ }
+
+ ScsiPortStallExecution(100);
+ }
+ if (j == 20) {
+
+#ifdef GATHER_STATS
+ DeviceExtension->Misses[2]++;
+#endif
+ DebugPrint((2,
+ "MitsumiRead: Request another timer\n"));
+
+ ScsiPortNotification(RequestTimerCall,
+ (PVOID)DeviceExtension,
+ MitsumiCallBack,
+ DeviceExtension->PollRate);
+
+ return FALSE;
+ }
+#ifdef GATHER_STATS
+ else {
+ DeviceExtension->Hits[2]++;
+ }
+#endif
+ //
+ // Update dataLength and try for another sector.
+ //
+
+ dataLength = DeviceExtension->ByteCount / 2048;
+
+ } else {
+
+ //
+ // Prepare to try for status.
+ //
+
+ DeviceExtension->StatusRetries = 0;
+#ifdef GATHER_STATS
+ DeviceExtension->FirstStatusTry = TRUE;
+#endif
+
+ ScsiPortNotification(RequestTimerCall,
+ (PVOID)DeviceExtension,
+ MitsumiCallBack,
+ DeviceExtension->PollRate / 2);
+ return FALSE;
+ }
+ }
+
+ //
+ // Read final status.
+ //
+
+ for (i = 0; i < 200; i++) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if (status & STEN) {
+ ScsiPortStallExecution(10);
+ } else {
+ break;
+ }
+ }
+
+ if (i == 200) {
+
+#ifdef GATHER_STATS
+ DeviceExtension->FirstStatusTry = FALSE;
+ DeviceExtension->Misses[3]++;
+#endif
+ if (DeviceExtension->StatusRetries >= MAX_STATUS_RETRIES) {
+
+ DebugPrint((1,
+ "MitsumiRead: Resetting due to timeout waiting for status\n"));
+
+ DeviceExtension->StatusRetries = 0;
+
+ //
+ // Clear state fields.
+ //
+
+ DeviceExtension->CurrentSrb = NULL;
+ DeviceExtension->ByteCount = 0;
+ DeviceExtension->DataBuffer = NULL;
+
+ //
+ // Reset the device.
+ //
+
+ MitsumiReset((PVOID)DeviceExtension, Srb->PathId);
+
+ ScsiPortNotification(ResetDetected,
+ DeviceExtension,
+ NULL);
+
+ Srb->SrbStatus = SRB_STATUS_BUS_RESET;
+
+ return FALSE;
+ } else {
+
+ DebugPrint((2,
+ "MitsumiRead: Status Retries for Status %x\n",
+ DeviceExtension->StatusRetries));
+
+ DeviceExtension->StatusRetries++;
+
+ //
+ // Request another callback to pick up the status
+ //
+
+ ScsiPortNotification(RequestTimerCall,
+ (PVOID)DeviceExtension,
+ MitsumiCallBack,
+ DeviceExtension->PollRate);
+ return FALSE;
+ }
+ }
+
+
+#ifdef GATHER_STATS
+ if (DeviceExtension->StatusRetries == 0) {
+ if (DeviceExtension->FirstStatusTry) {
+ DeviceExtension->Hits[3]++;
+ } else {
+ DebugPrint((1,"StatusRetries = 0, FirstStatusTry = FALSE\n"));
+ }
+ }
+#endif
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x04);
+ status = ScsiPortReadPortUchar(&baseIoAddress->Data);
+ DeviceExtension->DriveStatus = status;
+ ScsiPortWritePortUchar(&baseIoAddress->Control, 0x0C);
+
+ if (status & STATUS_READ_ERROR) {
+
+ DebugPrint((1,
+ "MitsumiRead: Read error %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ }
+
+ return TRUE;
+
+} //End MitsumiRead
+
+
+
+BOOLEAN
+GetTrackData(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN ULONG NumberTracks
+ )
+
+/*++
+
+Routine Description:
+
+ Puts the drive in 'TOC' mode so the TOC info. can be extracted for the
+ Subchannel Q.
+
+Arguments:
+
+ DeviceExtension - HBA miniport driver's adapter data storage
+ NumberTracks - The number of tracks determined to be on the disc.
+
+Return Value:
+
+ TRUE - if successful.
+
+--*/
+
+{
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ PSCSI_REQUEST_BLOCK Srb = DeviceExtension->CurrentSrb;
+ PCMD_PACKET packet = (PCMD_PACKET)Srb->SrbExtension;
+ PUCHAR data = DeviceExtension->DataBuffer;
+ UCHAR mask[100];
+ ULONG tracksFound = 0;
+ ULONG i,lba;
+ ULONG dataLength;
+ UCHAR status,minutes,seconds,frames,control,track,index;
+ UCHAR controlLow, controlHigh;
+ UCHAR dataBuffer[10];
+ ULONG zeroCount = 0,retry = 20000;
+
+ for (i = 0; i < 100; i++) {
+ mask[i] = 0;
+ }
+
+ //
+ // Seek to start of disk
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_READ_PLAY);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 2);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0);
+
+ //
+ // Wait for seek to complete
+ //
+
+ for (i = 0; i < 40000; i++) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if (!(status & STEN)) {
+ break;
+ } else if (!(status & DTEN)){
+
+ DebugPrint((1,
+ "GetTrackData: DTEN active %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ } else {
+
+ ScsiPortStallExecution(10);
+ }
+ }
+ if (i == 40000) {
+
+ DebugPrint((1,
+ "GetTrackData: STEN timed out %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (status & (STATUS_CMD_ERROR | STATUS_READ_ERROR)) {
+
+ DebugPrint((1,
+ "GetTrackData: Status %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ //
+ // Switch drive into "TOC DATA"
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x5);
+ ScsiPortStallExecution(1500);
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ DebugPrint((2,
+ "GetTrackData: Status after SET_DRV_MODE %x\n",
+ status));
+
+ //
+ // Set buffer pointer to start of track descriptors.
+ //
+
+ dataLength = 10;
+
+ while (tracksFound < NumberTracks) {
+
+ //
+ // Read sub-q to extract the embedded TOC data. This isn't pretty.
+ // So for the faint at heart - goto line 1245.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_READ_SUB_CHANNEL);
+ ScsiPortStallExecution(500);
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the Toc data
+ //
+
+ for (i = 0; i < dataLength; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,dataBuffer[i]);
+ if (dataBuffer[i] == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "GetTrackData: Error occurred on data read.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x1);
+ ScsiPortStallExecution(1500);
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ return FALSE;
+ }
+ }
+ } else {
+
+ //
+ // Bogus packet.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x1);
+ ScsiPortStallExecution(1500);
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ DebugPrint((1,
+ "GetTrackData: Error occurred sending command.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ //
+ // Update bitmask of tracks found, including dealing with First, last, and multisession.
+ //
+
+ track = BCD_TO_DEC(dataBuffer[1]);
+ index = BCD_TO_DEC(dataBuffer[2]);
+
+ if (track == 0 && retry--) {
+
+ switch (index) {
+
+ case 0xA0:
+
+ //
+ // First track
+ //
+
+ DebugPrint((2,"First track\n"));
+ break;
+
+ case 0xA1:
+
+ //
+ // Last track
+ //
+
+ DebugPrint((2,"Last track\n"));
+ break;
+
+ case 0xB0:
+
+ //
+ // Multi-session. Through it away.
+ //
+
+ break;
+
+ default:
+
+ //
+ // Normal tracks
+ //
+
+ if ( (!(mask[index])) && (index < 100)) {
+
+ DebugPrint((2,"Track %d\n",index));
+
+ //
+ // Set the appropriate bit.
+ //
+
+ mask[index] = 1;
+
+ //
+ // Munge data into buffer
+ //
+
+ DebugPrint((2,"GetTrackData: track %x raw control %x\n",
+ index,
+ dataBuffer[0]));
+
+ //
+ // These fines drives have ADR and CONTROL flipped. Have to
+ // swizzle the nibbles.
+ //
+
+ control = dataBuffer[0];
+ controlHigh = (control & 0xF0) >> 4;
+ controlLow = control & 0x0F;
+ control = controlHigh | (controlLow << 4);
+
+ DebugPrint((2,"GetTrackData: track %x munged control %x\n",
+ index,
+ control));
+
+ minutes = BCD_TO_DEC(dataBuffer[7]);
+ seconds = BCD_TO_DEC(dataBuffer[8]);
+ frames = BCD_TO_DEC(dataBuffer[9]);
+
+ DebugPrint((2,"GetTrackData: control %x, msf %x %x %x\n",
+ control,
+ minutes,
+ seconds,
+ frames));
+
+
+ if (!DeviceExtension->MSFAddress) {
+
+ lba = MSF_TO_LBA(minutes,seconds,frames);
+
+ //
+ // Swizzle the block address.
+ //
+
+ data[7+(8*(index-1))] = ((PFOUR_BYTE)&lba)->Byte3;
+ data[6+(8*(index-1))] = ((PFOUR_BYTE)&lba)->Byte2;
+ data[5+(8*(index-1))] = ((PFOUR_BYTE)&lba)->Byte1;
+ data[4+(8*(index-1))] = ((PFOUR_BYTE)&lba)->Byte0;
+
+ } else {
+
+ data[7+(8*(index-1))] = frames;
+ data[6+(8*(index-1))] = seconds;
+ data[5+(8*(index-1))] = minutes;
+ data[4+(8*(index-1))] = 0;
+ }
+
+ data[3+(8*(index-1))] = 0;
+ data[2+(8*(index-1))] = index;
+ data[1+(8*(index-1))] = control;
+ data[0+(8*(index-1))] = 0;
+
+ //
+ // Update number of tracks found.
+ //
+
+ tracksFound++;
+
+ }
+
+ break;
+
+ } // switch
+
+ } else {
+
+ if (zeroCount++ >= 2000) {
+
+ //
+ // A little defensive work. It's possible that this thing
+ // could spin forever.
+ //
+
+ DebugPrint((1,"Too many zeros\n"));
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x1);
+ ScsiPortStallExecution(1500);
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+ }
+ } // while
+
+ DebugPrint((2,"Retry = %d\n",retry));
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x1);
+
+ return TRUE;
+
+} // End ReadToc
+
+
+BOOLEAN
+MitsumiReadToc(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ DeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ TRUE - if successful.
+
+--*/
+{
+
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ PSCSI_REQUEST_BLOCK Srb = DeviceExtension->CurrentSrb;
+ PUCHAR data = DeviceExtension->DataBuffer;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ ULONG dataLength;
+ ULONG i,j,lba;
+ UCHAR status,leadOutM,leadOutS,leadOutF;
+
+
+ if (cdb->READ_TOC.Format == 0) {
+
+ dataLength = 8;
+
+ //
+ // Ensure that the drive is ready for this.
+ //
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_DISC_IN) || (status & STATUS_DOOR_OPEN) ) {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ if (Srb->SenseInfoBufferLength) {
+
+ PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_NOT_READY;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+
+ Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ }
+
+ return FALSE;
+ }
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the Toc data
+ //
+
+ for (i = 0; i < dataLength; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,*data);
+ if (*data == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "MitsumiReadToc: Error occurred on data read.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+ data++;
+ }
+
+ //
+ // All of the 'Toc' data has been read. Now munge it into a form that is useable.
+ // The Mitsumi TOC data is abbreviated at best. The exciting GetTrackData routine
+ // gets the actual stuff in which we are interested.
+ //
+
+ DeviceExtension->ByteCount = 0;
+ data = DeviceExtension->DataBuffer;
+
+ leadOutM = BCD_TO_DEC(data[2]);
+ leadOutS = BCD_TO_DEC(data[3]);
+ leadOutF = BCD_TO_DEC(data[4]);
+
+ //
+ // Set First and Last track.
+ //
+
+ data[2] = BCD_TO_DEC(data[0]);
+ data[3] = BCD_TO_DEC(data[1]);
+
+ //
+ // Set sizeof TOC data
+ //
+
+ data[0] = ((( data[3] - data[2]) * 8) + 2) >> 8;
+ data[1] = ((( data[3] - data[2]) * 8) + 2) & 0xFF;
+
+ DeviceExtension->DataBuffer += 4;
+
+ if (!GetTrackData(DeviceExtension,(data[3] - data[2] + 1))) {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ }
+
+ //
+ // Push buffer pointer to end of TOC data
+ //
+
+ DeviceExtension->DataBuffer += 8*(data[3]-data[2] + 1);
+
+ //
+ // Lead out area
+ //
+
+ DeviceExtension->DataBuffer[0] = 0;
+ DeviceExtension->DataBuffer[1] = 0x10;
+ DeviceExtension->DataBuffer[3] = 0;
+
+ DeviceExtension->DataBuffer[2] = 0xAA;
+
+ if (!DeviceExtension->MSFAddress) {
+
+ lba = MSF_TO_LBA(leadOutM,leadOutS,leadOutF);
+
+ DeviceExtension->DataBuffer[4] = ((PFOUR_BYTE)&lba)->Byte3;
+ DeviceExtension->DataBuffer[5] = ((PFOUR_BYTE)&lba)->Byte2;
+ DeviceExtension->DataBuffer[6] = ((PFOUR_BYTE)&lba)->Byte1;
+ DeviceExtension->DataBuffer[7] = ((PFOUR_BYTE)&lba)->Byte0;
+
+ } else {
+
+ DeviceExtension->DataBuffer[4] = 0;
+ DeviceExtension->DataBuffer[5] = leadOutM;
+ DeviceExtension->DataBuffer[6] = leadOutS;
+ DeviceExtension->DataBuffer[7] = leadOutF;
+ }
+
+ } else {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ } else {
+
+ //
+ // Session info.
+ //
+
+ dataLength = 4;
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_DISC_IN) || (status & STATUS_DOOR_OPEN) ) {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ if (Srb->SenseInfoBufferLength) {
+
+ PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_NOT_READY;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+
+ Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
+ }
+
+ return FALSE;
+ }
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the 'session info' data
+ //
+
+ for (i = 0; i < dataLength; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,*data);
+ if (*data == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "MitsumiReadToc: Error occurred on data read.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+ data++;
+ }
+
+ DeviceExtension->ByteCount = 0;
+ data = DeviceExtension->DataBuffer;
+
+ leadOutM = BCD_TO_DEC(data[1]);
+ leadOutS = BCD_TO_DEC(data[2]);
+ leadOutF = BCD_TO_DEC(data[3]);
+
+ if (!DeviceExtension->MSFAddress) {
+
+ lba = MSF_TO_LBA(leadOutM,leadOutS,leadOutF);
+
+ //
+ // Check for non-multi session disk. The data will
+ // be bogus if it is.
+ //
+
+ if ((LONG)lba < 0) {
+ lba = 0;
+ }
+
+ DeviceExtension->DataBuffer[8] = ((PFOUR_BYTE)&lba)->Byte3;
+ DeviceExtension->DataBuffer[9] = ((PFOUR_BYTE)&lba)->Byte2;
+ DeviceExtension->DataBuffer[10] = ((PFOUR_BYTE)&lba)->Byte1;
+ DeviceExtension->DataBuffer[11] = ((PFOUR_BYTE)&lba)->Byte0;
+
+ } else {
+
+ data[11] = leadOutF;
+ data[10] = leadOutS;
+ data[9] = leadOutM;
+ data[8] = 0;
+ }
+
+ //
+ // Stuff the rest of the buffer with meaningful data.(Look in the spec.)
+ //
+
+ data[7] = 0;
+ data[6] = 0;
+ data[5] = 0;
+ data[4] = 0;
+ data[3] = 0x1;
+ data[2] = 0x1;
+ data[1] = 0xA;
+ data[0] = 0;
+
+ } else {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+
+ }
+ }
+
+ return TRUE;
+}
+
+BOOLEAN
+MitsumiReadSubQ(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+{
+
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ ULONG dataLength = DeviceExtension->ByteCount;
+ PSCSI_REQUEST_BLOCK Srb = DeviceExtension->CurrentSrb;
+ PUCHAR dataBuffer = DeviceExtension->DataBuffer;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ ULONG i,j;
+ ULONG lba;
+ UCHAR status,minutes,seconds,frames;
+ UCHAR format = cdb->SUBCHANNEL.Format;
+
+ if (format == 1) {
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the position data
+ //
+
+ for (i = 0; i < 9; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,dataBuffer[i]);
+ if (dataBuffer[i] == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "GetTrackData: Error occurred on data read.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ return FALSE;
+ }
+ }
+ } else {
+
+ //
+ // Bogus packet.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_SET_DRV_MODE);
+ ScsiPortWritePortUchar(&baseIoAddress->Data, 0x1);
+ ScsiPortStallExecution(1500);
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ DebugPrint((1,
+ "GetTrackData: Error occurred sending command.\n"));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ return FALSE;
+ }
+
+ //
+ // Format the data correctly. Refer to the scsi spec.
+ //
+
+ if (DeviceExtension->MSFAddress) {
+
+ dataBuffer[15] = BCD_TO_DEC(dataBuffer[5]);
+ dataBuffer[14] = BCD_TO_DEC(dataBuffer[4]);
+ dataBuffer[13] = BCD_TO_DEC(dataBuffer[3]);
+ dataBuffer[12] = 0;
+ dataBuffer[11] = BCD_TO_DEC(dataBuffer[9]);
+ dataBuffer[10] = BCD_TO_DEC(dataBuffer[8]);
+ dataBuffer[9] = BCD_TO_DEC(dataBuffer[7]);
+ dataBuffer[8] = 0;
+
+ DebugPrint((3,"MitsumiSubQ: Current MSF %x %x %x\n",
+ dataBuffer[9],
+ dataBuffer[10],
+ dataBuffer[11]));
+
+ } else {
+
+ minutes = BCD_TO_DEC(dataBuffer[3]);
+ seconds = BCD_TO_DEC(dataBuffer[4]);
+ frames = BCD_TO_DEC(dataBuffer[5]);
+
+ lba = MSF_TO_LBA(minutes,seconds,frames);
+
+ dataBuffer[15] = ((PFOUR_BYTE)&lba)->Byte0;
+ dataBuffer[14] = ((PFOUR_BYTE)&lba)->Byte1;
+ dataBuffer[13] = ((PFOUR_BYTE)&lba)->Byte2;
+ dataBuffer[12] = ((PFOUR_BYTE)&lba)->Byte3;
+
+ minutes = BCD_TO_DEC(dataBuffer[7]);
+ seconds = BCD_TO_DEC(dataBuffer[8]);
+ frames = BCD_TO_DEC(dataBuffer[9]);
+
+ lba = MSF_TO_LBA(minutes,seconds,frames);
+
+ dataBuffer[11] = ((PFOUR_BYTE)&lba)->Byte0;
+ dataBuffer[10] = ((PFOUR_BYTE)&lba)->Byte1;
+ dataBuffer[9] = ((PFOUR_BYTE)&lba)->Byte2;
+ dataBuffer[8] = ((PFOUR_BYTE)&lba)->Byte3;
+
+ DebugPrint((3,"MitsumiSubQ: Current LBA %x\n",
+ lba));
+ }
+
+
+ dataBuffer[7] = BCD_TO_DEC(dataBuffer[2]);
+ dataBuffer[6] = BCD_TO_DEC(dataBuffer[1]);
+ dataBuffer[5] = BCD_TO_DEC(dataBuffer[0]);
+ dataBuffer[4] = format;
+
+ DebugPrint((3,"MitsumiSubQ: Track %x, index %x\n",
+ dataBuffer[6],
+ dataBuffer[7]));
+
+ dataBuffer[3] = 12;
+ dataBuffer[2] = 0;
+
+ if (status & STATUS_AUDIO) {
+ dataBuffer[1] = AUDIO_STATUS_PLAYING;
+ } else {
+ dataBuffer[1] = (UCHAR)DeviceExtension->AudioStatus;
+ DeviceExtension->AudioStatus = AUDIO_STATUS_NO_STATUS;
+ }
+
+ DebugPrint((3,"MitsumiSubQ: Audio Status %x\n",
+ dataBuffer[1]));
+
+ dataBuffer[0] = 0;
+
+ DeviceExtension->ByteCount = 0;
+
+ } else {
+
+ //
+ // Not supported right now.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ return FALSE;
+ }
+ return TRUE;
+
+}
+
+
+VOID
+MitsumiCallBack(
+ IN PVOID HwDeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ Timer call-back routine which functions as the ISR for this polling driver.
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ None
+
+--*/
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PREGISTERS baseIoAddress = deviceExtension->BaseIoAddress;
+ ULONG dataLength = deviceExtension->ByteCount;
+ PSCSI_REQUEST_BLOCK Srb = deviceExtension->CurrentSrb;
+ PCMD_PACKET packet;
+ PCDB cdb;
+ BOOLEAN requestSuccess = FALSE;
+ UCHAR scsiOp;
+ UCHAR status;
+
+ if (!Srb) {
+
+ //
+ // Something is hosed, just return.
+ //
+
+ DebugPrint((1,
+ "MitsumiCallBack: Null Srb.\n"));
+ return;
+ }
+
+ cdb = (PCDB)Srb->Cdb;
+ packet = (PCMD_PACKET)Srb->SrbExtension;
+ scsiOp = Srb->Cdb[0];
+
+ switch (scsiOp) {
+ case SCSIOP_READ_CAPACITY:
+
+ if (MitsumiReadCapacity(deviceExtension)) {
+ requestSuccess = TRUE;
+ }
+ break;
+
+ case SCSIOP_READ:
+
+ if (MitsumiRead(deviceExtension)) {
+
+ //
+ // Read was successful
+ //
+
+ requestSuccess = TRUE;
+
+ } else if (Srb->SrbStatus == SRB_STATUS_PENDING) {
+
+ //
+ // We have more data to transfer. Go away while the lightning fast
+ // mechanism does its work.
+ //
+
+ return;
+ }
+
+ break;
+
+ case SCSIOP_READ_TOC:
+
+ if (MitsumiReadToc(deviceExtension)) {
+ requestSuccess = TRUE;
+ }
+ break;
+
+ case SCSIOP_READ_SUB_CHANNEL:
+
+ if (MitsumiReadSubQ(deviceExtension)) {
+ requestSuccess = TRUE;
+ }
+ break;
+
+ case SCSIOP_PLAY_AUDIO_MSF:
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+ if (SUCCESS(status)) {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_SUCCESS;
+ requestSuccess = TRUE;
+
+ } else {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_ERROR;
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ }
+
+ break;
+
+ case SCSIOP_PAUSE_RESUME:
+
+ if(cdb->PAUSE_RESUME.Action) {
+
+ ULONG i;
+ UCHAR minutes,seconds,frames;
+
+ //
+ // We did a seek to the saved position. Now issue a play from here
+ // to the saved ending MSF.
+ //
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+ if (SUCCESS(status)) {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_SUCCESS;
+ requestSuccess = TRUE;
+
+ } else {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_ERROR;
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ }
+
+ minutes = (UCHAR)(deviceExtension->EndPosition / (60 * 75));
+ seconds = (UCHAR)((deviceExtension->EndPosition % (60 * 75)) / 75);
+ frames = (UCHAR)((deviceExtension->EndPosition % (60 * 75)) % 75);
+
+ DebugPrint((2,
+ "MitsumiBuildCommand: resume: lba %x, m %x, s %x, f%x\n",
+ deviceExtension->SavedPosition,
+ minutes,
+ seconds,
+ frames));
+
+ //
+ // Convert MSF to BCD. Don't need to setup start address and opcode since they are
+ // already there.
+ //
+
+ packet->Parameters[3] = DEC_TO_BCD(minutes);
+ packet->Parameters[4] = DEC_TO_BCD(seconds);
+ packet->Parameters[5] = DEC_TO_BCD(frames);
+
+
+ //
+ // Send the packet.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data,packet->OperationCode);
+ for (i = 0; i < 6; i++) {
+ ScsiPortWritePortUchar(&baseIoAddress->Data,packet->Parameters[i]);
+ }
+
+ //
+ // Wait for completion, and update status.
+ //
+
+ ScsiPortStallExecution(4000);
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+ if (SUCCESS(status)) {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_SUCCESS;
+ requestSuccess = TRUE;
+
+ } else {
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_ERROR;
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ }
+
+ } else {
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+ if (SUCCESS(status)) {
+ deviceExtension->AudioStatus = AUDIO_STATUS_PAUSED;
+
+ UpdateCurrentPosition(deviceExtension);
+
+ requestSuccess = TRUE;
+ } else {
+
+ DebugPrint((1,"MitsumiCallBack: Error on pause %x\n",
+ status));
+
+ deviceExtension->AudioStatus = AUDIO_STATUS_ERROR;
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ }
+ }
+
+ break;
+
+ case SCSIOP_START_STOP_UNIT:
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+ if (SUCCESS(status)) {
+ requestSuccess = TRUE;
+ } else {
+
+ DebugPrint((1,"MitsumiCallBack: Error on start/stop %x\n",
+ status));
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+ }
+
+ break;
+
+ default:
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ } //switch scsiOp
+
+
+ if (requestSuccess) {
+
+ //
+ // Update srb and scsi status.
+ //
+
+ if (deviceExtension->ByteCount) {
+ Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
+ } else {
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ }
+
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+ }
+
+ //
+ // Indicate command complete.
+ //
+
+ ScsiPortNotification(RequestComplete,
+ deviceExtension,
+ Srb);
+
+ //
+ // Indicate ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return;
+}
+
+
+BOOLEAN
+MitsumiReset(
+ IN PVOID HwDeviceExtension,
+ IN ULONG PathId
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - HBA miniport driver's adapter data storage
+
+Return Value:
+
+ Nothing.
+
+
+--*/
+
+{
+ PHW_DEVICE_EXTENSION deviceExtension = HwDeviceExtension;
+ PREGISTERS baseIoAddress = deviceExtension->BaseIoAddress;
+ UCHAR status;
+
+ //
+ // Clean out device extension and complete outstanding commands.
+ //
+
+ if (deviceExtension->CurrentSrb) {
+
+ //
+ // Complete outstanding request with SRB_STATUS_BUS_RESET.
+ //
+
+ ScsiPortCompleteRequest(deviceExtension,
+ 0xFF,
+ 0xFF,
+ 0xFF,
+ (ULONG)SRB_STATUS_BUS_RESET);
+
+ }
+
+ //
+ // Clear state fields.
+ //
+
+ deviceExtension->CurrentSrb = NULL;
+ deviceExtension->ByteCount = 0;
+ deviceExtension->DataBuffer = NULL;
+
+ //
+ // Reset the device.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Reset,0);
+ ScsiPortStallExecution(10);
+ ScsiPortWritePortUchar(&baseIoAddress->Status, 0);
+ ScsiPortStallExecution (10);
+
+ ReadStatus(deviceExtension,baseIoAddress,status);
+
+ //
+ // Wait 1 second for unit to recover.
+ //
+
+ ScsiPortStallExecution (1000 * 1000);
+
+ //
+ // Indicate ready for next request.
+ //
+
+ ScsiPortNotification(NextRequest,
+ deviceExtension,
+ NULL);
+
+ return TRUE;
+
+} // end MitsumiReset()
+
+BOOLEAN
+MitsumiBuildCommand(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - The hardware device extension.
+
+ Srb - The current Srb.
+
+Return Value:
+
+ status
+
+--*/
+
+{
+ PCMD_PACKET packet = (PCMD_PACKET)Srb->SrbExtension;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ ULONG i;
+ UCHAR minutes,seconds,frames;
+
+ //
+ // Zero the packet.
+ //
+
+ packet->OperationCode = 0;
+ for (i = 0; i < 6; i++) {
+ packet->Parameters[i] = 0;
+ }
+
+ DeviceExtension->DataBuffer = (PUCHAR)Srb->DataBuffer;
+ DeviceExtension->ByteCount = Srb->DataTransferLength;
+ DeviceExtension->MSFAddress = FALSE;
+
+ Srb->SrbStatus = SRB_STATUS_PENDING;
+
+ //
+ // Map CDB to Panasonic packet command
+ //
+
+ switch (Srb->Cdb[0]) {
+
+ case SCSIOP_READ:
+
+ DeviceExtension->StatusRetries = 0;
+
+#ifdef GATHER_STATS
+ DeviceExtension->Requests++;
+ DeviceExtension->FirstCall = TRUE;
+#endif
+ packet->OperationCode = (DeviceExtension->DriveType == FX001D) ? OP_READ_PLAY_DBL : OP_READ_PLAY;
+
+ //
+ // Convert starting LBA to MSF
+ //
+
+ LBA_TO_MSF(cdb,minutes,seconds,frames);
+
+ //
+ // Convert MSF to BCD
+ //
+
+ packet->Parameters[0] = DEC_TO_BCD(minutes);
+ packet->Parameters[1] = DEC_TO_BCD(seconds);
+ packet->Parameters[2] = DEC_TO_BCD(frames);
+
+ //
+ // Convert blocks to transfer to BCD
+ //
+
+ packet->Parameters[3] = 0;
+ packet->Parameters[4] = cdb->CDB10.TransferBlocksMsb;
+ packet->Parameters[5] = cdb->CDB10.TransferBlocksLsb;
+
+ packet->ParameterLength = 6;
+
+ break;
+
+ case SCSIOP_START_STOP_UNIT:
+
+ if (cdb->START_STOP.Start) {
+
+ if (cdb->START_STOP.LoadEject) {
+
+ //
+ // Load an ejected disk and spin up.
+ //
+
+ packet->OperationCode = OP_LOAD;
+ packet->ParameterLength = 0;
+
+
+ } else {
+
+ //
+ // Spin up the device by issuing a seek to start of disk.
+ //
+
+ packet->OperationCode = (DeviceExtension->DriveType == FX001D) ? OP_READ_PLAY_DBL : OP_READ_PLAY;
+ packet->Parameters[0] = 0;
+ packet->Parameters[1] = 2;
+ packet->Parameters[2] = 0;
+ packet->Parameters[3] = 0;
+ packet->Parameters[4] = 0;
+ packet->Parameters[5] = 0;
+
+ packet->ParameterLength = 6;
+
+
+ }
+ } else {
+
+ if (cdb->START_STOP.LoadEject) {
+
+ //
+ // Eject the disk
+ //
+
+ packet->OperationCode = OP_EJECT;
+ packet->ParameterLength = 0;
+
+ } else {
+
+ //
+ // Seek to start and hold.
+ //
+
+ packet->OperationCode = OP_READ_PLAY;
+ packet->Parameters[0] = 0;
+ packet->Parameters[1] = 2;
+ packet->Parameters[2] = 0;
+ packet->Parameters[3] = 0;
+ packet->Parameters[4] = 0;
+ packet->Parameters[5] = 0;
+
+ packet->ParameterLength = 6;
+ }
+ }
+
+ break;
+
+ case SCSIOP_PAUSE_RESUME:
+
+ //
+ // Issue pause/resume.
+ //
+
+
+ if(cdb->PAUSE_RESUME.Action) {
+
+ //
+ // Resume - issue a zero length play to the "saved" MSF.
+ //
+
+ packet->OperationCode = OP_READ_PLAY;
+
+ //
+ // Convert starting LBA to MSF
+ //
+
+ minutes = (UCHAR)(DeviceExtension->SavedPosition / (60 * 75));
+ seconds = (UCHAR)((DeviceExtension->SavedPosition % (60 * 75)) / 75);
+ frames = (UCHAR)((DeviceExtension->SavedPosition % (60 * 75)) % 75);
+
+ DebugPrint((2,
+ "MitsumiBuildCommand: resume: lba %x, m %x, s %x, f%x\n",
+ DeviceExtension->SavedPosition,
+ minutes,
+ seconds,
+ frames));
+
+ //
+ // Convert MSF to BCD
+ //
+
+ packet->Parameters[0] = DEC_TO_BCD(minutes);
+ packet->Parameters[1] = DEC_TO_BCD(seconds);
+ packet->Parameters[2] = DEC_TO_BCD(frames);
+
+ //
+ // Setup a 'play' of zero length.
+ //
+
+ packet->Parameters[3] = 0;
+ packet->Parameters[4] = 0;
+ packet->Parameters[5] = 0;
+
+ packet->ParameterLength = 6;
+
+
+ } else {
+
+ //
+ // Pause - issue Hold command.
+ //
+
+ packet->OperationCode = OP_PAUSE;
+ packet->ParameterLength = 0;
+
+ }
+
+
+ break;
+
+ case SCSIOP_PLAY_AUDIO_MSF:
+
+ //
+ // Update the status to ensure that a current audio play
+ // is not in progress.
+ //
+
+ CheckStatus(DeviceExtension);
+
+ if (DeviceExtension->DriveStatus & STATUS_AUDIO) {
+
+ //
+ // stop the current play by issuing hold command.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_PAUSE);
+
+ ScsiPortStallExecution(1000);
+
+ CheckStatus(DeviceExtension);
+ if (DeviceExtension->DriveStatus & STATUS_AUDIO) {
+ DebugPrint((1,"MitsumiBuildcommand: Audio still not paused. %x\n",
+ DeviceExtension->DriveStatus));
+ }
+
+
+ }
+
+ packet->OperationCode = OP_READ_PLAY;
+
+ //
+ // Convert MSF to BCD
+ //
+
+ packet->Parameters[0] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.StartingM);
+ packet->Parameters[1] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.StartingS);
+ packet->Parameters[2] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.StartingF);
+
+ //
+ // Convert end address to BCD
+ //
+
+ packet->Parameters[3] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.EndingM);
+ packet->Parameters[4] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.EndingS);
+ packet->Parameters[5] = DEC_TO_BCD(cdb->PLAY_AUDIO_MSF.EndingF);
+
+ //
+ // Setup EndPosition in DevExt. since we may get a pause/resume.
+ //
+
+ DeviceExtension->EndPosition = MSF_TO_LBA(cdb->PLAY_AUDIO_MSF.EndingM,
+ cdb->PLAY_AUDIO_MSF.EndingS,
+ cdb->PLAY_AUDIO_MSF.EndingF);
+
+ //
+ // Determine is this is a seek. If so, zero the ending address fields to indicate
+ // a play of zero length.
+ //
+
+ if (packet->Parameters[0] == packet->Parameters[3] &&
+ packet->Parameters[1] == packet->Parameters[4] &&
+ packet->Parameters[2] == packet->Parameters[5] ) {
+
+ packet->Parameters[3] = 0;
+ packet->Parameters[4] = 0;
+ packet->Parameters[5] = 0;
+
+ }
+ packet->ParameterLength = 6;
+
+ break;
+
+
+ case SCSIOP_READ_TOC:
+
+ //
+ // See if MSF addresses are enabled.
+ //
+
+ if ( cdb->READ_TOC.Msf) {
+ DeviceExtension->MSFAddress = TRUE;
+ }
+
+ //
+ // Setup the appropriate command - full read toc or session info.
+ //
+
+ if (cdb->READ_TOC.Format == 0) {
+
+ packet->OperationCode = OP_READ_TOC;
+ packet->ParameterLength = 0;
+
+ } else {
+
+ packet->OperationCode = OP_READ_SESSION;
+ packet->ParameterLength = 0;
+ }
+
+ break;
+
+
+ case SCSIOP_INQUIRY: {
+
+ PINQUIRYDATA inquiryData = Srb->DataBuffer;
+
+ //
+ // For now, support only one drive at drive select 0 on this controller.
+ // Actually, I have no idea if more can be supported.
+ //
+
+ if (Srb->Lun > 0 || Srb->TargetId > 0) {
+
+ Srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT;
+ return FALSE;
+
+ }
+
+ //
+ // Zero inquiry buffer.
+ //
+
+ for (i = 0; i < INQUIRYDATABUFFERSIZE; i++) {
+ ((PUCHAR)inquiryData)[i] = 0;
+ }
+
+ //
+ // Fill in the necessary fields of inquiry data.
+ //
+
+ inquiryData->DeviceType = READ_ONLY_DIRECT_ACCESS_DEVICE;
+ inquiryData->RemovableMedia = 1;
+
+ inquiryData->VendorId[0] = 'M';
+ inquiryData->VendorId[1] = 'I';
+ inquiryData->VendorId[2] = 'T';
+ inquiryData->VendorId[3] = 'S';
+ inquiryData->VendorId[4] = 'U';
+ inquiryData->VendorId[5] = 'M';
+ inquiryData->VendorId[6] = 'I';
+ inquiryData->VendorId[7] = ' ';
+
+ inquiryData->ProductId[0] = 'C';
+ inquiryData->ProductId[1] = 'R';
+ inquiryData->ProductId[2] = 'M';
+ inquiryData->ProductId[3] = 'C';
+ inquiryData->ProductId[4] = '-';
+
+ if (DeviceExtension->DriveType == LU005) {
+
+ inquiryData->ProductId[5] = 'L';
+ inquiryData->ProductId[6] = 'U';
+ inquiryData->ProductId[9] = '5';
+ inquiryData->ProductId[10] = 'S';
+
+ } else if (DeviceExtension->DriveType == FX001) {
+
+ inquiryData->ProductId[5] = 'F';
+ inquiryData->ProductId[6] = 'X';
+ inquiryData->ProductId[9] = '1';
+ inquiryData->ProductId[10] = ' ';
+
+ } else {
+
+ inquiryData->ProductId[5] = 'F';
+ inquiryData->ProductId[6] = 'X';
+ inquiryData->ProductId[9] = '1';
+ inquiryData->ProductId[10] = 'D';
+
+ }
+
+ inquiryData->ProductId[7] = '0';
+ inquiryData->ProductId[8] = '0';
+ inquiryData->ProductId[11] = ' ';
+ inquiryData->ProductId[12] = ' ';
+ inquiryData->ProductId[13] = ' ';
+ inquiryData->ProductId[14] = ' ';
+ inquiryData->ProductId[15] = ' ';
+
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+
+ break;
+ }
+
+ case SCSIOP_READ_CAPACITY:
+
+ //
+ // Create the READ_TOC_DATA packet. The returned values will be munged
+ // into a usable form.
+ //
+
+ packet->OperationCode = OP_READ_TOC;
+ packet->ParameterLength = 0;
+
+ break;
+
+ case SCSIOP_READ_SUB_CHANNEL:
+
+
+ if ( cdb->SUBCHANNEL.Msf) {
+ DeviceExtension->MSFAddress = TRUE;
+ }
+
+ //
+ // determine what sub-channel data format is required, since this device has 3 different commands
+ // for the SCSI READ_SUB_CHANNEL Opcode. In the callback routine, the remaining calls, if any, will
+ // be issued.
+ //
+
+ switch (cdb->SUBCHANNEL.Format) {
+
+ case 1:
+
+ //
+ // Current position.
+ //
+
+ packet->OperationCode = OP_READ_SUB_CHANNEL;
+ packet->ParameterLength = 0;
+
+ break;
+
+ case 2:
+
+ //
+ // Media catalogue number. TODO: support the rest of these.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ case 3:
+
+ //
+ // Track ISRC
+ //
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ break;
+
+ default:
+
+ //
+ // Bogus value.
+ //
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+ return FALSE;
+ }
+
+ break;
+
+ case SCSIOP_TEST_UNIT_READY:
+ case SCSIOP_REQUEST_SENSE:
+
+ break;
+
+ case SCSIOP_MODE_SENSE:
+ case SCSIOP_MODE_SELECT:
+ case SCSIOP_MEDIUM_REMOVAL:
+ case SCSIOP_READ_HEADER:
+
+ //
+ // Dont support this for now. Fall through to default.
+ //
+
+ default:
+
+ DebugPrint((1,
+ "MitsumiBuildCommand: Unsupported command %x\n",
+ Srb->Cdb[0]));
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+
+ return FALSE;
+
+ } // end switch
+
+ return TRUE;
+
+}
+
+BOOLEAN
+MitsumiSendCommand(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - The hardware device extension.
+
+ Srb - The current Srb.
+
+Return Value:
+
+ TRUE if command sent successfully.
+
+--*/
+
+{
+
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ PCDB cdb = (PCDB)Srb->Cdb;
+ PCMD_PACKET packet = (PCMD_PACKET)Srb->SrbExtension;
+ PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->SenseInfoBuffer;
+ ULONG i;
+ UCHAR sessionData[6];
+ UCHAR minutes,seconds,frames,frameData;
+ UCHAR status = DeviceExtension->DriveStatus;
+
+
+ //
+ // Map CDB to Mitsumi packet command
+ //
+
+ switch (Srb->Cdb[0]) {
+
+ case SCSIOP_READ:
+
+
+ break;
+
+ case SCSIOP_INQUIRY:
+
+ //
+ // Data buffer filled in during MitsumiBuildCommand.
+ //
+
+ return TRUE;
+
+ case SCSIOP_READ_TOC:
+ case SCSIOP_PAUSE_RESUME:
+ case SCSIOP_PLAY_AUDIO_MSF:
+ case SCSIOP_READ_CAPACITY:
+ case SCSIOP_READ_SUB_CHANNEL:
+ case SCSIOP_START_STOP_UNIT:
+
+ //
+ // Nothing to do here. Everything setup in BuildCommand.
+ //
+
+ break;
+
+ case SCSIOP_TEST_UNIT_READY:
+
+
+ if (!CheckStatus(DeviceExtension)) {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ return FALSE;
+ }
+
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+
+ return TRUE;
+
+ case SCSIOP_REQUEST_SENSE:
+
+ //
+ // Issue the ReadAndMapError command, which will create a request sense packet.
+ //
+
+ if (ReadAndMapError(DeviceExtension,Srb)) {
+
+ Srb->SrbStatus = SRB_STATUS_SUCCESS;
+ Srb->ScsiStatus = SCSISTAT_GOOD;
+
+ } else {
+
+ Srb->SrbStatus = SRB_STATUS_ERROR;
+ Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
+
+ }
+
+ //
+ // Update the drive status.
+ //
+
+ CheckStatus(DeviceExtension);
+
+ return TRUE;
+
+ case SCSIOP_MODE_SENSE:
+ case SCSIOP_MODE_SELECT:
+ case SCSIOP_READ_HEADER:
+ case SCSIOP_MEDIUM_REMOVAL:
+
+ //
+ // Dont support this for now. Fall through.
+ //
+
+ default:
+
+ DebugPrint((1,
+ "MitsumiSendCommand: Unsupported command %x\n",
+ Srb->Cdb[0]));
+
+ Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
+
+ } // end switch
+
+ if (Srb->SrbStatus == SRB_STATUS_PENDING) {
+
+ //
+ // Write the packet
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data,packet->OperationCode);
+ for (i = 0; i < packet->ParameterLength; i++) {
+ ScsiPortWritePortUchar(&baseIoAddress->Data,packet->Parameters[i]);
+ }
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+ }
+
+} // End MitsumiSendCommand()
+
+
+UCHAR
+WaitForSTEN(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ DeviceExtension - The device extension.
+
+
+Return Value:
+
+ TRUE if STEN signal asserted within timeout period.
+
+--*/
+
+{
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ ULONG i;
+ UCHAR status;
+
+ for (i = 0; i < 40 * 1000; i++) {
+ status = ScsiPortReadPortUchar(&baseIoAddress->Status);
+ if (status & STEN) {
+ ScsiPortStallExecution(10);
+ } else {
+ break;
+ }
+ }
+ if (i == 1000 * 40) {
+ return 0xFF;
+ }
+ return status;
+}
+
+BOOLEAN
+CheckStatus(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ DeviceExtension - The device extension.
+
+
+Return Value:
+
+ Drive status.
+
+--*/
+
+{
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ CMD_PACKET packet;
+ ULONG i;
+ UCHAR status;
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data,OP_READ_STATUS);
+ do {
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+ } while (status == 0xFF);
+
+ DeviceExtension->DriveStatus = status;
+ // TODO: Change to SUCCESS macro
+
+ if ( (status & (STATUS_DOOR_OPEN | STATUS_CMD_ERROR | STATUS_MEDIA_CHANGE | STATUS_READ_ERROR)) ||
+ (!(status & (STATUS_SPIN_UP | STATUS_DISC_IN))) ) {
+
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+
+BOOLEAN
+UpdateCurrentPosition(
+ IN PHW_DEVICE_EXTENSION DeviceExtension
+ )
+/*++
+
+Routine Description:
+
+ Determines the current MSF and stores it in the deviceExtension in LBA form.
+
+Arguments:
+
+ HwDeviceExtension - The hardware device extension.
+
+Return Value:
+
+ TRUE - If command succeeded.
+
+--*/
+{
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ ULONG i,
+ lba;
+ UCHAR status;
+ UCHAR dataBuffer[10];
+
+ //
+ // Issue SubQ command.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data, OP_READ_SUB_CHANNEL);
+
+ ScsiPortStallExecution(1000);
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the position data
+ //
+
+ for (i = 0; i < 9; i++) {
+ ReadStatus(DeviceExtension,baseIoAddress,dataBuffer[i]);
+ if (dataBuffer[i] == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "UpdateCurrentPosition: Error occurred on data read. %x\n",
+ status));
+
+ return FALSE;
+ }
+ }
+ } else {
+
+ DebugPrint((1,
+ "UpdateCurrentPosition: Error occurred sending command. %x\n",
+ status));
+
+ return FALSE;
+ }
+
+ //
+ // Convert the MSF to LBA and store in devExt.
+ //
+
+ BCD_TO_DEC(dataBuffer[7]);
+ BCD_TO_DEC(dataBuffer[8]);
+ BCD_TO_DEC(dataBuffer[9]);
+
+ lba = MSF_TO_LBA(dataBuffer[7],dataBuffer[8],dataBuffer[9]);
+
+ DebugPrint((2,
+ "UpdateCurrentPosition: M %x, S %x, F %x LBA %x\n",
+ dataBuffer[7],
+ dataBuffer[8],
+ dataBuffer[9],
+ lba));
+
+ DeviceExtension->SavedPosition = lba;
+
+ return TRUE;
+}
+
+
+
+BOOLEAN
+ReadAndMapError(
+ IN PHW_DEVICE_EXTENSION DeviceExtension,
+ IN PSCSI_REQUEST_BLOCK Srb
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ HwDeviceExtension - The hardware device extension.
+
+ Srb - The failing Srb.
+
+
+Return Value:
+
+ SRB Error
+
+--*/
+
+{
+ PREGISTERS baseIoAddress = DeviceExtension->BaseIoAddress;
+ PSENSE_DATA senseBuffer = (PSENSE_DATA)Srb->DataBuffer;
+ UCHAR status = DeviceExtension->DriveStatus;
+ ULONG i;
+ UCHAR errorData;
+
+ //
+ // Check drive status. It may have been updated at the point of error.
+ //
+
+ if ( (status & STATUS_DOOR_OPEN) ||
+ !(status & STATUS_DISC_IN)) {
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_NOT_READY;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+ return TRUE;
+ }
+
+
+ if (status & STATUS_CMD_ERROR ) {
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
+ senseBuffer->AdditionalSenseCode = 0x24;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+ return TRUE;
+ }
+
+ if (status & STATUS_MEDIA_CHANGE) {
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_UNIT_ATTENTION;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED;
+ senseBuffer->AdditionalSenseCodeQualifier = 0x0;
+ return TRUE;
+
+ }
+
+ if (!(status & STATUS_SPIN_UP)) {
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->SenseKey = SCSI_SENSE_NOT_READY;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_LUN_NOT_READY;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+ return TRUE;
+ }
+
+
+ //
+ // Setup sense buffer common values.
+ //
+
+ senseBuffer->ErrorCode = 0x70;
+ senseBuffer->Valid = 1;
+ senseBuffer->AdditionalSenseLength = 0xb;
+ senseBuffer->IncorrectLength = FALSE;
+ senseBuffer->SenseKey = 0;
+ senseBuffer->AdditionalSenseCode = 0;
+ senseBuffer->AdditionalSenseCodeQualifier = 0;
+
+ if (status & STATUS_READ_ERROR ) {
+
+ //
+ // Issue the request sense command.
+ //
+
+ ScsiPortWritePortUchar(&baseIoAddress->Data,OP_REQUEST_SENSE);
+
+
+ ReadStatus(DeviceExtension,baseIoAddress,status);
+
+ if (!(status & STATUS_CMD_ERROR)) {
+
+ //
+ // Read the sense data
+ //
+
+ ReadStatus(DeviceExtension,baseIoAddress,errorData);
+ if (errorData == 0xFF) {
+
+ //
+ // Timeout occurred.
+ //
+
+ DebugPrint((1,
+ "ReadAndMapError: Error occurred on data read.\n"));
+ return FALSE;
+ }
+
+ } else {
+
+ DebugPrint((1,
+ "ReadAndMapError: Error reading sense data\n"));
+ return FALSE;
+ }
+
+
+
+ //
+ // Map Mitsumi error code to Srb status
+ //
+
+ switch (errorData) {
+ case 0:
+
+ //
+ // No error.
+ //
+
+ senseBuffer->SenseKey = 0x00;
+ senseBuffer->AdditionalSenseCode = 0x00;
+ senseBuffer->AdditionalSenseCodeQualifier = 0x0;
+
+ break;
+
+ case 1:
+
+ //
+ // Mode error.
+ //
+
+ senseBuffer->SenseKey = 0x02;
+ senseBuffer->AdditionalSenseCode = 0x30;
+ senseBuffer->AdditionalSenseCodeQualifier = 0x02;
+
+ break;
+
+ case 2:
+
+ //
+ // Address error.
+ //
+
+ senseBuffer->SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_BLOCK;
+ break;
+
+ case 3:
+
+ //
+ // Fatal error.
+ //
+
+ senseBuffer->SenseKey = SCSI_SENSE_HARDWARE_ERROR;
+
+ break;
+
+ case 4:
+
+ //
+ // Seek error.
+ //
+
+ senseBuffer->SenseKey = SCSI_SENSE_MEDIUM_ERROR;
+ senseBuffer->AdditionalSenseCode = SCSI_ADSENSE_SEEK_ERROR;
+
+ break;
+
+ default:
+
+ senseBuffer->SenseKey = 0x00;
+ senseBuffer->AdditionalSenseCode = 0x00;
+ senseBuffer->AdditionalSenseCodeQualifier = 0x0;
+
+ }
+ }
+
+ return TRUE;
+
+} // end ReadAndMapError()
+
+