Skip to content

Commit 57ac200

Browse files
committed
windows: improved serial number detection on composite USB devices
1 parent 7183520 commit 57ac200

2 files changed

Lines changed: 81 additions & 1 deletion

File tree

enumerator/syscall_windows.go

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enumerator/usb_windows.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ func parseDeviceID(deviceID string, details *PortDetails) {
6262
//sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey
6363
//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW
6464

65+
//sys cmGetParent(outParentDev *devInstance, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Parent
66+
//sys cmGetDeviceIDSize(outLen *uint32, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Device_ID_Size
67+
//sys cmGetDeviceID(dev devInstance, buffer unsafe.Pointer, bufferSize uint32, flags uint32) (err cmError) = cfgmgr32.CM_Get_Device_IDW
68+
//sys cmMapCrToWin32Err(cmErr cmError, defaultErr uint32) (err uint32) = cfgmgr32.CM_MapCrToWin32Err
69+
6570
// Device registry property codes
6671
// (Codes marked as read-only (R) may only be used for
6772
// SetupDiGetDeviceRegistryProperty)
@@ -194,14 +199,46 @@ func (set devicesSet) destroy() {
194199
setupDiDestroyDeviceInfoList(set)
195200
}
196201

202+
type cmError uint32
203+
197204
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx
198205
type devInfoData struct {
199206
size uint32
200207
guid guid
201-
devInst uint32
208+
devInst devInstance
202209
reserved uintptr
203210
}
204211

212+
type devInstance uint32
213+
214+
func cmConvertError(cmErr cmError) error {
215+
if cmErr == 0 {
216+
return nil
217+
}
218+
winErr := cmMapCrToWin32Err(cmErr, 0)
219+
return fmt.Errorf("error %d", winErr)
220+
}
221+
222+
func (dev devInstance) getParent() (devInstance, error) {
223+
var res devInstance
224+
errN := cmGetParent(&res, dev, 0)
225+
return res, cmConvertError(errN)
226+
}
227+
228+
func (dev devInstance) GetDeviceID() (string, error) {
229+
var size uint32
230+
cmErr := cmGetDeviceIDSize(&size, dev, 0)
231+
if err := cmConvertError(cmErr); err != nil {
232+
return "", err
233+
}
234+
buff := make([]uint16, size)
235+
cmErr = cmGetDeviceID(dev, unsafe.Pointer(&buff[0]), uint32(len(buff)), 0)
236+
if err := cmConvertError(cmErr); err != nil {
237+
return "", err
238+
}
239+
return windows.UTF16ToString(buff[:]), nil
240+
}
241+
205242
type deviceInfo struct {
206243
set devicesSet
207244
data devInfoData
@@ -291,6 +328,20 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er
291328
}
292329
parseDeviceID(deviceID, details)
293330

331+
// On composite USB devices the serial number is usually reported on the parent
332+
// device, so let's navigate up one level and see if we can get this information
333+
if details.IsUSB && details.SerialNumber == "" {
334+
if parentInfo, err := device.data.devInst.getParent(); err == nil {
335+
if parentDeviceID, err := parentInfo.GetDeviceID(); err == nil {
336+
d := &PortDetails{}
337+
parseDeviceID(parentDeviceID, d)
338+
if details.VID == d.VID && details.PID == d.PID {
339+
details.SerialNumber = d.SerialNumber
340+
}
341+
}
342+
}
343+
}
344+
294345
/* spdrpDeviceDesc returns a generic name, e.g.: "CDC-ACM", which will be the same for 2 identical devices attached
295346
while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)",
296347
the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */

0 commit comments

Comments
 (0)