Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
521 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
#!/usr/bin/env python | ||
|
||
# portable serial port access with python | ||
# this is a wrapper module for different platform implementations of the | ||
# port enumeration feature | ||
# | ||
# (C) 2011 Chris Liechti <cliechti@gmx.net> | ||
# this is distributed under a free software license, see license.txt | ||
|
||
"""\ | ||
This module will provide a function called comports that returns an | ||
iterable (generator or list) that will enumerate available com ports. Note that | ||
on some systems non-existent ports may be listed. | ||
Additionally a grep function is supplied that can be used to search for ports | ||
based on their descriptions or hardware ID. | ||
""" | ||
|
||
import sys, os, re | ||
|
||
# chose an implementation, depending on os | ||
#~ if sys.platform == 'cli': | ||
#~ else: | ||
import os | ||
# chose an implementation, depending on os | ||
if os.name == 'nt': #sys.platform == 'win32': | ||
from serial_list_ports_windows import * | ||
elif os.name == 'posix': | ||
from serial_list_ports_posix import * | ||
#~ elif os.name == 'java': | ||
else: | ||
raise ImportError("Sorry: no implementation for your platform ('%s') available" % (os.name,)) | ||
|
||
|
||
def grep(regexp): | ||
"""\ | ||
Search for ports using a regular expression. Port name, description and | ||
hardware ID are searched. The function returns an iterable that returns the | ||
same tuples as comport() would do. | ||
""" | ||
for port, desc, hwid in comports(): | ||
if re.search(regexp, port, re.I) or re.search(regexp, desc) or re.search(regexp, hwid): | ||
yield port, desc, hwid | ||
|
||
|
||
def main(): | ||
import optparse | ||
|
||
parser = optparse.OptionParser( | ||
usage = "%prog [options] [<regexp>]", | ||
description = "Miniterm - A simple terminal program for the serial port." | ||
) | ||
|
||
parser.add_option("--debug", | ||
help="print debug messages and tracebacks (development mode)", | ||
dest="debug", | ||
default=False, | ||
action='store_true') | ||
|
||
parser.add_option("-v", "--verbose", | ||
help="show more messages (can be given multiple times)", | ||
dest="verbose", | ||
default=1, | ||
action='count') | ||
|
||
parser.add_option("-q", "--quiet", | ||
help="suppress all messages", | ||
dest="verbose", | ||
action='store_const', | ||
const=0) | ||
|
||
(options, args) = parser.parse_args() | ||
|
||
|
||
hits = 0 | ||
# get iteraror w/ or w/o filter | ||
if args: | ||
if len(args) > 1: | ||
parser.error('more than one regexp not supported') | ||
print "Filtered list with regexp: %r" % (args[0],) | ||
iterator = sorted(grep(args[0])) | ||
else: | ||
iterator = sorted(comports()) | ||
# list them | ||
for port, desc, hwid in iterator: | ||
print "%-20s" % (port,) | ||
if options.verbose > 1: | ||
print " desc: %s" % (desc,) | ||
print " hwid: %s" % (hwid,) | ||
hits += 1 | ||
if options.verbose: | ||
if hits: | ||
print "%d ports found" % (hits,) | ||
else: | ||
print "no ports found" | ||
|
||
# test | ||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
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) |
Oops, something went wrong.