|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
usb.c
Abstract: ESC/POS (serial) interface for USB Point-of-Sale devices
Author:
ervinp
Environment:
Kernel mode
Revision History:
--*/
#include <WDM.H>
#include <usbdi.h>
#include <usbdlib.h>
#include <usbioctl.h>
#include "escpos.h"
#include "debug.h"
NTSTATUS InitUSB(PARENTFDOEXT *parentFdoExt) /*++
Routine Description:
Intialize USB-related data
Arguments:
parentFdoExt - device extension for targetted device object
Return Value:
NT status code
--*/ { NTSTATUS status;
status = GetDeviceDescriptor(parentFdoExt); if (NT_SUCCESS(status)){ status = GetConfigDescriptor(parentFdoExt); if (NT_SUCCESS(status)){ status = SelectConfiguration(parentFdoExt); } }
return status; }
NTSTATUS GetConfigDescriptor(PARENTFDOEXT *parentFdoExt) /*++
Routine Description:
Function retrieves the configuration descriptor from the device
Arguments:
parentFdoExt - device extension for targetted device object
Return Value:
NT status code
--*/ { URB urb = { 0 }; USB_CONFIGURATION_DESCRIPTOR configDescBase = { 0 }; NTSTATUS status;
PAGED_CODE();
/*
* Get the first part of the configuration descriptor. * It will tell us the size of the full configuration descriptor, * including all the following interface descriptors, etc. */
UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, (PVOID)&configDescBase, NULL, sizeof(USB_CONFIGURATION_DESCRIPTOR), NULL);
status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)) {
ULONG configDescLen = configDescBase.wTotalLength; /*
* Now allocate the right-sized buffer for the full configuration descriptor. */ ASSERT(configDescLen < 0x1000); parentFdoExt->configDesc = ALLOCPOOL(NonPagedPool, configDescLen); if (parentFdoExt->configDesc) {
RtlZeroMemory(parentFdoExt->configDesc, configDescLen);
UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_CONFIGURATION_DESCRIPTOR_TYPE, 0, 0, parentFdoExt->configDesc, NULL, configDescLen, NULL);
status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)) { DBGVERBOSE(("Got config desc @ %ph, len=%xh.", parentFdoExt->configDesc, urb.UrbControlDescriptorRequest.TransferBufferLength)); ASSERT(urb.UrbControlDescriptorRequest.TransferBufferLength == configDescLen); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } }
ASSERT(NT_SUCCESS(status)); return status; }
NTSTATUS GetDeviceDescriptor(PARENTFDOEXT *parentFdoExt) /*++
Routine Description:
Function retrieves the device descriptor from the device
Arguments:
parentFdoExt - device extension for targetted device object
Return Value:
NT status code
--*/ { URB urb; NTSTATUS status;
PAGED_CODE();
RtlZeroMemory(&parentFdoExt->deviceDesc, sizeof(parentFdoExt->deviceDesc));
UsbBuildGetDescriptorRequest(&urb, (USHORT) sizeof(struct _URB_CONTROL_DESCRIPTOR_REQUEST), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, (PVOID)&parentFdoExt->deviceDesc, NULL, sizeof(parentFdoExt->deviceDesc), NULL);
status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)){ DBGVERBOSE(("Got device desc @ %ph, len=%xh (should be %xh).", (PVOID)&parentFdoExt->deviceDesc, urb.UrbControlDescriptorRequest.TransferBufferLength, sizeof(parentFdoExt->deviceDesc))); }
ASSERT(NT_SUCCESS(status)); return status; }
NTSTATUS ReadPipe( PARENTFDOEXT *parentFdoExt, USBD_PIPE_HANDLE pipeHandle, READPACKET *readPacket, BOOLEAN synchronous ) { PURB urb; NTSTATUS status;
ASSERT(pipeHandle); DBGVERBOSE(("ReadPipe: dataLen=%xh, sync=%xh", readPacket->length, (ULONG)synchronous));
urb = ALLOCPOOL(NonPagedPool, sizeof(URB)); if (urb){ RtlZeroMemory(urb, sizeof(URB));
urb->UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; urb->UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); urb->UrbBulkOrInterruptTransfer.PipeHandle = pipeHandle; urb->UrbBulkOrInterruptTransfer.TransferBufferLength = readPacket->length; urb->UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; urb->UrbBulkOrInterruptTransfer.TransferBuffer = readPacket->data; urb->UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_IN; urb->UrbBulkOrInterruptTransfer.UrbLink = NULL;
if (synchronous){ /*
* Synchronous read. */ status = SubmitUrb(parentFdoExt->topDevObj, urb, TRUE, NULL, 0); readPacket->length = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; FREEPOOL(urb); } else { /*
* Asynchronous read. * Completion routine will free URB. */ IncrementPendingActionCount(parentFdoExt); readPacket->urb = urb; status = SubmitUrb( parentFdoExt->topDevObj, urb, FALSE, // asynchronous
ReadPipeCompletion, // completion routine
readPacket // completion context
); } } else { status = STATUS_INSUFFICIENT_RESOURCES; }
return status; }
NTSTATUS ReadPipeCompletion(IN PDEVICE_OBJECT devObj, IN PIRP irp, IN PVOID context) { READPACKET *readPacket = (READPACKET *)context; NTSTATUS status = irp->IoStatus.Status; POSPDOEXT *pdoExt; KIRQL oldIrql;
ASSERT(readPacket->signature == READPACKET_SIG); pdoExt = readPacket->context;
/*
* Set the readPacket's length to the actual length returned by the device. */ ASSERT(readPacket->urb->UrbBulkOrInterruptTransfer.TransferBufferLength <= readPacket->length); readPacket->length = readPacket->urb->UrbBulkOrInterruptTransfer.TransferBufferLength;
DBGVERBOSE(("ReadPipeCompletion: irp=%ph, status=%xh, data=%ph, len=%xh, context=%ph.", irp, status, readPacket->data, readPacket->length, readPacket->context));
FREEPOOL(readPacket->urb); readPacket->urb = BAD_POINTER;
if (NT_SUCCESS(status)){
DBGSHOWBYTES("READ PIPE result", readPacket->data, readPacket->length);
if (pdoExt){
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql);
ASSERT(pdoExt->inputEndpointInfo.endpointIsBusy); pdoExt->inputEndpointInfo.endpointIsBusy = FALSE;
/*
* Queue this completed readPacket */ ASSERT(readPacket->offset == 0);
/*
* Do NOT queue empty readPackets. */ if(readPacket->length == 0) FreeReadPacket(readPacket); else { InsertTailList(&pdoExt->completedReadPacketsList, &readPacket->listEntry); pdoExt->totalQueuedReadDataLength += readPacket->length; }
KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
/*
* Now try to satisfy pending read IRPs with the completed readPacket data. */ SatisfyPendingReads(pdoExt); } else { DBGVERBOSE(("Just debug testing -- not processing read result")); FreeReadPacket(readPacket); } } else { FreeReadPacket(readPacket); }
/*
* If there are more read IRPs pending, issue another read. */ if (pdoExt){ BOOLEAN scheduleAnotherRead;
KeAcquireSpinLock(&pdoExt->devExtSpinLock, &oldIrql); scheduleAnotherRead = !IsListEmpty(&pdoExt->pendingReadIrpsList); KeReleaseSpinLock(&pdoExt->devExtSpinLock, oldIrql);
if (scheduleAnotherRead){ DBGVERBOSE(("ReadPipeCompletion: scheduling read workItem")); ExQueueWorkItem(&pdoExt->readWorkItem, DelayedWorkQueue); } }
/*
* This IRP was allocated by SubmitUrb(). Free it here. * Return STATUS_MORE_PROCESSING_REQUIRED so the kernel does not * continue processing this IRP. */ IoFreeIrp(irp); DecrementPendingActionCount(pdoExt->parentFdoExt); return STATUS_MORE_PROCESSING_REQUIRED; }
NTSTATUS WritePipe(PARENTFDOEXT *parentFdoExt, USBD_PIPE_HANDLE pipeHandle, PUCHAR data, ULONG dataLen) { URB urb; NTSTATUS status;
ASSERT(pipeHandle); DBGSHOWBYTES("WritePipe bytes:", data, dataLen);
urb.UrbBulkOrInterruptTransfer.Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; urb.UrbBulkOrInterruptTransfer.Hdr.Length = sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER); urb.UrbBulkOrInterruptTransfer.PipeHandle = pipeHandle; urb.UrbBulkOrInterruptTransfer.TransferBufferLength = dataLen; urb.UrbBulkOrInterruptTransfer.TransferBufferMDL = NULL; urb.UrbBulkOrInterruptTransfer.TransferBuffer = data; urb.UrbBulkOrInterruptTransfer.TransferFlags = USBD_SHORT_TRANSFER_OK | USBD_TRANSFER_DIRECTION_OUT; urb.UrbBulkOrInterruptTransfer.UrbLink = NULL;
status = SubmitUrb(parentFdoExt->topDevObj, &urb, TRUE, NULL, NULL);
return status; }
NTSTATUS SubmitUrb( PDEVICE_OBJECT pdo, PURB urb, BOOLEAN synchronous, PVOID completionRoutine, PVOID completionContext) /*++
Routine Description:
Send the URB to the USB device. If synchronous is TRUE, ignore the completion info and synchonize the IRP; otherwise, don't synchronize and set the provided completion routine for the IRP.
Arguments:
Return Value:
NT status code
--*/ { PIRP irp; NTSTATUS status;
/*
* Allocate the IRP to send the buffer down the USB stack. * * Don't use IoBuildDeviceIoControlRequest (because it queues * the IRP on the current thread's irp list and may * cause the calling process to hang if the IopCompleteRequest APC * does not fire and dequeue the IRP). */ irp = IoAllocateIrp(pdo->StackSize, FALSE);
if (irp){ PIO_STACK_LOCATION nextSp;
#if STATUS_ENDPOINT
DBGVERBOSE(("SubmitUrb: submitting URB %ph on IRP %ph (sync=%d)", urb, irp, synchronous)); #endif
nextSp = IoGetNextIrpStackLocation(irp); nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
/*
* Attach the URB to this IRP. */ nextSp->Parameters.Others.Argument1 = urb;
if (synchronous){
status = CallDriverSync(pdo, irp);
IoFreeIrp(irp); } else { /*
* Caller's completion routine will free the irp * when it completes. */ ASSERT(completionRoutine); ASSERT(completionContext); IoSetCompletionRoutine( irp, completionRoutine, completionContext, TRUE, TRUE, TRUE); status = IoCallDriver(pdo, irp); } } else { status = STATUS_INSUFFICIENT_RESOURCES; }
return status; }
NTSTATUS SelectConfiguration(PARENTFDOEXT *parentFdoExt) { USBD_INTERFACE_LIST_ENTRY interfaceList[2]; NTSTATUS status; /*
* We only look at vendor-class interfaces */ // BUGBUG - limited to one interface
interfaceList[0].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx( parentFdoExt->configDesc, parentFdoExt->configDesc, -1, -1, USB_INTERFACE_CLASS_VENDOR, -1, -1);
if (interfaceList[0].InterfaceDescriptor){ PURB urb;
interfaceList[1].InterfaceDescriptor = NULL;
urb = USBD_CreateConfigurationRequestEx(parentFdoExt->configDesc, &interfaceList[0]); if (urb){
status = SubmitUrb(parentFdoExt->topDevObj, urb, TRUE, NULL, NULL);
if (NT_SUCCESS(status)){ PUSBD_INTERFACE_INFORMATION interfaceInfo;
parentFdoExt->configHandle = urb->UrbSelectConfiguration.ConfigurationHandle;
interfaceInfo = &urb->UrbSelectConfiguration.Interface; parentFdoExt->interfaceInfo = MemDup(interfaceInfo, interfaceInfo->Length); if (parentFdoExt->interfaceInfo){ DBGVERBOSE(("SelectConfiguration: got interfaceInfo @ %ph.", parentFdoExt->interfaceInfo)); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { DBGERR(("SelectConfiguration: URB failed w/ %xh.", status)); }
FREEPOOL(urb); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = STATUS_DEVICE_DATA_ERROR; }
return status; }
NTSTATUS CreatePdoForEachEndpointPair(PARENTFDOEXT *parentFdoExt) /*++
Routine Description:
Walk the USB endpoints. For each input/output endpoint pair, create one PDO which will be exposed as a COM (serial) port interface.
BUGBUG - right now, this only looks at the first interface (on the default confuguration)
Arguments:
parentFdoExt - device extension for the targetted device object
Return Value:
NT status code
--*/ { NTSTATUS status; ULONG maxPossiblePDOs, deviceRelationsSize;
maxPossiblePDOs = (parentFdoExt->interfaceInfo->NumberOfPipes/2); deviceRelationsSize = sizeof(DEVICE_RELATIONS) + maxPossiblePDOs*sizeof(PDEVICE_OBJECT); parentFdoExt->deviceRelations = ALLOCPOOL(NonPagedPool, deviceRelationsSize);
if (parentFdoExt->deviceRelations){ ULONG inputPipeIndex = 0, outputPipeIndex = 0, statusPipeIndex = 0, comInterfaceIndex = 0;
RtlZeroMemory(parentFdoExt->deviceRelations, deviceRelationsSize);
status = STATUS_NO_MATCH;
while (TRUE){ UCHAR endPtAddr; USBD_PIPE_TYPE pipeType;
#if STATUS_ENDPOINT
/*
* In the case of Serial Emulation, the protocol is that all DATA endpoints * will be of type BULK and all STATUS endpoints will be of type INTERRUPT. */ if(parentFdoExt->posFlag & SERIAL_EMULATION) { while(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) { endPtAddr = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].EndpointAddress; pipeType = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].PipeType; if((pipeType == UsbdPipeTypeBulk) && (endPtAddr & USB_ENDPOINT_DIRECTION_MASK)) break; inputPipeIndex++; } while(outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) { endPtAddr = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].EndpointAddress; pipeType = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].PipeType; if((pipeType == UsbdPipeTypeBulk) && !(endPtAddr & USB_ENDPOINT_DIRECTION_MASK)) break; outputPipeIndex++; } while(statusPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) { endPtAddr = parentFdoExt->interfaceInfo->Pipes[statusPipeIndex].EndpointAddress; pipeType = parentFdoExt->interfaceInfo->Pipes[statusPipeIndex].PipeType; if((pipeType == UsbdPipeTypeInterrupt) && (endPtAddr & USB_ENDPOINT_DIRECTION_MASK)) break; statusPipeIndex++; } if(!(statusPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes)) break; } else { #endif
/*
* No Serial Emulation required. Find only DATA endpoints * which can be of either type in this case. */ while(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) { endPtAddr = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].EndpointAddress; pipeType = parentFdoExt->interfaceInfo->Pipes[inputPipeIndex].PipeType; if((pipeType == UsbdPipeTypeInterrupt) || (pipeType == UsbdPipeTypeBulk)) { if(endPtAddr & USB_ENDPOINT_DIRECTION_MASK) { break; } } inputPipeIndex++; } while(outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) { endPtAddr = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].EndpointAddress; pipeType = parentFdoExt->interfaceInfo->Pipes[outputPipeIndex].PipeType; if((pipeType == UsbdPipeTypeInterrupt) || (pipeType == UsbdPipeTypeBulk)) { if(!(endPtAddr & USB_ENDPOINT_DIRECTION_MASK)) { break; } } outputPipeIndex++; } #if STATUS_ENDPOINT
} #endif
if ((inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) && (outputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes)){
/*
* We've found a pair (in/out) of endpoints. * Create a PDO to represent a COM (serial) port interface on these endpoints. */ PUSBD_PIPE_INFORMATION inputPipeInfo = &parentFdoExt->interfaceInfo->Pipes[inputPipeIndex]; PUSBD_PIPE_INFORMATION outputPipeInfo = &parentFdoExt->interfaceInfo->Pipes[outputPipeIndex]; ENDPOINTINFO inputEndpointInfo, outputEndpointInfo, statusEndpointInfo; #if EPSON_PRINTER
/*
* On the EPSON printer, we want to talk to the endpoints size 0x40, * not the other pair with length 8. */ if ((inputPipeInfo->MaximumPacketSize == 8) && (outputPipeInfo->MaximumPacketSize == 8)){ inputPipeIndex++, outputPipeIndex++; continue; } #endif
inputEndpointInfo.pipeHandle = inputPipeInfo->PipeHandle; inputEndpointInfo.pipeLen = inputPipeInfo->MaximumPacketSize; inputEndpointInfo.endpointIsBusy = FALSE;
outputEndpointInfo.pipeHandle = outputPipeInfo->PipeHandle; outputEndpointInfo.pipeLen = outputPipeInfo->MaximumPacketSize; outputEndpointInfo.endpointIsBusy = FALSE;
#if STATUS_ENDPOINT
if(parentFdoExt->posFlag & SERIAL_EMULATION) { PUSBD_PIPE_INFORMATION statusPipeInfo = &parentFdoExt->interfaceInfo->Pipes[statusPipeIndex]; statusEndpointInfo.pipeHandle = statusPipeInfo->PipeHandle; statusEndpointInfo.pipeLen = statusPipeInfo->MaximumPacketSize; statusEndpointInfo.endpointIsBusy = FALSE; } #endif
status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, &inputEndpointInfo, &outputEndpointInfo, &statusEndpointInfo); if (NT_SUCCESS(status)){ DBGVERBOSE(("CreatePdoForEachEndpointPair: created COMPdo with in/out len %xh/%xh.", inputEndpointInfo.pipeLen, outputEndpointInfo.pipeLen)); inputPipeIndex++, outputPipeIndex++, statusPipeIndex++; } else { DBGERR(("CreatePdoForEachEndpointPair: CreateCOMPdo failed with %xh.", status)); break; } } else { if((parentFdoExt->posFlag & ODD_ENDPOINT) && ((inputPipeIndex + outputPipeIndex) < (2 * parentFdoExt->interfaceInfo->NumberOfPipes))) {
/*
* We've found an odd endpoint. * Create a PDO to represent a COM (serial) port interface on this endpoint. */ PUSBD_PIPE_INFORMATION oddPipeInfo = &parentFdoExt->interfaceInfo->Pipes[MIN(inputPipeIndex, outputPipeIndex)]; ENDPOINTINFO oddEndpointInfo;
oddEndpointInfo.pipeHandle = oddPipeInfo->PipeHandle; oddEndpointInfo.pipeLen = oddPipeInfo->MaximumPacketSize; oddEndpointInfo.endpointIsBusy = FALSE;
if(inputPipeIndex < parentFdoExt->interfaceInfo->NumberOfPipes) status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, &oddEndpointInfo, NULL, NULL); else status = CreateCOMPdo(parentFdoExt, comInterfaceIndex++, NULL, &oddEndpointInfo, NULL);
if (NT_SUCCESS(status)){ DBGVERBOSE(("CreatePdoForEachEndpointPair: created <odd> COMPdo with len %xh.", oddEndpointInfo.pipeLen)); } else { DBGERR(("CreatePdoForEachEndpointPair: CreateCOMPdo failed with %xh.", status)); break; } } break; } } } else { status = STATUS_INSUFFICIENT_RESOURCES; }
ASSERT(NT_SUCCESS(status)); return status; }
|