1# Copyright 2016 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5PLUGABLE_7PORT_LAYOUT = {1:7,
6                         2:6,
7                         3:5,
8                         4:{1:4, 2:3, 3:2, 4:1}}
9
10PLUGABLE_7PORT_USB3_LAYOUT = {1:{1:1, 2:2, 3:3, 4:4},
11                              2:5,
12                              3:6,
13                              4:7}
14
15class HubType(object):
16  def __init__(self, id_func, port_mapping):
17    """Defines a type of hub.
18
19    Args:
20      id_func: [USBNode -> bool] is a function that can be run on a node
21        to determine if the node represents this type of hub.
22      port_mapping: [dict(int:(int|dict))] maps virtual to physical port
23        numbers. For instance, {3:1, 1:2, 2:3} means that virtual port 3
24        corresponds to physical port 1, virtual port 1 corresponds to physical
25        port 2, and virtual port 2 corresponds to physical port 3. In the
26        case of hubs with "internal" topology, this is represented by nested
27        maps. For instance, {1:{1:1,2:2},2:{1:3,2:4}} means, e.g. that the
28        device plugged into physical port 3 will show up as being connected
29        to port 1, on a device which is connected to port 2 on the hub.
30    """
31    self._id_func = id_func
32    # v2p = "virtual to physical" ports
33    self._v2p_port = port_mapping
34
35  def IsType(self, node):
36    """Determines if the given Node is a hub of this type.
37
38    Args:
39      node: [USBNode] Node to check.
40    """
41    return self._id_func(node)
42
43  def GetPhysicalPortToNodeTuples(self, node):
44    """Gets devices connected to the physical ports on a hub of this type.
45
46    Args:
47      node: [USBNode] Node representing a hub of this type.
48
49    Yields:
50      A series of (int, USBNode) tuples giving a physical port
51      and the USBNode connected to it.
52
53    Raises:
54      ValueError: If the given node isn't a hub of this type.
55    """
56    if self.IsType(node):
57      for res in self._GppHelper(node, self._v2p_port):
58        yield res
59    else:
60      raise ValueError('Node must be a hub of this type')
61
62  def _GppHelper(self, node, mapping):
63    """Helper function for GetPhysicalPortToNodeMap.
64
65    Gets devices connected to physical ports, based on device tree
66    rooted at the given node and the mapping between virtual and physical
67    ports.
68
69    Args:
70      node: [USBNode] Root of tree to search for devices.
71      mapping: [dict] Mapping between virtual and physical ports.
72
73    Yields:
74      A series of (int, USBNode) tuples giving a physical port
75      and the Node connected to it.
76    """
77    for (virtual, physical) in mapping.iteritems():
78      if node.HasPort(virtual):
79        if isinstance(physical, dict):
80          for res in self._GppHelper(node.PortToDevice(virtual), physical):
81            yield res
82        else:
83          yield (physical, node.PortToDevice(virtual))
84
85def _is_plugable_7port_hub(node):
86  """Check if a node is a Plugable 7-Port Hub
87  (Model USB2-HUB7BC)
88  The topology of this device is a 4-port hub,
89  with another 4-port hub connected on port 4.
90  """
91  if '1a40:0101' not in node.desc:
92    return False
93  if not node.HasPort(4):
94    return False
95  return '1a40:0101' in node.PortToDevice(4).desc
96
97# Plugable 7-Port USB-3 Hubs show up twice in the USB devices list; they have
98# two different "branches", one which has USB2 devices and one which has
99# USB3 devices. The "part2" is the "USB-2" branch of the hub, the
100# "part3" is the "USB-3" branch of the hub.
101
102def _is_plugable_7port_usb3_part2_hub(node):
103  """Check if a node is the "USB2 branch" of
104  a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
105  The topology of this device is a 4-port hub,
106  with another 4-port hub connected on port 1.
107  """
108  if '2109:2811' not in node.desc:
109    return False
110  if not node.HasPort(1):
111    return False
112  return '2109:2811' in node.PortToDevice(1).desc
113
114def _is_plugable_7port_usb3_part3_hub(node):
115  """Check if a node is the "USB3 branch" of
116  a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
117  The topology of this device is a 4-port hub,
118  with another 4-port hub connected on port 1.
119  """
120  if '2109:8110' not in node.desc:
121    return False
122  if not node.HasPort(1):
123    return False
124  return '2109:8110' in node.PortToDevice(1).desc
125
126PLUGABLE_7PORT = HubType(_is_plugable_7port_hub, PLUGABLE_7PORT_LAYOUT)
127PLUGABLE_7PORT_USB3_PART2 = HubType(_is_plugable_7port_usb3_part2_hub,
128                                    PLUGABLE_7PORT_USB3_LAYOUT)
129PLUGABLE_7PORT_USB3_PART3 = HubType(_is_plugable_7port_usb3_part3_hub,
130                                    PLUGABLE_7PORT_USB3_LAYOUT)
131
132def GetHubType(type_name):
133  if type_name == 'plugable_7port':
134    return PLUGABLE_7PORT
135  if type_name == 'plugable_7port_usb3_part2':
136    return PLUGABLE_7PORT_USB3_PART2
137  if type_name == 'plugable_7port_usb3_part3':
138    return PLUGABLE_7PORT_USB3_PART3
139  else:
140    raise ValueError('Invalid hub type')
141