Index: tools/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py |
diff --git a/tools/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py b/tools/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cc770f7eba28672c6b2d43a6d49969415b50bca1 |
--- /dev/null |
+++ b/tools/telemetry/third_party/pyserial/serial/tools/list_ports_windows.py |
@@ -0,0 +1,240 @@ |
+import ctypes |
+import re |
+ |
+def ValidHandle(value, func, arguments): |
+ if value == 0: |
+ raise ctypes.WinError() |
+ return value |
+ |
+import serial |
+from serial.win32 import ULONG_PTR, is_64bit |
+from ctypes.wintypes import HANDLE |
+from ctypes.wintypes import BOOL |
+from ctypes.wintypes import HWND |
+from ctypes.wintypes import DWORD |
+from ctypes.wintypes import WORD |
+from ctypes.wintypes import LONG |
+from ctypes.wintypes import ULONG |
+from ctypes.wintypes import LPCSTR |
+from ctypes.wintypes import HKEY |
+from ctypes.wintypes import BYTE |
+ |
+NULL = 0 |
+HDEVINFO = ctypes.c_void_p |
+PCTSTR = ctypes.c_char_p |
+PTSTR = ctypes.c_void_p |
+CHAR = ctypes.c_char |
+LPDWORD = PDWORD = ctypes.POINTER(DWORD) |
+#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE) |
+LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types |
+ |
+ACCESS_MASK = DWORD |
+REGSAM = ACCESS_MASK |
+ |
+ |
+def byte_buffer(length): |
+ """Get a buffer for a string""" |
+ return (BYTE*length)() |
+ |
+def string(buffer): |
+ s = [] |
+ for c in buffer: |
+ if c == 0: break |
+ s.append(chr(c & 0xff)) # "& 0xff": hack to convert signed to unsigned |
+ return ''.join(s) |
+ |
+ |
+class GUID(ctypes.Structure): |
+ _fields_ = [ |
+ ('Data1', DWORD), |
+ ('Data2', WORD), |
+ ('Data3', WORD), |
+ ('Data4', BYTE*8), |
+ ] |
+ def __str__(self): |
+ return "{%08x-%04x-%04x-%s-%s}" % ( |
+ self.Data1, |
+ self.Data2, |
+ self.Data3, |
+ ''.join(["%02x" % d for d in self.Data4[:2]]), |
+ ''.join(["%02x" % d for d in self.Data4[2:]]), |
+ ) |
+ |
+class SP_DEVINFO_DATA(ctypes.Structure): |
+ _fields_ = [ |
+ ('cbSize', DWORD), |
+ ('ClassGuid', GUID), |
+ ('DevInst', DWORD), |
+ ('Reserved', ULONG_PTR), |
+ ] |
+ def __str__(self): |
+ return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst) |
+PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA) |
+ |
+PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p |
+ |
+setupapi = ctypes.windll.LoadLibrary("setupapi") |
+SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList |
+SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO] |
+SetupDiDestroyDeviceInfoList.restype = BOOL |
+ |
+SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameA |
+SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD] |
+SetupDiClassGuidsFromName.restype = BOOL |
+ |
+SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo |
+SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA] |
+SetupDiEnumDeviceInfo.restype = BOOL |
+ |
+SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsA |
+SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD] |
+SetupDiGetClassDevs.restype = HDEVINFO |
+SetupDiGetClassDevs.errcheck = ValidHandle |
+ |
+SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyA |
+SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD] |
+SetupDiGetDeviceRegistryProperty.restype = BOOL |
+ |
+SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdA |
+SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD] |
+SetupDiGetDeviceInstanceId.restype = BOOL |
+ |
+SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey |
+SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM] |
+SetupDiOpenDevRegKey.restype = HKEY |
+ |
+advapi32 = ctypes.windll.LoadLibrary("Advapi32") |
+RegCloseKey = advapi32.RegCloseKey |
+RegCloseKey.argtypes = [HKEY] |
+RegCloseKey.restype = LONG |
+ |
+RegQueryValueEx = advapi32.RegQueryValueExA |
+RegQueryValueEx.argtypes = [HKEY, LPCSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD] |
+RegQueryValueEx.restype = LONG |
+ |
+ |
+DIGCF_PRESENT = 2 |
+DIGCF_DEVICEINTERFACE = 16 |
+INVALID_HANDLE_VALUE = 0 |
+ERROR_INSUFFICIENT_BUFFER = 122 |
+SPDRP_HARDWAREID = 1 |
+SPDRP_FRIENDLYNAME = 12 |
+DICS_FLAG_GLOBAL = 1 |
+DIREG_DEV = 0x00000001 |
+KEY_READ = 0x20019 |
+ |
+# workaround for compatibility between Python 2.x and 3.x |
+Ports = serial.to_bytes([80, 111, 114, 116, 115]) # "Ports" |
+PortName = serial.to_bytes([80, 111, 114, 116, 78, 97, 109, 101]) # "PortName" |
+ |
+def comports(): |
+ GUIDs = (GUID*8)() # so far only seen one used, so hope 8 are enough... |
+ guids_size = DWORD() |
+ if not SetupDiClassGuidsFromName( |
+ Ports, |
+ GUIDs, |
+ ctypes.sizeof(GUIDs), |
+ ctypes.byref(guids_size)): |
+ raise ctypes.WinError() |
+ |
+ # repeat for all possible GUIDs |
+ for index in range(guids_size.value): |
+ g_hdi = SetupDiGetClassDevs( |
+ ctypes.byref(GUIDs[index]), |
+ None, |
+ NULL, |
+ DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports |
+ |
+ devinfo = SP_DEVINFO_DATA() |
+ devinfo.cbSize = ctypes.sizeof(devinfo) |
+ index = 0 |
+ while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)): |
+ index += 1 |
+ |
+ # get the real com port name |
+ hkey = SetupDiOpenDevRegKey( |
+ g_hdi, |
+ ctypes.byref(devinfo), |
+ DICS_FLAG_GLOBAL, |
+ 0, |
+ DIREG_DEV, # DIREG_DRV for SW info |
+ KEY_READ) |
+ port_name_buffer = byte_buffer(250) |
+ port_name_length = ULONG(ctypes.sizeof(port_name_buffer)) |
+ RegQueryValueEx( |
+ hkey, |
+ PortName, |
+ None, |
+ None, |
+ ctypes.byref(port_name_buffer), |
+ ctypes.byref(port_name_length)) |
+ RegCloseKey(hkey) |
+ |
+ # unfortunately does this method also include parallel ports. |
+ # we could check for names starting with COM or just exclude LPT |
+ # and hope that other "unknown" names are serial ports... |
+ if string(port_name_buffer).startswith('LPT'): |
+ continue |
+ |
+ # hardware ID |
+ szHardwareID = byte_buffer(250) |
+ # try to get ID that includes serial number |
+ if not SetupDiGetDeviceInstanceId( |
+ g_hdi, |
+ ctypes.byref(devinfo), |
+ ctypes.byref(szHardwareID), |
+ ctypes.sizeof(szHardwareID) - 1, |
+ None): |
+ # fall back to more generic hardware ID if that would fail |
+ if not SetupDiGetDeviceRegistryProperty( |
+ g_hdi, |
+ ctypes.byref(devinfo), |
+ SPDRP_HARDWAREID, |
+ None, |
+ ctypes.byref(szHardwareID), |
+ ctypes.sizeof(szHardwareID) - 1, |
+ None): |
+ # Ignore ERROR_INSUFFICIENT_BUFFER |
+ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: |
+ raise ctypes.WinError() |
+ # stringify |
+ szHardwareID_str = string(szHardwareID) |
+ |
+ # in case of USB, make a more readable string, similar to that form |
+ # that we also generate on other platforms |
+ if szHardwareID_str.startswith('USB'): |
+ m = re.search(r'VID_([0-9a-f]{4})&PID_([0-9a-f]{4})(\\(\w+))?', szHardwareID_str, re.I) |
+ if m: |
+ if m.group(4): |
+ szHardwareID_str = 'USB VID:PID=%s:%s SNR=%s' % (m.group(1), m.group(2), m.group(4)) |
+ else: |
+ szHardwareID_str = 'USB VID:PID=%s:%s' % (m.group(1), m.group(2)) |
+ |
+ # friendly name |
+ szFriendlyName = byte_buffer(250) |
+ if not SetupDiGetDeviceRegistryProperty( |
+ g_hdi, |
+ ctypes.byref(devinfo), |
+ SPDRP_FRIENDLYNAME, |
+ #~ SPDRP_DEVICEDESC, |
+ None, |
+ ctypes.byref(szFriendlyName), |
+ ctypes.sizeof(szFriendlyName) - 1, |
+ None): |
+ # Ignore ERROR_INSUFFICIENT_BUFFER |
+ #~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER: |
+ #~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value)) |
+ # ignore errors and still include the port in the list, friendly name will be same as port name |
+ yield string(port_name_buffer), 'n/a', szHardwareID_str |
+ else: |
+ yield string(port_name_buffer), string(szFriendlyName), szHardwareID_str |
+ |
+ SetupDiDestroyDeviceInfoList(g_hdi) |
+ |
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
+# test |
+if __name__ == '__main__': |
+ import serial |
+ |
+ for port, desc, hwid in sorted(comports()): |
+ print "%s: %s [%s]" % (port, desc, hwid) |