windows: improved serial number detection on composite USB devices
This commit is contained in:
@@ -38,6 +38,7 @@ func errnoErr(e syscall.Errno) error {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
|
modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
|
||||||
|
modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll")
|
||||||
|
|
||||||
procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW")
|
procSetupDiClassGuidsFromNameW = modsetupapi.NewProc("SetupDiClassGuidsFromNameW")
|
||||||
procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW")
|
procSetupDiGetClassDevsW = modsetupapi.NewProc("SetupDiGetClassDevsW")
|
||||||
@@ -46,6 +47,10 @@ var (
|
|||||||
procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW")
|
procSetupDiGetDeviceInstanceIdW = modsetupapi.NewProc("SetupDiGetDeviceInstanceIdW")
|
||||||
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
|
procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
|
||||||
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
|
||||||
|
procCM_Get_Parent = modcfgmgr32.NewProc("CM_Get_Parent")
|
||||||
|
procCM_Get_Device_ID_Size = modcfgmgr32.NewProc("CM_Get_Device_ID_Size")
|
||||||
|
procCM_Get_Device_IDW = modcfgmgr32.NewProc("CM_Get_Device_IDW")
|
||||||
|
procCM_MapCrToWin32Err = modcfgmgr32.NewProc("CM_MapCrToWin32Err")
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
|
func setupDiClassGuidsFromNameInternal(class string, guid *guid, guidSize uint32, requiredSize *uint32) (err error) {
|
||||||
@@ -136,3 +141,27 @@ func setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, prop
|
|||||||
res = r0 != 0
|
res = r0 != 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmGetParent(outParentDev *devInstance, dev devInstance, flags uint32) (cmErr cmError) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCM_Get_Parent.Addr(), 3, uintptr(unsafe.Pointer(outParentDev)), uintptr(dev), uintptr(flags))
|
||||||
|
cmErr = cmError(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmGetDeviceIDSize(outLen *uint32, dev devInstance, flags uint32) (cmErr cmError) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCM_Get_Device_ID_Size.Addr(), 3, uintptr(unsafe.Pointer(outLen)), uintptr(dev), uintptr(flags))
|
||||||
|
cmErr = cmError(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmGetDeviceID(dev devInstance, buffer unsafe.Pointer, bufferSize uint32, flags uint32) (err cmError) {
|
||||||
|
r0, _, _ := syscall.Syscall6(procCM_Get_Device_IDW.Addr(), 4, uintptr(dev), uintptr(buffer), uintptr(bufferSize), uintptr(flags), 0, 0)
|
||||||
|
err = cmError(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmMapCrToWin32Err(cmErr cmError, defaultErr uint32) (err uint32) {
|
||||||
|
r0, _, _ := syscall.Syscall(procCM_MapCrToWin32Err.Addr(), 2, uintptr(cmErr), uintptr(defaultErr), 0)
|
||||||
|
err = uint32(r0)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ func parseDeviceID(deviceID string, details *PortDetails) {
|
|||||||
//sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey
|
//sys setupDiOpenDevRegKey(set devicesSet, devInfo *devInfoData, scope dicsScope, hwProfile uint32, keyType uint32, samDesired regsam) (hkey syscall.Handle, err error) = setupapi.SetupDiOpenDevRegKey
|
||||||
//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW
|
//sys setupDiGetDeviceRegistryProperty(set devicesSet, devInfo *devInfoData, property deviceProperty, propertyType *uint32, outValue *byte, bufSize uint32, reqSize *uint32) (res bool) = setupapi.SetupDiGetDeviceRegistryPropertyW
|
||||||
|
|
||||||
|
//sys cmGetParent(outParentDev *devInstance, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Parent
|
||||||
|
//sys cmGetDeviceIDSize(outLen *uint32, dev devInstance, flags uint32) (cmErr cmError) = cfgmgr32.CM_Get_Device_ID_Size
|
||||||
|
//sys cmGetDeviceID(dev devInstance, buffer unsafe.Pointer, bufferSize uint32, flags uint32) (err cmError) = cfgmgr32.CM_Get_Device_IDW
|
||||||
|
//sys cmMapCrToWin32Err(cmErr cmError, defaultErr uint32) (err uint32) = cfgmgr32.CM_MapCrToWin32Err
|
||||||
|
|
||||||
// Device registry property codes
|
// Device registry property codes
|
||||||
// (Codes marked as read-only (R) may only be used for
|
// (Codes marked as read-only (R) may only be used for
|
||||||
// SetupDiGetDeviceRegistryProperty)
|
// SetupDiGetDeviceRegistryProperty)
|
||||||
@@ -194,14 +199,46 @@ func (set devicesSet) destroy() {
|
|||||||
setupDiDestroyDeviceInfoList(set)
|
setupDiDestroyDeviceInfoList(set)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type cmError uint32
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/hardware/ff552344(v=vs.85).aspx
|
||||||
type devInfoData struct {
|
type devInfoData struct {
|
||||||
size uint32
|
size uint32
|
||||||
guid guid
|
guid guid
|
||||||
devInst uint32
|
devInst devInstance
|
||||||
reserved uintptr
|
reserved uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type devInstance uint32
|
||||||
|
|
||||||
|
func cmConvertError(cmErr cmError) error {
|
||||||
|
if cmErr == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
winErr := cmMapCrToWin32Err(cmErr, 0)
|
||||||
|
return fmt.Errorf("error %d", winErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dev devInstance) getParent() (devInstance, error) {
|
||||||
|
var res devInstance
|
||||||
|
errN := cmGetParent(&res, dev, 0)
|
||||||
|
return res, cmConvertError(errN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dev devInstance) GetDeviceID() (string, error) {
|
||||||
|
var size uint32
|
||||||
|
cmErr := cmGetDeviceIDSize(&size, dev, 0)
|
||||||
|
if err := cmConvertError(cmErr); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
buff := make([]uint16, size)
|
||||||
|
cmErr = cmGetDeviceID(dev, unsafe.Pointer(&buff[0]), uint32(len(buff)), 0)
|
||||||
|
if err := cmConvertError(cmErr); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return windows.UTF16ToString(buff[:]), nil
|
||||||
|
}
|
||||||
|
|
||||||
type deviceInfo struct {
|
type deviceInfo struct {
|
||||||
set devicesSet
|
set devicesSet
|
||||||
data devInfoData
|
data devInfoData
|
||||||
@@ -291,6 +328,20 @@ func retrievePortDetailsFromDevInfo(device *deviceInfo, details *PortDetails) er
|
|||||||
}
|
}
|
||||||
parseDeviceID(deviceID, details)
|
parseDeviceID(deviceID, details)
|
||||||
|
|
||||||
|
// On composite USB devices the serial number is usually reported on the parent
|
||||||
|
// device, so let's navigate up one level and see if we can get this information
|
||||||
|
if details.IsUSB && details.SerialNumber == "" {
|
||||||
|
if parentInfo, err := device.data.devInst.getParent(); err == nil {
|
||||||
|
if parentDeviceID, err := parentInfo.GetDeviceID(); err == nil {
|
||||||
|
d := &PortDetails{}
|
||||||
|
parseDeviceID(parentDeviceID, d)
|
||||||
|
if details.VID == d.VID && details.PID == d.PID {
|
||||||
|
details.SerialNumber = d.SerialNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* spdrpDeviceDesc returns a generic name, e.g.: "CDC-ACM", which will be the same for 2 identical devices attached
|
/* spdrpDeviceDesc returns a generic name, e.g.: "CDC-ACM", which will be the same for 2 identical devices attached
|
||||||
while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)",
|
while spdrpFriendlyName returns a specific name, e.g.: "CDC-ACM (COM44)",
|
||||||
the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */
|
the result of spdrpFriendlyName is therefore unique and suitable as an alternative string to for a port choice */
|
||||||
|
|||||||
Reference in New Issue
Block a user