summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcliechti <cliechti@f19166aa-fa4f-0410-85c2-fa1106f25c8a>2011-08-19 01:20:44 +0000
committercliechti <cliechti@f19166aa-fa4f-0410-85c2-fa1106f25c8a>2011-08-19 01:20:44 +0000
commit909c1828e429d0bb21cc62800e0c183efc0459cc (patch)
treef94113700465000eda31a2d44029fd43eac548c2
parentf7721db43929b2290594c9ab6daef197ca87c814 (diff)
downloadpyserial-909c1828e429d0bb21cc62800e0c183efc0459cc.tar.gz
extract detailed hardware info on Linux
git-svn-id: http://svn.code.sf.net/p/pyserial/code/trunk/pyserial@414 f19166aa-fa4f-0410-85c2-fa1106f25c8a
-rw-r--r--serial/tools/list_ports_posix.py108
1 files changed, 107 insertions, 1 deletions
diff --git a/serial/tools/list_ports_posix.py b/serial/tools/list_ports_posix.py
index 2fb655b..ebfa4ef 100644
--- a/serial/tools/list_ports_posix.py
+++ b/serial/tools/list_ports_posix.py
@@ -1,5 +1,24 @@
import glob
import sys
+import os
+import re
+
+try:
+ import subprocess
+except ImportError:
+ def popen(argv):
+ try:
+ si, so = os.popen2(' '.join(argv))
+ return so.read().strip()
+ except:
+ raise IOError('lsusb failed')
+else:
+ def popen(argv):
+ try:
+ return subprocess.check_output(argv).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.
@@ -10,10 +29,97 @@ import sys
# 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, d, d) for d in devices]
+ return [(d, describe(d), hwinfo(d)) for d in devices]
elif plat == 'cygwin': # cygwin/win32
def comports():