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