/languages/Python/modules/serial/tools/list_ports_posix.py
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
- import glob
- import sys
- import os
- import re
-
- try:
- import subprocess
- except ImportError:
- def popen(argv):
- try:
- si, so = os.popen4(' '.join(argv))
- return so.read().strip()
- except:
- raise IOError('lsusb failed')
- else:
- def popen(argv):
- try:
- return subprocess.check_output(argv, stderr=subprocess.STDOUT).strip()
- except:
- raise IOError('lsusb failed')
-
-
- # The comports function is expected to return an iterable that yields tuples of
- # 3 strings: port name, human readable description and a hardware ID.
- #
- # as currently no method is known to get the second two strings easily, they
- # are currently just identical to the port name.
-
- # try to detect the OS so that a device can be selected...
- plat = sys.platform.lower()
-
- def read_line(filename):
- """help function to read a single line from a file. returns none"""
- try:
- f = open(filename)
- line = f.readline().strip()
- f.close()
- return line
- except IOError:
- return None
-
- def re_group(regexp, text):
- """search for regexp in text, return 1st group on match"""
- m = re.search(regexp, text)
- if m: return m.group(1)
-
-
- if plat[:5] == 'linux': # Linux (confirmed)
- # try to extract descriptions from sysfs. this was done by experimenting,
- # no guarantee that it works for all devices or in the future...
-
- def usb_sysfs_hw_string(sysfs_path):
- """given a path to a usb device in sysfs, return a string describing it"""
- bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
- snr = read_line(sysfs_path+'/serial')
- if snr:
- snr_txt = ' SNR=%s' % (snr,)
- else:
- snr_txt = ''
- return 'USB VID:PID=%s:%s%s' % (
- read_line(sysfs_path+'/idVendor'),
- read_line(sysfs_path+'/idProduct'),
- snr_txt
- )
-
- def usb_lsusb_string(sysfs_path):
- bus, dev = os.path.basename(os.path.realpath(sysfs_path)).split('-')
- try:
- desc = popen(['lsusb', '-v', '-s', '%s:%s' % (bus, dev)])
- # descriptions from device
- iManufacturer = re_group('iManufacturer\s+\w+ (.+)', desc)
- iProduct = re_group('iProduct\s+\w+ (.+)', desc)
- iSerial = re_group('iSerial\s+\w+ (.+)', desc) or ''
- # descriptions from kernel
- idVendor = re_group('idVendor\s+0x\w+ (.+)', desc)
- idProduct = re_group('idProduct\s+0x\w+ (.+)', desc)
- # create descriptions. prefer text from device, fall back to the others
- return '%s %s %s' % (iManufacturer or idVendor, iProduct or idProduct, iSerial)
- except IOError:
- return base
-
- def describe(device):
- """\
- Get a human readable description.
- For USB-Serial devices try to run lsusb to get a human readable description.
- For USB-CDC devices read the description from sysfs.
- """
- base = os.path.basename(device)
- # USB-Serial devices
- sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
- if os.path.exists(sys_dev_path):
- sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
- return usb_lsusb_string(sys_usb)
- # USB-CDC devices
- sys_dev_path = '/sys/class/tty/%s/device/interface' % (base,)
- if os.path.exists(sys_dev_path):
- return read_line(sys_dev_path)
- return base
-
- def hwinfo(device):
- """Try to get a HW identification using sysfs"""
- base = os.path.basename(device)
- if os.path.exists('/sys/class/tty/%s/device' % (base,)):
- # PCI based devices
- sys_id_path = '/sys/class/tty/%s/device/id' % (base,)
- if os.path.exists(sys_id_path):
- return read_line(sys_id_path)
- # USB-Serial devices
- sys_dev_path = '/sys/class/tty/%s/device/driver/%s' % (base, base)
- if os.path.exists(sys_dev_path):
- sys_usb = os.path.dirname(os.path.dirname(os.path.realpath(sys_dev_path)))
- return usb_sysfs_hw_string(sys_usb)
- # USB-CDC devices
- if base.startswith('ttyACM'):
- sys_dev_path = '/sys/class/tty/%s/device' % (base,)
- if os.path.exists(sys_dev_path):
- return usb_sysfs_hw_string(sys_dev_path + '/..')
- return 'n/a' # XXX directly remove these from the list?
-
- def comports():
- devices = glob.glob('/dev/ttyS*') + glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*')
- return [(d, describe(d), hwinfo(d)) for d in devices]
-
- elif plat == 'cygwin': # cygwin/win32
- def comports():
- devices = glob.glob('/dev/com*')
- return [(d, d, d) for d in devices]
-
- elif plat == 'openbsd3': # BSD
- def comports():
- devices = glob.glob('/dev/ttyp*')
- return [(d, d, d) for d in devices]
-
- elif plat[:3] == 'bsd' or \
- plat[:7] == 'freebsd' or \
- plat[:7] == 'openbsd': # BSD
-
- def comports():
- devices = glob.glob('/dev/cuad*')
- return [(d, d, d) for d in devices]
-
- elif plat[:6] == 'darwin': # OS X (confirmed)
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/tty.*')
- return [(d, d, d) for d in devices]
-
- elif plat[:6] == 'netbsd': # NetBSD
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/dty*')
- return [(d, d, d) for d in devices]
-
- elif plat[:4] == 'irix': # IRIX
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/ttyf*')
- return [(d, d, d) for d in devices]
-
- elif plat[:2] == 'hp': # HP-UX (not tested)
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/tty*p0')
- return [(d, d, d) for d in devices]
-
- elif plat[:5] == 'sunos': # Solaris/SunOS
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/tty*c')
- return [(d, d, d) for d in devices]
-
- elif plat[:3] == 'aix': # AIX
- def comports():
- """scan for available ports. return a list of device names."""
- devices = glob.glob('/dev/tty*')
- return [(d, d, d) for d in devices]
-
- else:
- # platform detection has failed...
- sys.stderr.write("""\
- don't know how to enumerate ttys on this system.
- ! I you know how the serial ports are named send this information to
- ! the author of this module:
-
- sys.platform = %r
- os.name = %r
- pySerial version = %s
-
- also add the naming scheme of the serial ports and with a bit luck you can get
- this module running...
- """ % (sys.platform, os.name, serial.VERSION))
- raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,))
-
- # test
- if __name__ == '__main__':
- for port, desc, hwid in sorted(comports()):
- print "%s: %s [%s]" % (port, desc, hwid)