ReactOS 0.4.17-dev-1281-g065abb3
portconn.cpp
Go to the documentation of this file.
1/*
2 * PROJECT: ReactOS SuperSpeed USB Hub Driver
3 * LICENSE: MIT (https://spdx.org/licenses/MIT)
4 * PURPOSE: Driver wide map of physical connectors to their USB 2 and USB 3 ports
5 * COPYRIGHT: Copyright 2026 Justin Miller <justin.miller@reactos.org>
6 */
7
8#include "usbhub3.h"
9#include "hubid.h"
10
11#define NDEBUG
12#include <debug.h>
13
14#define HUB_PROTOCOL_USB20 0x200
15#define HUB_PROTOCOL_USB30 0x300
16
22{
28};
29
31VOID
33HubConnectorLock(VOID)
34{
36}
37
39VOID
41HubConnectorUnlock(VOID)
42{
43 WdfWaitLockRelease(HubDriver.CompanionPortLock);
44}
45
46/* Field by field, so padding never takes part */
47static
53{
55
56 if (First->Type != Second->Type ||
57 First->Depth != Second->Depth ||
58 First->TypeCConnectorId != Second->TypeCConnectorId ||
59 !RtlEqualMemory(First->Location, Second->Location, sizeof(First->Location)))
60 {
61 return FALSE;
62 }
63
65 {
66 if (First->Path[Index] != Second->Path[Index])
67 return FALSE;
68 }
69
70 return TRUE;
71}
72
73/* Caller holds the map lock */
74static
78 _In_ const HubConnectorId* Id)
79{
82
84 {
86 if (HubConnectorIdsEqual(&Node->Id, Id))
87 return Node;
88 }
89
90 return NULL;
91}
92
93/* Caller holds the map lock. Matching on the slots keeps a stale key from hiding a port */
94static
99{
102
103 if (!Port->HasProperty(PortProperty::InConnectorMap))
104 return NULL;
105
107 {
109 if (Node->Usb20 == Port || Node->Usb30 == Port || Node->Usb30Second == Port)
110 return Node;
111 }
112
113 return NULL;
114}
115
116/* REGISTRATION **************************************************************/
117
118/* A Type-C connector without a mux names a serial less device by a port number */
119static
121NTAPI
125{
127
128 if (Node->Usb20 != NULL)
129 {
130 DPRINT1("Port %u: connector already has a USB 2 port (platform compliance)\n", Port->Number());
131 return STATUS_UNSUCCESSFUL;
132 }
133
134 if (TypeC && Node->Usb30 != NULL && !Node->Usb30->HasProperty(PortProperty::TypeCWithoutSwitch))
135 {
136 DPRINT1("Port %u: Type-C USB 2 port paired with a different USB 3 connector type (platform compliance)\n",
137 Port->Number());
138 return STATUS_UNSUCCESSFUL;
139 }
140
141 if (TypeC)
142 Port->m_InstancePortNumber = Port->Number();
143
144 Node->Usb20 = Port;
145 return STATUS_SUCCESS;
146}
147
148/* Both orientations of a Type-C connector share the lower port number */
149static
151NTAPI
155{
157 USHORT Shared;
158
159 if (Node->Usb30 == NULL)
160 {
161 if (TypeC && Node->Usb20 != NULL && !Node->Usb20->HasProperty(PortProperty::TypeCWithoutSwitch))
162 {
163 DPRINT1("Port %u: Type-C USB 3 port paired with a different USB 2 connector type (platform compliance)\n",
164 Port->Number());
165 return STATUS_UNSUCCESSFUL;
166 }
167
168 if (TypeC)
169 Port->m_InstancePortNumber = Port->Number();
170
171 Node->Usb30 = Port;
172 return STATUS_SUCCESS;
173 }
174
175 if (!TypeC ||
176 Node->Usb30Second != NULL ||
177 !Node->Usb30->HasProperty(PortProperty::TypeCWithoutSwitch))
178 {
179 DPRINT1("Port %u: connector has no room for another USB 3 port (platform compliance)\n", Port->Number());
180 return STATUS_UNSUCCESSFUL;
181 }
182
183 Shared = min(Port->Number(), Node->Usb30->Number());
184 Port->m_InstancePortNumber = Shared;
185 Node->Usb30->m_InstancePortNumber = Shared;
186 Node->Usb30Second = Port;
187 return STATUS_SUCCESS;
188}
189
190/* A port of unknown protocol succeeds without joining a node; a failed registration may leave an empty node */
193NTAPI
194HubConnectorRegisterPort(
196{
200
201 Node = HubConnectorFindById(&Port->m_ConnectorId);
202 if (Node == NULL)
203 {
205 if (Node == NULL)
206 {
207 DPRINT1("Port %u: connector node could not be allocated\n", Port->Number());
209 }
210
211 Node->Id = Port->m_ConnectorId;
213 }
214
215 switch (Port->m_Info.Protocol)
216 {
219 break;
220
223 break;
224
225 default:
226 DPRINT1("Port %u has unknown protocol 0x%x\n", Port->Number(), Port->m_Info.Protocol);
227 return STATUS_SUCCESS;
228 }
229
230 if (!NT_SUCCESS(Status))
231 return Status;
232
234 return STATUS_SUCCESS;
235}
236
237/* Only the port's own slot is emptied; the node is freed once all three slots are empty */
240NTAPI
241HubConnectorUnregisterPort(
243{
246
247 if (!Port->HasProperty(PortProperty::InConnectorMap))
248 return STATUS_SUCCESS;
249
252
253 if (Node == NULL)
254 {
255 DPRINT1("Port %u: registered port has no connector\n", Port->Number());
256 return STATUS_UNSUCCESSFUL;
257 }
258
259 if (Node->Usb20 == Port)
260 {
261 Node->Usb20 = NULL;
262 }
263 else if (Node->Usb30 == Port)
264 {
265 Node->Usb30 = Node->Usb30Second;
266 Node->Usb30Second = NULL;
267 }
268 else
269 {
270 Node->Usb30Second = NULL;
271 }
272
273 if (Node->Usb20 == NULL && Node->Usb30 == NULL && Node->Usb30Second == NULL)
274 {
275 RemoveEntryList(&Node->Link);
277 }
278
279 return STATUS_SUCCESS;
280}
281
282/* LOOKUPS *******************************************************************/
283
284/* Caller holds the map lock; the port returned is only safe to touch while it does */
285HubPort*
286NTAPI
290{
292
293 if (Index > 1)
294 {
295 DPRINT1("Companion index %lu out of range\n", Index);
296 return NULL;
297 }
298
300 if (Node == NULL)
301 {
302 if (Port->HasProperty(PortProperty::InConnectorMap))
303 DPRINT1("Port %u: registered port has no connector\n", Port->Number());
304
305 return NULL;
306 }
307
308 if (!Port->IsUsb30())
309 return (Index == 0) ? Node->Usb30 : Node->Usb30Second;
310
311 if (Index == 0)
312 return Node->Usb20;
313
314 return (Node->Usb30 == Port) ? Node->Usb30Second : Node->Usb30;
315}
316
317/* Only an identity value: the node may be gone by the time the caller looks */
318PVOID
319NTAPI
322{
324
325 if (!Port->HasProperty(PortProperty::InConnectorMap))
326 return NULL;
327
328 HubConnectorLock();
330 HubConnectorUnlock();
331
332 return Node;
333}
334
335/* MAPPING A HUB *************************************************************/
336
338static
339VOID
340NTAPI
342 _In_ HubFdo* Hub,
344 _In_ ULONG Usb30Port)
345{
346 RtlZeroMemory(&Port->m_ConnectorId, sizeof(Port->m_ConnectorId));
347 Port->m_ConnectorId.Type = HUB_CONNECTOR_ROOT;
348 RtlCopyMemory(Port->m_ConnectorId.Location, &Hub->m_HubNumber, sizeof(Hub->m_HubNumber));
349 Port->m_ConnectorId.Path[0] = Usb30Port;
350}
351
352/* The parent hub port's key; a hub hanging off an unmapped port gets an all zero key */
353static
354VOID
355NTAPI
357 _In_ HubFdo* Hub,
359{
361
362 if (Parent != NULL)
363 *Id = *Parent;
364 else
365 RtlZeroMemory(Id, sizeof(*Id));
366}
367
369static
371NTAPI
372HubConnectorMapAcpiPorts(
373 _In_ HubFdo* Hub)
374{
376 HubPort* Port;
379
381 {
382 Port = CONTAINING_RECORD(Entry, HubPort, m_HubLink);
383 if (!Port->HasProperty(PortProperty::AcpiPldValid) || !Port->HasProperty(PortProperty::Removable))
384 continue;
385
386 Id = &Port->m_ConnectorId;
387 RtlZeroMemory(Id, sizeof(*Id));
388 Id->Type = HUB_CONNECTOR_ACPI;
389 RtlCopyMemory(Id->Location, &Port->m_AcpiPld, sizeof(Id->Location));
390 Id->TypeCConnectorId = ((ULONG64)Port->m_AcpiPld.GroupToken << 8) | Port->m_AcpiPld.GroupPosition;
391
392 Status = HubConnectorRegisterPort(Port);
393 if (!NT_SUCCESS(Status))
394 return Status;
395 }
396
397 return STATUS_SUCCESS;
398}
399
400/* The n-th USB 2 root port pairs with the n-th USB 3 one; an integrated hub on the last USB 2 port takes the rest */
402static
403VOID
404NTAPI
405HubConnectorMapRootPorts(
406 _In_ HubFdo* Hub)
407{
408 ULONG Port20;
409 ULONG Port30;
410 ULONG Mapped;
411 HubPort* Usb20;
412 HubPort* Usb30;
413
414 for (Port20 = Hub->m_First20Port, Port30 = Hub->m_First30Port;
415 Port20 <= Hub->m_Last20Port && Port30 <= Hub->m_Last30Port;
416 Port20++, Port30++)
417 {
418 Usb20 = Hub->FindPort(Port20);
419 if (Usb20 == NULL)
420 continue;
421
422 if (Usb20->HasProperty(PortProperty::IntegratedHub))
423 {
424 if (Port20 != Hub->m_Last20Port)
425 {
426 DPRINT1("Hub %p: integrated hub on port %lu is not the last USB 2 port (platform compliance)\n",
427 Hub, Port20);
428 return;
429 }
430
431 /* The integrated hub port itself keeps no path */
432 Usb20->m_FirstCompanionPort = (USHORT)Port30;
433 Usb20->m_LastCompanionPort = Hub->m_Last30Port;
435
436 for (Mapped = Port30; Mapped <= Hub->m_Last30Port; Mapped++)
437 {
438 Usb30 = Hub->FindPort(Mapped);
439 if (Usb30 == NULL)
440 continue;
441
443 if (!NT_SUCCESS(HubConnectorRegisterPort(Usb30)))
444 return;
445 }
446
447 return;
448 }
449
451
452 /* A fixed USB 2 port leaves its USB 3 partner unmapped too */
453 if (!Usb20->HasProperty(PortProperty::Removable))
454 continue;
455
456 if (!NT_SUCCESS(HubConnectorRegisterPort(Usb20)))
457 return;
458
459 Usb30 = Hub->FindPort(Port30);
460 if (Usb30 == NULL)
461 {
462 DPRINT1("Hub %p: no USB 3 partner %lu for port %lu (platform compliance)\n", Hub, Port30, Port20);
463 continue;
464 }
465
467 if (!Usb30->HasProperty(PortProperty::Removable))
468 DPRINT1("Hub %p: USB 3 port %lu is fixed but its partner is not (platform compliance)\n", Hub, Port30);
469
470 if (!NT_SUCCESS(HubConnectorRegisterPort(Usb30)))
471 return;
472 }
473}
474
475/* Ports past the mapped range keep an all zero key */
477static
478VOID
479NTAPI
480HubConnectorMapIntegratedHubPorts(
481 _In_ HubFdo* Hub)
482{
484 ULONG Port30;
485 HubPort* Port;
486
487 for (Number = 1, Port30 = Hub->m_Parent.FirstCompanionPort;
488 Number <= Hub->m_PortCount && Port30 <= Hub->m_Parent.LastCompanionPort;
489 Number++, Port30++)
490 {
492 if (Port == NULL)
493 return;
494
495 HubConnectorCopyParentId(Hub, &Port->m_ConnectorId);
496 Port->m_ConnectorId.Path[0] = Port30;
497
498 if (Port->HasProperty(PortProperty::Removable) && !NT_SUCCESS(HubConnectorRegisterPort(Port)))
499 return;
500 }
501}
502
503/*
504 * Both halves of a USB 3 hub hang off one root connector key, so their
505 * downstream ports come out with equal keys and pair up.
506 */
508static
509VOID
510NTAPI
511HubConnectorMapExternalHubPorts(
512 _In_ HubFdo* Hub)
513{
515 HubPort* Port;
517
518 for (Number = 1; Number <= Hub->m_PortCount; Number++)
519 {
521 if (Port == NULL)
522 return;
523
524 Id = &Port->m_ConnectorId;
526
527 /* USB allows five tiers below the root, so this never runs past Path[5] */
528 if (Id->Depth + 1 >= HUB_CONNECTOR_MAX_PATH)
529 {
530 DPRINT1("Hub %p is nested too deep for the connector map\n", Hub);
531 return;
532 }
533
534 Id->Depth++;
535 Id->Path[Id->Depth] = Number;
536
537 if (Port->HasProperty(PortProperty::Removable) && !NT_SUCCESS(HubConnectorRegisterPort(Port)))
538 return;
539 }
540}
541
543VOID
544NTAPI
545HubConnectorUnmapPorts(
546 _In_ HubFdo* Hub)
547{
549 HubPort* Port;
550
551 PAGED_CODE();
552
553 for (Number = Hub->m_First20Port; Number <= Hub->m_Last20Port; Number++)
554 {
556 if (Port != NULL)
557 HubConnectorUnregisterPort(Port);
558 }
559
560 for (Number = Hub->m_First30Port; Number <= Hub->m_Last30Port; Number++)
561 {
563 if (Port != NULL)
564 HubConnectorUnregisterPort(Port);
565 }
566}
567
568/* ACPI keys first; if they do not form a consistent map, fall back to the topology */
570VOID
571NTAPI
572HubConnectorMapPorts(
573 _In_ HubFdo* Hub)
574{
576
577 PAGED_CODE();
578
580 {
581 Status = HubConnectorMapAcpiPorts(Hub);
582 if (NT_SUCCESS(Status))
583 {
584 DPRINT("Hub %p connectors mapped from ACPI\n", Hub);
585 return;
586 }
587
588 DPRINT1("Hub %p: ACPI connector map failed 0x%lx, using the topology (platform compliance)\n",
589 Hub, Status);
590 HubConnectorUnmapPorts(Hub);
592 }
593
594 if (Hub->IsRootHub())
595 HubConnectorMapRootPorts(Hub);
596 else if (Hub->m_Parent.FirstCompanionPort != 0)
597 HubConnectorMapIntegratedHubPorts(Hub);
598 else
599 HubConnectorMapExternalHubPorts(Hub);
600}
#define PAGED_CODE()
DWORD Id
WCHAR Second[]
Definition: FormatMessage.c:12
WCHAR First[]
Definition: FormatMessage.c:11
ACPI_PHYSICAL_ADDRESS ACPI_SIZE BOOLEAN Warn UINT32 *TableIdx UINT32 ACPI_TABLE_HEADER *OutTableHeader ACPI_TABLE_HEADER **OutTable ACPI_HANDLE UINT32 ACPI_WALK_CALLBACK ACPI_WALK_CALLBACK void void **ReturnValue UINT32 ACPI_BUFFER *RetPathPtr ACPI_OBJECT_HANDLER void *Data ACPI_OBJECT_HANDLER void **Data ACPI_STRING ACPI_OBJECT_LIST ACPI_BUFFER *ReturnObjectBuffer ACPI_DEVICE_INFO **ReturnBuffer ACPI_HANDLE Parent
Definition: acpixf.h:732
unsigned char BOOLEAN
Definition: actypes.h:127
LONG NTSTATUS
Definition: precomp.h:26
#define DPRINT1
Definition: precomp.h:8
HubFdo * Hub
Definition: bootdev.cpp:261
Definition: fdo.h:132
VOID ClearFlag(_In_ HubFlag Flag)
Definition: fdo.h:167
USHORT m_First30Port
Definition: fdo.h:382
ULONG m_HubNumber
Definition: fdo.h:351
USHORT m_First20Port
Definition: fdo.h:380
USHORT m_Last30Port
Definition: fdo.h:383
ULONG m_PortCount
Definition: fdo.h:379
BOOLEAN HasFlag(_In_ HubFlag Flag) const
Definition: fdo.h:153
USHORT m_Last20Port
Definition: fdo.h:381
HubMux m_Mux
Definition: fdo.h:407
UCXHUB_PARENT_INTERFACE m_Parent
Definition: fdo.h:355
BOOLEAN IsRootHub() const
Definition: fdo.h:174
HubPort * FindPort(_In_ ULONG PortNumber)
Definition: fanout.cpp:42
PLIST_ENTRY Entry
Definition: devhcd.cpp:1262
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:33
union node Node
Definition: types.h:1255
#define _IRQL_requires_(irql)
Definition: driverspecs.h:229
HubDriverState HubDriver
Definition: entry.cpp:13
#define RemoveEntryList(Entry)
Definition: env_spec_w32.h:986
#define InsertTailList(ListHead, Entry)
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
#define NonPagedPool
Definition: env_spec_w32.h:307
Status
Definition: gdiplustypes.h:24
CPPORT Port[4]
Definition: headless.c:38
FORCEINLINE VOID NTAPI HubClearPortProperty(_In_ HubPort *Port, _In_ PortProperty Property)
Definition: hubid.h:87
FORCEINLINE VOID NTAPI HubSetPortProperty(_In_ HubPort *Port, _In_ PortProperty Property)
Definition: hubid.h:77
#define HUB_CONNECTOR_ROOT
Definition: hubport.h:76
#define HUB_CONNECTOR_MAX_PATH
Definition: hubport.h:80
#define HUB_CONNECTOR_ACPI
Definition: hubport.h:77
#define HUB_TAG_PORT
Definition: hubutil.h:14
#define RtlEqualMemory(dst, src, len)
Definition: kdvm.h:18
if(dx< 0)
Definition: linetemp.h:194
#define ExFreePoolWithTag(_P, _T)
Definition: module.h:1109
unsigned __int64 ULONG64
Definition: imports.h:198
static PVOID ExAllocatePoolZero(ULONG PoolType, SIZE_T NumberOfBytes, ULONG Tag)
Definition: precomp.h:45
#define min(a, b)
Definition: monoChain.cc:55
_In_ ULONG Number
Definition: haltypes.h:1940
#define _Inout_
Definition: no_sal2.h:162
#define _Out_
Definition: no_sal2.h:160
#define _In_
Definition: no_sal2.h:158
unsigned short USHORT
Definition: pedump.c:61
#define HUB_PROTOCOL_USB30
Definition: portconn.cpp:15
static NTSTATUS NTAPI HubConnectorAddUsb30(_Inout_ HubConnectorNode *Node, _In_ HubPort *Port)
Definition: portconn.cpp:152
static VOID NTAPI HubConnectorSetRootId(_In_ HubFdo *Hub, _Inout_ HubPort *Port, _In_ ULONG Usb30Port)
Definition: portconn.cpp:341
static NTSTATUS NTAPI HubConnectorAddUsb20(_Inout_ HubConnectorNode *Node, _In_ HubPort *Port)
Definition: portconn.cpp:122
static VOID NTAPI HubConnectorCopyParentId(_In_ HubFdo *Hub, _Out_ HubConnectorId *Id)
Definition: portconn.cpp:356
static HubConnectorNode *NTAPI HubConnectorFindById(_In_ const HubConnectorId *Id)
Definition: portconn.cpp:77
static HubConnectorNode *NTAPI HubConnectorFindByPort(_In_ HubPort *Port)
Definition: portconn.cpp:97
#define HUB_PROTOCOL_USB20
Definition: portconn.cpp:14
PVOID NTAPI HubConnectorNodeForPort(_In_ HubPort *Port)
Definition: portconn.cpp:320
HubPort *NTAPI HubConnectorFindCompanion(_In_ HubPort *Port, _In_ ULONG Index)
Definition: portconn.cpp:287
static BOOLEAN NTAPI HubConnectorIdsEqual(_In_ const HubConnectorId *First, _In_ const HubConnectorId *Second)
Definition: portconn.cpp:50
@ TypeC
Definition: miniport.h:115
#define STATUS_SUCCESS
Definition: shellext.h:65
#define DPRINT
Definition: sndvol32.h:73
HubPort * Usb30Second
Definition: portconn.cpp:27
HubConnectorId Id
Definition: portconn.cpp:24
HubPort * Usb20
Definition: portconn.cpp:25
LIST_ENTRY Link
Definition: portconn.cpp:23
HubPort * Usb30
Definition: portconn.cpp:26
WDFWAITLOCK CompanionPortLock
Definition: hubdriver.h:100
LIST_ENTRY ConnectorMap
Definition: hubdriver.h:99
LIST_ENTRY Ports
Definition: fdo.h:69
Definition: typedefs.h:120
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
#define NTAPI
Definition: typedefs.h:36
#define RtlCopyMemory(Destination, Source, Length)
Definition: typedefs.h:263
#define RtlZeroMemory(Destination, Length)
Definition: typedefs.h:262
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
uint32_t ULONG
Definition: typedefs.h:59
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
Definition: dlist.c:348
@ InAcpiNamespace
_In_ WDFCOLLECTION _In_ ULONG Index
_Must_inspect_result_ FORCEINLINE NTSTATUS WdfWaitLockAcquire(_In_ _Requires_lock_not_held_(_Curr_) WDFWAITLOCK Lock, _In_opt_ PLONGLONG Timeout)
Definition: wdfsync.h:173