1
2
3
4
5
6
7
8
9
10
11
12
13 """Detect and enumerate com(serial) ports
14
15 You should close all com ports you have open before calling any
16 functions in this module. If you don't they will be detected as in
17 use.
18
19 Call the comscan() function It returns a list with each entry being a
20 dictionary of useful information. See the platform notes for what is
21 in each one.
22
23 For your convenience the entries in the list are also sorted into
24 an order that would make sense to the user.
25
26 Platform Notes:
27 ===============
28
29 w Windows9x
30 W WindowsNT/2K/XP
31 L Linux
32 M Mac
33
34 wWLM name string Serial device name
35 wWLM available Bool True if it is possible to open this device
36 wW active Bool Is the driver actually running? An example of when this is False
37 is USB devices or Bluetooth etc that aren't currently plugged in.
38 If you are presenting stuff for users, do not show entries where
39 this is false
40 w driverstatus dict status is some random number, problem is non-zero if there is some
41 issue (eg device disabled)
42 wW hardwareinstance string instance of the device in the registry as named by Windows
43 wWLM description string a friendly name to show users
44 wW driverdate tuple (year, month, day)
45 W driverversion string version string
46 wW driverprovider string the manufacturer of the device driver
47 wW driverdescription string some generic description of the driver
48 L device tuple (major, minor) device specification
49 L driver string the driver name from /proc/devices (eg ttyS or ttyUSB)
50 """
51
52 from __future__ import with_statement
53
54 version="$Revision: 4368 $"
55
56 import sys
57 import os
58 import time
59 import glob
60
63
65 return sys.platform.startswith('linux')
66
68 return sys.platform.startswith('darwin')
69
70 if _IsWindows():
71 import _winreg
72 import win32file
73 import win32con
74
76 """A class that is significantly easier to use to access the Registry"""
77 - def __init__(self, hive=_winreg.HKEY_LOCAL_MACHINE):
78 self.rootkey=_winreg.ConnectRegistry(None, hive)
79
81 """Returns a list of the child nodes of a key"""
82 k=_winreg.OpenKey(self.rootkey, key)
83 index=0
84 res=[]
85 while 1:
86 try:
87 subkey=_winreg.EnumKey(k, index)
88 res.append(subkey)
89 index+=1
90 except:
91
92 break
93 return res
94
96 """Doesn't throw exception if doesn't exist
97
98 @return: A list of zero or more items"""
99 try:
100 k=_winreg.OpenKey(self.rootkey, key)
101 except:
102 return []
103 index=0
104 res=[]
105 while 1:
106 try:
107 subkey=_winreg.EnumKey(k, index)
108 res.append(subkey)
109 index+=1
110 except WindowsError,e:
111 if e[0]==259:
112 break
113 elif e[0]==234:
114 index+=1
115 continue
116 raise
117 return res
118
119
121 """Gets a value
122
123 The result is returned as the correct type (string, int, etc)"""
124 k=_winreg.OpenKey(self.rootkey, key)
125 v,t=_winreg.QueryValueEx(k, node)
126 if t==2:
127 return int(v)
128 if t==3:
129
130 res=0
131 mult=1
132 for i in v:
133 res+=ord(i)*mult
134 mult*=256
135 return res
136
137 if isinstance(v, unicode):
138 try:
139 return str(v)
140 except:
141 pass
142 return v
143
145 """Gets a value and if nothing is found returns the default"""
146 try:
147 return self.getvalue(key, node)
148 except:
149 return default
150
151 - def findkey(self, start, lookfor, prependresult=""):
152 """Searches for the named key"""
153 res=[]
154 for i in self.getchildren(start):
155 if i==lookfor:
156 res.append(prependresult+i)
157 else:
158 l=self.findkey(start+"\\"+i, lookfor, prependresult+i+"\\")
159 res.extend(l)
160 return res
161
170
172 """Get detail about all com ports on Windows
173
174 This code functions on both win9x and nt/2k/xp"""
175
176 results={}
177 resultscount=0
178
179
180 activedrivers={}
181
182 reg=RegistryAccess(_winreg.HKEY_DYN_DATA)
183 k=r"Config Manager\Enum"
184 for device in reg.safegetchildren(k):
185 hw=reg.safegetvalue(k+"\\"+device, "hardwarekey")
186 if hw is None:
187 continue
188 status=reg.safegetvalue(k+"\\"+device, "status", -1)
189 problem=reg.safegetvalue(k+"\\"+device, "problem", -1)
190 activedrivers[hw.upper()]={ 'status': status, 'problem': problem }
191
192
193 reg=RegistryAccess(_winreg.HKEY_LOCAL_MACHINE)
194 k=r"SYSTEM\CurrentControlSet\Services"
195 for service in reg.safegetchildren(k):
196
197 count=int(reg.safegetvalue(k+"\\"+service+"\\Enum", "Count", 0))
198 next=int(reg.safegetvalue(k+"\\"+service+"\\Enum", "NextInstance", 0))
199 for id in range(max(count,next)):
200 hw=reg.safegetvalue(k+"\\"+service+"\\Enum", `id`)
201 if hw is None:
202 continue
203 activedrivers[hw.upper()]=None
204
205
206
207
208 reg=RegistryAccess(_winreg.HKEY_LOCAL_MACHINE)
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 for enumstr, driverlocation, portnamelocation in (
227 (r"SYSTEM\CurrentControlSet\Enum", r"SYSTEM\CurrentControlSet\Control\Class", r"\Device Parameters"),
228 (r"Enum", r"System\CurrentControlSet\Services\Class", ""),
229 ):
230 for category in reg.safegetchildren(enumstr):
231 catstr=enumstr+"\\"+category
232 for driver in reg.safegetchildren(catstr):
233 drvstr=catstr+"\\"+driver
234 for instance in reg.safegetchildren(drvstr):
235 inststr=drvstr+"\\"+instance
236
237
238 name=reg.safegetvalue(inststr+portnamelocation, "PORTNAME", "")
239
240
241 if len(name)<4 or name.lower()[:3]!="com":
242 continue
243
244
245 phantom=reg.safegetvalue(inststr, "Phantom", 0)
246 if phantom:
247 continue
248
249
250 klassguid=reg.safegetvalue(inststr, "ClassGUID")
251 if klassguid is not None:
252
253 klass=reg.safegetvalue(driverlocation+"\\"+klassguid, "Class")
254 else:
255
256 klass=reg.safegetvalue(inststr, "Class")
257
258 if klass is None:
259 continue
260 klass=klass.lower()
261 if klass=='ports':
262 klass='serial'
263 elif klass=='modem':
264 klass='modem'
265 else:
266 continue
267
268
269 try:
270 portnum=int(name[3:])
271 except:
272 continue
273
274
275 res={}
276
277 res['name']=name.upper()
278 res['class']=klass
279
280
281 kp=inststr[len(enumstr)+1:].upper()
282 if kp in activedrivers:
283 res['active']=True
284 if activedrivers[kp] is not None:
285 res['driverstatus']=activedrivers[kp]
286 else:
287 res['active']=False
288
289
290 if res['active']:
291 try:
292 usename=name
293 if sys.platform=='win32' and name.lower().startswith("com"):
294 usename="\\\\?\\"+name
295 print "scanning " +usename
296 ComPort = win32file.CreateFile(usename,
297 win32con.GENERIC_READ | win32con.GENERIC_WRITE, 0, None,
298 win32con.OPEN_EXISTING, win32con.FILE_ATTRIBUTE_NORMAL, None)
299 win32file.CloseHandle(ComPort)
300 res['available']=True
301 except Exception,e:
302 print usename,"is not available",e
303 res['available']=False
304 else:
305 res['available']=False
306
307
308 res['hardwareinstance']=kp
309
310
311 res['description']=reg.safegetvalue(inststr, "FriendlyName", "<No Description>")
312
313
314 drv=reg.safegetvalue(inststr, "Driver")
315
316 if drv is not None:
317 driverkey=driverlocation+"\\"+drv
318
319
320 for subkey, reskey in \
321 ("driverdate", "driverdate"), \
322 ("providername", "driverprovider"), \
323 ("driverdesc", "driverdescription"), \
324 ("driverversion", "driverversion"):
325 val=reg.safegetvalue(driverkey, subkey, None)
326 if val is None:
327 continue
328 if reskey=="driverdate":
329 try:
330 val2=val.split('-')
331 val=int(val2[2]), int(val2[0]), int(val2[1])
332 except:
333
334 continue
335 res[reskey]=val
336
337 results[resultscount]=res
338 resultscount+=1
339
340
341 return results
342
343
344
345
346
347
349 """Get all the ports on Linux
350
351 Note that Linux doesn't actually provide any way to enumerate actual ports.
352 Consequently we just look for device nodes. It still isn't possible to
353 establish if there are actual device drivers behind them. The availability
354 testing is done however.
355
356 @param maxnum: The highest numbered device to look for (eg maxnum of 17
357 will look for ttyS0 ... ttys17)
358 """
359
360
361 drivers={}
362
363
364 with file('/proc/devices', 'r') as f:
365 f.readline()
366 for line in f.readlines():
367 line=line.split()
368 if len(line)!=2:
369 break
370 major,driver=line
371 drivers[int(major)]=driver
372
373
374 devcache={}
375
376 resultscount=0
377 results={}
378 for prefix, description, klass in (
379 ("/dev/cua", "Standard serial port", "serial"),
380 ("/dev/ttyUSB", "USB to serial convertor", "serial"),
381 ("/dev/ttyACM", "USB modem", "modem"),
382 ("/dev/rfcomm", "Bluetooth", "modem"),
383 ("/dev/usb/ttyUSB", "USB to serial convertor", "serial"),
384 ("/dev/usb/tts/", "USB to serial convertor", "serial"),
385 ("/dev/usb/acm/", "USB modem", "modem"),
386 ("/dev/input/ttyACM", "USB modem", "modem")
387 ):
388 for num in range(maxnum+1):
389 name=prefix+`num`
390 if not os.path.exists(name):
391 continue
392 res={}
393 res['name']=name
394 res['class']=klass
395 res['description']=description+" ("+name+")"
396 dev=os.stat(name).st_rdev
397 try:
398 with file(name, 'rw'):
399 res['available']=True
400 except:
401 res['available']=False
402
403
404 major=(dev>>8)&0xff
405 minor=dev&0xff
406 res['device']=(major, minor)
407 if drivers.has_key(major):
408 res['driver']=drivers[major]
409
410 if res['available']:
411 if dev not in devcache or not devcache[dev][0]['available']:
412 results[resultscount]=res
413 resultscount+=1
414 devcache[dev]=[res]
415 continue
416
417 try:
418 devcache[dev].append(res)
419 except:
420 devcache[dev]=[res]
421
422 for dev in devcache:
423 if devcache[dev][0]['available']:
424 continue
425 results[resultscount]=devcache[dev][0]
426 resultscount+=1
427 return results
428
429
431 """Get all the ports on Mac
432
433 Just look for /dev/cu.* entries, they all seem to populate here whether
434 USB->Serial, builtin, bluetooth, etc...
435
436 """
437
438 resultscount=0
439 results={}
440 for name in glob.glob("/dev/cu.*"):
441 res={}
442 res['name']=name
443 if name.upper().rfind("MODEM") >= 0:
444 res['description']="Modem"+" ("+name+")"
445 res['class']="modem"
446 else:
447 res['description']="Serial"+" ("+name+")"
448 res['class']="serial"
449 try:
450 with file(name, 'rw'):
451 res['available']=True
452 except:
453 res['available']=False
454 results[resultscount]=res
455 resultscount+=1
456 return results
457
458
459
460
461
462
463
464
465
466
467
468
469
470
472 """Seperate a string and trailing number into a tuple
473
474 For example "com10" returns ("com", 10)
475 """
476 prefix=str
477 suffix=""
478
479 while len(prefix) and prefix[-1]>='0' and prefix[-1]<='9':
480 suffix=prefix[-1]+suffix
481 prefix=prefix[:-1]
482
483 if len(suffix):
484 return (prefix, int(suffix))
485 else:
486 return (prefix, None)
487
489 """Comparison function for two port names
490
491 In particular it looks for a number on the end, and sorts by the prefix (as a
492 string operation) and then by the number. This function is needed because
493 "com9" needs to come before "com10"
494 """
495
496 aa=_stringint(a[0])
497 bb=_stringint(b[0])
498
499 if aa==bb:
500 if a[1]==b[1]:
501 return 0
502 if a[1]<b[1]:
503 return -1
504 return 1
505 if aa<bb: return -1
506 return 1
507
526
527
528 if __name__=="__main__":
529 res=comscan()
530
531 output="ComScan "+version+"\n\n"
532
533 for r in res:
534 rkeys=r.keys()
535 rkeys.sort()
536
537 output+=r['name']+":\n"
538 offset=0
539 for rk in rkeys:
540 if rk=='name': continue
541 v=r[rk]
542 if not isinstance(v, type("")): v=`v`
543 op=' %s: %s ' % (rk, v)
544 if offset+len(op)>78:
545 output+="\n"+op
546 offset=len(op)+1
547 else:
548 output+=op
549 offset+=len(op)
550
551 if output[-1]!="\n":
552 output+="\n"
553 output+="\n"
554 offset=0
555
556 print output
557