@mcesystems/usb-device-listener 1.0.92 → 1.0.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
Binary file
|
|
@@ -332,6 +332,13 @@ void USBListener::HandleDeviceAdded(io_iterator_t iterator) {
|
|
|
332
332
|
} else {
|
|
333
333
|
obj.Set("logicalPort", env.Null());
|
|
334
334
|
}
|
|
335
|
+
Napi::Array pathArr = Napi::Array::New(env, data->portPath.size());
|
|
336
|
+
for (size_t j = 0; j < data->portPath.size(); j++) {
|
|
337
|
+
pathArr[j] = Napi::Number::New(env, data->portPath[j]);
|
|
338
|
+
}
|
|
339
|
+
obj.Set("portPath", pathArr);
|
|
340
|
+
obj.Set("parentHubVid", Napi::Number::New(env, data->parentHubVid));
|
|
341
|
+
obj.Set("parentHubPid", Napi::Number::New(env, data->parentHubPid));
|
|
335
342
|
jsCallback.Call({obj});
|
|
336
343
|
delete data;
|
|
337
344
|
};
|
|
@@ -385,6 +392,13 @@ void USBListener::HandleDeviceRemoved(io_iterator_t iterator) {
|
|
|
385
392
|
} else {
|
|
386
393
|
obj.Set("logicalPort", env.Null());
|
|
387
394
|
}
|
|
395
|
+
Napi::Array pathArr = Napi::Array::New(env, data->portPath.size());
|
|
396
|
+
for (size_t j = 0; j < data->portPath.size(); j++) {
|
|
397
|
+
pathArr[j] = Napi::Number::New(env, data->portPath[j]);
|
|
398
|
+
}
|
|
399
|
+
obj.Set("portPath", pathArr);
|
|
400
|
+
obj.Set("parentHubVid", Napi::Number::New(env, data->parentHubVid));
|
|
401
|
+
obj.Set("parentHubPid", Napi::Number::New(env, data->parentHubPid));
|
|
388
402
|
jsCallback.Call({obj});
|
|
389
403
|
delete data;
|
|
390
404
|
};
|
|
@@ -433,6 +447,22 @@ bool USBListener::GetDeviceInfoFromService(io_service_t service, DeviceInfo& inf
|
|
|
433
447
|
// Get Location ID (used for physical port identification)
|
|
434
448
|
info.locationInfo = GetLocationId(service);
|
|
435
449
|
|
|
450
|
+
// Get raw location ID for port path decoding
|
|
451
|
+
CFNumberRef locationRef = (CFNumberRef)IORegistryEntryCreateCFProperty(
|
|
452
|
+
service, CFSTR(kUSBDevicePropertyLocationID), kCFAllocatorDefault, 0);
|
|
453
|
+
if (!locationRef) {
|
|
454
|
+
locationRef = (CFNumberRef)IORegistryEntryCreateCFProperty(
|
|
455
|
+
service, CFSTR("locationID"), kCFAllocatorDefault, 0);
|
|
456
|
+
}
|
|
457
|
+
uint32_t locationId = 0;
|
|
458
|
+
if (locationRef) {
|
|
459
|
+
CFNumberGetValue(locationRef, kCFNumberSInt32Type, &locationId);
|
|
460
|
+
CFRelease(locationRef);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Fill port path and parent hub VID/PID (traverses IOKit hierarchy)
|
|
464
|
+
FillPortPathAndParentHub(service, locationId, info);
|
|
465
|
+
|
|
436
466
|
// Get USB serial number
|
|
437
467
|
info.serialNumber = GetSerialNumber(service);
|
|
438
468
|
|
|
@@ -480,6 +510,119 @@ std::string USBListener::GetLocationId(io_service_t service) {
|
|
|
480
510
|
return ss.str();
|
|
481
511
|
}
|
|
482
512
|
|
|
513
|
+
int USBListener::GetPortNumberFromService(io_service_t service) {
|
|
514
|
+
// Try "port" first (IOUSBHostDevice), then "PortNum" (older IOUSBDevice)
|
|
515
|
+
CFTypeRef portRef = IORegistryEntryCreateCFProperty(
|
|
516
|
+
service, CFSTR("port"), kCFAllocatorDefault, 0);
|
|
517
|
+
if (!portRef) {
|
|
518
|
+
portRef = IORegistryEntryCreateCFProperty(
|
|
519
|
+
service, CFSTR("PortNum"), kCFAllocatorDefault, 0);
|
|
520
|
+
}
|
|
521
|
+
if (!portRef || CFGetTypeID(portRef) != CFNumberGetTypeID()) {
|
|
522
|
+
if (portRef) CFRelease(portRef);
|
|
523
|
+
return 0;
|
|
524
|
+
}
|
|
525
|
+
int port = 0;
|
|
526
|
+
CFNumberGetValue((CFNumberRef)portRef, kCFNumberIntType, &port);
|
|
527
|
+
CFRelease(portRef);
|
|
528
|
+
return port;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
bool USBListener::GetVidPidFromService(io_service_t service, uint16_t& vid, uint16_t& pid) {
|
|
532
|
+
CFTypeRef vidRef = IORegistryEntryCreateCFProperty(
|
|
533
|
+
service, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0);
|
|
534
|
+
if (!vidRef) {
|
|
535
|
+
vidRef = IORegistryEntryCreateCFProperty(
|
|
536
|
+
service, CFSTR("idVendor"), kCFAllocatorDefault, 0);
|
|
537
|
+
}
|
|
538
|
+
CFTypeRef pidRef = IORegistryEntryCreateCFProperty(
|
|
539
|
+
service, CFSTR(kUSBProductID), kCFAllocatorDefault, 0);
|
|
540
|
+
if (!pidRef) {
|
|
541
|
+
pidRef = IORegistryEntryCreateCFProperty(
|
|
542
|
+
service, CFSTR("idProduct"), kCFAllocatorDefault, 0);
|
|
543
|
+
}
|
|
544
|
+
if (!vidRef || !pidRef || CFGetTypeID(vidRef) != CFNumberGetTypeID() ||
|
|
545
|
+
CFGetTypeID(pidRef) != CFNumberGetTypeID()) {
|
|
546
|
+
if (vidRef) CFRelease(vidRef);
|
|
547
|
+
if (pidRef) CFRelease(pidRef);
|
|
548
|
+
return false;
|
|
549
|
+
}
|
|
550
|
+
int v = 0, p = 0;
|
|
551
|
+
CFNumberGetValue((CFNumberRef)vidRef, kCFNumberIntType, &v);
|
|
552
|
+
CFNumberGetValue((CFNumberRef)pidRef, kCFNumberIntType, &p);
|
|
553
|
+
CFRelease(vidRef);
|
|
554
|
+
CFRelease(pidRef);
|
|
555
|
+
vid = static_cast<uint16_t>(v);
|
|
556
|
+
pid = static_cast<uint16_t>(p);
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
void USBListener::FillPortPathAndParentHub(io_service_t service, uint32_t locationId, DeviceInfo& info) {
|
|
561
|
+
info.portPath.clear();
|
|
562
|
+
info.parentHubVid = 0;
|
|
563
|
+
info.parentHubPid = 0;
|
|
564
|
+
|
|
565
|
+
std::vector<int> pathDeviceToRoot;
|
|
566
|
+
io_registry_entry_t current = service; // We don't retain - we don't own service
|
|
567
|
+
bool foundParentHub = false;
|
|
568
|
+
|
|
569
|
+
while (current) {
|
|
570
|
+
int portNum = GetPortNumberFromService(current);
|
|
571
|
+
if (portNum > 0) {
|
|
572
|
+
pathDeviceToRoot.push_back(portNum);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
io_registry_entry_t parentEntry = 0;
|
|
576
|
+
kern_return_t kr = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parentEntry);
|
|
577
|
+
|
|
578
|
+
if (current != service) {
|
|
579
|
+
IOObjectRelease(current);
|
|
580
|
+
}
|
|
581
|
+
current = 0;
|
|
582
|
+
|
|
583
|
+
if (kr != KERN_SUCCESS || parentEntry == 0) {
|
|
584
|
+
if (parentEntry) IOObjectRelease(parentEntry);
|
|
585
|
+
break;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Check if parent is a USB device (hub or controller with VID/PID)
|
|
589
|
+
if (!foundParentHub) {
|
|
590
|
+
uint16_t pvid = 0, ppid = 0;
|
|
591
|
+
if (GetVidPidFromService(parentEntry, pvid, ppid)) {
|
|
592
|
+
info.parentHubVid = pvid;
|
|
593
|
+
info.parentHubPid = ppid;
|
|
594
|
+
foundParentHub = true;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
current = parentEntry;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (current && current != service) {
|
|
602
|
+
IOObjectRelease(current);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Build port path: reverse to get root-to-device (match Windows format)
|
|
606
|
+
for (auto it = pathDeviceToRoot.rbegin(); it != pathDeviceToRoot.rend(); ++it) {
|
|
607
|
+
info.portPath.push_back(*it);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// If registry walk yielded no port path, decode from location ID
|
|
611
|
+
// macOS location ID encodes port path: nibbles (loc>>24)&0xFF, (loc>>20)&0x0F, etc.
|
|
612
|
+
if (info.portPath.empty() && locationId != 0) {
|
|
613
|
+
int p1 = static_cast<int>((locationId >> 24) & 0xFF);
|
|
614
|
+
int p2 = static_cast<int>((locationId >> 20) & 0x0F);
|
|
615
|
+
int p3 = static_cast<int>((locationId >> 16) & 0x0F);
|
|
616
|
+
int p4 = static_cast<int>((locationId >> 12) & 0x0F);
|
|
617
|
+
int p5 = static_cast<int>((locationId >> 8) & 0x0F);
|
|
618
|
+
if (p1 > 0) info.portPath.push_back(p1);
|
|
619
|
+
if (p2 > 0) info.portPath.push_back(p2);
|
|
620
|
+
if (p3 > 0) info.portPath.push_back(p3);
|
|
621
|
+
if (p4 > 0) info.portPath.push_back(p4);
|
|
622
|
+
if (p5 > 0) info.portPath.push_back(p5);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
483
626
|
void USBListener::EnumerateConnectedDevices() {
|
|
484
627
|
// This is called at startup via HandleDeviceAdded when draining the iterator
|
|
485
628
|
// No additional implementation needed
|
|
@@ -48,6 +48,9 @@ private:
|
|
|
48
48
|
// Helper methods
|
|
49
49
|
bool GetDeviceInfoFromService(io_service_t service, DeviceInfo& info);
|
|
50
50
|
std::string GetLocationId(io_service_t service);
|
|
51
|
+
void FillPortPathAndParentHub(io_service_t service, uint32_t locationId, DeviceInfo& info);
|
|
52
|
+
static int GetPortNumberFromService(io_service_t service);
|
|
53
|
+
static bool GetVidPidFromService(io_service_t service, uint16_t& vid, uint16_t& pid);
|
|
51
54
|
void EnumerateConnectedDevices();
|
|
52
55
|
void RunLoopThread();
|
|
53
56
|
|