1 // 2 // Copyright (C) Microsoft. All rights reserved. 3 // 4 #include "fxusbpch.hpp" 5 6 extern "C" { 7 #include "FxUsbPipeKm.tmh" 8 } 9 10 #include "Fxglobals.h" 11 12 VOID 13 FxUsbPipeTransferContext::StoreAndReferenceMemory( 14 __in FxRequestBuffer* Buffer 15 ) 16 /*++ 17 18 Routine Description: 19 virtual function which stores and references memory if it is an FxObject 20 and then fills in the appropriate fields in the URB. 21 22 Arguments: 23 Buffer - union which can be many types of memory 24 25 Return Value: 26 None 27 28 --*/ 29 { 30 RtlZeroMemory(m_Urb, sizeof(*m_Urb)); 31 32 m_Urb->Hdr.Function = URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER; 33 m_Urb->Hdr.Length = sizeof(*m_Urb); 34 35 FxUsbRequestContext::StoreAndReferenceMemory(Buffer); // __super call 36 37 Buffer->AssignValues(&m_Urb->TransferBuffer, 38 &m_Urb->TransferBufferMDL, 39 &m_Urb->TransferBufferLength); 40 41 // 42 // If we have built a partial MDL, use that instead. TransferBufferLength 43 // is still valid because the Offsets or length in Buffer will have been 44 // used to create this PartialMdl by the caller. 45 // 46 if (m_PartialMdl != NULL) { 47 m_Urb->TransferBufferMDL = m_PartialMdl; 48 } 49 } 50 51 __drv_functionClass(KDEFERRED_ROUTINE) 52 __drv_maxIRQL(DISPATCH_LEVEL) 53 __drv_minIRQL(DISPATCH_LEVEL) 54 __drv_requiresIRQL(DISPATCH_LEVEL) 55 __drv_sameIRQL 56 VOID 57 FxUsbPipeContinuousReader::_FxUsbPipeContinuousReadDpc( 58 __in struct _KDPC *Dpc, 59 __in_opt PVOID DeferredContext, 60 __in_opt PVOID SystemArgument1, 61 __in_opt PVOID SystemArgument2 62 ) 63 { 64 FxUsbPipeRepeatReader* pRepeater; 65 FxUsbPipe* pPipe; 66 67 UNREFERENCED_PARAMETER(DeferredContext); 68 UNREFERENCED_PARAMETER(SystemArgument1); 69 UNREFERENCED_PARAMETER(SystemArgument2); 70 71 #pragma prefast(push); 72 73 74 pRepeater = CONTAINING_RECORD(Dpc, FxUsbPipeRepeatReader, Dpc); 75 pPipe = pRepeater->Parent->m_Pipe; 76 77 // 78 // Ignore the return value because once we have sent the request, we 79 // want all processing to be done in the completion routine. 80 // 81 (void) IoCallDriver(pPipe->m_TargetDevice, 82 pRepeater->Request->GetSubmitIrp()); 83 #pragma prefast(pop); 84 } 85 86 _Must_inspect_result_ 87 NTSTATUS 88 FxUsbPipeContinuousReader::Config( 89 __in PWDF_USB_CONTINUOUS_READER_CONFIG Config, 90 __in size_t TotalBufferLength 91 ) 92 { 93 PFX_DRIVER_GLOBALS pFxDriverGlobals; 94 WDF_OBJECT_ATTRIBUTES attributes; 95 NTSTATUS status; 96 LONG i; 97 98 pFxDriverGlobals = m_Pipe->GetDriverGlobals(); 99 100 if (TotalBufferLength <= MAXUSHORT) { 101 m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES) 102 FxNPagedLookasideList(pFxDriverGlobals, pFxDriverGlobals->Tag); 103 } 104 else { 105 m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES) 106 FxNPagedLookasideListFromPool(pFxDriverGlobals, pFxDriverGlobals->Tag); 107 } 108 109 if (m_Lookaside == NULL) { 110 return STATUS_INSUFFICIENT_RESOURCES; 111 } 112 113 if (Config->BufferAttributes == NULL) { 114 WDF_OBJECT_ATTRIBUTES_INIT(&attributes); 115 } 116 else { 117 RtlCopyMemory(&attributes, 118 Config->BufferAttributes, 119 sizeof(WDF_OBJECT_ATTRIBUTES)); 120 } 121 122 // 123 // By specifying the loookaside as the parent for the memory objects that 124 // will be created, when we destroy the lookaside list, we will destroy any 125 // outstanding memory objects that have been allocated. This can happen if 126 // we initialize the repeater, but never send any i/o. (Normally the 127 // memory object would be freed when the read completes.) 128 // 129 attributes.ParentObject = m_Lookaside->GetObjectHandle(); 130 131 status = m_Lookaside->Initialize(TotalBufferLength, &attributes); 132 if (!NT_SUCCESS(status)) { 133 return status; 134 } 135 136 status = FxSystemWorkItem::_Create(pFxDriverGlobals, 137 m_Pipe->m_Device->GetDeviceObject(), 138 &m_WorkItem 139 ); 140 if (!NT_SUCCESS(status)) { 141 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO, 142 "Could not allocate workitem: %!STATUS!", status); 143 return status; 144 } 145 146 m_Offsets.BufferLength = Config->TransferLength; 147 m_Offsets.BufferOffset = Config->HeaderLength; 148 149 for (i = 0; i < m_NumReaders; i++) { 150 FxUsbPipeRepeatReader* pRepeater; 151 152 pRepeater = &m_Readers[i]; 153 154 pRepeater->Parent = this; 155 156 KeInitializeDpc(&pRepeater->Dpc, _FxUsbPipeContinuousReadDpc, NULL); 157 158 // 159 // This will allocate the PIRP 160 // 161 status = FxRequest::_Create(pFxDriverGlobals, 162 WDF_NO_OBJECT_ATTRIBUTES, 163 NULL, 164 m_Pipe, 165 FxRequestOwnsIrp, 166 FxRequestConstructorCallerIsFx, 167 &pRepeater->Request); 168 169 if (!NT_SUCCESS(status)) { 170 return status; 171 } 172 173 pRepeater->RequestIrp = pRepeater->Request->GetSubmitIrp(); 174 175 // 176 // Initialize the event before FormatRepeater clears it 177 // 178 status = pRepeater->ReadCompletedEvent.Initialize(NotificationEvent, TRUE); 179 180 if (!NT_SUCCESS(status)) { 181 DoTraceLevelMessage( 182 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET, 183 "Could not initialize ReadCompletedEvent: %!STATUS!", 184 status); 185 186 return status; 187 } 188 189 // 190 // This will allocate the context 191 // 192 status = FormatRepeater(pRepeater); 193 194 if (!NT_SUCCESS(status)) { 195 return status; 196 } 197 } 198 199 return STATUS_SUCCESS; 200 } 201 202 _Must_inspect_result_ 203 NTSTATUS 204 FxUsbPipe::FormatTransferRequest( 205 __in FxRequestBase* Request, 206 __in FxRequestBuffer* Buffer, 207 __in ULONG TransferFlags 208 ) 209 { 210 FxUsbPipeTransferContext* pContext; 211 NTSTATUS status; 212 size_t bufferSize; 213 ULONG dummyLength; 214 FX_URB_TYPE urbType; 215 216 // 217 // Make sure request is for the right type 218 // 219 if (!(IsType(WdfUsbPipeTypeBulk) || IsType(WdfUsbPipeTypeInterrupt))) { 220 status = STATUS_INVALID_DEVICE_REQUEST; 221 222 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 223 "WDFUSBPIPE %p not the right type, %!STATUS!", 224 GetHandle(), status); 225 226 return status; 227 } 228 229 bufferSize = Buffer->GetBufferLength(); 230 231 status = RtlSizeTToULong(bufferSize, &dummyLength); 232 if (!NT_SUCCESS(status)) { 233 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 234 "WDFUSBPIPE %p, buffer size truncated, %!STATUS!", 235 GetHandle(), status); 236 return status; 237 } 238 239 // 240 // On reads, check to make sure the read in value is an integral number of 241 // packet sizes 242 // 243 if (TransferFlags & USBD_TRANSFER_DIRECTION_IN) { 244 if (IsInEndpoint() == FALSE) { 245 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 246 "Pipe %p, sending __in transaction on a __out endpoint", 247 this); 248 249 return STATUS_INVALID_DEVICE_REQUEST; 250 } 251 252 if (m_CheckPacketSize && 253 (bufferSize % m_PipeInformation.MaximumPacketSize) != 0) { 254 return STATUS_INVALID_BUFFER_SIZE; 255 } 256 } 257 else { 258 if (IsOutEndpoint() == FALSE) { 259 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 260 "Pipe %p, sending __out transaction on an __in endpoint", 261 this); 262 263 return STATUS_INVALID_DEVICE_REQUEST; 264 } 265 } 266 267 status = Request->ValidateTarget(this); 268 if (!NT_SUCCESS(status)) { 269 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET, 270 "Pipe %p, Request %p, setting target failed, " 271 "status %!STATUS!", this, Request, status); 272 273 return status; 274 } 275 276 if (Request->HasContextType(FX_RCT_USB_PIPE_XFER)) { 277 pContext = (FxUsbPipeTransferContext*) Request->GetContext(); 278 } 279 else { 280 urbType = m_UsbDevice->GetFxUrbTypeForRequest(Request); 281 282 pContext = new(GetDriverGlobals()) FxUsbPipeTransferContext(urbType); 283 if (pContext == NULL) { 284 return STATUS_INSUFFICIENT_RESOURCES; 285 } 286 287 if (urbType == FxUrbTypeUsbdAllocated) { 288 status = pContext->AllocateUrb(m_USBDHandle); 289 if (!NT_SUCCESS(status)) { 290 delete pContext; 291 return status; 292 } 293 // 294 // Since the AllocateUrb routine calls USBD_xxxUrbAllocate APIs to allocate an Urb, it is 295 // important to release those resorces before the devnode is removed. Those 296 // resoruces are removed at the time Request is disposed. 297 // 298 Request->EnableContextDisposeNotification(); 299 } 300 301 Request->SetContext(pContext); 302 } 303 304 // 305 // Always set the memory after determining the context. This way we can 306 // free a previously referenced memory object if necessary. 307 // 308 if (Buffer->HasMdl()) { 309 PMDL pMdl; 310 311 pMdl=NULL; 312 ASSERT(pContext->m_PartialMdl == NULL); 313 314 // 315 // If it is an __in endpoint, the buffer will be written to by the 316 // controller, so request IoWriteAccess locking. 317 // 318 status = Buffer->GetOrAllocateMdl( 319 GetDriverGlobals(), 320 &pMdl, 321 &pContext->m_PartialMdl, 322 &pContext->m_UnlockPages, 323 IsInEndpoint() ? IoWriteAccess : IoReadAccess); 324 325 if (!NT_SUCCESS(status)) { 326 return status; 327 } 328 329 ASSERT(pMdl != NULL); 330 } 331 332 pContext->StoreAndReferenceMemory(Buffer); 333 334 pContext->SetUrbInfo(m_PipeInformation.PipeHandle, TransferFlags); 335 336 if (pContext->m_Urb == &pContext->m_UrbLegacy) { 337 urbType = FxUrbTypeLegacy; 338 } 339 else { 340 urbType = FxUrbTypeUsbdAllocated; 341 } 342 343 FxFormatUsbRequest(Request, (PURB)pContext->m_Urb, urbType, m_USBDHandle); 344 345 return STATUS_SUCCESS; 346 } 347 348 VOID 349 FxUsbPipe::GetInformation( 350 __out PWDF_USB_PIPE_INFORMATION PipeInformation 351 ) 352 { 353 // 354 // Do a field by field copy for the WDF structure, since fields could change. 355 // 356 PipeInformation->MaximumPacketSize = m_PipeInformation.MaximumPacketSize; 357 PipeInformation->EndpointAddress = m_PipeInformation.EndpointAddress; 358 PipeInformation->Interval = m_PipeInformation.Interval; 359 PipeInformation->PipeType = _UsbdPipeTypeToWdf(m_PipeInformation.PipeType); 360 PipeInformation->MaximumTransferSize = m_PipeInformation.MaximumTransferSize; 361 PipeInformation->SettingIndex = m_UsbInterface->GetConfiguredSettingIndex(); 362 } 363 364 WDF_USB_PIPE_TYPE 365 FxUsbPipe::GetType( 366 VOID 367 ) 368 { 369 return _UsbdPipeTypeToWdf(m_PipeInformation.PipeType); 370 } 371 372 BOOLEAN 373 FxUsbPipe::IsType( 374 __in WDF_USB_PIPE_TYPE Type 375 ) 376 { 377 return _UsbdPipeTypeToWdf(m_PipeInformation.PipeType) == Type ? TRUE : FALSE; 378 } 379 380