PageRenderTime 56ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/languages/Python/modules/serial/tools/list_ports_posix.py

https://bitbucket.org/ipre/calico
Python | 197 lines | 174 code | 11 blank | 12 comment | 14 complexity | 55011e9820633ca424d05a78dfb48657 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0, GPL-2.0, GPL-3.0, LGPL-2.0
  1. import glob
  2. import sys
  3. import os
  4. import re
  5. try:
  6. import subprocess
  7. except ImportError:
  8. def popen(argv):
  9. try:
  10. si, so = os.popen4(' '.join(argv))
  11. return so.read().strip()
  12. except:
  13. raise IOError('lsusb failed')
  14. else:
  15. def popen(argv):
  16. try:
  17. return subprocess.check_output(argv, stderr=subprocess.STDOUT).strip()
  18. except:
  19. raise IOError('lsusb failed')
  20. # The comports function is expected to return an iterable that yields tuples of
  21. # 3 strings: port name, human readable description and a hardware ID.
  22. #
  23. # as currently no method is known to get the second two strings easily, they
  24. # are currently just identical to the port name.
  25. # try to detect the OS so that a device can be selected...
  26. plat = sys.platform.lower()
  27. def read_line(filename):
  28. """help function to read a single line from a file. returns none"""
  29. try:
  30. f = open(filename)
  31. line = f.readline().strip()
  32. f.close()
  33. return line
  34. except IOError:
  35. return None
  36. def re_group(regexp, text):
  37. """search for regexp in text, return 1st group on match"""
  38. m = re.search(regexp, text)
  39. if m: return m.group(1)
  40. if plat[:5] == 'linux': # Linux (confirmed)
  41. # try to extract descriptions from sysfs. this was done by experimenting,
  42. # no guarantee that it works for all devices or in the future...
  43. def usb_sysfs_hw_string(sysfs_path):
  44. """given a path to a usb device in sysfs, return a string describing it"""
  45. bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
  46. snr = read_line(sysfs_path+'/serial')
  47. if snr:
  48. snr_txt = ' SNR=%s' % (snr,)
  49. else:
  50. snr_txt = ''
  51. return 'USB VID:PID=%s:%s%s' % (
  52. read_line(sysfs_path+'/idVendor'),
  53. read_line(sysfs_path+'/idProduct'),
  54. snr_txt
  55. )
  56. def usb_lsusb_string(sysfs_path):
  57. bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
  58. try:
  59. desc = popen(['lsusb', '-v', '-s', '%s:%s' % (bus, dev)])
  60. # descriptions from device
  61. iManufacturer = re_group('iManufacturer\s+\w+ (.+)', desc)
  62. iProduct = re_group('iProduct\s+\w+ (.+)', desc)
  63. iSerial = re_group('iSerial\s+\w+ (.+)', desc) or ''
  64. # descriptions from kernel
  65. idVendor = re_group('idVendor\s+0x\w+ (.+)', desc)
  66. idProduct = re_group('idProduct\s+0x\w+ (.+)', desc)
  67. # create descriptions. prefer text from device, fall back to the others
  68. return '%s %s %s' % (iManufacturer or idVendor, iProduct or idProduct, iSerial)
  69. except IOError:
  70. return base
  71. def describe(device):
  72. """\
  73. Get a human readable description.
  74. For USB-Serial devices try to run lsusb to get a human readable description.
  75. For USB-CDC devices read the description from sysfs.
  76. """
  77. base = os.path.basename(device)
  78. # USB-Serial devices
  79. sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
  80. if os.path.exists(sys_dev_path):
  81. sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
  82. return usb_lsusb_string(sys_usb)
  83. # USB-CDC devices
  84. sys_dev_path = '/sys/class/tty/%s/device/interface' % (base,)
  85. if os.path.exists(sys_dev_path):
  86. return read_line(sys_dev_path)
  87. return base
  88. def hwinfo(device):
  89. """Try to get a HW identification using sysfs"""
  90. base = os.path.basename(device)
  91. if os.path.exists('/sys/class/tty/%s/device' % (base,)):
  92. # PCI based devices
  93. sys_id_path = '/sys/class/tty/%s/device/id' % (base,)
  94. if os.path.exists(sys_id_path):
  95. return read_line(sys_id_path)
  96. # USB-Serial devices
  97. sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
  98. if os.path.exists(sys_dev_path):
  99. sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
  100. return usb_sysfs_hw_string(sys_usb)
  101. # USB-CDC devices
  102. if base.startswith('ttyACM'):
  103. sys_dev_path = '/sys/class/tty/%s/device' % (base,)
  104. if os.path.exists(sys_dev_path):
  105. return usb_sysfs_hw_string(sys_dev_path + '/..')
  106. return 'n/a' # XXX directly remove these from the list?
  107. def comports():
  108. devices = glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*')
  109. return [(d, describe(d), hwinfo(d)) for d in devices]
  110. elif plat == 'cygwin': # cygwin/win32
  111. def comports():
  112. devices = glob.glob('/dev/com*')
  113. return [(d, d, d) for d in devices]
  114. elif plat == 'openbsd3': # BSD
  115. def comports():
  116. devices = glob.glob('/dev/ttyp*')
  117. return [(d, d, d) for d in devices]
  118. elif plat[:3] == 'bsd' or \
  119. plat[:7] == 'freebsd' or \
  120. plat[:7] == 'openbsd': # BSD
  121. def comports():
  122. devices = glob.glob('/dev/cuad*')
  123. return [(d, d, d) for d in devices]
  124. elif plat[:6] == 'darwin': # OS X (confirmed)
  125. def comports():
  126. """scan for available ports. return a list of device names."""
  127. devices = glob.glob('/dev/tty.*')
  128. return [(d, d, d) for d in devices]
  129. elif plat[:6] == 'netbsd': # NetBSD
  130. def comports():
  131. """scan for available ports. return a list of device names."""
  132. devices = glob.glob('/dev/dty*')
  133. return [(d, d, d) for d in devices]
  134. elif plat[:4] == 'irix': # IRIX
  135. def comports():
  136. """scan for available ports. return a list of device names."""
  137. devices = glob.glob('/dev/ttyf*')
  138. return [(d, d, d) for d in devices]
  139. elif plat[:2] == 'hp': # HP-UX (not tested)
  140. def comports():
  141. """scan for available ports. return a list of device names."""
  142. devices = glob.glob('/dev/tty*p0')
  143. return [(d, d, d) for d in devices]
  144. elif plat[:5] == 'sunos': # Solaris/SunOS
  145. def comports():
  146. """scan for available ports. return a list of device names."""
  147. devices = glob.glob('/dev/tty*c')
  148. return [(d, d, d) for d in devices]
  149. elif plat[:3] == 'aix': # AIX
  150. def comports():
  151. """scan for available ports. return a list of device names."""
  152. devices = glob.glob('/dev/tty*')
  153. return [(d, d, d) for d in devices]
  154. else:
  155. # platform detection has failed...
  156. sys.stderr.write("""\
  157. don't know how to enumerate ttys on this system.
  158. ! I you know how the serial ports are named send this information to
  159. ! the author of this module:
  160. sys.platform = %r
  161. os.name = %r
  162. pySerial version = %s
  163. also add the naming scheme of the serial ports and with a bit luck you can get
  164. this module running...
  165. """ % (sys.platform, os.name, serial.VERSION))
  166. raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
  167. # test
  168. if __name__ == '__main__':
  169. for port, desc, hwid in sorted(comports()):
  170. print "%s: %s [%s]" % (port, desc, hwid)