/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: io.c Abstract: This module handles device ioctl's and read/write to the sdbus driver. Authors: Neil Sandlin (neilsa) 1-Jan-2002 Environment: Kernel mode Revision History : --*/ #include "pch.h" NTSTATUS SdbusPdoSubmitRequest( IN PFDO_EXTENSION fdoExtension, IN PPDO_EXTENSION pdoExtension, IN PSDBUS_REQUEST_PACKET SdRp, PSDBUS_WORKPACKET_COMPLETION_ROUTINE CompletionRoutine, IN PIRP Irp ); VOID SdbusPdoInternalIoctlCompletion ( IN PSD_WORK_PACKET WorkPacket, IN NTSTATUS status ); VOID SdbusPdoIoctlCompletion ( IN PSD_WORK_PACKET WorkPacket, IN NTSTATUS status ); NTSTATUS SdbusFdoDeviceControl( IN PDEVICE_OBJECT Fdo, IN PIRP Irp ) /*++ Routine Description: IOCTL device routine Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the IRP Return Value: Status --*/ { Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_INVALID_DEVICE_REQUEST; } NTSTATUS SdbusPdoInternalDeviceControl( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: IOCTL device routine Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the IRP Return Value: Status --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension; PDEVICE_OBJECT fdo = fdoExtension->DeviceObject; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x irp %.08x code %08x DISPATCH\n", Pdo, Irp, irpSp->Parameters.DeviceIoControl.IoControlCode)); switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SD_SUBMIT_REQUEST: { PSDBUS_REQUEST_PACKET SdRp = Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_REQUEST_PACKET)) { status = STATUS_INVALID_PARAMETER; break; } // // Build and queue a work packet to handle this request // status = SdbusPdoSubmitRequest(fdoExtension, pdoExtension, SdRp, SdbusPdoInternalIoctlCompletion, Irp); break; } default: status = STATUS_INVALID_DEVICE_REQUEST; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x irp %.08x complete %08x\n", Pdo, Irp, status)); break; } if (status != STATUS_PENDING) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x ioctl exits %08x\n", Pdo, status)); return status; } VOID SdbusPdoInternalIoctlCompletion ( IN PSD_WORK_PACKET WorkPacket, IN NTSTATUS status ) { PIRP irp = WorkPacket->CompletionContext; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %08x irp %08x ioctl complete %08x\n", WorkPacket->PdoExtension->DeviceObject, irp, status)); if (NT_SUCCESS(status)) { irp->IoStatus.Information = WorkPacket->Information; } irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); ExFreePool(WorkPacket); } NTSTATUS SdbusPdoSubmitRequest( IN PFDO_EXTENSION fdoExtension, IN PPDO_EXTENSION pdoExtension, IN PSDBUS_REQUEST_PACKET SdRp, PSDBUS_WORKPACKET_COMPLETION_ROUTINE CompletionRoutine, IN PIRP Irp ) { NTSTATUS status; PSD_WORK_PACKET workPacket; switch(SdRp->Function) { case SDRP_READ_BLOCK: status = SdbusBuildWorkPacket(fdoExtension, SDWP_READBLOCK, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.ReadBlock.ByteOffset = SdRp->Parameters.ReadBlock.ByteOffset; workPacket->Parameters.ReadBlock.Buffer = SdRp->Parameters.ReadBlock.Buffer; workPacket->Parameters.ReadBlock.Length = SdRp->Parameters.ReadBlock.Length; SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_WRITE_BLOCK: if ((*(fdoExtension->FunctionBlock->IsWriteProtected))(fdoExtension)) { status = STATUS_MEDIA_WRITE_PROTECTED; break; } status = SdbusBuildWorkPacket(fdoExtension, SDWP_WRITEBLOCK, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.WriteBlock.ByteOffset = SdRp->Parameters.WriteBlock.ByteOffset; workPacket->Parameters.WriteBlock.Buffer = SdRp->Parameters.WriteBlock.Buffer; workPacket->Parameters.WriteBlock.Length = SdRp->Parameters.WriteBlock.Length; IoMarkIrpPending(Irp); SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_READ_IO: status = SdbusBuildWorkPacket(fdoExtension, SDWP_READIO, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.ReadIo.Offset = SdRp->Parameters.ReadIo.Offset; workPacket->Parameters.ReadIo.Buffer = SdRp->Parameters.ReadIo.Buffer; workPacket->Parameters.ReadIo.Length = 1; IoMarkIrpPending(Irp); SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_READ_IO_EXTENDED: status = SdbusBuildWorkPacket(fdoExtension, SDWP_READIO_EXTENDED, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.ReadIo.Offset = SdRp->Parameters.ReadIoExtended.Offset; workPacket->Parameters.ReadIo.Buffer = SdRp->Parameters.ReadIoExtended.Buffer; workPacket->Parameters.ReadIo.Length = SdRp->Parameters.ReadIoExtended.Length; IoMarkIrpPending(Irp); SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_WRITE_IO: status = SdbusBuildWorkPacket(fdoExtension, SDWP_WRITEIO, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.WriteIo.Offset = SdRp->Parameters.WriteIo.Offset; workPacket->Parameters.WriteIo.Data = SdRp->Parameters.WriteIo.Data; workPacket->Parameters.WriteIo.Length = 1; IoMarkIrpPending(Irp); SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_WRITE_IO_EXTENDED: status = SdbusBuildWorkPacket(fdoExtension, SDWP_WRITEIO_EXTENDED, CompletionRoutine, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.WriteIo.Offset = SdRp->Parameters.WriteIoExtended.Offset; workPacket->Parameters.WriteIo.Buffer = SdRp->Parameters.WriteIoExtended.Buffer; workPacket->Parameters.WriteIo.Length = SdRp->Parameters.WriteIoExtended.Length; IoMarkIrpPending(Irp); SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; case SDRP_ACKNOWLEDGE_INTERRUPT: ASSERT((pdoExtension->Flags & SDBUS_PDO_CALLBACK_IN_SERVICE) != 0); pdoExtension->Flags &= ~SDBUS_PDO_CALLBACK_IN_SERVICE; (*(fdoExtension->FunctionBlock->AcknowledgeEvent))(fdoExtension, SDBUS_EVENT_CARD_INTERRUPT); status = STATUS_SUCCESS; break; default: status = STATUS_INVALID_PARAMETER; } return status; } NTSTATUS SdbusPdoDeviceControl( IN PDEVICE_OBJECT Pdo, IN PIRP Irp ) /*++ Routine Description: IOCTL device routine Arguments: DeviceObject - Pointer to the device object. Irp - Pointer to the IRP Return Value: Status --*/ { PPDO_EXTENSION pdoExtension = Pdo->DeviceExtension; PFDO_EXTENSION fdoExtension = pdoExtension->FdoExtension; PDEVICE_OBJECT fdo = fdoExtension->DeviceObject; PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_SUCCESS; PSD_WORK_PACKET workPacket; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x irp %.08x code %08x DISPATCH\n", Pdo, Irp, irpSp->Parameters.DeviceIoControl.IoControlCode)); switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SD_INTERFACE_OPEN: { PSDBUS_INTERFACE_DATA interfaceData = Irp->AssociatedIrp.SystemBuffer; if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_INTERFACE_DATA)) { status = STATUS_INVALID_PARAMETER; break; } if (interfaceData->DeviceGeneratesInterrupts) { pdoExtension->Flags |= SDBUS_PDO_GENERATES_IRQ; // // ISSUE: this is not multifunction-aware // if (interfaceData->CallbackAtDpcLevel) { // // reflect ISR at dispatch level // pdoExtension->Flags |= SDBUS_PDO_DPC_CALLBACK; } else { // // reflect ISR at passive level // KeInitializeEvent(&fdoExtension->CardInterruptEvent, SynchronizationEvent, FALSE); KeInitializeEvent(&fdoExtension->WorkItemExitEvent, SynchronizationEvent, FALSE); fdoExtension->Flags |= SDBUS_FDO_WORK_ITEM_ACTIVE; IoQueueWorkItem(fdoExtension->IoWorkItem, SdbusEventWorkItemProc, DelayedWorkQueue, NULL); } (*(fdoExtension->FunctionBlock->EnableEvent))(fdoExtension, SDBUS_EVENT_CARD_INTERRUPT); } // TO IMPLEMENT: can I validate this address at all? pdoExtension->CallbackRoutine = interfaceData->CallbackRoutine; pdoExtension->CallbackRoutineContext = interfaceData->CallbackRoutineContext; status = STATUS_SUCCESS; break; } case IOCTL_SD_INTERFACE_CLOSE: // // ISSUE: this is not multifunction-aware // if (fdoExtension->Flags & SDBUS_FDO_WORK_ITEM_ACTIVE) { fdoExtension->Flags &= ~SDBUS_FDO_WORK_ITEM_ACTIVE; KeSetEvent(&fdoExtension->WorkItemExitEvent, 0, FALSE); } status = STATUS_SUCCESS; break; case IOCTL_SD_GET_DEVICE_PARMS: { PSDBUS_DEVICE_PARAMETERS deviceParameters = Irp->AssociatedIrp.SystemBuffer; ULONG deviceSize; ULONGLONG capacity, blockNr, mult, block_len; PSD_CSD sdCsd; DebugPrint((SDBUS_DEBUG_IOCTL, "IOCTL_SD_GET_DEVICE_PARMS\n")); if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(SDBUS_DEVICE_PARAMETERS)) { status = STATUS_INVALID_PARAMETER; break; } //ISSUE: NEED TO IMPLEMENT: synchronization sdCsd = &fdoExtension->CardData->SdCsd; deviceSize = sdCsd->b.DeviceSizeHigh << 2 | sdCsd->c.DeviceSizeLow; mult = (1 << (sdCsd->c.DeviceSizeMultiplier+2)); blockNr = (deviceSize+1) * mult; block_len = (1 << sdCsd->b.MaxReadDataBlockLength); capacity = blockNr * block_len; deviceParameters->Capacity = capacity; deviceParameters->WriteProtected = (*(fdoExtension->FunctionBlock->IsWriteProtected))(fdoExtension); Irp->IoStatus.Information = sizeof(SDBUS_DEVICE_PARAMETERS); DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x irp %.08x complete %08x\n", Pdo, Irp, status)); break; } #if 0 case IOCTL_SD_READ_BLOCK: { PSDBUS_READ_PARAMETERS readParameters = Irp->AssociatedIrp.SystemBuffer; DebugPrint((SDBUS_DEBUG_IOCTL, "IOCTL_SD_READ_BLOCK - off %08x len %08x\n", (ULONG)readParameters->ByteOffset, readParameters->Length)); if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_READ_PARAMETERS)) || (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG_PTR))) { status = STATUS_INVALID_PARAMETER; break; } status = SdbusBuildWorkPacket(fdoExtension, SDWP_READBLOCK, SdbusPdoIoctlCompletion, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.ReadBlock.ByteOffset = readParameters->ByteOffset; workPacket->Parameters.ReadBlock.Buffer = readParameters->Buffer; workPacket->Parameters.ReadBlock.Length = readParameters->Length; IoMarkIrpPending(Irp); Irp = NULL; SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; } case IOCTL_SD_WRITE_BLOCK: { PSDBUS_WRITE_PARAMETERS writeParameters = Irp->AssociatedIrp.SystemBuffer; DebugPrint((SDBUS_DEBUG_IOCTL, "IOCTL_SD_WRITE_BLOCK - off %08x len %08x\n", (ULONG)writeParameters->ByteOffset, writeParameters->Length)); if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_WRITE_PARAMETERS)) || (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG_PTR))) { status = STATUS_INVALID_PARAMETER; break; } if ((*(fdoExtension->FunctionBlock->IsWriteProtected))(fdoExtension)) { status = STATUS_MEDIA_WRITE_PROTECTED; break; } status = SdbusBuildWorkPacket(fdoExtension, SDWP_WRITEBLOCK, SdbusPdoIoctlCompletion, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.WriteBlock.ByteOffset = writeParameters->ByteOffset; workPacket->Parameters.WriteBlock.Buffer = writeParameters->Buffer; workPacket->Parameters.WriteBlock.Length = writeParameters->Length; IoMarkIrpPending(Irp); Irp = NULL; SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; } case IOCTL_SD_IO_READ: { PSDBUS_IO_READ_PARAMETERS readParameters = Irp->AssociatedIrp.SystemBuffer; WORKPROC_FUNCTION workProcFunction; if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_IO_READ_PARAMETERS)) || (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG_PTR))) { status = STATUS_INVALID_PARAMETER; break; } if (readParameters->CmdType == 52) { workProcFunction = SDWP_READIO; } else if (readParameters->CmdType == 53) { workProcFunction = SDWP_READIO_EXTENDED; } else { status = STATUS_INVALID_PARAMETER; break; } status = SdbusBuildWorkPacket(fdoExtension, workProcFunction, SdbusPdoIoctlCompletion, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.ReadIo.Offset = readParameters->Offset; workPacket->Parameters.ReadIo.Buffer = readParameters->Buffer; workPacket->Parameters.ReadIo.Length = readParameters->Length; IoMarkIrpPending(Irp); Irp = NULL; SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; } case IOCTL_SD_IO_WRITE: { PSDBUS_IO_WRITE_PARAMETERS writeParameters = Irp->AssociatedIrp.SystemBuffer; WORKPROC_FUNCTION workProcFunction; if ((irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(SDBUS_IO_WRITE_PARAMETERS)) || (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(ULONG_PTR))) { status = STATUS_INVALID_PARAMETER; break; } if (writeParameters->CmdType == 52) { workProcFunction = SDWP_WRITEIO; } else if (writeParameters->CmdType == 53) { workProcFunction = SDWP_WRITEIO_EXTENDED; } else { status = STATUS_INVALID_PARAMETER; break; } status = SdbusBuildWorkPacket(fdoExtension, workProcFunction, SdbusPdoIoctlCompletion, Irp, &workPacket); if (!NT_SUCCESS(status)) { break; } workPacket->PdoExtension = pdoExtension; workPacket->Parameters.WriteIo.Offset = writeParameters->Offset; workPacket->Parameters.WriteIo.Buffer = writeParameters->Buffer; workPacket->Parameters.WriteIo.Length = writeParameters->Length; IoMarkIrpPending(Irp); Irp = NULL; SdbusQueueWorkPacket(fdoExtension, workPacket, WP_TYPE_IO); status = STATUS_PENDING; break; } #endif case IOCTL_SD_ACKNOWLEDGE_CARD_IRQ: ASSERT((pdoExtension->Flags & SDBUS_PDO_CALLBACK_IN_SERVICE) != 0); pdoExtension->Flags &= ~SDBUS_PDO_CALLBACK_IN_SERVICE; (*(fdoExtension->FunctionBlock->AcknowledgeEvent))(fdoExtension, SDBUS_EVENT_CARD_INTERRUPT); status = STATUS_SUCCESS; break; default: status = STATUS_INVALID_DEVICE_REQUEST; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x irp %.08x complete %08x\n", Pdo, Irp, status)); break; } if (status != STATUS_PENDING) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %.08x ioctl exits %08x\n", Pdo, status)); return status; } VOID SdbusPdoIoctlCompletion ( IN PSD_WORK_PACKET WorkPacket, IN NTSTATUS status ) { PIRP irp = WorkPacket->CompletionContext; DebugPrint((SDBUS_DEBUG_IOCTL, "pdo %08x irp %08x ioctl complete %08x\n", WorkPacket->PdoExtension->DeviceObject, irp, status)); if (NT_SUCCESS(status)) { PULONG_PTR outputBuffer = (PULONG_PTR)irp->AssociatedIrp.SystemBuffer; // ISSUE make this so the individual worker routine can decide how big the return buffer is *outputBuffer = WorkPacket->Information; irp->IoStatus.Information = sizeof(ULONG_PTR); } irp->IoStatus.Status = status; IoCompleteRequest(irp, IO_NO_INCREMENT); ExFreePool(WorkPacket); }