Initial commit
This commit is contained in:
0
deps/serial/tools/__init__.py
vendored
Normal file
0
deps/serial/tools/__init__.py
vendored
Normal file
126
deps/serial/tools/hexlify_codec.py
vendored
Normal file
126
deps/serial/tools/hexlify_codec.py
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
#! python
|
||||
#
|
||||
# This is a codec to create and decode hexdumps with spaces between characters. used by miniterm.
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2015-2016 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
"""\
|
||||
Python 'hex' Codec - 2-digit hex with spaces content transfer encoding.
|
||||
|
||||
Encode and decode may be a bit missleading at first sight...
|
||||
|
||||
The textual representation is a hex dump: e.g. "40 41"
|
||||
The "encoded" data of this is the binary form, e.g. b"@A"
|
||||
|
||||
Therefore decoding is binary to text and thus converting binary data to hex dump.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import codecs
|
||||
import serial
|
||||
|
||||
|
||||
try:
|
||||
unicode
|
||||
except (NameError, AttributeError):
|
||||
unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name
|
||||
|
||||
|
||||
HEXDIGITS = '0123456789ABCDEF'
|
||||
|
||||
|
||||
# Codec APIs
|
||||
|
||||
def hex_encode(data, errors='strict'):
|
||||
"""'40 41 42' -> b'@ab'"""
|
||||
return (serial.to_bytes([int(h, 16) for h in data.split()]), len(data))
|
||||
|
||||
|
||||
def hex_decode(data, errors='strict'):
|
||||
"""b'@ab' -> '40 41 42'"""
|
||||
return (unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data))), len(data))
|
||||
|
||||
|
||||
class Codec(codecs.Codec):
|
||||
def encode(self, data, errors='strict'):
|
||||
"""'40 41 42' -> b'@ab'"""
|
||||
return serial.to_bytes([int(h, 16) for h in data.split()])
|
||||
|
||||
def decode(self, data, errors='strict'):
|
||||
"""b'@ab' -> '40 41 42'"""
|
||||
return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
|
||||
|
||||
|
||||
class IncrementalEncoder(codecs.IncrementalEncoder):
|
||||
"""Incremental hex encoder"""
|
||||
|
||||
def __init__(self, errors='strict'):
|
||||
self.errors = errors
|
||||
self.state = 0
|
||||
|
||||
def reset(self):
|
||||
self.state = 0
|
||||
|
||||
def getstate(self):
|
||||
return self.state
|
||||
|
||||
def setstate(self, state):
|
||||
self.state = state
|
||||
|
||||
def encode(self, data, final=False):
|
||||
"""\
|
||||
Incremental encode, keep track of digits and emit a byte when a pair
|
||||
of hex digits is found. The space is optional unless the error
|
||||
handling is defined to be 'strict'.
|
||||
"""
|
||||
state = self.state
|
||||
encoded = []
|
||||
for c in data.upper():
|
||||
if c in HEXDIGITS:
|
||||
z = HEXDIGITS.index(c)
|
||||
if state:
|
||||
encoded.append(z + (state & 0xf0))
|
||||
state = 0
|
||||
else:
|
||||
state = 0x100 + (z << 4)
|
||||
elif c == ' ': # allow spaces to separate values
|
||||
if state and self.errors == 'strict':
|
||||
raise UnicodeError('odd number of hex digits')
|
||||
state = 0
|
||||
else:
|
||||
if self.errors == 'strict':
|
||||
raise UnicodeError('non-hex digit found: {!r}'.format(c))
|
||||
self.state = state
|
||||
return serial.to_bytes(encoded)
|
||||
|
||||
|
||||
class IncrementalDecoder(codecs.IncrementalDecoder):
|
||||
"""Incremental decoder"""
|
||||
def decode(self, data, final=False):
|
||||
return unicode(''.join('{:02X} '.format(ord(b)) for b in serial.iterbytes(data)))
|
||||
|
||||
|
||||
class StreamWriter(Codec, codecs.StreamWriter):
|
||||
"""Combination of hexlify codec and StreamWriter"""
|
||||
|
||||
|
||||
class StreamReader(Codec, codecs.StreamReader):
|
||||
"""Combination of hexlify codec and StreamReader"""
|
||||
|
||||
|
||||
def getregentry():
|
||||
"""encodings module API"""
|
||||
return codecs.CodecInfo(
|
||||
name='hexlify',
|
||||
encode=hex_encode,
|
||||
decode=hex_decode,
|
||||
incrementalencoder=IncrementalEncoder,
|
||||
incrementaldecoder=IncrementalDecoder,
|
||||
streamwriter=StreamWriter,
|
||||
streamreader=StreamReader,
|
||||
#~ _is_text_encoding=True,
|
||||
)
|
||||
110
deps/serial/tools/list_ports.py
vendored
Normal file
110
deps/serial/tools/list_ports.py
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Serial port enumeration. Console tool and backend selection.
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""\
|
||||
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.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
|
||||
# chose an implementation, depending on os
|
||||
#~ if sys.platform == 'cli':
|
||||
#~ else:
|
||||
if os.name == 'nt': # sys.platform == 'win32':
|
||||
from serial.tools.list_ports_windows import comports
|
||||
elif os.name == 'posix':
|
||||
from serial.tools.list_ports_posix import comports
|
||||
#~ elif os.name == 'java':
|
||||
else:
|
||||
raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
|
||||
def grep(regexp, include_links=False):
|
||||
"""\
|
||||
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.
|
||||
"""
|
||||
r = re.compile(regexp, re.I)
|
||||
for info in comports(include_links):
|
||||
port, desc, hwid = info
|
||||
if r.search(port) or r.search(desc) or r.search(hwid):
|
||||
yield info
|
||||
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Serial port enumeration')
|
||||
|
||||
parser.add_argument(
|
||||
'regexp',
|
||||
nargs='?',
|
||||
help='only show ports that match this regex')
|
||||
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
action='store_true',
|
||||
help='show more messages')
|
||||
|
||||
parser.add_argument(
|
||||
'-q', '--quiet',
|
||||
action='store_true',
|
||||
help='suppress all messages')
|
||||
|
||||
parser.add_argument(
|
||||
'-n',
|
||||
type=int,
|
||||
help='only output the N-th entry')
|
||||
|
||||
parser.add_argument(
|
||||
'-s', '--include-links',
|
||||
action='store_true',
|
||||
help='include entries that are symlinks to real devices')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
hits = 0
|
||||
# get iteraror w/ or w/o filter
|
||||
if args.regexp:
|
||||
if not args.quiet:
|
||||
sys.stderr.write("Filtered list with regexp: {!r}\n".format(args.regexp))
|
||||
iterator = sorted(grep(args.regexp, include_links=args.include_links))
|
||||
else:
|
||||
iterator = sorted(comports(include_links=args.include_links))
|
||||
# list them
|
||||
for n, (port, desc, hwid) in enumerate(iterator, 1):
|
||||
if args.n is None or args.n == n:
|
||||
sys.stdout.write("{:20}\n".format(port))
|
||||
if args.verbose:
|
||||
sys.stdout.write(" desc: {}\n".format(desc))
|
||||
sys.stdout.write(" hwid: {}\n".format(hwid))
|
||||
hits += 1
|
||||
if not args.quiet:
|
||||
if hits:
|
||||
sys.stderr.write("{} ports found\n".format(hits))
|
||||
else:
|
||||
sys.stderr.write("no ports found\n")
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
121
deps/serial/tools/list_ports_common.py
vendored
Normal file
121
deps/serial/tools/list_ports_common.py
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This is a helper module for the various platform dependent list_port
|
||||
# implementations.
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2015 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import glob
|
||||
import os
|
||||
import os.path
|
||||
|
||||
|
||||
def numsplit(text):
|
||||
"""\
|
||||
Convert string into a list of texts and numbers in order to support a
|
||||
natural sorting.
|
||||
"""
|
||||
result = []
|
||||
for group in re.split(r'(\d+)', text):
|
||||
if group:
|
||||
try:
|
||||
group = int(group)
|
||||
except ValueError:
|
||||
pass
|
||||
result.append(group)
|
||||
return result
|
||||
|
||||
|
||||
class ListPortInfo(object):
|
||||
"""Info collection base class for serial ports"""
|
||||
|
||||
def __init__(self, device, skip_link_detection=False):
|
||||
self.device = device
|
||||
self.name = os.path.basename(device)
|
||||
self.description = 'n/a'
|
||||
self.hwid = 'n/a'
|
||||
# USB specific data
|
||||
self.vid = None
|
||||
self.pid = None
|
||||
self.serial_number = None
|
||||
self.location = None
|
||||
self.manufacturer = None
|
||||
self.product = None
|
||||
self.interface = None
|
||||
# special handling for links
|
||||
if not skip_link_detection and device is not None and os.path.islink(device):
|
||||
self.hwid = 'LINK={}'.format(os.path.realpath(device))
|
||||
|
||||
def usb_description(self):
|
||||
"""return a short string to name the port based on USB info"""
|
||||
if self.interface is not None:
|
||||
return '{} - {}'.format(self.product, self.interface)
|
||||
elif self.product is not None:
|
||||
return self.product
|
||||
else:
|
||||
return self.name
|
||||
|
||||
def usb_info(self):
|
||||
"""return a string with USB related information about device"""
|
||||
return 'USB VID:PID={:04X}:{:04X}{}{}'.format(
|
||||
self.vid or 0,
|
||||
self.pid or 0,
|
||||
' SER={}'.format(self.serial_number) if self.serial_number is not None else '',
|
||||
' LOCATION={}'.format(self.location) if self.location is not None else '')
|
||||
|
||||
def apply_usb_info(self):
|
||||
"""update description and hwid from USB data"""
|
||||
self.description = self.usb_description()
|
||||
self.hwid = self.usb_info()
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, ListPortInfo) and self.device == other.device
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.device)
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, ListPortInfo):
|
||||
raise TypeError('unorderable types: {}() and {}()'.format(
|
||||
type(self).__name__,
|
||||
type(other).__name__))
|
||||
return numsplit(self.device) < numsplit(other.device)
|
||||
|
||||
def __str__(self):
|
||||
return '{} - {}'.format(self.device, self.description)
|
||||
|
||||
def __getitem__(self, index):
|
||||
"""Item access: backwards compatible -> (port, desc, hwid)"""
|
||||
if index == 0:
|
||||
return self.device
|
||||
elif index == 1:
|
||||
return self.description
|
||||
elif index == 2:
|
||||
return self.hwid
|
||||
else:
|
||||
raise IndexError('{} > 2'.format(index))
|
||||
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
def list_links(devices):
|
||||
"""\
|
||||
search all /dev devices and look for symlinks to known ports already
|
||||
listed in devices.
|
||||
"""
|
||||
links = []
|
||||
for device in glob.glob('/dev/*'):
|
||||
if os.path.islink(device) and os.path.realpath(device) in devices:
|
||||
links.append(device)
|
||||
return links
|
||||
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
print(ListPortInfo('dummy'))
|
||||
109
deps/serial/tools/list_ports_linux.py
vendored
Normal file
109
deps/serial/tools/list_ports_linux.py
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This is a module that gathers a list of serial ports including details on
|
||||
# GNU/Linux systems.
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import glob
|
||||
import os
|
||||
from serial.tools import list_ports_common
|
||||
|
||||
|
||||
class SysFS(list_ports_common.ListPortInfo):
|
||||
"""Wrapper for easy sysfs access and device info"""
|
||||
|
||||
def __init__(self, device):
|
||||
super(SysFS, self).__init__(device)
|
||||
# special handling for links
|
||||
if device is not None and os.path.islink(device):
|
||||
device = os.path.realpath(device)
|
||||
is_link = True
|
||||
else:
|
||||
is_link = False
|
||||
self.usb_device_path = None
|
||||
if os.path.exists('/sys/class/tty/{}/device'.format(self.name)):
|
||||
self.device_path = os.path.realpath('/sys/class/tty/{}/device'.format(self.name))
|
||||
self.subsystem = os.path.basename(os.path.realpath(os.path.join(self.device_path, 'subsystem')))
|
||||
else:
|
||||
self.device_path = None
|
||||
self.subsystem = None
|
||||
# check device type
|
||||
if self.subsystem == 'usb-serial':
|
||||
self.usb_interface_path = os.path.dirname(self.device_path)
|
||||
elif self.subsystem == 'usb':
|
||||
self.usb_interface_path = self.device_path
|
||||
else:
|
||||
self.usb_interface_path = None
|
||||
# fill-in info for USB devices
|
||||
if self.usb_interface_path is not None:
|
||||
self.usb_device_path = os.path.dirname(self.usb_interface_path)
|
||||
|
||||
try:
|
||||
num_if = int(self.read_line(self.usb_device_path, 'bNumInterfaces'))
|
||||
except ValueError:
|
||||
num_if = 1
|
||||
|
||||
self.vid = int(self.read_line(self.usb_device_path, 'idVendor'), 16)
|
||||
self.pid = int(self.read_line(self.usb_device_path, 'idProduct'), 16)
|
||||
self.serial_number = self.read_line(self.usb_device_path, 'serial')
|
||||
if num_if > 1: # multi interface devices like FT4232
|
||||
self.location = os.path.basename(self.usb_interface_path)
|
||||
else:
|
||||
self.location = os.path.basename(self.usb_device_path)
|
||||
|
||||
self.manufacturer = self.read_line(self.usb_device_path, 'manufacturer')
|
||||
self.product = self.read_line(self.usb_device_path, 'product')
|
||||
self.interface = self.read_line(self.usb_interface_path, 'interface')
|
||||
|
||||
if self.subsystem in ('usb', 'usb-serial'):
|
||||
self.apply_usb_info()
|
||||
#~ elif self.subsystem in ('pnp', 'amba'): # PCI based devices, raspi
|
||||
elif self.subsystem == 'pnp': # PCI based devices
|
||||
self.description = self.name
|
||||
self.hwid = self.read_line(self.device_path, 'id')
|
||||
elif self.subsystem == 'amba': # raspi
|
||||
self.description = self.name
|
||||
self.hwid = os.path.basename(self.device_path)
|
||||
|
||||
if is_link:
|
||||
self.hwid += ' LINK={}'.format(device)
|
||||
|
||||
def read_line(self, *args):
|
||||
"""\
|
||||
Helper function to read a single line from a file.
|
||||
One or more parameters are allowed, they are joined with os.path.join.
|
||||
Returns None on errors..
|
||||
"""
|
||||
try:
|
||||
with open(os.path.join(*args)) as f:
|
||||
line = f.readline().strip()
|
||||
return line
|
||||
except IOError:
|
||||
return None
|
||||
|
||||
|
||||
def comports(include_links=False):
|
||||
devices = glob.glob('/dev/ttyS*') # built-in serial ports
|
||||
devices.extend(glob.glob('/dev/ttyUSB*')) # usb-serial with own driver
|
||||
devices.extend(glob.glob('/dev/ttyXRUSB*')) # xr-usb-serial port exar (DELL Edge 3001)
|
||||
devices.extend(glob.glob('/dev/ttyACM*')) # usb-serial with CDC-ACM profile
|
||||
devices.extend(glob.glob('/dev/ttyAMA*')) # ARM internal port (raspi)
|
||||
devices.extend(glob.glob('/dev/rfcomm*')) # BT serial devices
|
||||
devices.extend(glob.glob('/dev/ttyAP*')) # Advantech multi-port serial controllers
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [info
|
||||
for info in [SysFS(d) for d in devices]
|
||||
if info.subsystem != "platform"] # hide non-present internal serial ports
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
for info in sorted(comports()):
|
||||
print("{0}: {0.subsystem}".format(info))
|
||||
299
deps/serial/tools/list_ports_osx.py
vendored
Normal file
299
deps/serial/tools/list_ports_osx.py
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This is a module that gathers a list of serial ports including details on OSX
|
||||
#
|
||||
# code originally from https://github.com/makerbot/pyserial/tree/master/serial/tools
|
||||
# with contributions from cibomahto, dgs3, FarMcKon, tedbrandston
|
||||
# and modifications by cliechti, hoihu, hardkrash
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2013-2020
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
|
||||
# List all of the callout devices in OS/X by querying IOKit.
|
||||
|
||||
# See the following for a reference of how to do this:
|
||||
# http://developer.apple.com/library/mac/#documentation/DeviceDrivers/Conceptual/WorkingWSerial/WWSerial_SerialDevs/SerialDevices.html#//apple_ref/doc/uid/TP30000384-CIHGEAFD
|
||||
|
||||
# More help from darwin_hid.py
|
||||
|
||||
# Also see the 'IORegistryExplorer' for an idea of what we are actually searching
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import ctypes
|
||||
|
||||
from serial.tools import list_ports_common
|
||||
|
||||
iokit = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/IOKit.framework/IOKit')
|
||||
cf = ctypes.cdll.LoadLibrary('/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation')
|
||||
|
||||
# kIOMasterPortDefault is no longer exported in BigSur but no biggie, using NULL works just the same
|
||||
kIOMasterPortDefault = 0 # WAS: ctypes.c_void_p.in_dll(iokit, "kIOMasterPortDefault")
|
||||
kCFAllocatorDefault = ctypes.c_void_p.in_dll(cf, "kCFAllocatorDefault")
|
||||
|
||||
kCFStringEncodingMacRoman = 0
|
||||
kCFStringEncodingUTF8 = 0x08000100
|
||||
|
||||
# defined in `IOKit/usb/USBSpec.h`
|
||||
kUSBVendorString = 'USB Vendor Name'
|
||||
kUSBSerialNumberString = 'USB Serial Number'
|
||||
|
||||
# `io_name_t` defined as `typedef char io_name_t[128];`
|
||||
# in `device/device_types.h`
|
||||
io_name_size = 128
|
||||
|
||||
# defined in `mach/kern_return.h`
|
||||
KERN_SUCCESS = 0
|
||||
# kern_return_t defined as `typedef int kern_return_t;` in `mach/i386/kern_return.h`
|
||||
kern_return_t = ctypes.c_int
|
||||
|
||||
iokit.IOServiceMatching.restype = ctypes.c_void_p
|
||||
|
||||
iokit.IOServiceGetMatchingServices.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
iokit.IOServiceGetMatchingServices.restype = kern_return_t
|
||||
|
||||
iokit.IORegistryEntryGetParentEntry.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
iokit.IOServiceGetMatchingServices.restype = kern_return_t
|
||||
|
||||
iokit.IORegistryEntryCreateCFProperty.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint32]
|
||||
iokit.IORegistryEntryCreateCFProperty.restype = ctypes.c_void_p
|
||||
|
||||
iokit.IORegistryEntryGetPath.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
|
||||
iokit.IORegistryEntryGetPath.restype = kern_return_t
|
||||
|
||||
iokit.IORegistryEntryGetName.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
iokit.IORegistryEntryGetName.restype = kern_return_t
|
||||
|
||||
iokit.IOObjectGetClass.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
|
||||
iokit.IOObjectGetClass.restype = kern_return_t
|
||||
|
||||
iokit.IOObjectRelease.argtypes = [ctypes.c_void_p]
|
||||
|
||||
|
||||
cf.CFStringCreateWithCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_int32]
|
||||
cf.CFStringCreateWithCString.restype = ctypes.c_void_p
|
||||
|
||||
cf.CFStringGetCStringPtr.argtypes = [ctypes.c_void_p, ctypes.c_uint32]
|
||||
cf.CFStringGetCStringPtr.restype = ctypes.c_char_p
|
||||
|
||||
cf.CFStringGetCString.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_long, ctypes.c_uint32]
|
||||
cf.CFStringGetCString.restype = ctypes.c_bool
|
||||
|
||||
cf.CFNumberGetValue.argtypes = [ctypes.c_void_p, ctypes.c_uint32, ctypes.c_void_p]
|
||||
cf.CFNumberGetValue.restype = ctypes.c_void_p
|
||||
|
||||
# void CFRelease ( CFTypeRef cf );
|
||||
cf.CFRelease.argtypes = [ctypes.c_void_p]
|
||||
cf.CFRelease.restype = None
|
||||
|
||||
# CFNumber type defines
|
||||
kCFNumberSInt8Type = 1
|
||||
kCFNumberSInt16Type = 2
|
||||
kCFNumberSInt32Type = 3
|
||||
kCFNumberSInt64Type = 4
|
||||
|
||||
|
||||
def get_string_property(device_type, property):
|
||||
"""
|
||||
Search the given device for the specified string property
|
||||
|
||||
@param device_type Type of Device
|
||||
@param property String to search for
|
||||
@return Python string containing the value, or None if not found.
|
||||
"""
|
||||
key = cf.CFStringCreateWithCString(
|
||||
kCFAllocatorDefault,
|
||||
property.encode("utf-8"),
|
||||
kCFStringEncodingUTF8)
|
||||
|
||||
CFContainer = iokit.IORegistryEntryCreateCFProperty(
|
||||
device_type,
|
||||
key,
|
||||
kCFAllocatorDefault,
|
||||
0)
|
||||
output = None
|
||||
|
||||
if CFContainer:
|
||||
output = cf.CFStringGetCStringPtr(CFContainer, 0)
|
||||
if output is not None:
|
||||
output = output.decode('utf-8')
|
||||
else:
|
||||
buffer = ctypes.create_string_buffer(io_name_size);
|
||||
success = cf.CFStringGetCString(CFContainer, ctypes.byref(buffer), io_name_size, kCFStringEncodingUTF8)
|
||||
if success:
|
||||
output = buffer.value.decode('utf-8')
|
||||
cf.CFRelease(CFContainer)
|
||||
return output
|
||||
|
||||
|
||||
def get_int_property(device_type, property, cf_number_type):
|
||||
"""
|
||||
Search the given device for the specified string property
|
||||
|
||||
@param device_type Device to search
|
||||
@param property String to search for
|
||||
@param cf_number_type CFType number
|
||||
|
||||
@return Python string containing the value, or None if not found.
|
||||
"""
|
||||
key = cf.CFStringCreateWithCString(
|
||||
kCFAllocatorDefault,
|
||||
property.encode("utf-8"),
|
||||
kCFStringEncodingUTF8)
|
||||
|
||||
CFContainer = iokit.IORegistryEntryCreateCFProperty(
|
||||
device_type,
|
||||
key,
|
||||
kCFAllocatorDefault,
|
||||
0)
|
||||
|
||||
if CFContainer:
|
||||
if (cf_number_type == kCFNumberSInt32Type):
|
||||
number = ctypes.c_uint32()
|
||||
elif (cf_number_type == kCFNumberSInt16Type):
|
||||
number = ctypes.c_uint16()
|
||||
cf.CFNumberGetValue(CFContainer, cf_number_type, ctypes.byref(number))
|
||||
cf.CFRelease(CFContainer)
|
||||
return number.value
|
||||
return None
|
||||
|
||||
def IORegistryEntryGetName(device):
|
||||
devicename = ctypes.create_string_buffer(io_name_size);
|
||||
res = iokit.IORegistryEntryGetName(device, ctypes.byref(devicename))
|
||||
if res != KERN_SUCCESS:
|
||||
return None
|
||||
# this works in python2 but may not be valid. Also I don't know if
|
||||
# this encoding is guaranteed. It may be dependent on system locale.
|
||||
return devicename.value.decode('utf-8')
|
||||
|
||||
def IOObjectGetClass(device):
|
||||
classname = ctypes.create_string_buffer(io_name_size)
|
||||
iokit.IOObjectGetClass(device, ctypes.byref(classname))
|
||||
return classname.value
|
||||
|
||||
def GetParentDeviceByType(device, parent_type):
|
||||
""" Find the first parent of a device that implements the parent_type
|
||||
@param IOService Service to inspect
|
||||
@return Pointer to the parent type, or None if it was not found.
|
||||
"""
|
||||
# First, try to walk up the IOService tree to find a parent of this device that is a IOUSBDevice.
|
||||
parent_type = parent_type.encode('utf-8')
|
||||
while IOObjectGetClass(device) != parent_type:
|
||||
parent = ctypes.c_void_p()
|
||||
response = iokit.IORegistryEntryGetParentEntry(
|
||||
device,
|
||||
"IOService".encode("utf-8"),
|
||||
ctypes.byref(parent))
|
||||
# If we weren't able to find a parent for the device, we're done.
|
||||
if response != KERN_SUCCESS:
|
||||
return None
|
||||
device = parent
|
||||
return device
|
||||
|
||||
|
||||
def GetIOServicesByType(service_type):
|
||||
"""
|
||||
returns iterator over specified service_type
|
||||
"""
|
||||
serial_port_iterator = ctypes.c_void_p()
|
||||
|
||||
iokit.IOServiceGetMatchingServices(
|
||||
kIOMasterPortDefault,
|
||||
iokit.IOServiceMatching(service_type.encode('utf-8')),
|
||||
ctypes.byref(serial_port_iterator))
|
||||
|
||||
services = []
|
||||
while iokit.IOIteratorIsValid(serial_port_iterator):
|
||||
service = iokit.IOIteratorNext(serial_port_iterator)
|
||||
if not service:
|
||||
break
|
||||
services.append(service)
|
||||
iokit.IOObjectRelease(serial_port_iterator)
|
||||
return services
|
||||
|
||||
|
||||
def location_to_string(locationID):
|
||||
"""
|
||||
helper to calculate port and bus number from locationID
|
||||
"""
|
||||
loc = ['{}-'.format(locationID >> 24)]
|
||||
while locationID & 0xf00000:
|
||||
if len(loc) > 1:
|
||||
loc.append('.')
|
||||
loc.append('{}'.format((locationID >> 20) & 0xf))
|
||||
locationID <<= 4
|
||||
return ''.join(loc)
|
||||
|
||||
|
||||
class SuitableSerialInterface(object):
|
||||
pass
|
||||
|
||||
|
||||
def scan_interfaces():
|
||||
"""
|
||||
helper function to scan USB interfaces
|
||||
returns a list of SuitableSerialInterface objects with name and id attributes
|
||||
"""
|
||||
interfaces = []
|
||||
for service in GetIOServicesByType('IOSerialBSDClient'):
|
||||
device = get_string_property(service, "IOCalloutDevice")
|
||||
if device:
|
||||
usb_device = GetParentDeviceByType(service, "IOUSBInterface")
|
||||
if usb_device:
|
||||
name = get_string_property(usb_device, "USB Interface Name") or None
|
||||
locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type) or ''
|
||||
i = SuitableSerialInterface()
|
||||
i.id = locationID
|
||||
i.name = name
|
||||
interfaces.append(i)
|
||||
return interfaces
|
||||
|
||||
|
||||
def search_for_locationID_in_interfaces(serial_interfaces, locationID):
|
||||
for interface in serial_interfaces:
|
||||
if (interface.id == locationID):
|
||||
return interface.name
|
||||
return None
|
||||
|
||||
|
||||
def comports(include_links=False):
|
||||
# XXX include_links is currently ignored. are links in /dev even supported here?
|
||||
# Scan for all iokit serial ports
|
||||
services = GetIOServicesByType('IOSerialBSDClient')
|
||||
ports = []
|
||||
serial_interfaces = scan_interfaces()
|
||||
for service in services:
|
||||
# First, add the callout device file.
|
||||
device = get_string_property(service, "IOCalloutDevice")
|
||||
if device:
|
||||
info = list_ports_common.ListPortInfo(device)
|
||||
# If the serial port is implemented by IOUSBDevice
|
||||
# NOTE IOUSBDevice was deprecated as of 10.11 and finally on Apple Silicon
|
||||
# devices has been completely removed. Thanks to @oskay for this patch.
|
||||
usb_device = GetParentDeviceByType(service, "IOUSBHostDevice")
|
||||
if not usb_device:
|
||||
usb_device = GetParentDeviceByType(service, "IOUSBDevice")
|
||||
if usb_device:
|
||||
# fetch some useful informations from properties
|
||||
info.vid = get_int_property(usb_device, "idVendor", kCFNumberSInt16Type)
|
||||
info.pid = get_int_property(usb_device, "idProduct", kCFNumberSInt16Type)
|
||||
info.serial_number = get_string_property(usb_device, kUSBSerialNumberString)
|
||||
# We know this is a usb device, so the
|
||||
# IORegistryEntryName should always be aliased to the
|
||||
# usb product name string descriptor.
|
||||
info.product = IORegistryEntryGetName(usb_device) or 'n/a'
|
||||
info.manufacturer = get_string_property(usb_device, kUSBVendorString)
|
||||
locationID = get_int_property(usb_device, "locationID", kCFNumberSInt32Type)
|
||||
info.location = location_to_string(locationID)
|
||||
info.interface = search_for_locationID_in_interfaces(serial_interfaces, locationID)
|
||||
info.apply_usb_info()
|
||||
ports.append(info)
|
||||
return ports
|
||||
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
for port, desc, hwid in sorted(comports()):
|
||||
print("{}: {} [{}]".format(port, desc, hwid))
|
||||
119
deps/serial/tools/list_ports_posix.py
vendored
Normal file
119
deps/serial/tools/list_ports_posix.py
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# This is a module that gathers a list of serial ports on POSIXy systems.
|
||||
# For some specific implementations, see also list_ports_linux, list_ports_osx
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2011-2015 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
"""\
|
||||
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.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import glob
|
||||
import sys
|
||||
import os
|
||||
from serial.tools import list_ports_common
|
||||
|
||||
# try to detect the OS so that a device can be selected...
|
||||
plat = sys.platform.lower()
|
||||
|
||||
if plat[:5] == 'linux': # Linux (confirmed) # noqa
|
||||
from serial.tools.list_ports_linux import comports
|
||||
|
||||
elif plat[:6] == 'darwin': # OS X (confirmed)
|
||||
from serial.tools.list_ports_osx import comports
|
||||
|
||||
elif plat == 'cygwin': # cygwin/win32
|
||||
# cygwin accepts /dev/com* in many contexts
|
||||
# (such as 'open' call, explicit 'ls'), but 'glob.glob'
|
||||
# and bare 'ls' do not; so use /dev/ttyS* instead
|
||||
def comports(include_links=False):
|
||||
devices = glob.glob('/dev/ttyS*')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:7] == 'openbsd': # OpenBSD
|
||||
def comports(include_links=False):
|
||||
devices = glob.glob('/dev/cua*')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:3] == 'bsd' or plat[:7] == 'freebsd':
|
||||
def comports(include_links=False):
|
||||
devices = glob.glob('/dev/cua*[!.init][!.lock]')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:6] == 'netbsd': # NetBSD
|
||||
def comports(include_links=False):
|
||||
"""scan for available ports. return a list of device names."""
|
||||
devices = glob.glob('/dev/dty*')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:4] == 'irix': # IRIX
|
||||
def comports(include_links=False):
|
||||
"""scan for available ports. return a list of device names."""
|
||||
devices = glob.glob('/dev/ttyf*')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:2] == 'hp': # HP-UX (not tested)
|
||||
def comports(include_links=False):
|
||||
"""scan for available ports. return a list of device names."""
|
||||
devices = glob.glob('/dev/tty*p0')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:5] == 'sunos': # Solaris/SunOS
|
||||
def comports(include_links=False):
|
||||
"""scan for available ports. return a list of device names."""
|
||||
devices = glob.glob('/dev/tty*c')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
elif plat[:3] == 'aix': # AIX
|
||||
def comports(include_links=False):
|
||||
"""scan for available ports. return a list of device names."""
|
||||
devices = glob.glob('/dev/tty*')
|
||||
if include_links:
|
||||
devices.extend(list_ports_common.list_links(devices))
|
||||
return [list_ports_common.ListPortInfo(d) for d in devices]
|
||||
|
||||
else:
|
||||
# platform detection has failed...
|
||||
import serial
|
||||
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 = {}
|
||||
|
||||
also add the naming scheme of the serial ports and with a bit luck you can get
|
||||
this module running...
|
||||
""".format(sys.platform, os.name, serial.VERSION))
|
||||
raise ImportError("Sorry: no implementation for your platform ('{}') available".format(os.name))
|
||||
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
for port, desc, hwid in sorted(comports()):
|
||||
print("{}: {} [{}]".format(port, desc, hwid))
|
||||
427
deps/serial/tools/list_ports_windows.py
vendored
Normal file
427
deps/serial/tools/list_ports_windows.py
vendored
Normal file
@@ -0,0 +1,427 @@
|
||||
#! python
|
||||
#
|
||||
# Enumerate serial ports on Windows including a human readable description
|
||||
# and hardware information.
|
||||
#
|
||||
# This file is part of pySerial. https://github.com/pyserial/pyserial
|
||||
# (C) 2001-2016 Chris Liechti <cliechti@gmx.net>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
# pylint: disable=invalid-name,too-few-public-methods
|
||||
import re
|
||||
import ctypes
|
||||
from ctypes.wintypes import BOOL
|
||||
from ctypes.wintypes import HWND
|
||||
from ctypes.wintypes import DWORD
|
||||
from ctypes.wintypes import WORD
|
||||
from ctypes.wintypes import LONG
|
||||
from ctypes.wintypes import ULONG
|
||||
from ctypes.wintypes import HKEY
|
||||
from ctypes.wintypes import BYTE
|
||||
import serial
|
||||
from serial.win32 import ULONG_PTR
|
||||
from serial.tools import list_ports_common
|
||||
|
||||
|
||||
def ValidHandle(value, func, arguments):
|
||||
if value == 0:
|
||||
raise ctypes.WinError()
|
||||
return value
|
||||
|
||||
|
||||
NULL = 0
|
||||
HDEVINFO = ctypes.c_void_p
|
||||
LPCTSTR = ctypes.c_wchar_p
|
||||
PCTSTR = ctypes.c_wchar_p
|
||||
PTSTR = ctypes.c_wchar_p
|
||||
LPDWORD = PDWORD = ctypes.POINTER(DWORD)
|
||||
#~ LPBYTE = PBYTE = ctypes.POINTER(BYTE)
|
||||
LPBYTE = PBYTE = ctypes.c_void_p # XXX avoids error about types
|
||||
|
||||
ACCESS_MASK = DWORD
|
||||
REGSAM = ACCESS_MASK
|
||||
|
||||
|
||||
class GUID(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('Data1', DWORD),
|
||||
('Data2', WORD),
|
||||
('Data3', WORD),
|
||||
('Data4', BYTE * 8),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return "{{{:08x}-{:04x}-{:04x}-{}-{}}}".format(
|
||||
self.Data1,
|
||||
self.Data2,
|
||||
self.Data3,
|
||||
''.join(["{:02x}".format(d) for d in self.Data4[:2]]),
|
||||
''.join(["{:02x}".format(d) for d in self.Data4[2:]]),
|
||||
)
|
||||
|
||||
|
||||
class SP_DEVINFO_DATA(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('cbSize', DWORD),
|
||||
('ClassGuid', GUID),
|
||||
('DevInst', DWORD),
|
||||
('Reserved', ULONG_PTR),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return "ClassGuid:{} DevInst:{}".format(self.ClassGuid, self.DevInst)
|
||||
|
||||
|
||||
PSP_DEVINFO_DATA = ctypes.POINTER(SP_DEVINFO_DATA)
|
||||
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA = ctypes.c_void_p
|
||||
|
||||
setupapi = ctypes.windll.LoadLibrary("setupapi")
|
||||
SetupDiDestroyDeviceInfoList = setupapi.SetupDiDestroyDeviceInfoList
|
||||
SetupDiDestroyDeviceInfoList.argtypes = [HDEVINFO]
|
||||
SetupDiDestroyDeviceInfoList.restype = BOOL
|
||||
|
||||
SetupDiClassGuidsFromName = setupapi.SetupDiClassGuidsFromNameW
|
||||
SetupDiClassGuidsFromName.argtypes = [PCTSTR, ctypes.POINTER(GUID), DWORD, PDWORD]
|
||||
SetupDiClassGuidsFromName.restype = BOOL
|
||||
|
||||
SetupDiEnumDeviceInfo = setupapi.SetupDiEnumDeviceInfo
|
||||
SetupDiEnumDeviceInfo.argtypes = [HDEVINFO, DWORD, PSP_DEVINFO_DATA]
|
||||
SetupDiEnumDeviceInfo.restype = BOOL
|
||||
|
||||
SetupDiGetClassDevs = setupapi.SetupDiGetClassDevsW
|
||||
SetupDiGetClassDevs.argtypes = [ctypes.POINTER(GUID), PCTSTR, HWND, DWORD]
|
||||
SetupDiGetClassDevs.restype = HDEVINFO
|
||||
SetupDiGetClassDevs.errcheck = ValidHandle
|
||||
|
||||
SetupDiGetDeviceRegistryProperty = setupapi.SetupDiGetDeviceRegistryPropertyW
|
||||
SetupDiGetDeviceRegistryProperty.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD]
|
||||
SetupDiGetDeviceRegistryProperty.restype = BOOL
|
||||
|
||||
SetupDiGetDeviceInstanceId = setupapi.SetupDiGetDeviceInstanceIdW
|
||||
SetupDiGetDeviceInstanceId.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, PTSTR, DWORD, PDWORD]
|
||||
SetupDiGetDeviceInstanceId.restype = BOOL
|
||||
|
||||
SetupDiOpenDevRegKey = setupapi.SetupDiOpenDevRegKey
|
||||
SetupDiOpenDevRegKey.argtypes = [HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM]
|
||||
SetupDiOpenDevRegKey.restype = HKEY
|
||||
|
||||
advapi32 = ctypes.windll.LoadLibrary("Advapi32")
|
||||
RegCloseKey = advapi32.RegCloseKey
|
||||
RegCloseKey.argtypes = [HKEY]
|
||||
RegCloseKey.restype = LONG
|
||||
|
||||
RegQueryValueEx = advapi32.RegQueryValueExW
|
||||
RegQueryValueEx.argtypes = [HKEY, LPCTSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD]
|
||||
RegQueryValueEx.restype = LONG
|
||||
|
||||
cfgmgr32 = ctypes.windll.LoadLibrary("Cfgmgr32")
|
||||
CM_Get_Parent = cfgmgr32.CM_Get_Parent
|
||||
CM_Get_Parent.argtypes = [PDWORD, DWORD, ULONG]
|
||||
CM_Get_Parent.restype = LONG
|
||||
|
||||
CM_Get_Device_IDW = cfgmgr32.CM_Get_Device_IDW
|
||||
CM_Get_Device_IDW.argtypes = [DWORD, PTSTR, ULONG, ULONG]
|
||||
CM_Get_Device_IDW.restype = LONG
|
||||
|
||||
CM_MapCrToWin32Err = cfgmgr32.CM_MapCrToWin32Err
|
||||
CM_MapCrToWin32Err.argtypes = [DWORD, DWORD]
|
||||
CM_MapCrToWin32Err.restype = DWORD
|
||||
|
||||
|
||||
DIGCF_PRESENT = 2
|
||||
DIGCF_DEVICEINTERFACE = 16
|
||||
INVALID_HANDLE_VALUE = 0
|
||||
ERROR_INSUFFICIENT_BUFFER = 122
|
||||
ERROR_NOT_FOUND = 1168
|
||||
SPDRP_HARDWAREID = 1
|
||||
SPDRP_FRIENDLYNAME = 12
|
||||
SPDRP_LOCATION_PATHS = 35
|
||||
SPDRP_MFG = 11
|
||||
DICS_FLAG_GLOBAL = 1
|
||||
DIREG_DEV = 0x00000001
|
||||
KEY_READ = 0x20019
|
||||
|
||||
|
||||
MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH = 5
|
||||
|
||||
|
||||
def get_parent_serial_number(child_devinst, child_vid, child_pid, depth=0, last_serial_number=None):
|
||||
""" Get the serial number of the parent of a device.
|
||||
|
||||
Args:
|
||||
child_devinst: The device instance handle to get the parent serial number of.
|
||||
child_vid: The vendor ID of the child device.
|
||||
child_pid: The product ID of the child device.
|
||||
depth: The current iteration depth of the USB device tree.
|
||||
"""
|
||||
|
||||
# If the traversal depth is beyond the max, abandon attempting to find the serial number.
|
||||
if depth > MAX_USB_DEVICE_TREE_TRAVERSAL_DEPTH:
|
||||
return '' if not last_serial_number else last_serial_number
|
||||
|
||||
# Get the parent device instance.
|
||||
devinst = DWORD()
|
||||
ret = CM_Get_Parent(ctypes.byref(devinst), child_devinst, 0)
|
||||
|
||||
if ret:
|
||||
win_error = CM_MapCrToWin32Err(DWORD(ret), DWORD(0))
|
||||
|
||||
# If there is no parent available, the child was the root device. We cannot traverse
|
||||
# further.
|
||||
if win_error == ERROR_NOT_FOUND:
|
||||
return '' if not last_serial_number else last_serial_number
|
||||
|
||||
raise ctypes.WinError(win_error)
|
||||
|
||||
# Get the ID of the parent device and parse it for vendor ID, product ID, and serial number.
|
||||
parentHardwareID = ctypes.create_unicode_buffer(250)
|
||||
|
||||
ret = CM_Get_Device_IDW(
|
||||
devinst,
|
||||
parentHardwareID,
|
||||
ctypes.sizeof(parentHardwareID) - 1,
|
||||
0)
|
||||
|
||||
if ret:
|
||||
raise ctypes.WinError(CM_MapCrToWin32Err(DWORD(ret), DWORD(0)))
|
||||
|
||||
parentHardwareID_str = parentHardwareID.value
|
||||
m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?',
|
||||
parentHardwareID_str,
|
||||
re.I)
|
||||
|
||||
# return early if we have no matches (likely malformed serial, traversed too far)
|
||||
if not m:
|
||||
return '' if not last_serial_number else last_serial_number
|
||||
|
||||
vid = None
|
||||
pid = None
|
||||
serial_number = None
|
||||
if m.group(1):
|
||||
vid = int(m.group(1), 16)
|
||||
if m.group(3):
|
||||
pid = int(m.group(3), 16)
|
||||
if m.group(7):
|
||||
serial_number = m.group(7)
|
||||
|
||||
# store what we found as a fallback for malformed serial values up the chain
|
||||
found_serial_number = serial_number
|
||||
|
||||
# Check that the USB serial number only contains alpha-numeric characters. It may be a windows
|
||||
# device ID (ephemeral ID).
|
||||
if serial_number and not re.match(r'^\w+$', serial_number):
|
||||
serial_number = None
|
||||
|
||||
if not vid or not pid:
|
||||
# If pid and vid are not available at this device level, continue to the parent.
|
||||
return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
|
||||
|
||||
if pid != child_pid or vid != child_vid:
|
||||
# If the VID or PID has changed, we are no longer looking at the same physical device. The
|
||||
# serial number is unknown.
|
||||
return '' if not last_serial_number else last_serial_number
|
||||
|
||||
# In this case, the vid and pid of the parent device are identical to the child. However, if
|
||||
# there still isn't a serial number available, continue to the next parent.
|
||||
if not serial_number:
|
||||
return get_parent_serial_number(devinst, child_vid, child_pid, depth + 1, found_serial_number)
|
||||
|
||||
# Finally, the VID and PID are identical to the child and a serial number is present, so return
|
||||
# it.
|
||||
return serial_number
|
||||
|
||||
|
||||
def iterate_comports():
|
||||
"""Return a generator that yields descriptions for serial ports"""
|
||||
PortsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough...
|
||||
ports_guids_size = DWORD()
|
||||
if not SetupDiClassGuidsFromName(
|
||||
"Ports",
|
||||
PortsGUIDs,
|
||||
ctypes.sizeof(PortsGUIDs),
|
||||
ctypes.byref(ports_guids_size)):
|
||||
raise ctypes.WinError()
|
||||
|
||||
ModemsGUIDs = (GUID * 8)() # so far only seen one used, so hope 8 are enough...
|
||||
modems_guids_size = DWORD()
|
||||
if not SetupDiClassGuidsFromName(
|
||||
"Modem",
|
||||
ModemsGUIDs,
|
||||
ctypes.sizeof(ModemsGUIDs),
|
||||
ctypes.byref(modems_guids_size)):
|
||||
raise ctypes.WinError()
|
||||
|
||||
GUIDs = PortsGUIDs[:ports_guids_size.value] + ModemsGUIDs[:modems_guids_size.value]
|
||||
|
||||
# repeat for all possible GUIDs
|
||||
for index in range(len(GUIDs)):
|
||||
bInterfaceNumber = None
|
||||
g_hdi = SetupDiGetClassDevs(
|
||||
ctypes.byref(GUIDs[index]),
|
||||
None,
|
||||
NULL,
|
||||
DIGCF_PRESENT) # was DIGCF_PRESENT|DIGCF_DEVICEINTERFACE which misses CDC ports
|
||||
|
||||
devinfo = SP_DEVINFO_DATA()
|
||||
devinfo.cbSize = ctypes.sizeof(devinfo)
|
||||
index = 0
|
||||
while SetupDiEnumDeviceInfo(g_hdi, index, ctypes.byref(devinfo)):
|
||||
index += 1
|
||||
|
||||
# get the real com port name
|
||||
hkey = SetupDiOpenDevRegKey(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
DICS_FLAG_GLOBAL,
|
||||
0,
|
||||
DIREG_DEV, # DIREG_DRV for SW info
|
||||
KEY_READ)
|
||||
port_name_buffer = ctypes.create_unicode_buffer(250)
|
||||
port_name_length = ULONG(ctypes.sizeof(port_name_buffer))
|
||||
RegQueryValueEx(
|
||||
hkey,
|
||||
"PortName",
|
||||
None,
|
||||
None,
|
||||
ctypes.byref(port_name_buffer),
|
||||
ctypes.byref(port_name_length))
|
||||
RegCloseKey(hkey)
|
||||
|
||||
# unfortunately does this method also include parallel ports.
|
||||
# we could check for names starting with COM or just exclude LPT
|
||||
# and hope that other "unknown" names are serial ports...
|
||||
if port_name_buffer.value.startswith('LPT'):
|
||||
continue
|
||||
|
||||
# hardware ID
|
||||
szHardwareID = ctypes.create_unicode_buffer(250)
|
||||
# try to get ID that includes serial number
|
||||
if not SetupDiGetDeviceInstanceId(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
#~ ctypes.byref(szHardwareID),
|
||||
szHardwareID,
|
||||
ctypes.sizeof(szHardwareID) - 1,
|
||||
None):
|
||||
# fall back to more generic hardware ID if that would fail
|
||||
if not SetupDiGetDeviceRegistryProperty(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
SPDRP_HARDWAREID,
|
||||
None,
|
||||
ctypes.byref(szHardwareID),
|
||||
ctypes.sizeof(szHardwareID) - 1,
|
||||
None):
|
||||
# Ignore ERROR_INSUFFICIENT_BUFFER
|
||||
if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
|
||||
raise ctypes.WinError()
|
||||
# stringify
|
||||
szHardwareID_str = szHardwareID.value
|
||||
|
||||
info = list_ports_common.ListPortInfo(port_name_buffer.value, skip_link_detection=True)
|
||||
|
||||
# in case of USB, make a more readable string, similar to that form
|
||||
# that we also generate on other platforms
|
||||
if szHardwareID_str.startswith('USB'):
|
||||
m = re.search(r'VID_([0-9a-f]{4})(&PID_([0-9a-f]{4}))?(&MI_(\d{2}))?(\\(.*))?', szHardwareID_str, re.I)
|
||||
if m:
|
||||
info.vid = int(m.group(1), 16)
|
||||
if m.group(3):
|
||||
info.pid = int(m.group(3), 16)
|
||||
if m.group(5):
|
||||
bInterfaceNumber = int(m.group(5))
|
||||
|
||||
# Check that the USB serial number only contains alpha-numeric characters. It
|
||||
# may be a windows device ID (ephemeral ID) for composite devices.
|
||||
if m.group(7) and re.match(r'^\w+$', m.group(7)):
|
||||
info.serial_number = m.group(7)
|
||||
else:
|
||||
info.serial_number = get_parent_serial_number(devinfo.DevInst, info.vid, info.pid)
|
||||
|
||||
# calculate a location string
|
||||
loc_path_str = ctypes.create_unicode_buffer(250)
|
||||
if SetupDiGetDeviceRegistryProperty(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
SPDRP_LOCATION_PATHS,
|
||||
None,
|
||||
ctypes.byref(loc_path_str),
|
||||
ctypes.sizeof(loc_path_str) - 1,
|
||||
None):
|
||||
m = re.finditer(r'USBROOT\((\w+)\)|#USB\((\w+)\)', loc_path_str.value)
|
||||
location = []
|
||||
for g in m:
|
||||
if g.group(1):
|
||||
location.append('{:d}'.format(int(g.group(1)) + 1))
|
||||
else:
|
||||
if len(location) > 1:
|
||||
location.append('.')
|
||||
else:
|
||||
location.append('-')
|
||||
location.append(g.group(2))
|
||||
if bInterfaceNumber is not None:
|
||||
location.append(':{}.{}'.format(
|
||||
'x', # XXX how to determine correct bConfigurationValue?
|
||||
bInterfaceNumber))
|
||||
if location:
|
||||
info.location = ''.join(location)
|
||||
info.hwid = info.usb_info()
|
||||
elif szHardwareID_str.startswith('FTDIBUS'):
|
||||
m = re.search(r'VID_([0-9a-f]{4})\+PID_([0-9a-f]{4})(\+(\w+))?', szHardwareID_str, re.I)
|
||||
if m:
|
||||
info.vid = int(m.group(1), 16)
|
||||
info.pid = int(m.group(2), 16)
|
||||
if m.group(4):
|
||||
info.serial_number = m.group(4)
|
||||
# USB location is hidden by FDTI driver :(
|
||||
info.hwid = info.usb_info()
|
||||
else:
|
||||
info.hwid = szHardwareID_str
|
||||
|
||||
# friendly name
|
||||
szFriendlyName = ctypes.create_unicode_buffer(250)
|
||||
if SetupDiGetDeviceRegistryProperty(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
SPDRP_FRIENDLYNAME,
|
||||
#~ SPDRP_DEVICEDESC,
|
||||
None,
|
||||
ctypes.byref(szFriendlyName),
|
||||
ctypes.sizeof(szFriendlyName) - 1,
|
||||
None):
|
||||
info.description = szFriendlyName.value
|
||||
#~ else:
|
||||
# Ignore ERROR_INSUFFICIENT_BUFFER
|
||||
#~ if ctypes.GetLastError() != ERROR_INSUFFICIENT_BUFFER:
|
||||
#~ raise IOError("failed to get details for %s (%s)" % (devinfo, szHardwareID.value))
|
||||
# ignore errors and still include the port in the list, friendly name will be same as port name
|
||||
|
||||
# manufacturer
|
||||
szManufacturer = ctypes.create_unicode_buffer(250)
|
||||
if SetupDiGetDeviceRegistryProperty(
|
||||
g_hdi,
|
||||
ctypes.byref(devinfo),
|
||||
SPDRP_MFG,
|
||||
#~ SPDRP_DEVICEDESC,
|
||||
None,
|
||||
ctypes.byref(szManufacturer),
|
||||
ctypes.sizeof(szManufacturer) - 1,
|
||||
None):
|
||||
info.manufacturer = szManufacturer.value
|
||||
yield info
|
||||
SetupDiDestroyDeviceInfoList(g_hdi)
|
||||
|
||||
|
||||
def comports(include_links=False):
|
||||
"""Return a list of info objects about serial ports"""
|
||||
return list(iterate_comports())
|
||||
|
||||
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
# test
|
||||
if __name__ == '__main__':
|
||||
for port, desc, hwid in sorted(comports()):
|
||||
print("{}: {} [{}]".format(port, desc, hwid))
|
||||
1042
deps/serial/tools/miniterm.py
vendored
Normal file
1042
deps/serial/tools/miniterm.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user