1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxUsbInterfaceUm.cpp
8 
9 Abstract:
10 
11 Author:
12 
13 Environment:
14 
15     user mode only
16 
17 Revision History:
18 
19 --*/
20 
21 #include "fxusbpch.hpp"
22 
23 extern "C" {
24 #include "FxUsbInterfaceUm.tmh"
25 }
26 
27 NTSTATUS
28 FxUsbInterface::SetWinUsbHandle(
29     _In_ UCHAR FrameworkInterfaceIndex
30 )
31 {
32     NTSTATUS status = STATUS_SUCCESS;
33     UMURB urb;
34 
35     if (m_InterfaceNumber != FrameworkInterfaceIndex) {
36         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGIOTARGET,
37                             "Composite device detected: Converting absolute interface "
38                             "index %d to relative interface index %d", m_InterfaceNumber,
39                             FrameworkInterfaceIndex);
40     }
41 
42     if (FrameworkInterfaceIndex == 0) {
43         m_WinUsbHandle = m_UsbDevice->m_WinUsbHandle;
44     }
45     else {
46         RtlZeroMemory(&urb, sizeof(UMURB));
47 
48         urb.UmUrbGetAssociatedInterface.Hdr.InterfaceHandle = m_UsbDevice->m_WinUsbHandle;
49         urb.UmUrbGetAssociatedInterface.Hdr.Function = UMURB_FUNCTION_GET_ASSOCIATED_INTERFACE;
50         urb.UmUrbGetAssociatedInterface.Hdr.Length = sizeof(_UMURB_GET_ASSOCIATED_INTERFACE);
51 
52         //
53         // If this is using the WinUSB dispatcher, this will ultimately call
54         // WinUsb_GetAssociatedInterface which expects a 0-based index but starts
55         // counting from index 1. To get the handle for interface n, we pass n-1
56         // and WinUSB will return the handle for (n-1)+1.
57         //
58         // The NativeUSB dispatcher ultimately calls WdfUsbTargetDeviceGetInterface.
59         // Unlike WinUSB.sys, this starts counting from index zero. The NativeUSB
60         // dispatcher expects this framework quirk and adjusts accordingly. See
61         // WudfNativeUsbDispatcher.cpp for more information.
62         //
63         // The actual interface number may differ from the interface index in
64         // composite devices. In all cases, the interface index (starting from zero)
65         // must be used.
66         //
67         urb.UmUrbGetAssociatedInterface.InterfaceIndex = FrameworkInterfaceIndex - 1;
68 
69         status = m_UsbDevice->SendSyncUmUrb(&urb, 5);
70 
71         if (NT_SUCCESS(status)) {
72             m_WinUsbHandle = urb.UmUrbGetAssociatedInterface.InterfaceHandle;
73         }
74         else {
75             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
76                                 "Failed to retrieve WinUsb interface handle");
77             m_WinUsbHandle = NULL;
78         }
79     }
80 
81     return status;
82 }
83 
84 NTSTATUS
85 FxUsbInterface::MakeAndConfigurePipes(
86     __in PWDF_OBJECT_ATTRIBUTES PipesAttributes,
87     __in UCHAR NumPipes
88     )
89 {
90     NTSTATUS status = STATUS_SUCCESS;
91     UCHAR iPipe;
92     FxUsbPipe* pPipe;
93     FxUsbPipe** ppPipes;
94     //
95     // Zero pipes are a valid configuration, this simplifies the code below.
96     //
97     ULONG size = (NumPipes == 0 ? 1 : NumPipes) * sizeof(FxUsbPipe*);
98     UMURB urb;
99 
100     ppPipes = (FxUsbPipe**)FxPoolAllocate(GetDriverGlobals(), NonPagedPool, size);
101     if (ppPipes == NULL) {
102         status = STATUS_INSUFFICIENT_RESOURCES;
103         DoTraceLevelMessage(
104             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
105             "Unable to allocate memory %!STATUS!", status);
106         goto Done;
107     }
108 
109     RtlZeroMemory(ppPipes, size);
110 
111     for (iPipe = 0; iPipe < NumPipes; iPipe++) {
112         ppPipes[iPipe] = new (GetDriverGlobals(), PipesAttributes)
113             FxUsbPipe(GetDriverGlobals(), m_UsbDevice);
114 
115         if (ppPipes[iPipe] == NULL) {
116             status = STATUS_INSUFFICIENT_RESOURCES;
117             DoTraceLevelMessage(
118                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
119                 "Unable to allocate memory for the pipes %!STATUS!", status);
120             goto Done;
121         }
122 
123         pPipe = ppPipes[iPipe];
124 
125         status = pPipe->Init(m_UsbDevice->m_Device);
126         if (!NT_SUCCESS(status)) {
127             DoTraceLevelMessage(
128                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
129                 "Init pipe failed  %!STATUS!", status);
130             goto Done;
131         }
132 
133         status = pPipe->Commit(PipesAttributes, NULL, this);
134         if (!NT_SUCCESS(status)) {
135             DoTraceLevelMessage(
136                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
137                 "Commit pipe failed  %!STATUS!", status);
138             goto Done;
139         }
140     }
141 
142     if (IsInterfaceConfigured()) {
143         //
144         // Delete the old pipes
145         //
146         m_UsbDevice->CleanupInterfacePipesAndDelete(this);
147     }
148 
149     SetNumConfiguredPipes(NumPipes);
150     SetConfiguredPipes(ppPipes);
151 
152     for (iPipe = 0; iPipe < m_NumberOfConfiguredPipes ; iPipe++) {
153         RtlZeroMemory(&urb, sizeof(UMURB));
154 
155         urb.UmUrbQueryPipe.Hdr.InterfaceHandle = m_WinUsbHandle;
156         urb.UmUrbQueryPipe.Hdr.Function = UMURB_FUNCTION_QUERY_PIPE;
157         urb.UmUrbQueryPipe.Hdr.Length = sizeof(_UMURB_QUERY_PIPE);
158 
159         urb.UmUrbQueryPipe.AlternateSetting = m_CurAlternateSetting;
160         urb.UmUrbQueryPipe.PipeID = iPipe;
161 
162         status = m_UsbDevice->SendSyncUmUrb(&urb, 2);
163 
164         if (!NT_SUCCESS(status)) {
165             DoTraceLevelMessage(
166                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
167                 "Send UMURB_FUNCTION_QUERY_PIPE failed  %!STATUS!", status);
168             goto Done;
169         }
170 
171         m_ConfiguredPipes[iPipe]->InitPipe(&urb.UmUrbQueryPipe.PipeInformation,
172                                            m_InterfaceNumber,
173                                            this);
174     }
175 
176 Done:
177     if (!NT_SUCCESS(status)) {
178         if (ppPipes != NULL) {
179             ASSERT(ppPipes != m_ConfiguredPipes);
180 
181             for (iPipe = 0; iPipe < NumPipes; iPipe++) {
182                 if (ppPipes[iPipe] != NULL) {
183                     ppPipes[iPipe]->DeleteFromFailedCreate();
184                 }
185             }
186 
187             FxPoolFree(ppPipes);
188             ppPipes = NULL;
189         }
190     }
191 
192     return status;
193 }
194 
195 NTSTATUS
196 FxUsbInterface::UpdatePipeAttributes(
197     __in PWDF_OBJECT_ATTRIBUTES PipesAttributes
198     )
199 {
200     NTSTATUS status = STATUS_SUCCESS;
201     UCHAR iPipe;
202     FxUsbPipe** ppPipes;
203     UCHAR numberOfPipes;
204 
205     numberOfPipes = m_NumberOfConfiguredPipes;
206     ppPipes = m_ConfiguredPipes;
207 
208     for (iPipe = 0; iPipe < numberOfPipes; iPipe++) {
209         status = FxObjectAllocateContext(ppPipes[iPipe],
210                                          PipesAttributes,
211                                          TRUE,
212                                          NULL);
213         if (!NT_SUCCESS(status)) {
214             DoTraceLevelMessage(
215                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGIOTARGET,
216                 "UpdatePipeAttributes failed  %!STATUS!", status);
217             break;
218         }
219     }
220 
221     //
222     // Pipe attributes are updated as part of select
223     // config and it is ok for the client driver to configure
224     // twice with the same attributes. In a similar scenario,
225     // KMDF will return STATUS_SUCCESS, so we should do the
226     // same for UMDF for consistency
227     //
228     if (status == STATUS_OBJECT_NAME_EXISTS) {
229         status = STATUS_SUCCESS;
230     }
231 
232     return status;
233 }
234 
235