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, 2: 6, 3: 5, 4: {1: 4, 2: 3, 3: 2, 4: 1}}
6
7PLUGABLE_7PORT_USB3_LAYOUT = {1: {1: 1, 2: 2, 3: 3, 4: 4}, 2: 5, 3: 6, 4: 7}
8
9KEEDOX_LAYOUT = {1: 1, 2: 2, 3: 3, 4: {1: 4, 2: 5, 3: 6, 4: 7}}
10
11VIA_LAYOUT = {1: 1, 2: 2, 3: 3, 4: {1: 4, 2: 5, 3: 6, 4: 7}}
12
13
14class HubType(object):
15  def __init__(self, id_func, port_mapping):
16    """Defines a type of hub.
17
18    Args:
19      id_func: [USBNode -> bool] is a function that can be run on a node
20        to determine if the node represents this type of hub.
21      port_mapping: [dict(int:(int|dict))] maps virtual to physical port
22        numbers. For instance, {3:1, 1:2, 2:3} means that virtual port 3
23        corresponds to physical port 1, virtual port 1 corresponds to physical
24        port 2, and virtual port 2 corresponds to physical port 3. In the
25        case of hubs with "internal" topology, this is represented by nested
26        maps. For instance, {1:{1:1,2:2},2:{1:3,2:4}} means, e.g. that the
27        device plugged into physical port 3 will show up as being connected
28        to port 1, on a device which is connected to port 2 on the hub.
29    """
30    self._id_func = id_func
31    # v2p = "virtual to physical" ports
32    self._v2p_port = port_mapping
33
34  def IsType(self, node):
35    """Determines if the given Node is a hub of this type.
36
37    Args:
38      node: [USBNode] Node to check.
39    """
40    return self._id_func(node)
41
42  def GetPhysicalPortToNodeTuples(self, node):
43    """Gets devices connected to the physical ports on a hub of this type.
44
45    Args:
46      node: [USBNode] Node representing a hub of this type.
47
48    Yields:
49      A series of (int, USBNode) tuples giving a physical port
50      and the USBNode connected to it.
51
52    Raises:
53      ValueError: If the given node isn't a hub of this type.
54    """
55    if self.IsType(node):
56      for res in self._GppHelper(node, self._v2p_port):
57        yield res
58    else:
59      raise ValueError('Node must be a hub of this type')
60
61  def _GppHelper(self, node, mapping):
62    """Helper function for GetPhysicalPortToNodeMap.
63
64    Gets devices connected to physical ports, based on device tree
65    rooted at the given node and the mapping between virtual and physical
66    ports.
67
68    Args:
69      node: [USBNode] Root of tree to search for devices.
70      mapping: [dict] Mapping between virtual and physical ports.
71
72    Yields:
73      A series of (int, USBNode) tuples giving a physical port
74      and the Node connected to it.
75    """
76    for (virtual, physical) in mapping.items():
77      if node.HasPort(virtual):
78        if isinstance(physical, dict):
79          for res in self._GppHelper(node.PortToDevice(virtual), physical):
80            yield res
81        else:
82          yield (physical, node.PortToDevice(virtual))
83
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
98# Plugable 7-Port USB-3 Hubs show up twice in the USB devices list; they have
99# two different "branches", one which has USB2 devices and one which has
100# USB3 devices. The "part2" is the "USB-2" branch of the hub, the
101# "part3" is the "USB-3" branch of the hub.
102
103
104def _is_plugable_7port_usb3_part2_hub(node):
105  """Check if a node is the "USB2 branch" of
106  a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
107  The topology of this device is a 4-port hub,
108  with another 4-port hub connected on port 1.
109  """
110  if '2109:2811' not in node.desc:
111    return False
112  if not node.HasPort(1):
113    return False
114  return '2109:2811' in node.PortToDevice(1).desc
115
116
117def _is_plugable_7port_usb3_part3_hub(node):
118  """Check if a node is the "USB3 branch" of
119  a Plugable 7-Port USB-3 Hub (Model USB3-HUB7BC)
120  The topology of this device is a 4-port hub,
121  with another 4-port hub connected on port 1.
122  """
123  if '2109:8110' not in node.desc:
124    return False
125  if not node.HasPort(1):
126    return False
127  return '2109:8110' in node.PortToDevice(1).desc
128
129
130def _is_keedox_hub(node):
131  """Check if a node is a Keedox hub.
132  The topology of this device is a 4-port hub,
133  with another 4-port hub connected on port 4.
134  """
135  if '0bda:5411' not in node.desc:
136    return False
137  if not node.HasPort(4):
138    return False
139  return '0bda:5411' in node.PortToDevice(4).desc
140
141
142def _is_via_hub(node):
143  """Check if a node is a Via Labs hub.
144  The topology of this device is a 4-port hub,
145  with another 4-port hub connected on port 4.
146  """
147  if '2109:2812' not in node.desc and '2109:0812' not in node.desc:
148    return False
149  if not node.HasPort(4):
150    return False
151  return ('2109:2812' in node.PortToDevice(4).desc
152          or '2109:0812' in node.PortToDevice(4).desc)
153
154
155PLUGABLE_7PORT = HubType(_is_plugable_7port_hub, PLUGABLE_7PORT_LAYOUT)
156PLUGABLE_7PORT_USB3_PART2 = HubType(_is_plugable_7port_usb3_part2_hub,
157                                    PLUGABLE_7PORT_USB3_LAYOUT)
158PLUGABLE_7PORT_USB3_PART3 = HubType(_is_plugable_7port_usb3_part3_hub,
159                                    PLUGABLE_7PORT_USB3_LAYOUT)
160KEEDOX = HubType(_is_keedox_hub, KEEDOX_LAYOUT)
161VIA = HubType(_is_via_hub, VIA_LAYOUT)
162
163ALL_HUBS = [
164    PLUGABLE_7PORT, PLUGABLE_7PORT_USB3_PART2, PLUGABLE_7PORT_USB3_PART3,
165    KEEDOX, VIA
166]
167
168
169def GetHubType(type_name):
170  if type_name == 'plugable_7port':
171    return PLUGABLE_7PORT
172  elif type_name == 'plugable_7port_usb3_part2':
173    return PLUGABLE_7PORT_USB3_PART2
174  elif type_name == 'plugable_7port_usb3_part3':
175    return PLUGABLE_7PORT_USB3_PART3
176  elif type_name == 'keedox':
177    return KEEDOX
178  elif type_name == 'via':
179    return VIA
180  raise ValueError('Invalid hub type')
181