/*! \brief Activate and deactive a line on the IEC serial bus This function activates (sets to 0V, L) and deactivates (set to 5V, H) lines on the IEC serial bus. \param Pdx Pointer to the device extension. \param Set The mask of which lines should be set. This has to be a bitwise OR between the constants IEC_DATA, IEC_CLOCK, IEC_ATN, and IEC_RESET \param Release The mask of which lines should be released. This has to be a bitwise OR between the constants IEC_DATA, IEC_CLOCK, IEC_ATN, and IEC_RESET \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. \remark If a bit is specified in the Set as well as in the Release mask, the effect is undefined. */ NTSTATUS cbmiec_iec_setrelease(IN PDEVICE_EXTENSION Pdx, IN USHORT Set, IN USHORT Release) { NTSTATUS ntStatus; FUNC_ENTER(); FUNC_PARAM((DBG_PREFIX "set = 0x%02x, release = 0x%02x", Set, Release)); ntStatus = STATUS_SUCCESS; DBG_ASSERT((Set & Release) == 0); // Set the correct line as given by the call if ( (Set & ~(IEC_LINE_DATA | IEC_LINE_CLOCK | IEC_LINE_ATN | IEC_LINE_RESET)) || (Release & ~(IEC_LINE_DATA | IEC_LINE_CLOCK | IEC_LINE_ATN | IEC_LINE_RESET))) { // there was some bit set that is not recognized, return // with an error ntStatus = STATUS_INVALID_PARAMETER; } else { ULONG set_mask = 0; ULONG release_mask = 0; SET_RELEASE_LINE(DATA, DATA); SET_RELEASE_LINE(CLOCK, CLK); SET_RELEASE_LINE(ATN, ATN); SET_RELEASE_LINE(RESET, RESET); #ifdef TEST_BIDIR #define PP_BIDIR_OUT PP_LP_BIDIR #define IEC_LINE_BIDIR PP_BIDIR_OUT SET_RELEASE_LINE(BIDIR, BIDIR); #undef PP_BIDIR_OUT #undef IEC_LINE_BIDIR #endif // #ifdef TEST_BIDIR CBMIEC_SET_RELEASE(set_mask, release_mask); } FUNC_LEAVE_NTSTATUS(ntStatus ); }
/*! \brief Send an UNLISTEN over the IEC bus This function sends an UNLISTEN to the IEC bus. \param Pdx Pointer to the device extension. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. */ NTSTATUS cbmiec_unlisten(IN PDEVICE_EXTENSION Pdx) { NTSTATUS ntStatus; ULONG sent; UCHAR buffer; FUNC_ENTER(); // send a 0x3F (unlisten) under control of ATN buffer = 0x3f; ntStatus = cbmiec_i_raw_write(Pdx, &buffer, 1, &sent, 1, 0); Pdx->DoNotReleaseBus = FALSE; FUNC_LEAVE_NTSTATUS(ntStatus); }
NTSTATUS cbm_cleanup(IN PDEVICE_OBJECT Fdo, IN PIRP Irp) { PIO_STACK_LOCATION irpSp; PDEVICE_EXTENSION pdx; NTSTATUS ntStatus; FUNC_ENTER(); pdx = Fdo->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation(Irp); /* Let the QUEUE itself perform all relevant steps */ QueueCleanup(&pdx->IrpQueue, irpSp->FileObject); /* We're done, complete the IRP */ ntStatus = QueueCompleteIrp(NULL, Irp, STATUS_SUCCESS, 0); FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Send a TALK over the IEC bus This function sends a TALK to the IEC bus. \param Pdx Pointer to the device extension. \param Device Device (primary) address \param Secaddr Secondary address \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. */ NTSTATUS cbmiec_talk(IN PDEVICE_EXTENSION Pdx, IN UCHAR Device, IN UCHAR Secaddr) { NTSTATUS ntStatus; ULONG sent; UCHAR buffer[2]; FUNC_ENTER(); FUNC_PARAM((DBG_PREFIX "Device = 0x%02x, Secaddr = 0x%02x", (int)Device, (int)Secaddr)); // send a 0x4x / 0x6y (talk device x, secaddr y) under control of ATN buffer[0] = 0x40 | Device; buffer[1] = 0x60 | Secaddr; ntStatus = cbmiec_i_raw_write(Pdx, buffer, 2, &sent, 1, 1); Pdx->DoNotReleaseBus = TRUE; FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Initialize the IEC bus This function initializes the IEC bus itself, and sets some variables in the device extension. It has to be called before any other IEC function is called. \param Pdx Pointer to the device extension. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it returns one of the error status values. */ NTSTATUS cbmiec_init(IN PDEVICE_EXTENSION Pdx) { NTSTATUS ntStatus; FUNC_ENTER(); // Initialize the event which is used to wake up the // task in wait_for_listener() DBG_IRQL( == PASSIVE_LEVEL); KeInitializeEvent(&Pdx->EventWaitForListener, SynchronizationEvent, FALSE); #ifdef USE_DPC // Initialize the DPC object which will be used for waking // up cbmiec_wait_for_listener() later DBG_IRQL( == PASSIVE_LEVEL) IoInitializeDpcRequest(Pdx->Fdo, cbmiec_dpc); #endif // #ifdef USE_DPC ntStatus = cbmiec_testcable(Pdx); if (!NT_SUCCESS(ntStatus)) { FUNC_LEAVE_NTSTATUS(ntStatus); } Pdx->IecBusy = FALSE; if (!Pdx->DoNotReleaseBus) { CBMIEC_RELEASE(PP_RESET_OUT | PP_DATA_OUT | PP_ATN_OUT | PP_LP_BIDIR | PP_LP_IRQ); CBMIEC_SET(PP_CLK_OUT); } FUNC_LEAVE_NTSTATUS_CONST(STATUS_SUCCESS); }
/*! \brief Allocate a parallel port for using it This function allocates a parallel port, preventing other drivers from accessing it. \param Pdx Pointer to a device extension which contains the DEVICE_OBJECT of the parallel port driver. This function has to be balanced with a corresponding ParPortFree() This function must be run at IRQL == PASSIVE_LEVEL. */ NTSTATUS ParPortAllocate(PDEVICE_EXTENSION Pdx) { NTSTATUS ntStatus; FUNC_ENTER(); DBG_ASSERT(Pdx); DBG_ASSERT(Pdx->ParallelPortAllocated == FALSE); // allocate the parallel port ntStatus = ParPortIoctlInOut(Pdx, IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE, NULL, 0, NULL, 0); // if we were successfull, remember this in the pdx if (NT_SUCCESS(ntStatus)) { Pdx->ParallelPortAllocated = TRUE; } FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Check the buffer of an read or write IRP Check the buffer of an read or write IRP \param IrpSp: Pointer to the IO_STACK_LOCATION of the IRP which contains the input buffer. \return If the provided buffer is valid, this function returns STATUS_SUCCESS. If not, it returns an appropriate error value. */ static NTSTATUS cbm_checkbuffer(IN PIO_STACK_LOCATION IrpSp) { NTSTATUS ntStatus; FUNC_ENTER(); // The following code assumes that the read and write structure are exactly // the same (despite the name). This ASSERT() makes sure that we are informed // if this is not the case. DBG_ASSERT(&IrpSp->Parameters.Read.ByteOffset == &IrpSp->Parameters.Write.ByteOffset); if ((IrpSp->Parameters.Write.ByteOffset.HighPart != 0) || (IrpSp->Parameters.Write.ByteOffset.LowPart != 0)) { ntStatus = STATUS_INVALID_PARAMETER; } else { ntStatus = STATUS_SUCCESS; } FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Executes IOCTLs Executes IRPs containing the IRP_MJ_DEVICE_CONTROL I/O function code. \param Pdx Pointer to the DEVICE_EXTENSION structure. \param Irp Pointer to an IRP structure that describes the requested I/O operation. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it return one of the error status values: \n STATUS_SUCCESS - Success. \n STATUS_BUFFER_TOO_SMALL - Buffer too small. This function does not perform any validity checks on the input and output buffer! This should already been done in cbm_devicecontrol. */ NTSTATUS cbm_execute_devicecontrol(IN PDEVICE_EXTENSION Pdx, IN PIRP Irp) { PPAR_SET_INFORMATION setInfo; PIO_STACK_LOCATION irpSp; ULONG_PTR returnLength; NTSTATUS ntStatus; FUNC_ENTER(); // As not every IOCTL needs to return a value, we initialize // the return length here. This way, it needs only be altered // if the IOCTL returns some value. returnLength = 0; // get the current IRP stack location irpSp = IoGetCurrentIrpStackLocation(Irp); PERF_EVENT_IOCTL_EXECUTE(irpSp->Parameters.DeviceIoControl.IoControlCode); DBG_IRPPATH_EXECUTE("Execute Ioctl"); // Call the appropriate function for processing the IOCTL // PrimaryAddresses are ANDed with 0x1F, as these are the only legitimate // primary addresses allowed for a IEC serial bus. switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case CBMCTRL_TALK: DBG_IRP(CBMCTRL_TALK); ntStatus = cbmiec_talk(Pdx, INPUTVALUE(CBMT_TALK_IN)->PrimaryAddress & 0x1F, INPUTVALUE(CBMT_TALK_IN)->SecondaryAddress); break; case CBMCTRL_LISTEN: DBG_IRP(CBMCTRL_LISTEN); ntStatus = cbmiec_listen(Pdx, INPUTVALUE(CBMT_LISTEN_IN)->PrimaryAddress & 0x1F, INPUTVALUE(CBMT_LISTEN_IN)->SecondaryAddress); break; case CBMCTRL_UNTALK: DBG_IRP(CBMCTRL_UNTALK); ntStatus = cbmiec_untalk(Pdx); break; case CBMCTRL_UNLISTEN: DBG_IRP(CBMCTRL_UNLISTEN); ntStatus = cbmiec_unlisten(Pdx); break; case CBMCTRL_OPEN: DBG_IRP(CBMCTRL_OPEN); ntStatus = cbmiec_open(Pdx, INPUTVALUE(CBMT_OPEN_IN)->PrimaryAddress & 0x1F, INPUTVALUE(CBMT_OPEN_IN)->SecondaryAddress); break; case CBMCTRL_CLOSE: DBG_IRP(CBMCTRL_CLOSE); ntStatus = cbmiec_close(Pdx,INPUTVALUE(CBMT_CLOSE_IN)->PrimaryAddress & 0x1F, INPUTVALUE(CBMT_CLOSE_IN)->SecondaryAddress); break; case CBMCTRL_RESET: DBG_IRP(CBMCTRL_RESET); ntStatus = cbmiec_reset(Pdx); break; case CBMCTRL_GET_EOI: DBG_IRP(CBMCTRL_GET_EOI); returnLength = sizeof(CBMT_GET_EOI_OUT); ntStatus = cbmiec_get_eoi(Pdx, &(OUTPUTVALUE(CBMT_GET_EOI_OUT)->Decision)); break; case CBMCTRL_CLEAR_EOI: DBG_IRP(CBMCTRL_CLEAR_EOI); ntStatus = cbmiec_clear_eoi(Pdx); break; case CBMCTRL_PP_READ: DBG_IRP(CBMCTRL_PP_READ); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_PP_READ_OUT), STATUS_SUCCESS); returnLength = sizeof(CBMT_PP_READ_OUT); ntStatus = cbmiec_pp_read(Pdx, &(OUTPUTVALUE(CBMT_PP_READ_OUT)->Byte)); break; case CBMCTRL_PP_WRITE: DBG_IRP(CBMCTRL_PP_WRITE); ntStatus = cbmiec_pp_write(Pdx, INPUTVALUE(CBMT_PP_WRITE_IN)->Byte); break; case CBMCTRL_IEC_POLL: DBG_IRP(CBMCTRL_IEC_POLL); returnLength = sizeof(CBMT_IEC_POLL_OUT); ntStatus = cbmiec_iec_poll(Pdx, &(OUTPUTVALUE(CBMT_IEC_POLL_OUT)->Line)); break; case CBMCTRL_IEC_SET: DBG_IRP(CBMCTRL_IEC_SET); ntStatus = cbmiec_iec_set(Pdx, INPUTVALUE(CBMT_IEC_SET_IN)->Line); break; case CBMCTRL_IEC_RELEASE: DBG_IRP(CBMCTRL_IEC_RELEASE); ntStatus = cbmiec_iec_release(Pdx, INPUTVALUE(CBMT_IEC_RELEASE_IN)->Line); break; case CBMCTRL_IEC_SETRELEASE: DBG_IRP(CBMCTRL_IEC_SETRELEASE); ntStatus = cbmiec_iec_setrelease(Pdx, INPUTVALUE(CBMT_IEC_SETRELEASE_IN)->State, INPUTVALUE(CBMT_IEC_SETRELEASE_IN)->Line); break; case CBMCTRL_IEC_WAIT: DBG_IRP(CBMCTRL_IEC_WAIT); returnLength = sizeof(CBMT_IEC_WAIT_OUT); ntStatus = cbmiec_iec_wait(Pdx, INPUTVALUE(CBMT_IEC_WAIT_IN)->Line, INPUTVALUE(CBMT_IEC_WAIT_IN)->State, &(OUTPUTVALUE(CBMT_IEC_WAIT_OUT)->Line)); break; case CBMCTRL_PARBURST_READ: DBG_IRP(CBMCTRL_PARBURST_READ); returnLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; ntStatus = cbmiec_parallel_burst_read(Pdx, &(OUTPUTVALUE(CBMT_PARBURST_PREAD_OUT)->Byte)); break; case CBMCTRL_PARBURST_WRITE: DBG_IRP(CBMCTRL_PARBURST_READ); returnLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; ntStatus = cbmiec_parallel_burst_write(Pdx, INPUTVALUE(CBMT_PARBURST_PWRITE_IN)->Byte); break; case CBMCTRL_PARBURST_READ_TRACK: DBG_IRP(CBMCTRL_PARBURST_READ_TRACK); returnLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; ntStatus = cbmiec_parallel_burst_read_track(Pdx, Irp->AssociatedIrp.SystemBuffer, (ULONG) returnLength); break; case CBMCTRL_PARBURST_WRITE_TRACK: DBG_IRP(CBMCTRL_PARBURST_WRITE_TRACK); returnLength = irpSp->Parameters.DeviceIoControl.InputBufferLength; ntStatus = cbmiec_parallel_burst_write_track(Pdx, Irp->AssociatedIrp.SystemBuffer, (ULONG) returnLength); break; case CBMCTRL_I_INSTALL: DBG_IRP(CBMCTRL_I_INSTALL); returnLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; ntStatus = cbm_install(Pdx, OUTPUTVALUE(CBMT_I_INSTALL_OUT), (PULONG) &returnLength); break; case CBMCTRL_PARPORT_LOCK: DBG_IRP(CBMCTRL_PARPORT_LOCK); ntStatus = cbm_lock(Pdx); break; case CBMCTRL_PARPORT_UNLOCK: DBG_IRP(CBMCTRL_PARPORT_UNLOCK); ntStatus = cbm_unlock(Pdx); break; case CBMCTRL_UPDATE: DBG_IRP(CBMCTRL_UPDATE); cbm_init_registry(NULL, Pdx); ntStatus = STATUS_SUCCESS; break; #if DBG case CBMCTRL_I_READDBG: DBG_IRP(CBMCTRL_I_READDBG); returnLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength; ntStatus = cbm_dbg_readbuffer(Pdx, OUTPUTVALUE(CHAR), (PULONG) &returnLength); break; #endif // #if DBG default: // As cbm_devicecontrol() already checked the IRP, // this piece of code should never be entered. If it // is, this is a sign of a forgotten IOCTL, or a severe // programming error DBG_ERROR((DBG_PREFIX "unknown IRP_MJ_DEVICE_CONTROL")); DBG_ASSERT(("THIS SHOULD NOT HAPPEN!", 0)); ntStatus = STATUS_INVALID_PARAMETER; break; } // If an error occurred, make sure not to return anything. if (!NT_SUCCESS(ntStatus)) { returnLength = 0; } // Complete the request: DBG_IRPPATH_COMPLETE("Execute Ioctl"); QueueCompleteIrp(&Pdx->IrpQueue, Irp, ntStatus, returnLength); FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Services IOCTLs Services IRPs containing the IRP_MJ_DEVICE_CONTROL I/O function code. \param Fdo Pointer to a DEVICE_OBJECT structure. This is the device object for the target device, previously created by the driver's AddDevice routine. \param Irp Pointer to an IRP structure that describes the requested I/O operation. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it return one of the error status values: \n STATUS_SUCCESS - Success. \n STATUS_PENDING - Request pending. \n STATUS_BUFFER_TOO_SMALL - Buffer too small. \n STATUS_INVALID_PARAMETER - Invalid io control request. The driver's DriverEntry routine stored this routine's address in DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]. Generally, all Dispatch routines execute in an arbitrary thread context at IRQL PASSIVE_LEVEL, but there are exceptions. */ NTSTATUS cbm_devicecontrol(IN PDEVICE_OBJECT Fdo, IN PIRP Irp) { PPAR_SET_INFORMATION setInfo; PIO_STACK_LOCATION irpSp; PDEVICE_EXTENSION pdx; NTSTATUS ntStatus; BOOLEAN fastStart; FUNC_ENTER(); // get the device extension pdx = Fdo->DeviceExtension; // get the current IRP stack location irpSp = IoGetCurrentIrpStackLocation(Irp); DBG_IRPPATH_PROCESS("Ioctl"); // assume we do not want to perform a faststart of this IRP fastStart = FALSE; // Now, check the input and/or output buffers of the given // IOCTLs if they are at least as big as the specification. // If not, the IRP (and thus the IOCTL) is failed switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case CBMCTRL_TALK: DBG_IRP(CBMCTRL_TALK); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_TALK_IN), STATUS_SUCCESS); break; case CBMCTRL_LISTEN: DBG_IRP(CBMCTRL_LISTEN); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_LISTEN_IN), STATUS_SUCCESS); break; case CBMCTRL_UNTALK: DBG_IRP(CBMCTRL_UNTALK); ntStatus = STATUS_SUCCESS; break; case CBMCTRL_UNLISTEN: DBG_IRP(CBMCTRL_UNLISTEN); ntStatus = STATUS_SUCCESS; break; case CBMCTRL_OPEN: DBG_IRP(CBMCTRL_OPEN); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_OPEN_IN), STATUS_SUCCESS); break; case CBMCTRL_CLOSE: DBG_IRP(CBMCTRL_CLOSE); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_CLOSE_IN), STATUS_SUCCESS); break; case CBMCTRL_RESET: DBG_IRP(CBMCTRL_RESET); ntStatus = STATUS_SUCCESS; break; case CBMCTRL_GET_EOI: DBG_IRP(CBMCTRL_GET_EOI); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_GET_EOI_OUT), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_CLEAR_EOI: DBG_IRP(CBMCTRL_CLEAR_EOI); ntStatus = STATUS_SUCCESS; fastStart = TRUE; break; case CBMCTRL_PP_READ: DBG_IRP(CBMCTRL_PP_READ); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_PP_READ_OUT), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_PP_WRITE: DBG_IRP(CBMCTRL_PP_WRITE); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_PP_WRITE_IN), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_IEC_POLL: DBG_IRP(CBMCTRL_IEC_POLL); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_IEC_POLL_OUT), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_IEC_SET: DBG_IRP(CBMCTRL_IEC_SET); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_IEC_SET_IN), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_IEC_RELEASE: DBG_IRP(CBMCTRL_IEC_RELEASE); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_IEC_RELEASE_IN), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_IEC_SETRELEASE: DBG_IRP(CBMCTRL_IEC_SETRELEASE); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_IEC_SETRELEASE_IN), STATUS_SUCCESS); fastStart = TRUE; break; case CBMCTRL_IEC_WAIT: DBG_IRP(CBMCTRL_IEC_WAIT); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_IEC_WAIT_OUT), cbm_checkinputbuffer(irpSp, sizeof(CBMT_IEC_WAIT_IN), STATUS_SUCCESS)); break; case CBMCTRL_PARBURST_READ: DBG_IRP(CBMCTRL_PARBURST_READ); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_PARBURST_PREAD_OUT), STATUS_SUCCESS); break; case CBMCTRL_PARBURST_WRITE: DBG_IRP(CBMCTRL_PARBURST_WRITE); ntStatus = cbm_checkinputbuffer(irpSp, sizeof(CBMT_PARBURST_PWRITE_IN), STATUS_SUCCESS); break; case CBMCTRL_PARBURST_READ_TRACK: DBG_IRP(CBMCTRL_PARBURST_READ_TRACK); ntStatus = cbm_checkoutputbuffer(irpSp, 1, STATUS_SUCCESS); break; case CBMCTRL_PARBURST_WRITE_TRACK: DBG_IRP(CBMCTRL_PARBURST_WRITE_TRACK); ntStatus = cbm_checkinputbuffer(irpSp, 1, STATUS_SUCCESS); break; case CBMCTRL_I_INSTALL: DBG_IRP(CBMCTRL_I_INSTALL); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CBMT_I_INSTALL_OUT), STATUS_SUCCESS); break; case CBMCTRL_PARPORT_LOCK: DBG_IRP(CBMCTRL_PARPORT_LOCK); ntStatus = STATUS_SUCCESS; break; case CBMCTRL_PARPORT_UNLOCK: DBG_IRP(CBMCTRL_PARPORT_UNLOCK); ntStatus = STATUS_SUCCESS; break; case CBMCTRL_UPDATE: DBG_IRP(CBMCTRL_UPDATE); ntStatus = STATUS_SUCCESS; fastStart = TRUE; break; #if DBG case CBMCTRL_I_READDBG: DBG_IRP(CBMCTRL_I_READDBG); ntStatus = cbm_checkoutputbuffer(irpSp, sizeof(CHAR), STATUS_SUCCESS); break; #endif // #if DBG default: DBG_ERROR((DBG_PREFIX "unknown IRP_MJ_DEVICE_CONTROL")); ntStatus = STATUS_INVALID_PARAMETER; break; } if (NT_SUCCESS(ntStatus)) { PERF_EVENT_IOCTL_QUEUE(irpSp->Parameters.DeviceIoControl.IoControlCode); // queue the IRP to be processed // If faststart is TRUE, it will be processed immediately // (for performance reasons) ntStatus = QueueStartPacket(&pdx->IrpQueue, Irp, fastStart, Fdo); } else { // there was an error, complete the request // with that error status QueueCompleteIrp(NULL, Irp, ntStatus, 0); } FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \internal \brief Send an IOCTL to the parallel port driver This function sends an IOCTL to the parallel port driver. It is an internal helper function for the following functions. \param Pdx Pointer to the device extension of the driver \param Ioctl The IOCTL to perform \param InBuffer Pointer to a buffer which holds the input. This can be NULL. \param InBufferLength Length of the buffer pointed to by InBuffer. Must be 0 if InBuffer == NULL. \param OutBuffer Pointer to a buffer which will get the output. This can be NULL. \param OutBufferLength Length of the buffer pointed to by OutBuffer. Must be 0 if OutBuffer == NULL. This function must be run at IRQL == PASSIVE_LEVEL. */ static NTSTATUS ParPortIoctlInOut(IN PDEVICE_EXTENSION Pdx, IN ULONG Ioctl, IN PVOID InBuffer, IN ULONG InBufferLength, OUT PVOID OutBuffer, IN ULONG OutBufferLength) { IO_STATUS_BLOCK ioStatusBlock; NTSTATUS ntStatus; KEVENT event; // event to be signalled when the IOCTL has finished PIRP irp; FUNC_ENTER(); // Initialize the event which we will use to be notified // when the IRP has finished DBG_IRQL( == PASSIVE_LEVEL); KeInitializeEvent(&event, NotificationEvent, FALSE); ntStatus = STATUS_SUCCESS; // build an IRP for this IOCTL DBG_IRQL( == PASSIVE_LEVEL); irp = IoBuildDeviceIoControlRequest( Ioctl, Pdx->ParallelPortFdo, InBuffer, InBufferLength, OutBuffer, OutBufferLength, TRUE, // it's an internal device control &event, &ioStatusBlock ); if (irp) { PIO_STACK_LOCATION irpStack; // get the current IRP stack location DBG_IRQL( <= DISPATCH_LEVEL); irpStack = IoGetNextIrpStackLocation(irp); // Reference the file object we are about to call. // This ensures the driver is not removed while we call it, // even if the underlying hardware is removed DBG_IRQL( <= DISPATCH_LEVEL); ObReferenceObject(Pdx->ParallelPortFileObject); // tell the IRP stack location to which file object we are // referring irpStack->FileObject = Pdx->ParallelPortFileObject; // Call the driver to perform the requested IOCTL DBG_IRQL( <= DISPATCH_LEVEL); ntStatus = IoCallDriver(Pdx->ParallelPortFdo, irp); // We're done, we can dereference the file object again DBG_IRQL( <= DISPATCH_LEVEL); ObDereferenceObject(Pdx->ParallelPortFileObject); if (!NT_SUCCESS(ntStatus)) { DBG_WARN((DBG_PREFIX "IoCallDriver FAILED!")); } else { // wait for the IRP to be completed DBG_IRQL( <= DISPATCH_LEVEL /* = only if timeout of NULL */); ntStatus = KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, // we are not alertable NULL); if (!NT_SUCCESS(ntStatus)) { DBG_WARN((DBG_PREFIX "KeWaitForSingleObject FAILED!")); } } } FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Initialize the knowledge on a parallel port This function gets some knowledge on a parallel port, and stores this info into the given DEVICE_EXTENSION. \param ParallelPortName UNICODE_STRING which holds the name of the parallel port driver \param Pdx Device extension which will be initialized with the needed knowledge on the parallel port. This function should be called before any other parallel port function is called. Usually, it is done in the driver's AddDevice (WDM) or DriverEntry (WKM, WDM) function. One of the purposes of this function is to make sure the parallel port driver is not unloaded from memory (via IoGetDeviceObjectPointer()). This function must be run at IRQL == PASSIVE_LEVEL. */ NTSTATUS ParPortInit(PUNICODE_STRING ParallelPortName, PDEVICE_EXTENSION Pdx) { NTSTATUS ntStatus; FUNC_ENTER(); DBG_ASSERT(ParallelPortName); DBG_ASSERT(Pdx); // First of all, get the PDEVICE_OBJECT of the parallel port driver DBG_IRQL( == PASSIVE_LEVEL); ntStatus = IoGetDeviceObjectPointer(ParallelPortName, FILE_READ_ATTRIBUTES, &Pdx->ParallelPortFileObject, &Pdx->ParallelPortFdo); if (!NT_SUCCESS(ntStatus)) { DBG_WARN((DBG_PREFIX "IoGetDeviceObjectPointer() FAILED!")); FUNC_LEAVE_NTSTATUS(ntStatus); } // Allocate memory to hold to parallel port info DBG_IRQL( == PASSIVE_LEVEL); Pdx->PortInfo = (PPARALLEL_PORT_INFORMATION) ExAllocatePoolWithTag(NonPagedPool, sizeof(*Pdx->PortInfo), MTAG_PPINFO); // If we got memory, get the info out of the parallel port driver if (Pdx->PortInfo) { ntStatus = ParPortIoctlInOut(Pdx, IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO, NULL, 0, Pdx->PortInfo, sizeof(*Pdx->PortInfo)); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } if (NT_SUCCESS(ntStatus)) { Pdx->ParPortPortAddress = Pdx->PortInfo->Controller; DBG_PPORT((DBG_PREFIX "Got parallel port information:")); DBG_PPORT((DBG_PREFIX "- OriginalController = 0x%p", Pdx->PortInfo->OriginalController)); DBG_PPORT((DBG_PREFIX "- Controller = 0x%p", Pdx->PortInfo->Controller)); DBG_PPORT((DBG_PREFIX "- Span of controller = 0x%08x", Pdx->PortInfo->SpanOfController)); DBG_PPORT((DBG_PREFIX "- TryAllocatePort = 0x%p", Pdx->PortInfo->TryAllocatePort)); DBG_PPORT((DBG_PREFIX "- FreePort = 0x%p", Pdx->PortInfo->FreePort)); DBG_PPORT((DBG_PREFIX "- QueryNumWaiters = 0x%p", Pdx->PortInfo->QueryNumWaiters)); DBG_PPORT((DBG_PREFIX "- Context = 0x%p", Pdx->PortInfo->Context)); } // if we failed getting the parallel port info, but there was memory // allocated, free the memory. if (!NT_SUCCESS(ntStatus) && Pdx->PortInfo) { DBG_IRQL( < DISPATCH_LEVEL); ExFreePool(Pdx->PortInfo); Pdx->PortInfo = NULL; }
/*! \brief Services reads from or writes to the driver Services reads from or writes to the driver \param Fdo Pointer to a DEVICE_OBJECT structure. This is the device object for the target device, previously created by the driver's AddDevice routine. \param Irp Pointer to an IRP structure that describes the requested I/O operation. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it return one of the error status values. The driver's DriverEntry routine stored this routine's address in DriverObject->MajorFunction[IRP_MJ_READ] and DriverObject->MajorFunction[IRP_MJ_WRITE]. Generally, all Dispatch routines execute in an arbitrary thread context at IRQL PASSIVE_LEVEL, but there are exceptions. */ NTSTATUS cbm_readwrite(IN PDEVICE_OBJECT Fdo, IN PIRP Irp) { PIO_STACK_LOCATION irpSp; PDEVICE_EXTENSION pdx; NTSTATUS ntStatus; ULONG readWriteBytesProcessed; ULONG readWriteLength; PUCHAR readWriteBuffer; FUNC_ENTER(); // get the device extension pdx = Fdo->DeviceExtension; // get the current IRP stack location irpSp = IoGetCurrentIrpStackLocation(Irp); // Check if the buffer is valid ntStatus = cbm_checkbuffer(irpSp); DBG_IRPPATH_PROCESS("read/write"); // Number of read (or written) bytes are 0 until now. // This is needed for finding out if we had success, or if something went wrong readWriteLength = 0; if (NT_SUCCESS(ntStatus)) { // The following code assumes that the read and write structure are exactly // the same (despite the name). This ASSERT() makes sure that we are informed // if this is not the case. DBG_ASSERT(&irpSp->Parameters.Read.Length == &irpSp->Parameters.Write.Length); // Get the number of bytes to be read or written readWriteLength = irpSp->Parameters.Read.Length; // If we do performance measurements, log the appropriate event if (irpSp->MajorFunction == IRP_MJ_READ) { PERF_EVENT_READ_QUEUE(irpSp->Parameters.Read.Length); } else { PERF_EVENT_WRITE_QUEUE(irpSp->Parameters.Write.Length); } // If the read or write request has another length than 0, // then queue the IRP for being processed later. if (readWriteLength != 0) { // now, queue the IRP to be processed ntStatus = QueueStartPacket(&pdx->IrpQueue, Irp, FALSE, Fdo); } } if (!NT_SUCCESS(ntStatus) || readWriteLength == 0) { // there was an error, or a read/write request with length 0: // Thus, complete the request QueueCompleteIrp(NULL, Irp, ntStatus, 0); } FUNC_LEAVE_NTSTATUS(ntStatus); }
/*! \brief Executes reads from or writes to the driver Services reads from or writes to the driver \param Pdx Pointer to the DEVICE_EXTENSION structure. \param Irp Pointer to an IRP structure that describes the requested I/O operation. \return If the routine succeeds, it returns STATUS_SUCCESS. Otherwise, it return one of the error status values. This function does not perform any validity checks on the input and output buffer! This should already been done in cbm_readwrite. */ NTSTATUS cbm_execute_readwrite(IN PDEVICE_EXTENSION Pdx, IN PIRP Irp) { PIO_STACK_LOCATION irpSp; NTSTATUS ntStatus; ULONG readWriteBytesProcessed; ULONG readWriteLength; PUCHAR readWriteBuffer; FUNC_ENTER(); // get the current IRP stack location irpSp = IoGetCurrentIrpStackLocation(Irp); // The following code assumes that the read and write structure are exactly // the same (despite the name). This ASSERT() makes sure that we are informed // if this is not the case. DBG_ASSERT(&irpSp->Parameters.Read.Length == &irpSp->Parameters.Write.Length); // Find out how much bytes are to be read/written readWriteLength = irpSp->Parameters.Read.Length; // get the buffer where the bytes to be written are / where the bytes to be read // should be placed readWriteBuffer = Irp->AssociatedIrp.SystemBuffer; // If we do performance measurements, log the appropriate event if (irpSp->MajorFunction == IRP_MJ_READ) { PERF_EVENT_READ_EXECUTE(irpSp->Parameters.Read.Length); } else { PERF_EVENT_WRITE_EXECUTE(irpSp->Parameters.Write.Length); } DBG_IRPPATH_EXECUTE("read/write"); // As this has been tested in cbm_readwrite() already, we should not get // any zero length here. Anyway, be sure that this does not happen with the // help of this ASSERT() DBG_ASSERT(readWriteLength != 0); if (readWriteLength != 0) { // Execute the appropriate function (read or write) switch (irpSp->MajorFunction) { case IRP_MJ_READ: ntStatus = cbmiec_raw_read(Pdx, readWriteBuffer, readWriteLength, &readWriteBytesProcessed); break; case IRP_MJ_WRITE: ntStatus = cbmiec_raw_write(Pdx, readWriteBuffer, readWriteLength, &readWriteBytesProcessed); break; default: DBG_ERROR((DBG_PREFIX "UNKNOWN IRP_MJ code in cbm_readwrite!")); ntStatus = STATUS_INTERNAL_ERROR; readWriteBytesProcessed = 0; break; } } // It would not make sense to pend the IRP here. Thus, make sure this does // not happen at all DBG_ASSERT(ntStatus != STATUS_PENDING); // If the read or write has not been pended, we are ready the complete this // IRP //! \todo Remove this, is this does not make sense. if (ntStatus != STATUS_PENDING) { QueueCompleteIrp(&Pdx->IrpQueue, Irp, ntStatus, readWriteBytesProcessed); } FUNC_LEAVE_NTSTATUS(ntStatus); }