1 //
2 //    Copyright (C) Microsoft.  All rights reserved.
3 //
4 #include "fxusbpch.hpp"
5 
6 extern "C" {
7 #include "FxUsbPipeUm.tmh"
8 }
9 
10 #include "Fxglobals.h"
11 
12 VOID
13 FxUsbPipeRequestContext::SetInfo(
14     __in WDF_USB_REQUEST_TYPE Type,
15     __in WINUSB_INTERFACE_HANDLE WinUsbHandle,
16     __in UCHAR PipeId,
17     __in USHORT Function
18     )
19 {
20     RtlZeroMemory(&m_UmUrb, sizeof(m_UmUrb));
21 
22     m_UmUrb.UmUrbPipeRequest.Hdr.InterfaceHandle = WinUsbHandle;
23     m_UmUrb.UmUrbPipeRequest.Hdr.Function = Function;
24     m_UmUrb.UmUrbPipeRequest.Hdr.Length = sizeof(m_UmUrb.UmUrbPipeRequest);
25 
26     m_UmUrb.UmUrbPipeRequest.PipeID = PipeId;
27 
28     SetUsbType(Type);
29 }
30 
31 VOID
32 FxUsbPipeTransferContext::StoreAndReferenceMemory(
33     __in FxRequestBuffer* Buffer
34     )
35 /*++
36 
37 Routine Description:
38     virtual function which stores and references memory if it is an FxObject
39     and then fills in the appropriate fields in the URB.
40 
41 Arguments:
42     Buffer - union which can be many types of memory
43 
44 Return Value:
45     None
46 
47   --*/
48 {
49     RtlZeroMemory(&m_UmUrb, sizeof(m_UmUrb));
50 
51     m_UmUrb.UmUrbHeader.Function = UMURB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER;
52     m_UmUrb.UmUrbHeader.Length = sizeof(_UMURB_BULK_OR_INTERRUPT_TRANSFER);
53 
54     FxUsbRequestContext::StoreAndReferenceMemory(Buffer); // __super call
55 
56     Buffer->AssignValues(&m_UmUrb.UmUrbBulkOrInterruptTransfer.TransferBuffer,
57                          NULL,
58                          &m_UmUrb.UmUrbBulkOrInterruptTransfer.TransferBufferLength);
59 }
60 
61 VOID
62 FxUsbPipeContinuousReader::_ReadWorkItem(
63     __in MdDeviceObject /*DeviceObject*/,
64     __in_opt PVOID Context
65     )
66 {
67     FxUsbPipeRepeatReader * pRepeater;
68     pRepeater = (FxUsbPipeRepeatReader *)Context;
69 
70     pRepeater->RequestIrp->Forward();
71 }
72 
73 _Must_inspect_result_
74 NTSTATUS
75 FxUsbPipeContinuousReader::Config(
76     __in PWDF_USB_CONTINUOUS_READER_CONFIG Config,
77     __in size_t TotalBufferLength
78     )
79 {
80     PFX_DRIVER_GLOBALS pFxDriverGlobals;
81     WDF_OBJECT_ATTRIBUTES attributes;
82     NTSTATUS status;
83     LONG i;
84 
85     pFxDriverGlobals = m_Pipe->GetDriverGlobals();
86 
87 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
88     if (TotalBufferLength <= MAXUSHORT) {
89         m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
90             FxNPagedLookasideList(pFxDriverGlobals, pFxDriverGlobals->Tag);
91     }
92     else {
93         m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
94             FxNPagedLookasideListFromPool(pFxDriverGlobals, pFxDriverGlobals->Tag);
95     }
96 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
97     m_Lookaside = new(pFxDriverGlobals, WDF_NO_OBJECT_ATTRIBUTES)
98             FxNPagedLookasideList(pFxDriverGlobals, pFxDriverGlobals->Tag);
99 #endif
100 
101     if (m_Lookaside == NULL) {
102         return STATUS_INSUFFICIENT_RESOURCES;
103     }
104 
105     if (Config->BufferAttributes == NULL) {
106         WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
107     }
108     else {
109         RtlCopyMemory(&attributes,
110                       Config->BufferAttributes,
111                       sizeof(WDF_OBJECT_ATTRIBUTES));
112     }
113 
114     //
115     // By specifying the loookaside as the parent for the memory objects that
116     // will be created, when we destroy the lookaside list, we will destroy any
117     // outstanding memory objects that have been allocated.  This can happen if
118     // we initialize the repeater, but never send any i/o.  (Normally the
119     // memory object would be freed when the read completes.)
120     //
121     attributes.ParentObject = m_Lookaside->GetObjectHandle();
122 
123     status = m_Lookaside->Initialize(TotalBufferLength, &attributes);
124     if (!NT_SUCCESS(status)) {
125         return status;
126     }
127 
128     status = FxSystemWorkItem::_Create(pFxDriverGlobals,
129                                       m_Pipe->m_Device->GetDeviceObject(),
130                                       &m_WorkItem
131                                       );
132     if (!NT_SUCCESS(status)) {
133         DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGIO,
134                             "Could not allocate workitem: %!STATUS!", status);
135         return status;
136     }
137 
138     m_Offsets.BufferLength = Config->TransferLength;
139     m_Offsets.BufferOffset = Config->HeaderLength;
140 
141     for (i = 0; i < m_NumReaders; i++) {
142         FxUsbPipeRepeatReader* pRepeater;
143 
144         pRepeater = &m_Readers[i];
145 
146         pRepeater->Parent = this;
147 
148 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
149         KeInitializeDpc(&pRepeater->Dpc, _FxUsbPipeContinuousReadDpc, NULL);
150 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
151         pRepeater->m_ReadWorkItem.Allocate(m_Pipe->m_Device->GetDeviceObject());
152 #endif
153 
154         //
155         // This will allocate the PIRP
156         //
157         status = FxRequest::_Create(pFxDriverGlobals,
158                                     WDF_NO_OBJECT_ATTRIBUTES,
159                                     NULL,
160                                     m_Pipe,
161                                     FxRequestOwnsIrp,
162                                     FxRequestConstructorCallerIsFx,
163                                     &pRepeater->Request);
164 
165         if (!NT_SUCCESS(status)) {
166             return status;
167         }
168 
169         pRepeater->RequestIrp = pRepeater->Request->GetSubmitIrp();
170 
171         //
172         // Initialize the event before FormatRepeater clears it
173         //
174         status = pRepeater->ReadCompletedEvent.Initialize(NotificationEvent, TRUE);
175 
176         if (!NT_SUCCESS(status)) {
177             DoTraceLevelMessage(
178                 pFxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
179                 "Could not initialize ReadCompletedEvent: %!STATUS!",
180                 status);
181 
182             return status;
183         }
184 
185         //
186         // This will allocate the context
187         //
188         status = FormatRepeater(pRepeater);
189 
190         if (!NT_SUCCESS(status)) {
191             return status;
192         }
193     }
194 
195     return STATUS_SUCCESS;
196 }
197 
198 VOID
199 FxUsbPipe::InitPipe(
200     __in PWINUSB_PIPE_INFORMATION PipeInfo,
201     __in UCHAR InterfaceNumber,
202     __in FxUsbInterface* UsbInterface
203     )
204 {
205     RtlCopyMemory(&m_PipeInformationUm, PipeInfo, sizeof(m_PipeInformationUm));
206     m_InterfaceNumber = InterfaceNumber;
207 
208     if (m_UsbInterface != NULL) {
209         m_UsbInterface->RELEASE(this);
210         m_UsbInterface = NULL;
211     }
212 
213     m_UsbInterface = UsbInterface;
214     m_UsbInterface->ADDREF(this);
215 }
216 
217 _Must_inspect_result_
218 NTSTATUS
219 FxUsbPipe::FormatTransferRequest(
220     __in FxRequestBase* Request,
221     __in FxRequestBuffer* Buffer,
222     __in ULONG TransferFlags
223     )
224 {
225     FxUsbPipeTransferContext* pContext;
226     NTSTATUS status;
227     size_t bufferSize;
228     ULONG dummyLength;
229 
230     //
231     // Make sure request is for the right type
232     //
233     if (!(IsType(WdfUsbPipeTypeBulk) || IsType(WdfUsbPipeTypeInterrupt))) {
234         status = STATUS_INVALID_DEVICE_REQUEST;
235 
236         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
237                             "WDFUSBPIPE %p not the right type, %!STATUS!",
238                             GetHandle(), status);
239 
240         return status;
241     }
242 
243     bufferSize = Buffer->GetBufferLength();
244 
245     status = RtlSizeTToULong(bufferSize, &dummyLength);
246     if (!NT_SUCCESS(status)) {
247         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
248                             "WDFUSBPIPE %p, buffer size truncated, %!STATUS!",
249                             GetHandle(), status);
250         return status;
251     }
252 
253     //
254     // On reads, check to make sure the read in value is an integral number of
255     // packet sizes
256     //
257     if (TransferFlags & USBD_TRANSFER_DIRECTION_IN) {
258         if (IsInEndpoint() == FALSE) {
259             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
260                                 "Pipe %p, sending __in transaction on a __out endpoint",
261                                 this);
262 
263             return STATUS_INVALID_DEVICE_REQUEST;
264         }
265 
266         if (m_CheckPacketSize &&
267             (bufferSize % m_PipeInformationUm.MaximumPacketSize) != 0) {
268             return STATUS_INVALID_BUFFER_SIZE;
269         }
270     }
271     else {
272         if (IsOutEndpoint() == FALSE) {
273             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
274                                 "Pipe %p, sending __out transaction on an __in endpoint",
275                                 this);
276 
277             return STATUS_INVALID_DEVICE_REQUEST;
278         }
279     }
280 
281     status = Request->ValidateTarget(this);
282     if (!NT_SUCCESS(status)) {
283         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
284                             "Pipe %p, Request %p, setting target failed, "
285                             "status %!STATUS!", this, Request, status);
286 
287         return status;
288     }
289 
290     if (Request->HasContextType(FX_RCT_USB_PIPE_XFER)) {
291         pContext = (FxUsbPipeTransferContext*) Request->GetContext();
292     }
293     else {
294         pContext = new(GetDriverGlobals()) FxUsbPipeTransferContext(FxUrbTypeLegacy);
295         if (pContext == NULL) {
296             return STATUS_INSUFFICIENT_RESOURCES;
297         }
298 
299         Request->SetContext(pContext);
300     }
301 
302     pContext->StoreAndReferenceMemory(Buffer);
303 
304     pContext->m_UmUrb.UmUrbHeader.InterfaceHandle = m_UsbInterface->m_WinUsbHandle;
305 
306     pContext->m_UmUrb.UmUrbBulkOrInterruptTransfer.PipeID = m_PipeInformationUm.PipeId;
307     pContext->m_UmUrb.UmUrbBulkOrInterruptTransfer.InPipe = IsInEndpoint();
308 
309     FxUsbUmFormatRequest(Request, &pContext->m_UmUrb.UmUrbHeader, m_UsbDevice->m_pHostTargetFile);
310 
311     return STATUS_SUCCESS;
312 }
313 
314 VOID
315 FxUsbPipe::GetInformation(
316     __out PWDF_USB_PIPE_INFORMATION PipeInformation
317     )
318 {
319 
320 
321 
322 
323     PipeInformation->MaximumPacketSize = m_PipeInformationUm.MaximumPacketSize;
324     PipeInformation->EndpointAddress = m_PipeInformationUm.PipeId;
325     PipeInformation->Interval = m_PipeInformationUm.Interval;
326     PipeInformation->PipeType = _UsbdPipeTypeToWdf(m_PipeInformationUm.PipeType);
327     PipeInformation->SettingIndex = m_UsbInterface->GetConfiguredSettingIndex();
328 }
329 
330 WDF_USB_PIPE_TYPE
331 FxUsbPipe::GetType(
332     VOID
333     )
334 {
335     return _UsbdPipeTypeToWdf(m_PipeInformationUm.PipeType);
336 }
337 
338 BOOLEAN
339 FxUsbPipe::IsType(
340     __in WDF_USB_PIPE_TYPE Type
341     )
342 {
343     return _UsbdPipeTypeToWdf(m_PipeInformationUm.PipeType) == Type ? TRUE : FALSE;
344 }
345 
346