Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 # Copyright 2016 The Chromium Authors. All rights reserved. | 2 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import re | 6 import re |
| 7 import sys | 7 import sys |
| 8 import argparse | 8 import argparse |
| 9 import json | |
| 10 import collections | |
| 9 | 11 |
| 12 from devil import base_error | |
| 10 from devil.utils import cmd_helper | 13 from devil.utils import cmd_helper |
| 14 from devil.utils import usb_hubs | |
| 11 from devil.utils import lsusb | 15 from devil.utils import lsusb |
| 12 | 16 |
| 13 # Note: In the documentation below, "virtual port" refers to the port number | 17 # Note: In the documentation below, "virtual port" refers to the port number |
| 14 # as observed by the system (e.g. by usb-devices) and "physical port" refers | 18 # as observed by the system (e.g. by usb-devices) and "physical port" refers |
| 15 # to the physical numerical label on the physical port e.g. on a USB hub. | 19 # to the physical numerical label on the physical port e.g. on a USB hub. |
| 16 # The mapping between virtual and physical ports is not always the identity | 20 # The mapping between virtual and physical ports is not always the identity |
| 17 # (e.g. the port labeled "1" on a USB hub does not always show up as "port 1" | 21 # (e.g. the port labeled "1" on a USB hub does not always show up as "port 1" |
| 18 # when you plug something into it) but, as far as we are aware, the mapping | 22 # when you plug something into it) but, as far as we are aware, the mapping |
| 19 # between virtual and physical ports is always the same for a given | 23 # between virtual and physical ports is always the same for a given |
| 20 # model of USB hub. When "port number" is referenced without specifying, it | 24 # model of USB hub. When "port number" is referenced without specifying, it |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 46 | 50 |
| 47 def GetBattorList(device_tree_map): | 51 def GetBattorList(device_tree_map): |
| 48 return [x for x in GetTTYList() if IsBattor(x, device_tree_map)] | 52 return [x for x in GetTTYList() if IsBattor(x, device_tree_map)] |
| 49 | 53 |
| 50 | 54 |
| 51 def IsBattor(tty_string, device_tree_map): | 55 def IsBattor(tty_string, device_tree_map): |
| 52 (bus, device) = GetBusDeviceFromTTY(tty_string) | 56 (bus, device) = GetBusDeviceFromTTY(tty_string) |
| 53 node = device_tree_map[bus].FindDeviceNumber(device) | 57 node = device_tree_map[bus].FindDeviceNumber(device) |
| 54 return 'Future Technology Devices International' in node.desc | 58 return 'Future Technology Devices International' in node.desc |
| 55 | 59 |
| 60 def GetBattorSerialNumbers(device_tree_map): | |
| 61 for x in GetTTYList(): | |
| 62 if IsBattor(x, device_tree_map): | |
| 63 (bus, device) = GetBusDeviceFromTTY(x) | |
| 64 devnode = device_tree_map[bus].FindDeviceNumber(device) | |
| 65 yield devnode.serial | |
| 66 | |
| 67 class BattorError(base_error.BaseError): | |
| 68 pass | |
| 56 | 69 |
| 57 # Class to identify nodes in the USB topology. USB topology is organized as | 70 # Class to identify nodes in the USB topology. USB topology is organized as |
| 58 # a tree. | 71 # a tree. |
| 59 class USBNode(object): | 72 class USBNode(object): |
| 60 def __init__(self): | 73 def __init__(self): |
| 61 self._port_to_node = {} | 74 self._port_to_node = {} |
| 62 | 75 |
| 63 @property | 76 @property |
| 64 def desc(self): | 77 def desc(self): |
| 65 raise NotImplementedError | 78 raise NotImplementedError |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 | 257 |
| 245 | 258 |
| 246 _T_LINE_REGEX = re.compile(r'T: Bus=(?P<bus>\d{2}) Lev=(?P<lev>\d{2}) ' | 259 _T_LINE_REGEX = re.compile(r'T: Bus=(?P<bus>\d{2}) Lev=(?P<lev>\d{2}) ' |
| 247 r'Prnt=(?P<prnt>\d{2,3}) Port=(?P<port>\d{2}) ' | 260 r'Prnt=(?P<prnt>\d{2,3}) Port=(?P<port>\d{2}) ' |
| 248 r'Cnt=(?P<cnt>\d{2}) Dev#=(?P<dev>.{3}) .*') | 261 r'Cnt=(?P<cnt>\d{2}) Dev#=(?P<dev>.{3}) .*') |
| 249 | 262 |
| 250 _S_LINE_REGEX = re.compile(r'S: SerialNumber=(?P<serial>.*)') | 263 _S_LINE_REGEX = re.compile(r'S: SerialNumber=(?P<serial>.*)') |
| 251 _LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}): (.*)') | 264 _LSUSB_BUS_DEVICE_RE = re.compile(r'^Bus (\d{3}) Device (\d{3}): (.*)') |
| 252 | 265 |
| 253 | 266 |
| 254 def GetBusNumberToDeviceTreeMap(fast=False): | 267 def GetBusNumberToDeviceTreeMap(fast=True): |
| 255 """Gets devices currently attached. | 268 """Gets devices currently attached. |
| 256 | 269 |
| 257 Args: | 270 Args: |
| 258 fast [bool]: whether to do it fast (only get description, not | 271 fast [bool]: whether to do it fast (only get description, not |
| 259 the whole dictionary, from lsusb) | 272 the whole dictionary, from lsusb) |
| 260 | 273 |
| 261 Returns: | 274 Returns: |
| 262 map of {bus number: bus object} | 275 map of {bus number: bus object} |
| 263 where the bus object has all the devices attached to it in a tree. | 276 where the bus object has all the devices attached to it in a tree. |
| 264 """ | 277 """ |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 304 match = _S_LINE_REGEX.match(line) | 317 match = _S_LINE_REGEX.match(line) |
| 305 if match: | 318 if match: |
| 306 if bus_num == -1: | 319 if bus_num == -1: |
| 307 raise ValueError('S line appears before T line in input file') | 320 raise ValueError('S line appears before T line in input file') |
| 308 # put the serial number in the device | 321 # put the serial number in the device |
| 309 tree[bus_num].FindDeviceNumber(device_num).serial = match.group('serial') | 322 tree[bus_num].FindDeviceNumber(device_num).serial = match.group('serial') |
| 310 | 323 |
| 311 return tree | 324 return tree |
| 312 | 325 |
| 313 | 326 |
| 314 class HubType(object): | |
| 315 def __init__(self, id_func, port_mapping): | |
| 316 """Defines a type of hub. | |
| 317 | |
| 318 Args: | |
| 319 id_func: [USBNode -> bool] is a function that can be run on a node | |
| 320 to determine if the node represents this type of hub. | |
| 321 port_mapping: [dict(int:(int|dict))] maps virtual to physical port | |
| 322 numbers. For instance, {3:1, 1:2, 2:3} means that virtual port 3 | |
| 323 corresponds to physical port 1, virtual port 1 corresponds to physical | |
| 324 port 2, and virtual port 2 corresponds to physical port 3. In the | |
| 325 case of hubs with "internal" topology, this is represented by nested | |
| 326 maps. For instance, {1:{1:1,2:2},2:{1:3,2:4}} means, e.g. that the | |
| 327 device plugged into physical port 3 will show up as being connected | |
| 328 to port 1, on a device which is connected to port 2 on the hub. | |
| 329 """ | |
| 330 self._id_func = id_func | |
| 331 # v2p = "virtual to physical" ports | |
| 332 self._v2p_port = port_mapping | |
| 333 | |
| 334 def IsType(self, node): | |
| 335 """Determines if the given Node is a hub of this type. | |
| 336 | |
| 337 Args: | |
| 338 node: [USBNode] Node to check. | |
| 339 """ | |
| 340 return self._id_func(node) | |
| 341 | |
| 342 def GetPhysicalPortToNodeTuples(self, node): | |
| 343 """Gets devices connected to the physical ports on a hub of this type. | |
| 344 | |
| 345 Args: | |
| 346 node: [USBNode] Node representing a hub of this type. | |
| 347 | |
| 348 Yields: | |
| 349 A series of (int, USBNode) tuples giving a physical port | |
| 350 and the USBNode connected to it. | |
| 351 | |
| 352 Raises: | |
| 353 ValueError: If the given node isn't a hub of this type. | |
| 354 """ | |
| 355 if self.IsType(node): | |
| 356 for res in self._GppHelper(node, self._v2p_port): | |
| 357 yield res | |
| 358 else: | |
| 359 raise ValueError('Node must be a hub of this type') | |
| 360 | |
| 361 def _GppHelper(self, node, mapping): | |
| 362 """Helper function for GetPhysicalPortToNodeMap. | |
| 363 | |
| 364 Gets devices connected to physical ports, based on device tree | |
| 365 rooted at the given node and the mapping between virtual and physical | |
| 366 ports. | |
| 367 | |
| 368 Args: | |
| 369 node: [USBNode] Root of tree to search for devices. | |
| 370 mapping: [dict] Mapping between virtual and physical ports. | |
| 371 | |
| 372 Yields: | |
| 373 A series of (int, USBNode) tuples giving a physical port | |
| 374 and the Node connected to it. | |
| 375 """ | |
| 376 for (virtual, physical) in mapping.iteritems(): | |
| 377 if node.HasPort(virtual): | |
| 378 if isinstance(physical, dict): | |
| 379 for res in self._GppHelper(node.PortToDevice(virtual), physical): | |
| 380 yield res | |
| 381 else: | |
| 382 yield (physical, node.PortToDevice(virtual)) | |
| 383 | |
| 384 | |
| 385 def GetHubsOnBus(bus, hub_types): | 327 def GetHubsOnBus(bus, hub_types): |
| 386 """Scans for all hubs on a bus of given hub types. | 328 """Scans for all hubs on a bus of given hub types. |
| 387 | 329 |
| 388 Args: | 330 Args: |
| 389 bus: [USBNode] Bus object. | 331 bus: [USBNode] Bus object. |
| 390 hub_types: [iterable(HubType)] Possible types of hubs. | 332 hub_types: [iterable(usb_hubs.HubType)] Possible types of hubs. |
| 391 | 333 |
| 392 Yields: | 334 Yields: |
| 393 Sequence of tuples representing (hub, type of hub) | 335 Sequence of tuples representing (hub, type of hub) |
| 394 """ | 336 """ |
| 395 for device in bus.AllNodes(): | 337 for device in bus.AllNodes(): |
| 396 for hub_type in hub_types: | 338 for hub_type in hub_types: |
| 397 if hub_type.IsType(device): | 339 if hub_type.IsType(device): |
| 398 yield (device, hub_type) | 340 yield (device, hub_type) |
| 399 | 341 |
| 400 | 342 |
| 401 def GetPhysicalPortToNodeMap(hub, hub_type): | 343 def GetPhysicalPortToNodeMap(hub, hub_type): |
| 402 """Gets physical-port:node mapping for a given hub. | 344 """Gets physical-port:node mapping for a given hub. |
| 403 Args: | 345 Args: |
| 404 hub: [USBNode] Hub to get map for. | 346 hub: [USBNode] Hub to get map for. |
| 405 hub_type: [HubType] Which type of hub it is. | 347 hub_type: [usb_hubs.HubType] Which type of hub it is. |
| 406 | 348 |
| 407 Returns: | 349 Returns: |
| 408 Dict of {physical port: node} | 350 Dict of {physical port: node} |
| 409 """ | 351 """ |
| 410 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) | 352 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) |
| 411 return {port: device for (port, device) in port_device} | 353 return {port: device for (port, device) in port_device} |
| 412 | 354 |
| 413 | 355 |
| 414 def GetPhysicalPortToBusDeviceMap(hub, hub_type): | 356 def GetPhysicalPortToBusDeviceMap(hub, hub_type): |
| 415 """Gets physical-port:(bus#, device#) mapping for a given hub. | 357 """Gets physical-port:(bus#, device#) mapping for a given hub. |
| 416 Args: | 358 Args: |
| 417 hub: [USBNode] Hub to get map for. | 359 hub: [USBNode] Hub to get map for. |
| 418 hub_type: [HubType] Which type of hub it is. | 360 hub_type: [usb_hubs.HubType] Which type of hub it is. |
| 419 | 361 |
| 420 Returns: | 362 Returns: |
| 421 Dict of {physical port: (bus number, device number)} | 363 Dict of {physical port: (bus number, device number)} |
| 422 """ | 364 """ |
| 423 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) | 365 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) |
| 424 return {port: (device.bus_num, device.device_num) | 366 return {port: (device.bus_num, device.device_num) |
| 425 for (port, device) in port_device} | 367 for (port, device) in port_device} |
| 426 | 368 |
| 427 | 369 |
| 428 def GetPhysicalPortToSerialMap(hub, hub_type): | 370 def GetPhysicalPortToSerialMap(hub, hub_type): |
| 429 """Gets physical-port:serial# mapping for a given hub. | 371 """Gets physical-port:serial# mapping for a given hub. |
| 430 Args: | 372 Args: |
| 431 hub: [USBNode] Hub to get map for. | 373 hub: [USBNode] Hub to get map for. |
| 432 hub_type: [HubType] Which type of hub it is. | 374 hub_type: [usb_hubs.HubType] Which type of hub it is. |
| 433 | 375 |
| 434 Returns: | 376 Returns: |
| 435 Dict of {physical port: serial number)} | 377 Dict of {physical port: serial number)} |
| 436 """ | 378 """ |
| 437 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) | 379 port_device = hub_type.GetPhysicalPortToNodeTuples(hub) |
| 438 return {port: device.serial | 380 return {port: device.serial |
| 439 for (port, device) in port_device | 381 for (port, device) in port_device |
| 440 if device.serial} | 382 if device.serial} |
| 441 | 383 |
| 442 | 384 |
| 443 def GetPhysicalPortToTTYMap(device, hub_type): | 385 def GetPhysicalPortToTTYMap(device, hub_type): |
| 444 """Gets physical-port:tty-string mapping for a given hub. | 386 """Gets physical-port:tty-string mapping for a given hub. |
| 445 Args: | 387 Args: |
| 446 hub: [USBNode] Hub to get map for. | 388 hub: [USBNode] Hub to get map for. |
| 447 hub_type: [HubType] Which type of hub it is. | 389 hub_type: [usb_hubs.HubType] Which type of hub it is. |
| 448 | 390 |
| 449 Returns: | 391 Returns: |
| 450 Dict of {physical port: tty-string)} | 392 Dict of {physical port: tty-string)} |
| 451 """ | 393 """ |
| 452 port_device = hub_type.GetPhysicalPortToNodeTuples(device) | 394 port_device = hub_type.GetPhysicalPortToNodeTuples(device) |
| 453 bus_device_to_tty = GetBusDeviceToTTYMap() | 395 bus_device_to_tty = GetBusDeviceToTTYMap() |
| 454 return {port: bus_device_to_tty[(device.bus_num, device.device_num)] | 396 return {port: bus_device_to_tty[(device.bus_num, device.device_num)] |
| 455 for (port, device) in port_device | 397 for (port, device) in port_device |
| 456 if (device.bus_num, device.device_num) in bus_device_to_tty} | 398 if (device.bus_num, device.device_num) in bus_device_to_tty} |
| 457 | 399 |
| 458 | 400 |
| 459 def CollectHubMaps(hub_types, map_func, device_tree_map=None, fast=False): | 401 def CollectHubMaps(hub_types, map_func, device_tree_map=None, fast=False): |
| 460 """Runs a function on all hubs in the system and collects their output. | 402 """Runs a function on all hubs in the system and collects their output. |
| 461 | 403 |
| 462 Args: | 404 Args: |
| 463 hub_types: [HubType] List of possible hub types. | 405 hub_types: [usb_hubs.HubType] List of possible hub types. |
| 464 map_func: [string] Function to run on each hub. | 406 map_func: [string] Function to run on each hub. |
| 465 device_tree: Previously constructed device tree map, if any. | 407 device_tree: Previously constructed device tree map, if any. |
| 466 fast: Whether to construct device tree fast, if not already provided | 408 fast: Whether to construct device tree fast, if not already provided |
| 467 | 409 |
| 468 Yields: | 410 Yields: |
| 469 Sequence of dicts of {physical port: device} where the type of | 411 Sequence of dicts of {physical port: device} where the type of |
| 470 device depends on the ident keyword. Each dict is a separate hub. | 412 device depends on the ident keyword. Each dict is a separate hub. |
| 471 """ | 413 """ |
| 472 if device_tree_map is None: | 414 if device_tree_map is None: |
| 473 device_tree_map = GetBusNumberToDeviceTreeMap(fast=fast) | 415 device_tree_map = GetBusNumberToDeviceTreeMap(fast=fast) |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 545 return result | 487 return result |
| 546 | 488 |
| 547 | 489 |
| 548 # This dictionary described the mapping between physical and | 490 # This dictionary described the mapping between physical and |
| 549 # virtual ports on a Plugable 7-Port Hub (model USB2-HUB7BC). | 491 # virtual ports on a Plugable 7-Port Hub (model USB2-HUB7BC). |
| 550 # Keys are the virtual ports, values are the physical port. | 492 # Keys are the virtual ports, values are the physical port. |
| 551 # The entry 4:{1:4, 2:3, 3:2, 4:1} indicates that virtual port | 493 # The entry 4:{1:4, 2:3, 3:2, 4:1} indicates that virtual port |
| 552 # 4 connects to another 'virtual' hub that itself has the | 494 # 4 connects to another 'virtual' hub that itself has the |
| 553 # virtual-to-physical port mapping {1:4, 2:3, 3:2, 4:1}. | 495 # virtual-to-physical port mapping {1:4, 2:3, 3:2, 4:1}. |
| 554 | 496 |
| 555 PLUGABLE_7PORT_LAYOUT = {1:7, | |
| 556 2:6, | |
| 557 3:5, | |
| 558 4:{1:4, 2:3, 3:2, 4:1}} | |
| 559 | 497 |
| 560 def TestUSBTopologyScript(): | 498 def TestUSBTopologyScript(): |
| 561 """Test display and hub identification.""" | 499 """Test display and hub identification.""" |
| 562 # Identification criteria for Plugable 7-Port Hub | 500 # Identification criteria for Plugable 7-Port Hub |
| 563 def _is_plugable_7port_hub(node): | |
| 564 """Check if a node is a Plugable 7-Port Hub | |
| 565 (Model USB2-HUB7BC) | |
| 566 The topology of this device is a 4-port hub, | |
| 567 with another 4-port hub connected on port 4. | |
| 568 """ | |
| 569 if not isinstance(node, USBDeviceNode): | |
| 570 return False | |
| 571 if '4-Port HUB' not in node.desc: | |
| 572 return False | |
| 573 if not node.HasPort(4): | |
| 574 return False | |
| 575 return '4-Port HUB' in node.PortToDevice(4).desc | |
| 576 | |
| 577 plugable_7port = HubType(_is_plugable_7port_hub, | |
| 578 PLUGABLE_7PORT_LAYOUT) | |
| 579 print '==== USB TOPOLOGY SCRIPT TEST ====' | 501 print '==== USB TOPOLOGY SCRIPT TEST ====' |
| 580 | 502 |
| 581 # Display devices | 503 # Display devices |
| 582 print '==== DEVICE DISPLAY ====' | 504 print '==== DEVICE DISPLAY ====' |
| 583 device_trees = GetBusNumberToDeviceTreeMap(fast=True) | 505 device_trees = GetBusNumberToDeviceTreeMap() |
| 584 for device_tree in device_trees.values(): | 506 for device_tree in device_trees.values(): |
| 585 device_tree.Display() | 507 device_tree.Display() |
| 586 print | 508 print |
| 587 | 509 |
| 588 # Display TTY information about devices plugged into hubs. | 510 # Display TTY information about devices plugged into hubs. |
| 589 print '==== TTY INFORMATION ====' | 511 print '==== TTY INFORMATION ====' |
| 590 for port_map in GetAllPhysicalPortToTTYMaps([plugable_7port], | 512 for port_map in GetAllPhysicalPortToTTYMaps([usb_hubs.PLUGABLE_7PORT], |
| 591 device_tree_map=device_trees): | 513 device_tree_map=device_trees): |
| 592 print port_map | 514 print port_map |
| 593 print | 515 print |
| 594 | 516 |
| 595 # Display serial number information about devices plugged into hubs. | 517 # Display serial number information about devices plugged into hubs. |
| 596 print '==== SERIAL NUMBER INFORMATION ====' | 518 print '==== SERIAL NUMBER INFORMATION ====' |
| 597 for port_map in GetAllPhysicalPortToSerialMaps([plugable_7port], | 519 for port_map in GetAllPhysicalPortToSerialMaps([usb_hubs.PLUGABLE_7PORT], |
| 598 device_tree_map=device_trees): | 520 device_tree_map=device_trees): |
| 599 print port_map | 521 print port_map |
| 522 | |
| 523 print | |
| 524 # Display phone to BattOr map | |
| 525 print GenerateSerialMap() | |
| 600 print '' | 526 print '' |
| 527 | |
| 601 return 0 | 528 return 0 |
| 602 | 529 |
| 530 | |
| 531 def ReadSerialMapFile(filename): | |
|
jbudorick
2016/03/30 19:28:58
I should've brought this up on whatever review int
| |
| 532 """Reads JSON file giving phone-to-battor serial number map. | |
| 533 | |
| 534 Parses a JSON file consisting of a list of items of the following form: | |
| 535 [{'phone': <phone serial 1>, 'battor': <battor serial 1>}, | |
| 536 {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...] | |
| 537 | |
| 538 indicating which phone serial numbers should be matched with | |
| 539 which BattOr serial numbers. Returns dictionary of the form: | |
| 540 | |
| 541 {<phone serial 1>: <BattOr serial 1>, | |
| 542 <phone serial 2>: <BattOr serial 2>} | |
| 543 | |
| 544 Args: | |
| 545 filename: Name of file to read. | |
| 546 """ | |
| 547 result = {} | |
| 548 with open(filename, 'r') as infile: | |
| 549 in_dict = json.load(infile) | |
| 550 for x in in_dict: | |
| 551 result[x['phone']] = x['battor'] | |
| 552 return result | |
| 553 | |
| 554 def GenerateSerialMapFile(filename, hub_types=None): | |
| 555 """Writes a map of phone serial numbers to BattOr serial numbers to file. | |
| 556 | |
| 557 Writes a JSON file consisting of a list of items of the following form: | |
| 558 [{'phone': <phone serial 1>, 'battor': <battor serial 1>}, | |
| 559 {'phone': <phone serial 2>, 'battor': <battor serial 2>}, ...] | |
| 560 | |
| 561 indicating which phone serial numbers should be matched with | |
| 562 which BattOr serial numbers. Mapping is based on the physical port numbers | |
| 563 of the hubs that the BattOrs and phones are connected to. | |
| 564 | |
| 565 Args: | |
| 566 filename: Name of file to write. | |
| 567 """ | |
| 568 result = [] | |
| 569 for (phone, battor) in GenerateSerialMap(hub_types).iteritems(): | |
| 570 result.append({'phone': phone, 'battor': battor}) | |
| 571 with open(filename, 'w') as outfile: | |
| 572 json.dump(result, outfile) | |
| 573 | |
| 574 def GenerateSerialMap(hub_types=None): | |
| 575 """Generates a map of phone serial numbers to BattOr serial numbers. | |
| 576 | |
| 577 Generates a dict of: | |
| 578 {<phone serial 1>: <battor serial 1>, | |
| 579 <phone serial 2>: <battor serial 2>} | |
| 580 indicating which phone serial numbers should be matched with | |
| 581 which BattOr serial numbers. Mapping is based on the physical port numbers | |
| 582 of the hubs that the BattOrs and phones are connected to. | |
| 583 | |
| 584 Args: | |
| 585 hub_types: List of hub types to check for | |
|
jbudorick
2016/03/30 19:28:58
nit: add that this defaults to ['plugable_7port']
alexandermont
2016/03/30 23:35:53
Done
| |
| 586 """ | |
| 587 hub_types = [usb_hubs.GetHubType(x) | |
| 588 for x in hub_types or ['plugable_7port']] | |
| 589 devtree = GetBusNumberToDeviceTreeMap() | |
| 590 battor_serials = list(GetBattorSerialNumbers(devtree)) | |
| 591 p2serial = GetAllPhysicalPortToSerialMaps(hub_types, | |
|
jbudorick
2016/03/30 19:28:58
nit: rename p2serial
alexandermont
2016/03/30 23:35:52
Done
| |
| 592 device_tree_map=devtree) | |
| 593 port_to_devices = collections.defaultdict(list) | |
|
jbudorick
2016/03/30 19:28:57
Rather than a list of serials, wdyt about having t
alexandermont
2016/03/30 23:35:52
See below.
| |
| 594 result = {} | |
| 595 for hub in p2serial: | |
| 596 for (port, serial) in hub.iteritems(): | |
| 597 port_to_devices[port].append(serial) | |
|
jbudorick
2016/03/30 19:28:58
Following from the above, you'd then check whether
alexandermont
2016/03/30 23:35:53
This won't work if the value in the dictionary is
| |
| 598 for (port, serial_list) in port_to_devices.iteritems(): | |
| 599 battor_serial = [x for x in serial_list if x in battor_serials] | |
| 600 if len(battor_serial) >= 2: | |
| 601 raise BattorError('Duplicate BattOr serial numbers detected') | |
| 602 if len(battor_serial) == 1: | |
| 603 battor_serial = battor_serial[0] | |
| 604 phone_serial = [x for x in serial_list if x != battor_serial] | |
| 605 if len(phone_serial) >= 2: | |
| 606 raise BattorError('Multiple phones matched with same BattOr') | |
| 607 if len(phone_serial) == 0: | |
| 608 raise BattorError('BattOr has no matching phone') | |
| 609 if battor_serial in result: | |
| 610 raise BattorError('Duplicate BattOr serial numbers detected') | |
| 611 result[phone_serial[0]] = battor_serial | |
| 612 return result | |
| 613 | |
| 614 def _PhoneToPathMap(serial, serial_map, devtree): | |
| 615 """Maps phone serial number to TTY path, assuming serial map is provided.""" | |
| 616 battor_serial = serial_map[serial] | |
| 617 for tree in devtree.values(): | |
| 618 for node in tree.AllNodes(): | |
| 619 if isinstance(node, USBDeviceNode): | |
| 620 if node.serial == battor_serial: | |
| 621 bus_device_to_tty = GetBusDeviceToTTYMap() | |
| 622 bus_device = (node.bus_num, node.device_num) | |
| 623 try: | |
| 624 return bus_device_to_tty[bus_device] | |
| 625 except KeyError: | |
| 626 raise BattorError('Device with given serial number not a BattOr ' | |
| 627 '(does not have TTY path)') | |
| 628 | |
| 629 | |
| 630 def GetBattorPathFromPhoneSerial(serial, serial_map=None): | |
| 631 """Gets the TTY path (e.g. '/dev/ttyUSB0') to communicate with the BattOr. | |
| 632 | |
| 633 (1) If serial_map is a dict, serial_map is treated as a dictionary mapping | |
|
jbudorick
2016/03/30 19:28:57
Why is this handling two arguments in one...? Hand
alexandermont
2016/03/30 23:35:52
Done
| |
| 634 phone serial numbers to BattOr serial numbers. This function will get the | |
| 635 TTY path for the given BattOr serial number. | |
| 636 | |
| 637 (2) If serial_map is a string, serial_map is treated as the name of a | |
| 638 phone-to-BattOr mapping file (generated with GenerateSerialMapFile) | |
| 639 and this will be loaded and used as the dict to map port numbers to | |
| 640 BattOr serial numbers. | |
| 641 | |
| 642 Args: | |
| 643 serial: Serial number of phone connected on the same physical port that | |
| 644 the BattOr is connected to. | |
| 645 serial_map_file: Map of phone serial numbers to BattOr serial numbers. | |
| 646 hub_types: List of hub types to check for. Used only if serial_map_file | |
| 647 is None. | |
| 648 | |
| 649 Returns: | |
| 650 Device string used to communicate with device. | |
| 651 | |
| 652 Raises: | |
| 653 ValueError: If serial number is not given. | |
| 654 BattorError: If BattOr not found or unexpected USB topology. | |
| 655 """ | |
| 656 # If there's only one BattOr connected to the system, just use that one. | |
| 657 # This allows for use on, e.g., a developer's workstation with no hubs. | |
| 658 devtree = GetBusNumberToDeviceTreeMap() | |
| 659 all_battors = GetBattorList(devtree) | |
| 660 if len(all_battors) == 1: | |
| 661 return '/dev/' + all_battors[0] | |
| 662 | |
| 663 if isinstance(serial_map, str): | |
| 664 serial_map = ReadSerialMapFile(serial_map) | |
| 665 | |
| 666 if not isinstance(serial_map, dict): | |
| 667 raise ValueError('Incorrect data type for serial_map') | |
| 668 | |
| 669 if not serial: | |
| 670 raise BattorError('Two or more BattOrs connected, no serial provided') | |
| 671 | |
| 672 tty_string = _PhoneToPathMap(serial, serial_map, devtree) | |
| 673 | |
| 674 if not tty_string: | |
| 675 raise BattorError('No device with given serial number detected.') | |
| 676 | |
| 677 if IsBattor(tty_string, devtree): | |
| 678 return '/dev/' + tty_string | |
| 679 else: | |
| 680 raise BattorError('Device with given serial number is not a BattOr.') | |
| 681 | |
| 603 def parse_options(argv): | 682 def parse_options(argv): |
| 604 """Parses and checks the command-line options. | 683 """Parses and checks the command-line options. |
| 605 | 684 |
| 606 Returns: | 685 Returns: |
| 607 A tuple containing the options structure and a list of categories to | 686 A tuple containing the options structure and a list of categories to |
| 608 be traced. | 687 be traced. |
| 609 """ | 688 """ |
| 610 USAGE = '''./find_usb_devices [--help] | 689 USAGE = '''./find_usb_devices [--help] |
| 611 This script shows the mapping between USB devices and port numbers. | 690 This script shows the mapping between USB devices and port numbers. |
| 612 Clients are not intended to call this script from the command line. | 691 Clients are not intended to call this script from the command line. |
| 613 Clients are intended to call the functions in this script directly. | 692 Clients are intended to call the functions in this script directly. |
| 614 For instance, GetAllPhysicalPortToSerialMaps(...) | 693 For instance, GetAllPhysicalPortToSerialMaps(...) |
| 615 Running this script with --help will display this message. | 694 Running this script with --help will display this message. |
| 616 Running this script without --help will display information about | 695 Running this script without --help will display information about |
| 617 devices attached, TTY mapping, and serial number mapping, | 696 devices attached, TTY mapping, and serial number mapping, |
| 618 for testing purposes. See design document for API documentation. | 697 for testing purposes. See design document for API documentation. |
| 619 ''' | 698 ''' |
| 620 parser = argparse.ArgumentParser(usage=USAGE) | 699 parser = argparse.ArgumentParser(usage=USAGE) |
| 621 return parser.parse_args(argv[1:]) | 700 return parser.parse_args(argv[1:]) |
| 622 | 701 |
| 623 def main(): | 702 def main(): |
| 624 parse_options(sys.argv) | 703 parse_options(sys.argv) |
| 625 TestUSBTopologyScript() | 704 TestUSBTopologyScript() |
| 626 | 705 |
| 627 if __name__ == "__main__": | 706 if __name__ == "__main__": |
| 628 sys.exit(main()) | 707 sys.exit(main()) |
| OLD | NEW |