1import collections 2import logging 3 4 5PortId = collections.namedtuple('PortId', ['bus', 'port_number']) 6 7# Mapping from bus ID and port number to the GPIO Index. 8# We know of no way to detect this through tools, why the board 9# specific setup is hard coded here. 10_PORT_ID_TO_GPIO_INDEX_DICT = { 11 'guado': { 12 # On Guados, there are three gpios that control usb port power: 13 PortId(bus=1, port_number=2): 218, # Front left USB 2 14 PortId(bus=2, port_number=1): 218, # Front left USB 3 15 PortId(bus=1, port_number=3): 219, # Front right USB 2 16 PortId(bus=2, port_number=2): 219, # Front right USB 3 17 # Back ports (same GPIO is used for both ports) 18 PortId(bus=1, port_number=5): 209, # Back upper USB 2 19 PortId(bus=2, port_number=3): 209, # Back upper USB 3 20 PortId(bus=1, port_number=6): 209, # Back lower USB 2 21 PortId(bus=2, port_number=4): 209, # Back lower USB 3 22 }, 23 # On Fizz, there are in total 5 usb ports and per port usb power 24 # is controlled by EC with user space command: 25 # ectool gpioset USBx_ENABLE 0/1 (x from 1 to 5). 26 'fizz': { 27 # USB 2 bus. 28 PortId(bus=1, port_number=3): 4, # Front right USB 2 29 PortId(bus=1, port_number=4): 5, # Front left USB 2 30 PortId(bus=1, port_number=5): 1, # Back left USB 2 31 PortId(bus=1, port_number=6): 2, # Back middle USB 2 32 PortId(bus=1, port_number=2): 3, # Back right USB 2 33 # USB 3 bus. 34 PortId(bus=2, port_number=3): 4, # Front right USB 3 35 PortId(bus=2, port_number=4): 5, # Front left USB 3 36 PortId(bus=2, port_number=5): 1, # Back left USB 3 37 PortId(bus=2, port_number=6): 2, # Back middle USB 3 38 PortId(bus=2, port_number=2): 3, # Back right USB 3 39 } 40} 41 42 43def _get_gpio_index(board, port_id): 44 return _PORT_ID_TO_GPIO_INDEX_DICT[board][port_id] 45 46 47class UsbPortManager(object): 48 """ 49 Manages USB ports. 50 51 Can for example power cycle them. 52 """ 53 def __init__(self, host): 54 """ 55 Initializes with a host. 56 57 @param host a Host object. 58 """ 59 self._host = host 60 61 def set_port_power(self, port_ids, power_on): 62 """ 63 Turns on or off power to the USB port for peripheral devices. 64 65 @param port_ids Iterable of PortId instances (i.e. bus, port_number 66 tuples) to set power for. 67 @param power_on If true, turns power on. If false, turns power off. 68 """ 69 for port_id in port_ids: 70 gpio_index = _get_gpio_index(self._get_board(), port_id) 71 self._set_gpio_power(self._get_board(), gpio_index, power_on) 72 73 def _get_board(self): 74 # host.get_board() adds 'board: ' in front of the board name 75 return self._host.get_board().split(':')[1].strip() 76 77 def _set_gpio_power_guado(self, gpio_index, power_on): 78 """ 79 Turns on or off the power for a specific GPIO on board Guado. 80 81 @param gpio_idx The index of the gpio to set the power for. 82 @param power_on If True, powers on the GPIO. If False, powers it off. 83 """ 84 gpio_path = '/sys/class/gpio/gpio{}'.format(gpio_index) 85 did_export = False 86 if not self._host.path_exists(gpio_path): 87 did_export = True 88 self._run('echo {} > /sys/class/gpio/export'.format( 89 gpio_index)) 90 try: 91 self._run('echo out > {}/direction'.format(gpio_path)) 92 value_string = '1' if power_on else '0' 93 self._run('echo {} > {}/value'.format( 94 value_string, gpio_path)) 95 finally: 96 if did_export: 97 self._run('echo {} > /sys/class/gpio/unexport'.format( 98 gpio_index)) 99 100 def _set_gpio_power_fizz(self, gpio_idx, power_on): 101 """ 102 Turns on or off the power for a specific GPIO on board Fizz. 103 104 @param gpio_idx The index of the gpio to set the power for. 105 @param power_on If True, powers on the GPIO. If False, powers it off. 106 """ 107 value_string = '1' if power_on else '0' 108 cmd = 'ectool gpioset USB{}_ENABLE {}'.format(gpio_idx, 109 value_string) 110 self._run(cmd) 111 112 def _set_gpio_power(self, board, gpio_index, power_on): 113 """ 114 Turns on or off the power for a specific GPIO. 115 116 @param board Board type. Currently support: Guado, Fizz. 117 @param gpio_idx The index of the gpio to set the power for. 118 @param power_on If True, powers on the GPIO. If False, powers it off. 119 """ 120 if board == 'guado': 121 self._set_gpio_power_guado(gpio_index, power_on) 122 elif board == 'fizz': 123 self._set_gpio_power_fizz(gpio_index, power_on) 124 else: 125 raise ValueError('Unsupported board type {}.'.format(board)) 126 127 def _run(self, command): 128 logging.debug('Running: "%s"', command) 129 res = self._host.run(command) 130 logging.debug('Result: "%s"', res) 131 132