• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# This is a helper module for the various platform dependent list_port
4# implementations.
5#
6# This file is part of pySerial. https://github.com/pyserial/pyserial
7# (C) 2015 Chris Liechti <cliechti@gmx.net>
8#
9# SPDX-License-Identifier:    BSD-3-Clause
10
11from __future__ import absolute_import
12
13import re
14import glob
15import os
16import os.path
17
18
19def numsplit(text):
20    """\
21    Convert string into a list of texts and numbers in order to support a
22    natural sorting.
23    """
24    result = []
25    for group in re.split(r'(\d+)', text):
26        if group:
27            try:
28                group = int(group)
29            except ValueError:
30                pass
31            result.append(group)
32    return result
33
34
35class ListPortInfo(object):
36    """Info collection base class for serial ports"""
37
38    def __init__(self, device, skip_link_detection=False):
39        self.device = device
40        self.name = os.path.basename(device)
41        self.description = 'n/a'
42        self.hwid = 'n/a'
43        # USB specific data
44        self.vid = None
45        self.pid = None
46        self.serial_number = None
47        self.location = None
48        self.manufacturer = None
49        self.product = None
50        self.interface = None
51        # special handling for links
52        if not skip_link_detection and device is not None and os.path.islink(device):
53            self.hwid = 'LINK={}'.format(os.path.realpath(device))
54
55    def usb_description(self):
56        """return a short string to name the port based on USB info"""
57        if self.interface is not None:
58            return '{} - {}'.format(self.product, self.interface)
59        elif self.product is not None:
60            return self.product
61        else:
62            return self.name
63
64    def usb_info(self):
65        """return a string with USB related information about device"""
66        return 'USB VID:PID={:04X}:{:04X}{}{}'.format(
67            self.vid or 0,
68            self.pid or 0,
69            ' SER={}'.format(self.serial_number) if self.serial_number is not None else '',
70            ' LOCATION={}'.format(self.location) if self.location is not None else '')
71
72    def apply_usb_info(self):
73        """update description and hwid from USB data"""
74        self.description = self.usb_description()
75        self.hwid = self.usb_info()
76
77    def __eq__(self, other):
78        return isinstance(other, ListPortInfo) and self.device == other.device
79
80    def __hash__(self):
81        return hash(self.device)
82
83    def __lt__(self, other):
84        if not isinstance(other, ListPortInfo):
85            raise TypeError('unorderable types: {}() and {}()'.format(
86                type(self).__name__,
87                type(other).__name__))
88        return numsplit(self.device) < numsplit(other.device)
89
90    def __str__(self):
91        return '{} - {}'.format(self.device, self.description)
92
93    def __getitem__(self, index):
94        """Item access: backwards compatible -> (port, desc, hwid)"""
95        if index == 0:
96            return self.device
97        elif index == 1:
98            return self.description
99        elif index == 2:
100            return self.hwid
101        else:
102            raise IndexError('{} > 2'.format(index))
103
104
105# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106def list_links(devices):
107    """\
108    search all /dev devices and look for symlinks to known ports already
109    listed in devices.
110    """
111    links = []
112    for device in glob.glob('/dev/*'):
113        if os.path.islink(device) and os.path.realpath(device) in devices:
114            links.append(device)
115    return links
116
117
118# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
119# test
120if __name__ == '__main__':
121    print(ListPortInfo('dummy'))
122