#include <initguid.h>
#include "ProtoTypes.h"
#include "public.h"
#pragma alloc_text(PAGE, EvtDeviceIoRead)
#pragma alloc_text(PAGE, EvtDeviceIoWrite)
#pragma alloc_text(PAGE, EvtDeviceIoControlEntry)
#pragma alloc_text(PAGE, EvtDeviceIoControlSerial)
#pragma alloc_text(PAGE, EvtIoReadComplete)
#pragma alloc_text(PAGE, EvtIoWriteComplete)
#pragma alloc_text(PAGE, IoCtlGetSwitchPack)
#pragma alloc_text(PAGE, IoCtlGetSwitchPackChange)
#pragma alloc_text(PAGE, IoCtlSetLightBar)
#pragma alloc_text(PAGE, IoCtlGetLightBar)
#pragma alloc_text(PAGE, llSetLightBar)
#pragma alloc_text(PAGE, llGetLightBar)
/*............................................................................*/
/* callback function for handling USB interrupts. */
/* the switch pack state has to be bit order reversed, and bit value inverted */
/* because the physical representation of the data is not the same as the */
/* logical representation. */
/* this function is executed at IRQL = DISPATCH */
/* If there is a IO control request queued for a change notification, it is */
/* completed with the interrupt data. multiple requests can be queued, but */
/* only the first one is completed. */
/*............................................................................*/
VOID
EvtUsbDeviceInterrupt(
WDFUSBPIPE Pipe,
WDFMEMORY Buffer,
size_t NumBytesTransferred,
WDFCONTEXT Context
)
{
NTSTATUS status;
BYTE temp;
size_t size;
PDEVICE_CONTEXT devCtx = Context;
WDFREQUEST Request = NULL;
BYTE *packState = WdfMemoryGetBuffer(Buffer, &size);
UNREFERENCED_PARAMETER(Pipe);
ASSERT(size == sizeof(BYTE));
ASSERT(NumBytesTransferred == size);
ASSERT(packState != NULL);
temp = *packState;
temp = (temp & 0x01) << 7 |
(temp & 0x02) << 5 |
(temp & 0x04) << 3 |
(temp & 0x08) << 1 |
(temp & 0x10) >> 1 |
(temp & 0x20) >> 3 |
(temp & 0x40) >> 5 |
(temp & 0x80) >> 7;
KdPrint((__DRIVER_NAME "Converted switch pack from 0x%02x to 0x%02x\n",
(ULONG)*packState, (ULONG)temp));
devCtx->ActSwitchPack = ~temp;
/*is there an io control queued? if so then complete the first one*/
status = WdfIoQueueRetrieveNextRequest(devCtx->SwitchChangeRequestQueue,
&Request);
if(NT_SUCCESS(status))
{
BYTE* outBuffer;
status = WdfRequestRetrieveOutputBuffer(Request,
sizeof(BYTE),
&outBuffer,
NULL);
if(NT_SUCCESS(status))
{
/*do not use the value in the device context, since that may already have
changed because of a second interrupt while this one was handled.*/
*outBuffer = temp;
WdfRequestCompleteWithInformation(Request, status, sizeof(BYTE));
}
else
WdfRequestComplete(Request, status);
KdPrint((__DRIVER_NAME "Completed async pending IOCTL.\n"));
}
}
/*............................................................................*/
/* callback function for handling write requests. */
/*............................................................................*/
VOID
EvtDeviceIoWrite(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
WDFMEMORY requestMem;
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
UNREFERENCED_PARAMETER(Length);
KdPrint((__DRIVER_NAME "Received a write request of %d bytes\n", Length));
status = WdfRequestRetrieveInputMemory(Request, &requestMem);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfRequestRetrieveInputMemory failed with status 0x%08x\n", status));
WdfRequestComplete(Request, status);
return;
}
status = WdfUsbTargetPipeFormatRequestForWrite(
devCtx->UsbBulkOutPipe,
Request,
requestMem,
NULL);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfUsbTargetPipeFormatRequestForWrite failed with status 0x%08x\n", status));
WdfRequestComplete(Request, status);
return;
}
WdfRequestSetCompletionRoutine(Request,
EvtIoWriteComplete,
devCtx->UsbBulkOutPipe);
if(FALSE == WdfRequestSend(Request,
WdfUsbTargetPipeGetIoTarget(devCtx->UsbBulkOutPipe),
NULL))
{
KdPrint((__DRIVER_NAME "WdfRequestSend failed with status 0x%08x\n", status));
status = WdfRequestGetStatus(Request);
}
else
return;
WdfRequestComplete(Request, status);
}
/*............................................................................*/
/* callback function for handling read requests */
/*............................................................................*/
VOID
EvtDeviceIoRead(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
WDFMEMORY requestMem;
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
UNREFERENCED_PARAMETER(Length);
KdPrint((__DRIVER_NAME "Received a read request of %d bytes\n", Length));
status = WdfRequestRetrieveOutputMemory(Request, &requestMem);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfRequestRetrieveOutputMemory failed with status 0x%08x\n", status));
WdfRequestComplete(Request, status);
return;
}
status = WdfUsbTargetPipeFormatRequestForRead(
devCtx->UsbBulkInPipe,
Request,
requestMem,
NULL);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfUsbTargetPipeFormatRequestForRead failed with status 0x%08x\n", status));
WdfRequestComplete(Request, status);
return;
}
WdfRequestSetCompletionRoutine(Request,
EvtIoReadComplete,
devCtx->UsbBulkInPipe);
if(FALSE == WdfRequestSend(Request,
WdfUsbTargetPipeGetIoTarget(devCtx->UsbBulkInPipe),
NULL))
{
KdPrint((__DRIVER_NAME "WdfRequestSend failed with status 0x%08x\n", status));
status = WdfRequestGetStatus(Request);
}
else
return;
WdfRequestComplete(Request, status);
}
/*............................................................................*/
/* entry point for all device IO control operations. here the driver will */
/* determine what has to happen with the request. change requests get put on */
/* the manual queue. all other requests get passed to the serialized queue. */
/*............................................................................*/
VOID
EvtDeviceIoControlEntry(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
switch(IoControlCode)
{
case IOCTL_WDF_USB_GET_SWITCHSTATE:
IoCtlGetSwitchPack(Queue, Request, OutputBufferLength, InputBufferLength);
break;
case IOCTL_WDF_USB_GET_SWITCHSTATE_CHANGE:
IoCtlGetSwitchPackChange(Queue, Request, OutputBufferLength, InputBufferLength);
break;
default:
{
PDEVICE_CONTEXT devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
WdfRequestForwardToIoQueue(Request, devCtx->IoControlSerialQueue);
}
break;
}
}
/*............................................................................*/
/* callback function for handling serialized device IO control operations. */
/*............................................................................*/
VOID
EvtDeviceIoControlSerial(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength,
IN ULONG IoControlCode
)
{
switch(IoControlCode)
{
case IOCTL_WDF_USB_SET_LIGHTBAR:
IoCtlSetLightBar(Queue, Request, OutputBufferLength, InputBufferLength);
break;
case IOCTL_WDF_USB_GET_LIGHTBAR:
IoCtlGetLightBar(Queue, Request, OutputBufferLength, InputBufferLength);
break;
default:
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
break;
}
}
/*............................................................................*/
/* callback function for signalling the completion of a read request. */
/*............................................................................*/
VOID
EvtIoReadComplete(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
IN PWDF_REQUEST_COMPLETION_PARAMS Params,
IN WDFCONTEXT Context)
{
PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(Target);
usbCompletionParams = Params->Parameters.Usb.Completion;
if(NT_SUCCESS(Params->IoStatus.Status))
{
KdPrint((__DRIVER_NAME "Completed the read request with %d bytes\n",
usbCompletionParams->Parameters.PipeRead.Length));
}
else
{
KdPrint((__DRIVER_NAME "Failed the read request with status 0x%08x\n",
Params->IoStatus.Status));
}
WdfRequestCompleteWithInformation(Request,
Params->IoStatus.Status,
usbCompletionParams->Parameters.PipeRead.Length);
}
/*............................................................................*/
/* callback function for signalling the completion of a write request. */
/*............................................................................*/
VOID
EvtIoWriteComplete(
IN WDFREQUEST Request,
IN WDFIOTARGET Target,
IN PWDF_REQUEST_COMPLETION_PARAMS Params,
IN WDFCONTEXT Context)
{
PWDF_USB_REQUEST_COMPLETION_PARAMS usbCompletionParams;
UNREFERENCED_PARAMETER(Context);
UNREFERENCED_PARAMETER(Target);
usbCompletionParams = Params->Parameters.Usb.Completion;
if(NT_SUCCESS(Params->IoStatus.Status))
{
KdPrint((__DRIVER_NAME "Completed the write request with %d bytes\n",
usbCompletionParams->Parameters.PipeWrite.Length));
}
else
{
KdPrint((__DRIVER_NAME "Failed the read request with status 0x%08x\n",
Params->IoStatus.Status));
}
WdfRequestCompleteWithInformation(Request,
Params->IoStatus.Status,
usbCompletionParams->Parameters.PipeWrite.Length);
}
/*............................................................................*/
/* return the current value of the switch pack state. */
/*............................................................................*/
VOID
IoCtlGetSwitchPack(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
BYTE *outChar = NULL;
size_t length = 0;
PDEVICE_CONTEXT devCtx = NULL;
UNREFERENCED_PARAMETER(InputBufferLength);
if(OutputBufferLength < sizeof(BYTE))
{
KdPrint((__DRIVER_NAME
"IOCTL_WDF_USB_GET_SWITCHSTATE OutputBufferLength < sizeof(BYTE)\n"));
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
status = WdfRequestRetrieveOutputBuffer(Request,
sizeof(BYTE),
&outChar,
&length);
if(NT_SUCCESS(status))
{
ASSERT(length >= sizeof(BYTE));
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
*outChar = devCtx->ActSwitchPack;
}
WdfRequestCompleteWithInformation(Request, status, sizeof(BYTE));
}
/*............................................................................*/
/* Get a the value of the switch pack, but only when a new value is received. */
/* this means that the IO control request is queued in the manual queue until */
/* it can be completed by the USB interrupt callbcack function. */
/*............................................................................*/
VOID
IoCtlGetSwitchPackChange(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(Queue);
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
/*If the request is succesfull the request ownership is also transferred
back to the framework.*/
status = WdfRequestForwardToIoQueue(Request,
devCtx->SwitchChangeRequestQueue);
/*if the request cannot be forwarded it has to be completed with
the appropriate status code because the driver still owns the request.*/
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfRequestForwardToIoQueue failed with code 0x%08x.\n", status));
WdfRequestComplete(Request, status);
}
}
/*............................................................................*/
/* set the state of the LEDs in the LED array. */
/*............................................................................*/
VOID
IoCtlSetLightBar(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
WDFMEMORY memHandle = NULL;
UNREFERENCED_PARAMETER(OutputBufferLength);
if(InputBufferLength < sizeof(BYTE))
{
KdPrint((__DRIVER_NAME
"IOCTL_WDF_USB_SET_LIGHTBAR InputBufferLength < sizeof(BYTE)\n"));
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
status = WdfRequestRetrieveInputMemory(Request, &memHandle);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"Could not retrieve the lightbar memory handle\n"));
WdfRequestComplete(Request, status);
return;
}
status = llSetLightBar(devCtx, memHandle);
WdfRequestCompleteWithInformation(Request, status, sizeof(BYTE));
}
/*............................................................................*/
/* get the state of the LEDs in the LED array. */
/*............................................................................*/
VOID
IoCtlGetLightBar(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t OutputBufferLength,
IN size_t InputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_CONTEXT devCtx = NULL;
WDFMEMORY memHandle = NULL;
KdPrint((__DRIVER_NAME "entering IoCtlGetLightBar\n"));
UNREFERENCED_PARAMETER(InputBufferLength);
if(OutputBufferLength < sizeof(BYTE))
{
KdPrint((__DRIVER_NAME
"IOCTL_WDF_USB_SET_LIGHTBAR OutputBufferLength < sizeof(BYTE)\n"));
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
devCtx = GetDeviceContext(WdfIoQueueGetDevice(Queue));
status = WdfRequestRetrieveOutputMemory(Request, &memHandle);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"Could not retrieve the lightbar memory handle\n"));
WdfRequestComplete(Request, status);
return;
}
status = llGetLightBar(devCtx, memHandle);
WdfRequestCompleteWithInformation(Request, status, sizeof(BYTE));
}
/*............................................................................*/
/* low level USB function that sets the state of the LED array. */
/*............................................................................*/
NTSTATUS
llSetLightBar(
IN PDEVICE_CONTEXT Context,
IN WDFMEMORY State
)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_USB_CONTROL_SETUP_PACKET controlPacket;
BYTE physVal = 0;
WDF_MEMORY_DESCRIPTOR memDescriptor;
BYTE *inChar = NULL;
size_t length = 0;
inChar = WdfMemoryGetBuffer(State, &length);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"Could not retrieve the lightbar memory pointer\n"));
return status;
}
ASSERT(length >= sizeof(BYTE));
/*translate the supplied logical value to a value that represents the
values of the LEDs in the pyhsical light array. we copy the value
back into the memory buffer because we will reuse it for the vendor
command*/
physVal = ((*inChar & 0x07) << 5) | ((*inChar & 0xF8) >> 3);
KdPrint((__DRIVER_NAME "Original value = 0x%x, new value = 0x%x\n",
*inChar, physVal));
*inChar = physVal;
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&memDescriptor, State, NULL);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(
&controlPacket,
BmRequestHostToDevice,
BmRequestToDevice,
VC_SET_LIGHT_BAR,
0,
0);
status = WdfUsbTargetDeviceSendControlTransferSynchronously(
Context->UsbDevice,
NULL,
NULL,
&controlPacket,
&memDescriptor,
NULL);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfUsbTargetDeviceSendControlTransferSynchronouslyfailed with status 0x%08x\n",
status));
return status;
}
return status;
}
/*............................................................................*/
/* low level USB function that retrieves the state of the LED array. */
/*............................................................................*/
NTSTATUS
llGetLightBar(
IN PDEVICE_CONTEXT Context,
IN WDFMEMORY State
)
{
NTSTATUS status = STATUS_SUCCESS;
WDF_USB_CONTROL_SETUP_PACKET controlPacket;
WDF_MEMORY_DESCRIPTOR memDescriptor;
BYTE logicalVal = 0;
BYTE *inChar = NULL;
size_t length = 0;
KdPrint((__DRIVER_NAME "entering llGetLightBar\n"));
inChar = WdfMemoryGetBuffer(State, &length);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"Could not retrieve the lightbar memory pointer\n"));
return status;
}
ASSERT(length >= sizeof(BYTE));
ASSERT(NULL != inChar);
/*initialize the descriptor that will be passed to the USB driver*/
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&memDescriptor, State, NULL);
WDF_USB_CONTROL_SETUP_PACKET_INIT_VENDOR(
&controlPacket,
BmRequestDeviceToHost,
BmRequestToDevice,
VC_GET_LIGHT_BAR,
0,
0);
status = WdfUsbTargetDeviceSendControlTransferSynchronously(
Context->UsbDevice,
NULL,
NULL,
&controlPacket,
&memDescriptor,
NULL);
if(!NT_SUCCESS(status))
{
KdPrint((__DRIVER_NAME
"WdfUsbTargetDeviceSendControlTransferSynchronouslyfailed with status 0x%08x\n",
status));
return status;
}
/*translate the supplied physical value to a value that represents the
values of the LEDs in the logical light array.*/
logicalVal = ((*inChar & 0x1F) << 3) | ((*inChar & 0xE0) >> 5);
KdPrint((__DRIVER_NAME "Original value = 0x%x, new value = 0x%x\n",
*inChar, logicalVal));
*inChar = logicalVal;
return status;
}