|
15 | 15 | limitations under the License.
|
16 | 16 | """
|
17 | 17 |
|
18 |
| -import re |
19 |
| -import subprocess |
20 |
| -import platform |
21 |
| -try: |
22 |
| - from plistlib import loads |
23 |
| -except ImportError: |
24 |
| - from plistlib import readPlistFromString as loads |
25 |
| -from xml.parsers.expat import ExpatError |
26 |
| - |
27 |
| -from .lstools_base import MbedLsToolsBase |
28 |
| - |
29 |
| -import logging |
30 |
| -logger = logging.getLogger("mbedls.lstools_darwin") |
31 |
| -logger.addHandler(logging.NullHandler()) |
32 |
| -DEBUG = logging.DEBUG |
33 |
| -del logging |
34 |
| - |
35 |
| -mbed_volume_name_match = re.compile(r'\b(mbed|SEGGER MSD|ATMEL EDBG Media)\b', re.I) |
36 |
| - |
37 |
| - |
38 |
| -def _plist_from_popen(popen): |
39 |
| - out, _ = popen.communicate() |
40 |
| - if not out: |
41 |
| - return [] |
42 |
| - try: |
43 |
| - return loads(out) |
44 |
| - except ExpatError: |
45 |
| - return [] |
46 |
| - |
47 |
| -def _find_TTY(obj): |
48 |
| - ''' Find the first tty (AKA IODialinDevice) that we can find in the |
49 |
| - children of the specified object, or None if no tty is present. |
50 |
| - ''' |
51 |
| - try: |
52 |
| - return obj['IODialinDevice'] |
53 |
| - except KeyError: |
54 |
| - for child in obj.get('IORegistryEntryChildren', []): |
55 |
| - found = _find_TTY(child) |
56 |
| - if found: |
57 |
| - return found |
58 |
| - return None |
59 |
| - |
60 |
| - |
61 |
| -def _prune(current, keys): |
62 |
| - """ Reduce the amount of data we have to sift through to only |
63 |
| - include the specified keys, and children that contain the |
64 |
| - specified keys |
65 |
| - """ |
66 |
| - pruned_current = {k: current[k] for k in keys if k in current} |
67 |
| - pruned_children = list( |
68 |
| - filter(None, [_prune(c, keys) for c in |
69 |
| - current.get('IORegistryEntryChildren', [])])) |
70 |
| - keep_current = any(k in current for k in keys) or pruned_children |
71 |
| - if keep_current: |
72 |
| - if pruned_children: |
73 |
| - pruned_current['IORegistryEntryChildren'] = pruned_children |
74 |
| - return pruned_current |
75 |
| - else: |
76 |
| - return {} |
77 |
| - |
78 |
| - |
79 |
| -def _dfs_usb_info(obj, parents): |
80 |
| - """ Find all of the usb info that we can from this particular IORegistry |
81 |
| - tree with depth first search (and searching the parent stack....) |
82 |
| - """ |
83 |
| - output = {} |
84 |
| - if ('BSD Name' in obj and obj['BSD Name'].startswith('disk') and |
85 |
| - mbed_volume_name_match.search(obj['IORegistryEntryName'])): |
86 |
| - disk_id = obj['BSD Name'] |
87 |
| - usb_info = { |
88 |
| - 'serial':None, |
89 |
| - 'vendor_id':None, |
90 |
| - 'product_id':None, |
91 |
| - 'tty':None, |
92 |
| - } |
93 |
| - for parent in [obj] + parents: |
94 |
| - if 'USB Serial Number' in parent: |
95 |
| - usb_info['serial'] = parent['USB Serial Number'] |
96 |
| - if 'idVendor' in parent and 'idProduct' in parent: |
97 |
| - usb_info['vendor_id'] = format(parent['idVendor'], '04x') |
98 |
| - usb_info['product_id'] = format(parent['idProduct'], '04x') |
99 |
| - if usb_info['serial']: |
100 |
| - usb_info['tty'] = _find_TTY(parent) |
101 |
| - if all(usb_info.values()): |
102 |
| - break |
103 |
| - logger.debug("found usb info %r", usb_info) |
104 |
| - output[disk_id] = usb_info |
105 |
| - for child in obj.get('IORegistryEntryChildren', []): |
106 |
| - output.update(_dfs_usb_info(child, [obj] + parents)) |
107 |
| - return output |
108 |
| - |
109 |
| - |
110 |
| -class MbedLsToolsDarwin(MbedLsToolsBase): |
111 |
| - """ mbed-enabled platform detection on Mac OS X |
112 |
| - """ |
113 |
| - |
114 |
| - def __init__(self, **kwargs): |
115 |
| - MbedLsToolsBase.__init__(self, **kwargs) |
116 |
| - self.mac_version = float('.'.join(platform.mac_ver()[0].split('.')[:2])) |
117 |
| - |
118 |
| - def find_candidates(self): |
119 |
| - # {volume_id: {serial:, vendor_id:, product_id:, tty:}} |
120 |
| - volumes = self._volumes() |
121 |
| - |
122 |
| - # {volume_id: mount_point} |
123 |
| - mounts = self._mount_points() |
124 |
| - return [ |
125 |
| - { |
126 |
| - 'mount_point': mounts[v], |
127 |
| - 'serial_port': volumes[v]['tty'], |
128 |
| - 'target_id_usb_id': volumes[v].get('serial'), |
129 |
| - 'vendor_id': volumes[v].get('vendor_id'), |
130 |
| - 'product_id': volumes[v].get('product_id') |
131 |
| - } for v in set(volumes.keys()) and set(mounts.keys()) |
132 |
| - if v in mounts and v in volumes |
133 |
| - ] |
134 |
| - |
135 |
| - def _mount_points(self): |
136 |
| - ''' Returns map {volume_id: mount_point} ''' |
137 |
| - diskutil_ls = subprocess.Popen(['diskutil', 'list', '-plist'], stdout=subprocess.PIPE) |
138 |
| - disks = _plist_from_popen(diskutil_ls) |
139 |
| - |
140 |
| - if logger.isEnabledFor(DEBUG): |
141 |
| - import pprint |
142 |
| - logger.debug("disks dict \n%s", pprint.PrettyPrinter(indent=2).pformat(disks)) |
143 |
| - return {disk['DeviceIdentifier']: disk.get('MountPoint', None) |
144 |
| - for disk in disks['AllDisksAndPartitions']} |
145 |
| - |
146 |
| - def _volumes(self): |
147 |
| - ''' returns a map {volume_id: {serial:, vendor_id:, product_id:, tty:}''' |
148 |
| - |
149 |
| - # to find all the possible mbed volumes, we look for registry entries |
150 |
| - # under all possible USB tree which have a "BSD Name" that starts with |
151 |
| - # "disk" # (i.e. this is a USB disk), and have a IORegistryEntryName that |
152 |
| - # matches /\cmbed/ |
153 |
| - # Once we've found a disk, we can search up for a parent with a valid |
154 |
| - # serial number, and then search down again to find a tty that's part |
155 |
| - # of the same composite device |
156 |
| - # ioreg -a -r -n <usb_controller_name> -l |
157 |
| - usb_controllers = ['AppleUSBXHCI', 'AppleUSBUHCI', 'AppleUSBEHCI', |
158 |
| - 'AppleUSBOHCI', 'IOUSBHostDevice'] |
159 |
| - |
160 |
| - cmp_par = '-n' |
161 |
| - # For El Captain we need to list all the instances of (-c) rather than |
162 |
| - # compare names (-n) |
163 |
| - if self.mac_version >= 10.11: |
164 |
| - cmp_par = '-c' |
165 |
| - |
166 |
| - for usb_controller in usb_controllers: |
167 |
| - ioreg_usb = subprocess.Popen(['ioreg', '-a', '-r', cmp_par, usb_controller, '-l'], stdout=subprocess.PIPE) |
168 |
| - usb_tree = _plist_from_popen(ioreg_usb) |
169 |
| - |
170 |
| - r = {} |
171 |
| - |
172 |
| - for name, obj in enumerate(usb_tree): |
173 |
| - pruned_obj = _prune(obj, ['USB Serial Number', 'idVendor', 'BSD Name', |
174 |
| - 'IORegistryEntryName', 'idProduct', 'IODialinDevice']) |
175 |
| - if logger.isEnabledFor(DEBUG): |
176 |
| - import pprint |
177 |
| - logger.debug("finding in \n%s", pprint.PrettyPrinter(indent=2).pformat(pruned_obj)) |
178 |
| - r.update(_dfs_usb_info(pruned_obj, [])) |
179 |
| - |
180 |
| - logger.debug("_volumes return %r", r) |
181 |
| - return r |
| 18 | +from mbed_os_tools.detect.darwin import MbedLsToolsDarwin |
0 commit comments