testing commit ability
[goodfet] / client / scanwin32.py
1 # Win32 serial port scanner from PySerial.
2 # Modified by Andrew Q Righter for the GoodFET Project.
3
4 import ctypes
5 import re
6 import serial
7
8 def ValidHandle(value):
9     if value == 0:
10         raise ctypes.WinError()
11     return value
12
13 NULL = 0
14 HDEVINFO = ctypes.c_int
15 BOOL = ctypes.c_int
16 CHAR = ctypes.c_char
17 PCTSTR = ctypes.c_char_p
18 HWND = ctypes.c_uint
19 DWORD = ctypes.c_ulong
20 PDWORD = ctypes.POINTER(DWORD)
21 ULONG = ctypes.c_ulong
22 ULONG_PTR = ctypes.POINTER(ULONG)
23 #~ PBYTE = ctypes.c_char_p
24 PBYTE = ctypes.c_void_p
25
26 class GUID(ctypes.Structure):
27     _fields_ = [
28         ('Data1', ctypes.c_ulong),
29         ('Data2', ctypes.c_ushort),
30         ('Data3', ctypes.c_ushort),
31         ('Data4', ctypes.c_ubyte*8),
32     ]
33     def __str__(self):
34         return "{%08x-%04x-%04x-%s-%s}" % (
35             self.Data1,
36             self.Data2,
37             self.Data3,
38             ''.join(["%02x" % d for d in self.Data4[:2]]),
39             ''.join(["%02x" % d for d in self.Data4[2:]]),
40         )
41
42 class SP_DEVINFO_DATA(ctypes.Structure):
43     _fields_ = [
44         ('cbSize', DWORD),
45         ('ClassGuid', GUID),
46         ('DevInst', DWORD),
47         ('Reserved', ULONG_PTR),
48     ]
49     def __str__(self):
50         return "ClassGuid:%s DevInst:%s" % (self.ClassGuid, self.DevInst)
51 PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
52
53 class SP_DEVICE_INTERFACE_DATA(ctypes.Structure):
54     _fields_ = [
55         ('cbSize', DWORD),
56         ('InterfaceClassGuid', GUID),
57         ('Flags', DWORD),
58         ('Reserved', ULONG_PTR),
59     ]
60     def __str__(self):
61         return "InterfaceClassGuid:%s Flags:%s" % (self.InterfaceClassGuid, self.Flags)
62
63 PSP_DEVICE_INTERFACE_DATA = ctypes.POINTER(SP_DEVICE_INTERFACE_DATA)
64
65 PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
66
67 class dummy(ctypes.Structure):
68     _fields_=[("d1", DWORD), ("d2", CHAR)]
69     _pack_ = 1
70 SIZEOF_SP_DEVICE_INTERFACE_DETAIL_DATA_A = ctypes.sizeof(dummy)
71
72 SetupDiDestroyDeviceInfoList = ctypes.windll.setupapi.SetupDiDestroyDeviceInfoList
73 SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
74 SetupDiDestroyDeviceInfoList.restype = BOOL
75
76 SetupDiGetClassDevs = ctypes.windll.setupapi.SetupDiGetClassDevsA
77 SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
78 SetupDiGetClassDevs.restype = ValidHandle # HDEVINFO
79
80 SetupDiEnumDeviceInterfaces = ctypes.windll.setupapi.SetupDiEnumDeviceInterfaces
81 SetupDiEnumDeviceInterfaces.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, ctypes.POINTER(GUID), DWORD, PSP_DEVICE_INTERFACE_DATA]
82 SetupDiEnumDeviceInterfaces.restype = BOOL
83
84 SetupDiGetDeviceInterfaceDetail = ctypes.windll.setupapi.SetupDiGetDeviceInterfaceDetailA
85 SetupDiGetDeviceInterfaceDetail.argtypes = [HDEVINFO, PSP_DEVICE_INTERFACE_DATA, PSP_DEVICE_INTERFACE_DETAIL_DATA, DWORD, PDWORD, PSP_DEVINFO_DATA]
86 SetupDiGetDeviceInterfaceDetail.restype = BOOL
87
88 SetupDiGetDeviceRegistryProperty = ctypes.windll.setupapi.SetupDiGetDeviceRegistryPropertyA
89 SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
90 SetupDiGetDeviceRegistryProperty.restype = BOOL
91
92
93 GUID_CLASS_COMPORT = GUID(0x86e0d1e0L, 0x8089, 0x11d0,
94     (ctypes.c_ubyte*8)(0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73))
95
96 DIGCF_PRESENT = 2
97 DIGCF_DEVICEINTERFACE = 16
98 INVALID_HANDLE_VALUE = 0
99 ERROR_INSUFFICIENT_BUFFER = 122
100 SPDRP_HARDWAREID = 1
101 SPDRP_FRIENDLYNAME = 12
102 SPDRP_LOCATION_INFORMATION = 13
103 ERROR_NO_MORE_ITEMS = 259
104
105 class winScan():
106
107     def comports(available_only=True):
108         """This generator scans the device registry for com ports and yields
109         (order, port, desc, hwid).  If available_only is true only return currently
110         existing ports. Order is a helper to get sorted lists. it can be ignored
111         otherwise."""
112         flags = DIGCF_DEVICEINTERFACE
113         if available_only:
114             flags |= DIGCF_PRESENT
115         g_hdi = SetupDiGetClassDevs(ctypes.byref(GUID_CLASS_COMPORT), None, NULL, flags);
116         #~ for i in range(256):
117         for dwIndex in range(256):
118             did = SP_DEVICE_INTERFACE_DATA()
119             did.cbSize = ctypes.sizeof(did)
120
121             if not SetupDiEnumDeviceInterfaces(
122                 g_hdi,
123                 None,
124                 ctypes.byref(GUID_CLASS_COMPORT),
125                 dwIndex,
126                 ctypes.byref(did)
127             ):
128                 if ctypes.GetLastError() != ERROR_NO_MORE_ITEMS:
129                     raise ctypes.WinError()
130                 break
131
132             dwNeeded = DWORD()
133             # get the size
134             if not SetupDiGetDeviceInterfaceDetail(
135                 g_hdi,
136                 ctypes.byref(did),
137                 None, 0, ctypes.byref(dwNeeded),
138                 None
139             ):
140                 # Ignore ERROR_INSUFFICIENT_BUFFER
141                 if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
142                     raise ctypes.WinError()
143             # allocate buffer
144             class SP_DEVICE_INTERFACE_DETAIL_DATA_A(ctypes.Structure):
145                 _fields_ = [
146                     ('cbSize', DWORD),
147                     ('DevicePath', CHAR*(dwNeeded.value - ctypes.sizeof(DWORD))),
148                 ]
149                 def __str__(self):
150                     return "DevicePath:%s" % (self.DevicePath,)
151             idd = SP_DEVICE_INTERFACE_DETAIL_DATA_A()
152             idd.cbSize = SIZEOF_SP_DEVICE_INTERFACE_DETAIL_DATA_A
153             devinfo = SP_DEVINFO_DATA()
154             devinfo.cbSize = ctypes.sizeof(devinfo)
155             if not SetupDiGetDeviceInterfaceDetail(
156                 g_hdi,
157                 ctypes.byref(did),
158                 ctypes.byref(idd), dwNeeded, None,
159                 ctypes.byref(devinfo)
160             ):
161                 raise ctypes.WinError()
162
163             # hardware ID
164             szHardwareID = ctypes.create_string_buffer(250)
165             if not SetupDiGetDeviceRegistryProperty(
166                 g_hdi,
167                 ctypes.byref(devinfo),
168                 SPDRP_HARDWAREID,
169                 None,
170                 ctypes.byref(szHardwareID), ctypes.sizeof(szHardwareID) - 1,
171                 None
172             ):
173                 # Ignore ERROR_INSUFFICIENT_BUFFER
174                 if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
175                     raise ctypes.WinError()
176
177             # friendly name
178             szFriendlyName = ctypes.create_string_buffer(1024)
179             if not SetupDiGetDeviceRegistryProperty(
180                 g_hdi,
181                 ctypes.byref(devinfo),
182                 SPDRP_FRIENDLYNAME,
183                 None,
184                 ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1,
185                 None
186             ):
187                 # Ignore ERROR_INSUFFICIENT_BUFFER
188                 if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
189                     #~ raise ctypes.WinError()
190                     # not getting friendly name for com0com devices, try something else
191                     szFriendlyName = ctypes.create_string_buffer(1024)
192                     if SetupDiGetDeviceRegistryProperty(
193                         g_hdi,
194                         ctypes.byref(devinfo),
195                         SPDRP_LOCATION_INFORMATION,
196                         None,
197                         ctypes.byref(szFriendlyName), ctypes.sizeof(szFriendlyName) - 1,
198                         None
199                     ):
200                         port_name = "\\\\.\\" + szFriendlyName.value
201                         order = None
202                     else:
203                         port_name = szFriendlyName.value
204                         order = None
205             else:
206                 try:
207                     m = re.search(r"\((.*?(\d+))\)", szFriendlyName.value)
208                     #~ print szFriendlyName.value, m.groups()
209                     port_name = m.group(1)
210                     order = int(m.group(2))
211                 except AttributeError, msg:
212                     port_name = szFriendlyName.value
213                     order = None
214             yield order, port_name, szFriendlyName.value, szHardwareID.value
215
216         SetupDiDestroyDeviceInfoList(g_hdi)
217
218 '''
219 if __name__ == '__main__':
220     import serial
221     #print "-"*78
222     #print "Serial ports"
223     #print "-"*78
224
225     for order, port, desc, hwid in sorted(comports()):
226         print "%-10s: %s (%s) ->" % (port, desc, hwid),
227         try:
228             serial.Serial(port) # test open
229         except serial.serialutil.SerialException:
230             print "Can't be openend"
231         else:
232             print "Ready"
233     print
234
235     #
236     # ADDED ITERATORS TO LIST FOR CONVENIENCE
237     #
238     aList = []
239     for order, port, desc, hwid in comports():
240         aList.append(port)
241         aList.append(desc)
242         aList.append(hwid)
243     print aList
244
245     try:
246         serial.Serial(port)
247     except serial.serialutil.SerialException:
248         print "Can not be opened."
249     else:
250         print "Ready"
251      
252     #
253     # List of all ports the system knows (registry)
254     #
255     print "-"*78
256     print "All serial ports (registry)"
257     print "-"*78
258     for order, port, desc, hwid in sorted(comports(False)):
259         print "%-10s: %s (%s)" % (port, desc, hwid)
260 '''