1
2
3
4
5 package oshi.hardware.platform.mac;
6
7 import java.util.ArrayList;
8 import java.util.Collections;
9 import java.util.HashMap;
10 import java.util.List;
11 import java.util.Locale;
12 import java.util.Map;
13
14 import com.sun.jna.platform.mac.CoreFoundation;
15 import com.sun.jna.platform.mac.CoreFoundation.CFIndex;
16 import com.sun.jna.platform.mac.CoreFoundation.CFMutableDictionaryRef;
17 import com.sun.jna.platform.mac.CoreFoundation.CFStringRef;
18 import com.sun.jna.platform.mac.CoreFoundation.CFTypeRef;
19 import com.sun.jna.platform.mac.IOKit.IOIterator;
20 import com.sun.jna.platform.mac.IOKit.IORegistryEntry;
21 import com.sun.jna.platform.mac.IOKitUtil;
22
23 import oshi.annotation.concurrent.Immutable;
24 import oshi.hardware.UsbDevice;
25 import oshi.hardware.common.AbstractUsbDevice;
26
27
28
29
30 @Immutable
31 public class MacUsbDevice extends AbstractUsbDevice {
32
33 private static final CoreFoundation CF = CoreFoundation.INSTANCE;
34
35 private static final String IOUSB = "IOUSB";
36 private static final String IOSERVICE = "IOService";
37
38 public MacUsbDevice(String name, String vendor, String vendorId, String productId, String serialNumber,
39 String uniqueDeviceId, List<UsbDevice> connectedDevices) {
40 super(name, vendor, vendorId, productId, serialNumber, uniqueDeviceId, connectedDevices);
41 }
42
43
44
45
46
47
48
49
50
51
52
53
54
55 public static List<UsbDevice> getUsbDevices(boolean tree) {
56 List<UsbDevice> devices = getUsbDevices();
57 if (tree) {
58 return devices;
59 }
60 List<UsbDevice> deviceList = new ArrayList<>();
61
62
63 for (UsbDevice device : devices) {
64 addDevicesToList(deviceList, device.getConnectedDevices());
65 }
66 return deviceList;
67 }
68
69 private static List<UsbDevice> getUsbDevices() {
70
71 Map<Long, String> nameMap = new HashMap<>();
72 Map<Long, String> vendorMap = new HashMap<>();
73 Map<Long, String> vendorIdMap = new HashMap<>();
74 Map<Long, String> productIdMap = new HashMap<>();
75 Map<Long, String> serialMap = new HashMap<>();
76 Map<Long, List<Long>> hubMap = new HashMap<>();
77
78 List<Long> usbControllers = new ArrayList<>();
79 IORegistryEntry root = IOKitUtil.getRoot();
80
81
82 IOIterator iter = root.getChildIterator(IOUSB);
83 if (iter != null) {
84
85 CFStringRef locationIDKey = CFStringRef.createCFString("locationID");
86 CFStringRef ioPropertyMatchKey = CFStringRef.createCFString("IOPropertyMatch");
87
88
89 IORegistryEntry device = iter.next();
90 while (device != null) {
91 long id = 0L;
92
93 IORegistryEntry controller = device.getParentEntry(IOSERVICE);
94 if (controller != null) {
95
96 id = controller.getRegistryEntryID();
97
98 nameMap.put(id, controller.getName());
99
100
101
102 CFTypeRef ref = controller.createCFProperty(locationIDKey);
103 if (ref != null) {
104 getControllerIdByLocation(id, ref, locationIDKey, ioPropertyMatchKey, vendorIdMap,
105 productIdMap);
106 ref.release();
107 }
108 controller.release();
109 }
110 usbControllers.add(id);
111
112
113
114 addDeviceAndChildrenToMaps(device, id, nameMap, vendorMap, vendorIdMap, productIdMap, serialMap,
115 hubMap);
116
117 device.release();
118 device = iter.next();
119 }
120 locationIDKey.release();
121 ioPropertyMatchKey.release();
122 iter.release();
123 }
124 root.release();
125
126
127 List<UsbDevice> controllerDevices = new ArrayList<>();
128 for (Long controller : usbControllers) {
129 controllerDevices.add(getDeviceAndChildren(controller, "0000", "0000", nameMap, vendorMap, vendorIdMap,
130 productIdMap, serialMap, hubMap));
131 }
132 return controllerDevices;
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146
147 private static void addDeviceAndChildrenToMaps(IORegistryEntry device, long parentId, Map<Long, String> nameMap,
148 Map<Long, String> vendorMap, Map<Long, String> vendorIdMap, Map<Long, String> productIdMap,
149 Map<Long, String> serialMap, Map<Long, List<Long>> hubMap) {
150
151
152 long id = device.getRegistryEntryID();
153
154 hubMap.computeIfAbsent(parentId, x -> new ArrayList<>()).add(id);
155
156 nameMap.put(id, device.getName().trim());
157
158 String vendor = device.getStringProperty("USB Vendor Name");
159 if (vendor != null) {
160 vendorMap.put(id, vendor.trim());
161 }
162
163 Long vendorId = device.getLongProperty("idVendor");
164 if (vendorId != null) {
165 vendorIdMap.put(id, String.format(Locale.ROOT, "%04x", 0xffff & vendorId));
166 }
167
168 Long productId = device.getLongProperty("idProduct");
169 if (productId != null) {
170 productIdMap.put(id, String.format(Locale.ROOT, "%04x", 0xffff & productId));
171 }
172
173 String serial = device.getStringProperty("USB Serial Number");
174 if (serial != null) {
175 serialMap.put(id, serial.trim());
176 }
177
178
179 IOIterator childIter = device.getChildIterator(IOUSB);
180 IORegistryEntry childDevice = childIter.next();
181 while (childDevice != null) {
182 addDeviceAndChildrenToMaps(childDevice, id, nameMap, vendorMap, vendorIdMap, productIdMap, serialMap,
183 hubMap);
184
185 childDevice.release();
186 childDevice = childIter.next();
187 }
188 childIter.release();
189 }
190
191 private static void addDevicesToList(List<UsbDevice> deviceList, List<UsbDevice> list) {
192 for (UsbDevice device : list) {
193 deviceList.add(
194 new MacUsbDevice(device.getName(), device.getVendor(), device.getVendorId(), device.getProductId(),
195 device.getSerialNumber(), device.getUniqueDeviceId(), Collections.emptyList()));
196 addDevicesToList(deviceList, device.getConnectedDevices());
197 }
198 }
199
200
201
202
203
204
205
206
207
208
209
210 private static void getControllerIdByLocation(long id, CFTypeRef locationId, CFStringRef locationIDKey,
211 CFStringRef ioPropertyMatchKey, Map<Long, String> vendorIdMap, Map<Long, String> productIdMap) {
212
213 CFMutableDictionaryRef propertyDict = CF.CFDictionaryCreateMutable(CF.CFAllocatorGetDefault(), new CFIndex(0),
214 null, null);
215 propertyDict.setValue(locationIDKey, locationId);
216 CFMutableDictionaryRef matchingDict = CF.CFDictionaryCreateMutable(CF.CFAllocatorGetDefault(), new CFIndex(0),
217 null, null);
218 matchingDict.setValue(ioPropertyMatchKey, propertyDict);
219
220
221 IOIterator serviceIterator = IOKitUtil.getMatchingServices(matchingDict);
222
223 propertyDict.release();
224
225
226
227 boolean found = false;
228 if (serviceIterator != null) {
229 IORegistryEntry matchingService = serviceIterator.next();
230 while (matchingService != null && !found) {
231
232 IORegistryEntry parent = matchingService.getParentEntry(IOSERVICE);
233
234
235 if (parent != null) {
236 byte[] vid = parent.getByteArrayProperty("vendor-id");
237 if (vid != null && vid.length >= 2) {
238 vendorIdMap.put(id, String.format(Locale.ROOT, "%02x%02x", vid[1], vid[0]));
239 found = true;
240 }
241
242
243 byte[] pid = parent.getByteArrayProperty("device-id");
244 if (pid != null && pid.length >= 2) {
245 productIdMap.put(id, String.format(Locale.ROOT, "%02x%02x", pid[1], pid[0]));
246 found = true;
247 }
248 parent.release();
249 }
250
251 matchingService.release();
252 matchingService = serviceIterator.next();
253 }
254 serviceIterator.release();
255 }
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 private static MacUsbDevice getDeviceAndChildren(Long registryEntryId, String vid, String pid,
273 Map<Long, String> nameMap, Map<Long, String> vendorMap, Map<Long, String> vendorIdMap,
274 Map<Long, String> productIdMap, Map<Long, String> serialMap, Map<Long, List<Long>> hubMap) {
275 String vendorId = vendorIdMap.getOrDefault(registryEntryId, vid);
276 String productId = productIdMap.getOrDefault(registryEntryId, pid);
277 List<Long> childIds = hubMap.getOrDefault(registryEntryId, new ArrayList<>());
278 List<UsbDevice> usbDevices = new ArrayList<>();
279 for (Long id : childIds) {
280 usbDevices.add(getDeviceAndChildren(id, vendorId, productId, nameMap, vendorMap, vendorIdMap, productIdMap,
281 serialMap, hubMap));
282 }
283 Collections.sort(usbDevices);
284 return new MacUsbDevice(nameMap.getOrDefault(registryEntryId, vendorId + ":" + productId),
285 vendorMap.getOrDefault(registryEntryId, ""), vendorId, productId,
286 serialMap.getOrDefault(registryEntryId, ""), "0x" + Long.toHexString(registryEntryId), usbDevices);
287 }
288 }