Something went wrong. Try again.
Reactos
Something went wrong. Try again.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199// Copyright (c) 2004, Antony C. Roberts
// Use of this file is subject to the terms// described in the LICENSE.TXT file that// accompanies this file.//// Your use of this file indicates your// acceptance of the terms described in// LICENSE.TXT.//// http://www.freebt.net
#include "fbtusb.h"#include "fbtpnp.h"#include "fbtpwr.h"#include "fbtdev.h"#include "fbtwmi.h"#include "fbtrwr.h"
#include "fbtusr.h"
// Dispatch routine for CreateHandleNTSTATUS NTAPI FreeBT_DispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ //ULONG i; NTSTATUS ntStatus; PFILE_OBJECT fileObject; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; //PFREEBT_PIPE_CONTEXT pipeContext; PUSBD_INTERFACE_INFORMATION interface;
PAGED_CODE();
FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Entered\n"));
irpStack = IoGetCurrentIrpStackLocation(Irp); fileObject = irpStack->FileObject; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
if (deviceExtension->DeviceState != Working) { ntStatus = STATUS_INVALID_DEVICE_STATE; goto FreeBT_DispatchCreate_Exit;
}
if (deviceExtension->UsbInterface) { interface = deviceExtension->UsbInterface;
}
else { FreeBT_DbgPrint(1, ("UsbInterface not found\n")); ntStatus = STATUS_INVALID_DEVICE_STATE; goto FreeBT_DispatchCreate_Exit;
}
if (fileObject) { fileObject->FsContext = NULL; }
else { ntStatus = STATUS_INVALID_PARAMETER; goto FreeBT_DispatchCreate_Exit;
}
if (deviceExtension->OpenHandleCount>0) { ntStatus = STATUS_ACCESS_VIOLATION; goto FreeBT_DispatchCreate_Exit;
}
// opening a device as opposed to pipe. ntStatus = STATUS_SUCCESS;
InterlockedIncrement(&deviceExtension->OpenHandleCount);
// the device is idle if it has no open handles or pending PnP Irps // since we just received an open handle request, cancel idle req. if (deviceExtension->SSEnable) CancelSelectSuspend(deviceExtension);
FreeBT_DispatchCreate_Exit: Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT);
FreeBT_DbgPrint(3, ("FreeBT_DispatchCreate: Leaving\n")); return ntStatus;
}
// Dispatch routine for CloseHandleNTSTATUS NTAPI FreeBT_DispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ NTSTATUS ntStatus; PFILE_OBJECT fileObject; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; //PFREEBT_PIPE_CONTEXT pipeContext; //PUSBD_PIPE_INFORMATION pipeInformation;
PAGED_CODE();
irpStack = IoGetCurrentIrpStackLocation(Irp); fileObject = irpStack->FileObject; //pipeContext = NULL; //pipeInformation = NULL; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Entered\n"));
ntStatus = STATUS_SUCCESS; Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT);
InterlockedDecrement(&deviceExtension->OpenHandleCount);
FreeBT_DbgPrint(3, ("FreeBT_DispatchClose: Leaving\n"));
return ntStatus;
}
// Called when a HCI Send on the control pipe completesNTSTATUS NTAPI FreeBT_HCISendCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context){ //ULONG stageLength; NTSTATUS ntStatus;
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCISendCompletion, status=0x%08X\n", Irp->IoStatus.Status));
if (Irp->PendingReturned) IoMarkIrpPending(Irp);
ExFreePool(Context); FreeBT_IoDecrement(DeviceObject->DeviceExtension); ntStatus = Irp->IoStatus.Status; Irp->IoStatus.Information = 0;
return ntStatus;
}
// Called the DeviceIOControl handler to send an HCI command received from the user// HCI Commands are sent on the (default) control pipeNTSTATUS NTAPI FreeBT_SendHCICommand(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID IoBuffer, IN ULONG InputBufferLength){ PDEVICE_EXTENSION deviceExtension; //ULONG urbFlags; //ULONG stageLength; //PVOID pBuffer; PURB urb; NTSTATUS ntStatus; PIO_STACK_LOCATION nextStack; //PFBT_HCI_CMD_HEADER pHCICommand; //LARGE_INTEGER delay;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; if (!deviceExtension) { ntStatus=STATUS_INVALID_PARAMETER; FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Failed to get DeviceExtension\n")); Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return ntStatus;
}
// The user is doing a reset, reset all the pipes as well, so that any // old events or data are removed /*pHCICommand=(PFBT_HCI_CMD_HEADER)IoBuffer; if (pHCICommand->OpCode==FBT_HCI_CMD_RESET) { FreeBT_ResetPipe(DeviceObject, deviceExtension->EventPipe.PipeHandle); FreeBT_ResetPipe(DeviceObject, deviceExtension->DataInPipe.PipeHandle); FreeBT_ResetPipe(DeviceObject, deviceExtension->DataOutPipe.PipeHandle); FreeBT_ResetPipe(DeviceObject, deviceExtension->AudioInPipe.PipeHandle); FreeBT_ResetPipe(DeviceObject, deviceExtension->AudioOutPipe.PipeHandle);
// Wait a second for the device to recover FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Sleeping\n")); delay.QuadPart = -10000 * 5000; // 5s KeWaitForSingleObject(&deviceExtension->DelayEvent, Executive, UserMode, FALSE, &delay);
FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Finished sleeping\n"));
}*/
// Create the URB urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST)); if(urb == NULL) { FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_SendHCICommand: Failed to alloc mem for urb\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return ntStatus;
}
UsbBuildVendorRequest( urb, URB_FUNCTION_CLASS_DEVICE, // This works, for CSR and Silicon Wave sizeof(struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST), 0, 0, 0, 0, 0, IoBuffer, NULL, InputBufferLength, NULL);
// use the original irp as an internal device control irp nextStack = IoGetNextIrpStackLocation(Irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.Others.Argument1 = (PVOID) urb; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)FreeBT_HCISendCompletion, urb, TRUE, TRUE, TRUE);
// We return STATUS_PENDING; call IoMarkIrpPending. IoMarkIrpPending(Irp);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::")); FreeBT_IoIncrement(deviceExtension);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: Sending IRP %X to underlying driver\n", Irp)); ntStatus=IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: IoCallDriver fails with status %X\n", ntStatus));
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand::")); FreeBT_IoDecrement(deviceExtension);
// If the device was surprise removed out, the pipeInformation field is invalid. // similarly if the request was cancelled, then we need not reset the device. if((ntStatus != STATUS_CANCELLED) && (ntStatus != STATUS_DEVICE_NOT_CONNECTED)) ntStatus = FreeBT_ResetDevice(DeviceObject);
else FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
Irp->IoStatus.Status = ntStatus; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_SendHCICommand: Completed successfully\n"));
return STATUS_PENDING;
}
// Called when a HCI Get on the event pipe completesNTSTATUS NTAPI FreeBT_HCIEventCompletion(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context){ //ULONG stageLength; NTSTATUS ntStatus; PIO_STACK_LOCATION nextStack; PURB urb;
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_HCIEventCompletion, status=0x%08X\n", Irp->IoStatus.Status));
if (Irp->PendingReturned) IoMarkIrpPending(Irp);
// initialize variables urb=(PURB)Context; ntStatus = Irp->IoStatus.Status; Irp->IoStatus.Information = urb->UrbBulkOrInterruptTransfer.TransferBufferLength; nextStack = IoGetNextIrpStackLocation(Irp);
ExFreePool(Context); FreeBT_IoDecrement(DeviceObject->DeviceExtension);
return ntStatus;
}
// Called from the DeviceIOControl handler to wait for an event on the interrupt pipeNTSTATUS NTAPI FreeBT_GetHCIEvent(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID IoBuffer, IN ULONG InputBufferLength){ PDEVICE_EXTENSION deviceExtension; PURB urb; NTSTATUS ntStatus; PIO_STACK_LOCATION nextStack;
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Entered\n"));
urb = NULL; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER)); if (urb==NULL) { FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_GetHCIEvent: Failed to alloc mem for urb\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; goto FreeBT_GetHCIEvent_Exit;
}
UsbBuildInterruptOrBulkTransferRequest( urb, sizeof(struct _URB_BULK_OR_INTERRUPT_TRANSFER), deviceExtension->EventPipe.PipeHandle, IoBuffer, NULL, InputBufferLength, USBD_SHORT_TRANSFER_OK|USBD_TRANSFER_DIRECTION_IN, NULL);
// use the original irp as an internal device control irp, which we send down to the // USB class driver in order to get our request out on the wire nextStack = IoGetNextIrpStackLocation(Irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.Others.Argument1 = (PVOID) urb; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
IoSetCompletionRoutine( Irp, (PIO_COMPLETION_ROUTINE)FreeBT_HCIEventCompletion, urb, TRUE, TRUE, TRUE);
// We return STATUS_PENDING; call IoMarkIrpPending. IoMarkIrpPending(Irp);
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::")); FreeBT_IoIncrement(deviceExtension);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, Irp); if (!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: IoCallDriver fails with status %X\n", ntStatus));
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent::")); FreeBT_IoDecrement(deviceExtension);
// If the device was surprise removed out, the pipeInformation field is invalid. // similarly if the request was cancelled, then we need not reset the pipe. if((ntStatus != STATUS_CANCELLED) && (ntStatus != STATUS_DEVICE_NOT_CONNECTED)) { ntStatus = FreeBT_ResetPipe(DeviceObject, deviceExtension->EventPipe.PipeHandle); if(!NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(1, ("FreeBT_ResetPipe failed\n")); ntStatus = FreeBT_ResetDevice(DeviceObject);
}
}
else { FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: ntStatus is STATUS_CANCELLED or STATUS_DEVICE_NOT_CONNECTED\n"));
}
goto FreeBT_GetHCIEvent_Exit;
}
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Leaving\n"));
// Return STATUS_PENDING, when the lower driver completes the request, // the FreeBT_HCIEventCompletion completion routine. return STATUS_PENDING;
FreeBT_GetHCIEvent_Exit: Irp->IoStatus.Status=ntStatus; Irp->IoStatus.Information=0;
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_GetHCIEvent: Failure (0x%08x), completing IRP\n", ntStatus)); IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
// DeviceIOControl dispatchNTSTATUS NTAPI FreeBT_DispatchDevCtrl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){ ULONG code; PVOID ioBuffer; ULONG inputBufferLength; ULONG outputBufferLength; ULONG info; NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack;
info = 0; irpStack = IoGetCurrentIrpStackLocation(Irp); code = irpStack->Parameters.DeviceIoControl.IoControlCode; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
ioBuffer = Irp->AssociatedIrp.SystemBuffer; inputBufferLength = irpStack->Parameters.DeviceIoControl.InputBufferLength; outputBufferLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
if (deviceExtension->DeviceState != Working) { FreeBT_DbgPrint(1, ("FBTUSB: Invalid device state\n")); ntStatus = STATUS_INVALID_DEVICE_STATE; goto FreeBT_DispatchDevCtrlExit;
}
FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_DispatchDevCtrl::"));
// Make sure that any selective suspend request has been completed. if (deviceExtension->SSEnable) { FreeBT_DbgPrint(3, ("Waiting on the IdleReqPendEvent\n")); KeWaitForSingleObject(&deviceExtension->NoIdleReqPendEvent, Executive, KernelMode, FALSE, NULL);
}
switch(code) { case IOCTL_FREEBT_HCI_SEND_CMD: FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD received\n")); if (inputBufferLength<FBT_HCI_CMD_MIN_SIZE) { ntStatus = STATUS_BUFFER_TOO_SMALL; FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too small\n")); goto FreeBT_DispatchDevCtrlExit;
}
if (inputBufferLength>FBT_HCI_CMD_MAX_SIZE) { ntStatus = STATUS_INVALID_BUFFER_SIZE; FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_SEND_CMD: Buffer too long\n")); goto FreeBT_DispatchDevCtrlExit;
}
return FreeBT_SendHCICommand(DeviceObject, Irp, ioBuffer, inputBufferLength); break;
case IOCTL_FREEBT_HCI_GET_EVENT: FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_GET_EVENT received\n")); if (outputBufferLength<FBT_HCI_EVENT_MAX_SIZE) { ntStatus = STATUS_BUFFER_TOO_SMALL; FreeBT_DbgPrint(3, ("FBTUSB: IOCTL_FREEBT_HCI_GET_EVENT: Buffer too small\n")); goto FreeBT_DispatchDevCtrlExit;
}
return FreeBT_GetHCIEvent(DeviceObject, Irp, ioBuffer, outputBufferLength); break;
default: FreeBT_DbgPrint(3, ("FBTUSB: Invalid IOCTL 0x%08x received\n", code)); ntStatus = STATUS_INVALID_DEVICE_REQUEST; break;
}
FreeBT_DispatchDevCtrlExit: Irp->IoStatus.Information = 0; Irp->IoStatus.Status = ntStatus; IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;}
// Submit URB_FUNCTION_RESET_PIPENTSTATUS NTAPI FreeBT_ResetPipe(IN PDEVICE_OBJECT DeviceObject, IN USBD_PIPE_HANDLE PipeHandle){ PURB urb; NTSTATUS ntStatus; PDEVICE_EXTENSION deviceExtension;
urb = NULL; deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
urb = (PURB)ExAllocatePool(NonPagedPool, sizeof(struct _URB_PIPE_REQUEST)); if (urb) { urb->UrbHeader.Length = (USHORT) sizeof(struct _URB_PIPE_REQUEST); urb->UrbHeader.Function = URB_FUNCTION_RESET_PIPE; urb->UrbPipeRequest.PipeHandle = PipeHandle;
ntStatus = CallUSBD(DeviceObject, urb);
ExFreePool(urb);
}
else ntStatus = STATUS_INSUFFICIENT_RESOURCES;
if(NT_SUCCESS(ntStatus)) { FreeBT_DbgPrint(3, ("FBTUSB: FreeBT_ResetPipe - success\n")); ntStatus = STATUS_SUCCESS;
}
else FreeBT_DbgPrint(1, ("FBTUSB: FreeBT_ResetPipe - failed\n"));
return ntStatus;
}
// Call FreeBT_ResetParentPort to reset the deviceNTSTATUS NTAPI FreeBT_ResetDevice(IN PDEVICE_OBJECT DeviceObject){ NTSTATUS ntStatus; ULONG portStatus;
FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Entered\n"));
ntStatus = FreeBT_GetPortStatus(DeviceObject, &portStatus);
if ( (NT_SUCCESS(ntStatus)) && (!(portStatus & USBD_PORT_ENABLED)) && (portStatus & USBD_PORT_CONNECTED)) ntStatus=FreeBT_ResetParentPort(DeviceObject);
FreeBT_DbgPrint(3, ("FreeBT_ResetDevice: Leaving\n"));
return ntStatus;
}
// Read port status from the lower driver (USB class driver)NTSTATUS NTAPI FreeBT_GetPortStatus(IN PDEVICE_OBJECT DeviceObject, IN OUT PULONG PortStatus){ NTSTATUS ntStatus; KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION nextStack; PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; *PortStatus = 0;
KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_INTERNAL_USB_GET_PORT_STATUS, deviceExtension->TopOfStackDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &ioStatus);
if (NULL == irp) { FreeBT_DbgPrint(1, ("memory alloc for irp failed\n")); return STATUS_INSUFFICIENT_RESOURCES;
}
nextStack = IoGetNextIrpStackLocation(irp); ASSERT(nextStack != NULL); nextStack->Parameters.Others.Argument1 = PortStatus;
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp); if (STATUS_PENDING==ntStatus) KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
else ioStatus.Status = ntStatus;
ntStatus = ioStatus.Status; FreeBT_DbgPrint(3, ("FreeBT_GetPortStatus: Leaving\n"));
return ntStatus;
}
// Sends an IOCTL_INTERNAL_USB_RESET_PORT via the lower driverNTSTATUS NTAPI FreeBT_ResetParentPort(IN PDEVICE_OBJECT DeviceObject){ NTSTATUS ntStatus; KEVENT event; PIRP irp; IO_STATUS_BLOCK ioStatus; PIO_STACK_LOCATION nextStack; PDEVICE_EXTENSION deviceExtension;
FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Entered\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
KeInitializeEvent(&event, NotificationEvent, FALSE); irp = IoBuildDeviceIoControlRequest( IOCTL_INTERNAL_USB_RESET_PORT, deviceExtension->TopOfStackDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &ioStatus);
if (NULL == irp) { FreeBT_DbgPrint(1, ("memory alloc for irp failed\n")); return STATUS_INSUFFICIENT_RESOURCES;
}
nextStack = IoGetNextIrpStackLocation(irp); ASSERT(nextStack != NULL);
ntStatus = IoCallDriver(deviceExtension->TopOfStackDeviceObject, irp); if(STATUS_PENDING == ntStatus) KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
else ioStatus.Status = ntStatus;
ntStatus = ioStatus.Status;
FreeBT_DbgPrint(3, ("FreeBT_ResetParentPort: Leaving\n"));
return ntStatus;
}
// Send an idle request to the lower driverNTSTATUS NTAPI SubmitIdleRequestIrp(IN PDEVICE_EXTENSION DeviceExtension){ PIRP irp; NTSTATUS ntStatus; KIRQL oldIrql; PUSB_IDLE_CALLBACK_INFO idleCallbackInfo; PIO_STACK_LOCATION nextStack;
FreeBT_DbgPrint(3, ("SubmitIdleRequest: Entered\n"));
irp = NULL; idleCallbackInfo = NULL;
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
if(PowerDeviceD0 != DeviceExtension->DevPower) {
ntStatus = STATUS_POWER_STATE_INVALID;
goto SubmitIdleRequestIrp_Exit; }
KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
if(InterlockedExchange(&DeviceExtension->IdleReqPend, 1)) {
FreeBT_DbgPrint(1, ("Idle request pending..\n"));
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
ntStatus = STATUS_DEVICE_BUSY;
goto SubmitIdleRequestIrp_Exit; }
// // clear the NoIdleReqPendEvent because we are about // to submit an idle request. Since we are so early // to clear this event, make sure that if we fail this // request we set back the event. // KeClearEvent(&DeviceExtension->NoIdleReqPendEvent);
idleCallbackInfo = (PUSB_IDLE_CALLBACK_INFO)ExAllocatePool(NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO));
if(idleCallbackInfo) {
idleCallbackInfo->IdleCallback = (USB_IDLE_CALLBACK)IdleNotificationCallback;
idleCallbackInfo->IdleContext = (PVOID)DeviceExtension;
ASSERT(DeviceExtension->IdleCallbackInfo == NULL);
DeviceExtension->IdleCallbackInfo = idleCallbackInfo;
// // we use IoAllocateIrp to create an irp to selectively suspend the // device. This irp lies pending with the hub driver. When appropriate // the hub driver will invoked callback, where we power down. The completion // routine is invoked when we power back. // irp = IoAllocateIrp(DeviceExtension->TopOfStackDeviceObject->StackSize, FALSE);
if(irp == NULL) {
FreeBT_DbgPrint(1, ("cannot build idle request irp\n"));
KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
ExFreePool(idleCallbackInfo);
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
goto SubmitIdleRequestIrp_Exit; }
nextStack = IoGetNextIrpStackLocation(irp);
nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION;
nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo;
nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO);
IoSetCompletionRoutine(irp, (PIO_COMPLETION_ROUTINE)IdleNotificationRequestComplete, DeviceExtension, TRUE, TRUE, TRUE);
DeviceExtension->PendingIdleIrp = irp;
// // we initialize the count to 2. // The reason is, if the CancelSelectSuspend routine manages // to grab the irp from the device extension, then the last of the // CancelSelectSuspend routine/IdleNotificationRequestComplete routine // to execute will free this irp. We need to have this schema so that // 1. completion routine does not attempt to touch the irp freed by // CancelSelectSuspend routine. // 2. CancelSelectSuspend routine doesnt wait for ever for the completion // routine to complete! // DeviceExtension->FreeIdleIrpCount = 2;
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
// // check if the device is idle. // A check here ensures that a race condition did not // completely reverse the call sequence of SubmitIdleRequestIrp // and CancelSelectiveSuspend //
if(!CanDeviceSuspend(DeviceExtension) || PowerDeviceD0 != DeviceExtension->DevPower) {
// // IRPs created using IoBuildDeviceIoControlRequest should be // completed by calling IoCompleteRequest and not merely // deallocated. //
FreeBT_DbgPrint(1, ("Device is not idle\n"));
KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
DeviceExtension->IdleCallbackInfo = NULL;
DeviceExtension->PendingIdleIrp = NULL;
KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
if(idleCallbackInfo) {
ExFreePool(idleCallbackInfo); }
// // it is still safe to touch the local variable "irp" here. // the irp has not been passed down the stack, the irp has // no cancellation routine. The worse position is that the // CancelSelectSuspend has run after we released the spin // lock above. It is still essential to free the irp. // if(irp) {
IoFreeIrp(irp); }
ntStatus = STATUS_UNSUCCESSFUL;
goto SubmitIdleRequestIrp_Exit; }
FreeBT_DbgPrint(3, ("Cancel the timers\n")); // // Cancel the timer so that the DPCs are no longer fired. // Thus, we are making judicious usage of our resources. // we do not need DPCs because we already have an idle irp pending. // The timers are re-initialized in the completion routine. // KeCancelTimer(&DeviceExtension->Timer);
ntStatus = IoCallDriver(DeviceExtension->TopOfStackDeviceObject, irp);
if(!NT_SUCCESS(ntStatus)) {
FreeBT_DbgPrint(1, ("IoCallDriver failed\n"));
goto SubmitIdleRequestIrp_Exit; } } else {
FreeBT_DbgPrint(1, ("Memory allocation for idleCallbackInfo failed\n"));
KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
ntStatus = STATUS_INSUFFICIENT_RESOURCES; }
SubmitIdleRequestIrp_Exit:
FreeBT_DbgPrint(3, ("SubmitIdleRequest: Leaving\n"));
return ntStatus;}
VOID NTAPI IdleNotificationCallback(IN PDEVICE_EXTENSION DeviceExtension){ NTSTATUS ntStatus; POWER_STATE powerState; KEVENT irpCompletionEvent; PIRP_COMPLETION_CONTEXT irpContext;
FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Entered\n"));
// // Dont idle, if the device was just disconnected or being stopped // i.e. return for the following DeviceState(s) // NotStarted, Stopped, PendingStop, PendingRemove, SurpriseRemoved, Removed //
if(DeviceExtension->DeviceState != Working) {
return; }
// // If there is not already a WW IRP pending, submit one now // if(DeviceExtension->WaitWakeEnable) {
IssueWaitWake(DeviceExtension); }
// // power down the device //
irpContext = (PIRP_COMPLETION_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(IRP_COMPLETION_CONTEXT));
if(!irpContext) {
FreeBT_DbgPrint(1, ("FBTUSB: IdleNotificationCallback: Failed to alloc memory for irpContext\n")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else {
// // increment the count. In the HoldIoRequestWorkerRoutine, the // count is decremented twice (one for the system Irp and the // other for the device Irp. An increment here compensates for // the sytem irp..The decrement corresponding to this increment // is in the completion function //
FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::")); FreeBT_IoIncrement(DeviceExtension);
powerState.DeviceState = (DEVICE_POWER_STATE) DeviceExtension->PowerDownLevel;
KeInitializeEvent(&irpCompletionEvent, NotificationEvent, FALSE);
irpContext->DeviceExtension = DeviceExtension; irpContext->Event = &irpCompletionEvent;
ntStatus = PoRequestPowerIrp( DeviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, (PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc, irpContext, NULL);
if(STATUS_PENDING == ntStatus) {
FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback::" "waiting for the power irp to complete\n"));
KeWaitForSingleObject(&irpCompletionEvent, Executive, KernelMode, FALSE, NULL); } }
if(!NT_SUCCESS(ntStatus)) {
if(irpContext) {
ExFreePool(irpContext); } }
FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationCallback: Leaving\n"));}
NTSTATUS NTAPI IdleNotificationRequestComplete(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PDEVICE_EXTENSION DeviceExtension){ NTSTATUS ntStatus; POWER_STATE powerState; KIRQL oldIrql; LARGE_INTEGER dueTime; PIRP idleIrp; PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Entered\n"));
idleIrp = NULL;
ntStatus = Irp->IoStatus.Status; if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED) { FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestCompete: Idle irp completes with error::")); switch(ntStatus) { case STATUS_INVALID_DEVICE_REQUEST: FreeBT_DbgPrint(3, ("STATUS_INVALID_DEVICE_REQUEST\n")); break;
case STATUS_CANCELLED: FreeBT_DbgPrint(3, ("STATUS_CANCELLED\n")); break;
case STATUS_DEVICE_BUSY: FreeBT_DbgPrint(3, ("STATUS_DEVICE_BUSY\n")); break;
case STATUS_POWER_STATE_INVALID: FreeBT_DbgPrint(3, ("STATUS_POWER_STATE_INVALID\n")); goto IdleNotificationRequestComplete_Exit;
default: FreeBT_DbgPrint(3, ("default: status = %X\n", ntStatus)); break;
}
// if in error, issue a SetD0 FreeBT_DbgPrint(3, ("FBTUSB: IdleNotificationRequestComplete::")); FreeBT_IoIncrement(DeviceExtension);
powerState.DeviceState = PowerDeviceD0; ntStatus = PoRequestPowerIrp( DeviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, (PREQUEST_POWER_COMPLETE) PoIrpAsyncCompletionFunc, DeviceExtension, NULL);
if(!NT_SUCCESS(ntStatus)) FreeBT_DbgPrint(1, ("PoRequestPowerIrp failed\n"));
}
IdleNotificationRequestComplete_Exit: KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql); idleCallbackInfo = DeviceExtension->IdleCallbackInfo; DeviceExtension->IdleCallbackInfo = NULL;
idleIrp = (PIRP) InterlockedExchangePointer((PVOID*)&DeviceExtension->PendingIdleIrp, NULL); InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
if(idleCallbackInfo) ExFreePool(idleCallbackInfo);
// Since the irp was created using IoAllocateIrp, // the Irp needs to be freed using IoFreeIrp. // Also return STATUS_MORE_PROCESSING_REQUIRED so that // the kernel does not reference this in the near future. if(idleIrp) { FreeBT_DbgPrint(3, ("completion routine has a valid irp and frees it\n")); IoFreeIrp(Irp); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
}
else { // The CancelSelectiveSuspend routine has grabbed the Irp from the device // extension. Now the last one to decrement the FreeIdleIrpCount should // free the irp. if (0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) { FreeBT_DbgPrint(3, ("completion routine frees the irp\n")); IoFreeIrp(Irp); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
}
}
if(DeviceExtension->SSEnable) { FreeBT_DbgPrint(3, ("Set the timer to fire DPCs\n")); dueTime.QuadPart = -10000 * IDLE_INTERVAL; // 5000 ms KeSetTimerEx(&DeviceExtension->Timer, dueTime, IDLE_INTERVAL, &DeviceExtension->DeferredProcCall); FreeBT_DbgPrint(3, ("IdleNotificationRequestCompete: Leaving\n"));
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID NTAPI CancelSelectSuspend(IN PDEVICE_EXTENSION DeviceExtension){ PIRP irp; KIRQL oldIrql;
FreeBT_DbgPrint(3, ("CancelSelectSuspend: Entered\n"));
irp = NULL;
KeAcquireSpinLock(&DeviceExtension->IdleReqStateLock, &oldIrql);
if(!CanDeviceSuspend(DeviceExtension)) { FreeBT_DbgPrint(3, ("Device is not idle\n")); irp = (PIRP) InterlockedExchangePointer((PVOID*)&DeviceExtension->PendingIdleIrp, NULL);
}
KeReleaseSpinLock(&DeviceExtension->IdleReqStateLock, oldIrql);
// since we have a valid Irp ptr, // we can call IoCancelIrp on it, // without the fear of the irp // being freed underneath us. if(irp) { // This routine has the irp pointer. // It is safe to call IoCancelIrp because we know that // the compleiton routine will not free this irp unless... // // if(IoCancelIrp(irp)) { FreeBT_DbgPrint(3, ("IoCancelIrp returns TRUE\n"));
}
else { FreeBT_DbgPrint(3, ("IoCancelIrp returns FALSE\n"));
}
// ....we decrement the FreeIdleIrpCount from 2 to 1. // if completion routine runs ahead of us, then this routine // decrements the FreeIdleIrpCount from 1 to 0 and hence shall // free the irp. if(0 == InterlockedDecrement(&DeviceExtension->FreeIdleIrpCount)) { FreeBT_DbgPrint(3, ("CancelSelectSuspend frees the irp\n")); IoFreeIrp(irp); KeSetEvent(&DeviceExtension->NoIdleReqPendEvent, IO_NO_INCREMENT, FALSE);
}
}
FreeBT_DbgPrint(3, ("CancelSelectSuspend: Leaving\n"));
return;
}
VOID NTAPI PoIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus){ PIRP_COMPLETION_CONTEXT irpContext; irpContext = NULL;
FreeBT_DbgPrint(3, ("PoIrpCompletionFunc::"));
if(Context) irpContext = (PIRP_COMPLETION_CONTEXT) Context;
// all we do is set the event and decrement the count if(irpContext) { KeSetEvent(irpContext->Event, 0, FALSE); FreeBT_IoDecrement(irpContext->DeviceExtension); ExFreePool(irpContext);
}
return;
}
VOID NTAPI PoIrpAsyncCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus){ PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Context; FreeBT_DbgPrint(3, ("PoIrpAsyncCompletionFunc::")); FreeBT_IoDecrement(DeviceExtension);
return;
}
VOID NTAPI WWIrpCompletionFunc(IN PDEVICE_OBJECT DeviceObject, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus){ PDEVICE_EXTENSION DeviceExtension = (PDEVICE_EXTENSION) Context;
FreeBT_DbgPrint(3, ("WWIrpCompletionFunc::")); FreeBT_IoDecrement(DeviceExtension);
return;
}