blob: 6c9b622b9b3302621584deacb4c7e4d41df391c3 [file] [log] [blame]
Joseph Hwang24a960b2018-06-25 12:59:56 +08001# -*- coding: utf-8 -*-
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +08002# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5"""Chameleond Driver for FPGA customized platform with the TIO card."""
6
Joseph Hwang4c855d12019-12-23 18:26:09 +08007from __future__ import print_function
8
Tom Wai-Hong Tamba7451d2014-10-16 06:44:17 +08009import functools
Cheng-Yi Chiang6eac7232015-05-24 15:54:10 +080010import glob
Joseph Hwang4c855d12019-12-23 18:26:09 +080011# TODO: to port chromite.lib.cros_logging to replace legacy logging
12import logging # pylint: disable=cros-logging-import
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080013import os
Shijin Abrahame23f9282019-07-24 10:55:15 -070014import time
Yu-Hsuan Hsu16482eb2020-02-13 16:12:12 +080015import xmlrpc.client
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080016
Yu-Hsuan Hsu04cff0e2020-02-13 11:55:10 +080017from . import chameleon_common # pylint: disable=W0611
Yuan3342be72014-07-21 18:12:00 +080018
Moja Hsu28e1aab2017-03-30 15:24:35 +080019from chameleond.devices import audio_board
20from chameleond.devices import avsync_probe
Joseph Hwangae09d492019-10-30 19:05:20 +080021from chameleond.devices import bluetooth_base_flow
Moja Hsu70a5a602017-02-24 14:43:03 +080022from chameleond.devices import bluetooth_hid_flow
23from chameleond.devices import codec_flow
24from chameleond.devices import input_flow
Cheng-Yi Chiang93a23132017-04-17 17:17:52 +080025from chameleond.devices import motor_board
Moja Hsu70a5a602017-02-24 14:43:03 +080026from chameleond.devices import usb_audio_flow
27from chameleond.devices import usb_hid_flow
Cheng-Yi Chiang08eab5f2018-03-08 01:30:55 +080028from chameleond.devices import usb_printer_device
Joseph Hwang4c855d12019-12-23 18:26:09 +080029from chameleond.interface import ChameleondInterface
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +080030from chameleond.utils import caching_server
Moja Hsu28e1aab2017-03-30 15:24:35 +080031from chameleond.utils import device_manager
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080032from chameleond.utils import fpga
Moja Hsu3e2fdcc2017-03-24 18:42:57 +080033from chameleond.utils import flow_manager
Tom Wai-Hong Tamf7f6e3c2014-10-15 04:53:52 +080034from chameleond.utils import i2c
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080035from chameleond.utils import ids
Joseph Hwang1c210e62016-06-08 12:04:53 +080036from chameleond.utils import system_tools
Selina Liuce9aa3f2015-08-10 14:17:28 +080037from chameleond.utils import usb
Cheng-Yi Chiang08eab5f2018-03-08 01:30:55 +080038from chameleond.utils import usb_printer_control
Joseph Hwang24a960b2018-06-25 12:59:56 +080039from chameleond.utils.common import lazy
Neeraj Poojary342e2d22018-10-31 12:21:43 -070040from chameleond.utils import bluetooth_a2dp
Joseph Hwang1c210e62016-06-08 12:04:53 +080041
Yu-Hsuan Hsu356006e2020-02-13 14:30:56 +080042if os.environ.get('PLATFORM') != 'FPGA':
43 # pylint: disable=C0412
44 from chameleond.utils import bluetooth_tester
Joseph Hwang4c855d12019-12-23 18:26:09 +080045
Yu-Hsuan Hsu5320fa32019-10-31 16:35:47 +080046if os.environ.get('PLATFORM') == 'RASPI':
Joseph Hwang4c855d12019-12-23 18:26:09 +080047 # pylint: disable=C0412
Yu-Hsuan Hsu5320fa32019-10-31 16:35:47 +080048 from chameleond.devices import raspi_bluetooth_flow
49
Joseph Hwang4c855d12019-12-23 18:26:09 +080050
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080051class DriverError(Exception):
52 """Exception raised when any error on FPGA driver."""
53 pass
54
55
Moja Hsu28e1aab2017-03-30 15:24:35 +080056def _DeviceMethod(device_id):
57 """Decorator that checks if the device exists.
58
59 Args:
60 device_id: The device ID.
61 """
62 def _ActualDecorator(func):
63 @functools.wraps(func)
64 def wrapper(instance, *args, **kwargs):
65 if not instance.HasDevice(device_id):
66 raise DriverError('There is no %s' % ids.DEVICE_NAMES[device_id])
67 return func(instance, *args, **kwargs)
68 return wrapper
69 return _ActualDecorator
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +080070
71
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080072class ChameleondDriver(ChameleondInterface):
73 """Chameleond Driver for FPGA customized platform."""
74
75 _I2C_BUS_MAIN = 0
Cheng-Yi Chiang433d9842015-02-22 16:52:35 +080076 _I2C_BUS_AUDIO_CODEC = 1
Cheng-Yi Chiang93a23132017-04-17 17:17:52 +080077 _I2C_BUS_EXT_BOARD = 3
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080078
Tom Wai-Hong Tam32ce3432014-06-12 09:17:43 +080079 # Time to wait for video frame dump to start before a timeout error is raised
Tom Wai-Hong Tam268def62014-07-09 07:45:33 +080080 _TIMEOUT_FRAME_DUMP_PROBE = 60.0
Tom Wai-Hong Tam32ce3432014-06-12 09:17:43 +080081
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +080082 # The frame index which is used for the regular DumpPixels API.
83 _DEFAULT_FRAME_INDEX = 0
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +080084 _DEFAULT_FRAME_LIMIT = _DEFAULT_FRAME_INDEX + 1
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +080085
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +080086 # Limit the period of async capture to 3min (in 60fps).
87 _MAX_CAPTURED_FRAME_COUNT = 3 * 60 * 60
88
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080089 def __init__(self, *args, **kwargs):
90 super(ChameleondDriver, self).__init__(*args, **kwargs)
Joseph Hwang24a960b2018-06-25 12:59:56 +080091
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -080092 self._platform = os.environ.get('PLATFORM')
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -080093 logging.info('platform: %s', self._platform)
Daniel Winkler6bb5d8b2019-08-13 11:53:24 -070094
Michael Sun3330a042021-04-03 22:13:10 +000095 self.dump_bt_peer_info()
Shijin Abraham7f183c42020-05-14 14:10:38 -070096
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +080097 self._captured_params = {}
Chen-Hao Chang@google.com89d88052016-08-16 15:44:23 +080098 self._process = None
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +080099
Joseph Hwang24a960b2018-06-25 12:59:56 +0800100 # waihong@chromium.org suggests to use a lazy wrapper which instantiates
101 # the following control objects when requested at the first time.
102 self._main_bus = lazy(i2c.I2cBus)(self._I2C_BUS_MAIN)
103 self._ext_board_bus = lazy(i2c.I2cBus)(self._I2C_BUS_EXT_BOARD)
104 self._audio_codec_bus = lazy(i2c.I2cBus)(self._I2C_BUS_AUDIO_CODEC)
105 self._fpga_ctrl = lazy(fpga.FpgaController)()
106 self._usb_audio_ctrl = lazy(usb.USBAudioController)()
107 self._usb_hid_ctrl = lazy(usb.USBController)('g_hid')
108 self._usb_printer_ctrl = lazy(usb_printer_control.USBPrinterController)()
109 self._bluetooth_hid_ctrl = lazy(usb.USBController)(
Alexander Lent980519b2017-08-25 18:38:07 -0700110 bluetooth_hid_flow.BluetoothHIDMouseFlow.DRIVER)
Neeraj Poojary342e2d22018-10-31 12:21:43 -0700111 self._bluetooth_a2dp_sink_ctrl = lazy(usb.USBController)(
112 bluetooth_a2dp.BluetoothA2DPSinkFlow.DRIVER)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800113 # See explanation for using DRIVER_MODULE in bluetooth_nrf52.py
114 self._ble_hid_ctrl = lazy(usb.USBController)(
115 bluetooth_hid_flow.BleHIDMouseFlow.DRIVER_MODULE)
Joseph Hwangae09d492019-10-30 19:05:20 +0800116 # A base control comes without any driver.
117 self._base_ctrl = lazy(usb.USBController)('')
Selina Liuce9aa3f2015-08-10 14:17:28 +0800118
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -0800119 if self._platform == 'CHROMEOS':
Joseph Hwang24a960b2018-06-25 12:59:56 +0800120 self._devices = self.init_devices_for_chromeos()
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -0800121 elif self._platform == 'RASPI':
Neeraj Poojary8d940f92019-07-01 11:35:36 -0700122 self._devices = self.init_devices_for_raspi()
Joseph Hwang24a960b2018-06-25 12:59:56 +0800123 else:
124 self._devices = self.init_devices_for_fpga()
Moja Hsu28e1aab2017-03-30 15:24:35 +0800125
126 self._device_manager = device_manager.DeviceManager(self._devices)
127 self._device_manager.Init()
128 self._flows = self._device_manager.GetDetectedFlows()
129
Joseph Hwang24a960b2018-06-25 12:59:56 +0800130 # Allow to access the methods through object.
Moja Hsu28e1aab2017-03-30 15:24:35 +0800131 # Hence, there is no need to export the methods in ChameleondDriver.
Joseph Hwang24a960b2018-06-25 12:59:56 +0800132 # An object in the following would be None if it is not instantiated
133 # in self._devices above.
Moja Hsu28e1aab2017-03-30 15:24:35 +0800134 self.audio_board = self._device_manager.GetChameleonDevice(ids.AUDIO_BOARD)
Joseph Hwang88a3ae22019-12-26 13:51:58 +0800135 self.bluetooth_audio = self._device_manager.GetChameleonDevice(
136 ids.BLUETOOTH_AUDIO)
Joseph Hwangae09d492019-10-30 19:05:20 +0800137 self.bluetooth_base = self._device_manager.GetChameleonDevice(
138 ids.BLUETOOTH_BASE)
Moja Hsu28e1aab2017-03-30 15:24:35 +0800139 self.bluetooth_mouse = self._device_manager.GetChameleonDevice(
140 ids.BLUETOOTH_HID_MOUSE)
Daniel Winkler3a068112019-08-29 14:11:21 -0700141 self.bluetooth_keyboard = self._device_manager.GetChameleonDevice(
142 ids.BLUETOOTH_HID_KEYBOARD)
Daniel Winkler0901e5c2020-01-27 15:21:09 -0800143 self.bluetooth_tester = self._device_manager.GetChameleonDevice(
144 ids.BLUETOOTH_TESTER)
Moja Hsu28e1aab2017-03-30 15:24:35 +0800145 self.avsync_probe = self._device_manager.GetChameleonDevice(
146 ids.AVSYNC_PROBE)
Cheng-Yi Chiang93a23132017-04-17 17:17:52 +0800147 self.motor_board = self._device_manager.GetChameleonDevice(ids.MOTOR_BOARD)
Cheng-Yi Chiang08eab5f2018-03-08 01:30:55 +0800148 self.printer = self._device_manager.GetChameleonDevice(ids.USB_PRINTER)
Neeraj Poojary342e2d22018-10-31 12:21:43 -0700149 self.bluetooth_a2dp_sink = self._device_manager.GetChameleonDevice(
150 ids.BLUETOOTH_A2DP_SINK)
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800151 self.ble_mouse = self._device_manager.GetChameleonDevice(
152 ids.BLE_MOUSE)
Daniel Winklerc011eea2019-10-02 10:41:25 -0700153 self.ble_keyboard = self._device_manager.GetChameleonDevice(
154 ids.BLE_KEYBOARD)
Yu Liu85ee0a22020-02-13 17:15:06 -0800155 self.ble_phone = self._device_manager.GetChameleonDevice(
156 ids.BLE_PHONE)
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800157 self._flow_manager = flow_manager.FlowManager(self._flows)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800158
159 self.Reset()
160
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -0800161
162 def get_platform(self):
Shijin Abrahamc440eff2019-12-17 10:30:44 -0800163 return self._platform
Shijin Abrahamcd6bffe2019-12-13 15:42:58 -0800164
Joseph Hwang24a960b2018-06-25 12:59:56 +0800165 def init_devices_for_chromeos(self):
166 devices = {
167 ids.BLUETOOTH_HID_MOUSE:
168 bluetooth_hid_flow.BluetoothHIDMouseFlow(
169 ids.BLUETOOTH_HID_MOUSE, self._bluetooth_hid_ctrl),
Neeraj Poojary342e2d22018-10-31 12:21:43 -0700170 ids.BLUETOOTH_A2DP_SINK:
171 bluetooth_a2dp.BluetoothA2DPSinkFlow(
172 ids.BLUETOOTH_A2DP_SINK, self._bluetooth_a2dp_sink_ctrl),
Neeraj Poojary59f8b152019-02-01 14:15:57 -0800173 ids.BLE_MOUSE:
174 bluetooth_hid_flow.BleHIDMouseFlow(
175 ids.BLE_MOUSE, self._ble_hid_ctrl),
Joseph Hwangae09d492019-10-30 19:05:20 +0800176 ids.BLUETOOTH_BASE:
177 bluetooth_base_flow.BluetoothBaseFlow(
178 ids.BLUETOOTH_BASE, self._base_ctrl),
Joseph Hwang24a960b2018-06-25 12:59:56 +0800179 }
180 return devices
181
182 def init_devices_for_fpga(self):
183 devices = {
184 ids.DP1: input_flow.DpInputFlow(
185 ids.DP1, self._main_bus, self._fpga_ctrl),
186 ids.DP2: input_flow.DpInputFlow(
187 ids.DP2, self._main_bus, self._fpga_ctrl),
188 ids.HDMI: input_flow.HdmiInputFlow(
189 ids.HDMI, self._main_bus, self._fpga_ctrl),
190 ids.VGA: input_flow.VgaInputFlow(
191 ids.VGA, self._main_bus, self._fpga_ctrl),
192 ids.MIC: codec_flow.InputCodecFlow(
193 ids.MIC, self._audio_codec_bus, self._fpga_ctrl),
194 ids.LINEIN: codec_flow.InputCodecFlow(
195 ids.LINEIN, self._audio_codec_bus, self._fpga_ctrl),
196 ids.LINEOUT: codec_flow.OutputCodecFlow(
197 ids.LINEOUT, self._audio_codec_bus, self._fpga_ctrl),
198 ids.USB_AUDIO_IN: usb_audio_flow.InputUSBAudioFlow(
199 ids.USB_AUDIO_IN, self._usb_audio_ctrl),
200 ids.USB_AUDIO_OUT: usb_audio_flow.OutputUSBAudioFlow(
201 ids.USB_AUDIO_OUT, self._usb_audio_ctrl),
202 ids.USB_KEYBOARD: usb_hid_flow.KeyboardUSBHIDFlow(
203 ids.USB_KEYBOARD, self._usb_hid_ctrl),
204 ids.USB_TOUCH: usb_hid_flow.TouchUSBHIDFlow(
205 ids.USB_TOUCH, self._usb_hid_ctrl),
206 ids.AVSYNC_PROBE: avsync_probe.AVSyncProbe(ids.AVSYNC_PROBE),
207 ids.AUDIO_BOARD: audio_board.AudioBoard(self._ext_board_bus),
208 ids.MOTOR_BOARD: motor_board.MotorBoard(self._ext_board_bus),
209 ids.USB_PRINTER: usb_printer_device.USBPrinter(self._usb_printer_ctrl),
210 }
211
Joseph Hwang24a960b2018-06-25 12:59:56 +0800212 return devices
213
Neeraj Poojary8d940f92019-07-01 11:35:36 -0700214 def init_devices_for_raspi(self):
215 devices = {
Daniel Winkler3a068112019-08-29 14:11:21 -0700216 ids.BLUETOOTH_HID_KEYBOARD: raspi_bluetooth_flow.RaspiHIDKeyboard(),
Daniel Winklere9ad3db2019-09-10 16:35:04 -0700217 ids.BLUETOOTH_HID_MOUSE: raspi_bluetooth_flow.RaspiHIDMouse(),
Daniel Winklerc011eea2019-10-02 10:41:25 -0700218 ids.BLE_KEYBOARD: raspi_bluetooth_flow.RaspiHIDKeyboard(),
219 ids.BLE_MOUSE: raspi_bluetooth_flow.RaspiHIDMouse(),
Joseph Hwangb72bc6c2019-12-13 20:18:28 +0800220 ids.BLUETOOTH_BASE:
221 bluetooth_base_flow.BluetoothBaseFlow(
222 ids.BLUETOOTH_BASE, self._base_ctrl),
Daniel Winkler0901e5c2020-01-27 15:21:09 -0800223 ids.BLUETOOTH_TESTER: bluetooth_tester.BluetoothTester(),
Yu Liu85ee0a22020-02-13 17:15:06 -0800224 ids.BLE_PHONE: raspi_bluetooth_flow.RaspiBLEPhone(),
Joseph Hwang88a3ae22019-12-26 13:51:58 +0800225 ids.BLUETOOTH_AUDIO: raspi_bluetooth_flow.RaspiBluetoothAudioFlow(),
Neeraj Poojary8d940f92019-07-01 11:35:36 -0700226 }
227 return devices
228
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800229 def Reset(self):
230 """Resets Chameleon board."""
Tom Wai-Hong Tam6b0b3882014-12-17 04:09:29 +0800231 logging.info('Execute the reset process')
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800232 self._flow_manager.Reset()
Moja Hsu28e1aab2017-03-30 15:24:35 +0800233 self._device_manager.Reset()
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +0800234
Cheng-Yi Chiang6eac7232015-05-24 15:54:10 +0800235 self._ClearAudioFiles()
Cheng-Yi Chiang08eab5f2018-03-08 01:30:55 +0800236 self._ClearPrinterFiles()
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +0800237 caching_server.ClearCachedDir()
Cheng-Yi Chiang6eac7232015-05-24 15:54:10 +0800238
Joseph Hwang1c210e62016-06-08 12:04:53 +0800239 def Reboot(self):
240 """Reboots Chameleon board."""
241 logging.info('The chameleon board is going to reboot.')
242 system_tools.SystemTools.Call('reboot')
243
Michael Sun3330a042021-04-03 22:13:10 +0000244 def dump_bt_peer_info(self):
245 """Dump the kernel, BlueZ, chameleon bundle info"""
246 kernel_info = system_tools.SystemTools.Output('uname', '-a').strip()
247 bluez_info = system_tools.SystemTools.Output('bluetoothd', '-v').strip()
248
249 logging.info('kernel_info: %s', kernel_info)
250 logging.info('bluez_version: %s', bluez_info)
251 logging.info('bt_pkg_commit: %s', self.get_bt_commit_hash())
252
Shijin Abraham7f183c42020-05-14 14:10:38 -0700253 def get_bt_commit_hash(self):
254 """Return the last commit hash in chameleon_commits."""
255 try:
Shijin Abrahamb2e3b512020-07-17 10:45:06 -0700256 output = system_tools.SystemTools.Output('grep', 'BUNDLE_COMMIT_HASH',
257 '/etc/default/chameleond')
Shijin Abraham7f183c42020-05-14 14:10:38 -0700258 logging.debug('output is %s', output)
Shijin Abrahamc66fb7f2020-07-22 23:08:56 -0700259 commit = output.split('=')[1].strip().replace('"', '')
Shijin Abraham7f183c42020-05-14 14:10:38 -0700260 logging.debug('got commit %s', commit)
261 return commit
262 except Exception as e:
263 logging.error('Exception %s in get_bt_commit_hash', str(e))
264 return ''
265
Shijin Abraham58fdd462019-12-27 11:37:22 -0800266 def log_message(self, msg):
267 """Write the msg to the chamelond log and system log."""
268 try:
269 logging.info(msg)
270 system_tools.SystemTools.Call('logger', msg)
271 except Exception as e:
272 logging.error("Exception '%s' in log_message msg '%s'", str(e), msg)
273
Moja Hsu28e1aab2017-03-30 15:24:35 +0800274 def GetDetectedStatus(self):
275 """Returns detetcted status of all devices.
276
277 User can use this API to know the capability of the chameleon board.
278
279 Returns:
280 A list of a tuple of detected devices' strings detected status.
281 e.g. [('HDMI', True), ('MIC', False)]
282 """
283 detected_list = []
284 for device_id in self._devices:
285 detected = False
286 if self._device_manager.GetChameleonDevice(device_id):
287 detected = True
288 detected_list.append((ids.DEVICE_NAMES[device_id], detected))
289 return detected_list
290
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800291 def GetSupportedPorts(self):
292 """Returns all supported ports on the board.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800293
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800294 Not like the ProbePorts() method which only returns the ports which
295 are connected, this method returns all supported ports on the board.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800296
297 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800298 A tuple of port_id, for all supported ports on the board.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800299 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800300 return self._flow_manager.GetSupportedPorts()
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800301
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800302 def GetSupportedInputs(self):
303 """Returns all supported input ports on the board.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800304
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800305 Not like the ProbeInputs() method which only returns the input ports which
306 are connected, this method returns all supported input ports on the board.
307
308 Returns:
309 A tuple of port_id, for all supported input port on the board.
310 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800311 return self._flow_manager.GetSupportedInputs()
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800312
313 def GetSupportedOutputs(self):
314 """Returns all supported output ports on the board.
315
316 Not like the ProbeOutputs() method which only returns the output ports which
317 are connected, this method returns all supported output ports on the board.
318
319 Returns:
320 A tuple of port_id, for all supported output port on the board.
321 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800322 return self._flow_manager.GetSupportedOutputs()
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800323
324 def IsPhysicalPlugged(self, port_id):
325 """Returns true if the physical cable is plugged between DUT and Chameleon.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800326
327 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800328 port_id: The ID of the input/output port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800329
330 Returns:
331 True if the physical cable is plugged; otherwise, False.
332 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800333 return self._flow_manager.IsPhysicalPlugged(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800334
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800335 def ProbePorts(self):
336 """Probes all the connected ports on Chameleon board.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800337
338 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800339 A tuple of port_id, for the ports connected to DUT.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800340 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800341 return self._flow_manager.ProbePorts()
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800342
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800343 def ProbeInputs(self):
344 """Probes all the connected input ports on Chameleon board.
345
346 Returns:
347 A tuple of port_id, for the input ports connected to DUT.
348 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800349 return self._flow_manager.ProbeInputs()
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800350
351 def ProbeOutputs(self):
352 """Probes all the connected output ports on Chameleon board.
353
354 Returns:
355 A tuple of port_id, for the output ports connected to DUT.
356 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800357 return self._flow_manager.ProbeOutputs()
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800358
359 def GetConnectorType(self, port_id):
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800360 """Returns the human readable string for the connector type.
361
362 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800363 port_id: The ID of the input/output port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800364
365 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800366 A string, like "HDMI", "DP", "MIC", etc.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800367 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800368 return self._flow_manager.GetConnectorType(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800369
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800370 def HasAudioSupport(self, port_id):
371 """Returns true if the port has audio support.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800372
373 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800374 port_id: The ID of the input/output port.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800375
376 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800377 True if the input/output port has audio support; otherwise, False.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800378 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800379 return self._flow_manager.HasAudioSupport(port_id)
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800380
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800381 def HasVideoSupport(self, port_id):
382 """Returns true if the port has video support.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800383
384 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800385 port_id: The ID of the input/output port.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800386
387 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800388 True if the input/output port has video support; otherwise, False.
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800389 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800390 return self._flow_manager.HasVideoSupport(port_id)
Tom Wai-Hong Tam1b17ade2014-10-14 07:59:43 +0800391
Tom Wai-Hong Tam26009b12014-11-06 07:02:40 +0800392 def SetVgaMode(self, port_id, mode):
393 """Sets the mode for VGA monitor.
394
395 Args:
396 port_id: The ID of the VGA port.
Tom Wai-Hong Tam45411412014-11-11 04:07:36 +0800397 mode: A string of the mode name, e.g. 'PC_1920x1080x60'. Use 'auto'
398 to detect the VGA mode automatically.
Tom Wai-Hong Tam26009b12014-11-06 07:02:40 +0800399 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800400 return self._flow_manager.SetVgaMode(port_id, mode)
Tom Wai-Hong Tam26009b12014-11-06 07:02:40 +0800401
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800402 def WaitVideoInputStable(self, port_id, timeout=None):
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800403 """Waits the video input stable or timeout.
404
405 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800406 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800407 timeout: The time period to wait for.
408
409 Returns:
410 True if the video input becomes stable within the timeout period;
411 otherwise, False.
412 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800413 return self._flow_manager.WaitVideoInputStable(port_id, timeout)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800414
415 def CreateEdid(self, edid):
416 """Creates an internal record of EDID using the given byte array.
417
418 Args:
419 edid: A byte array of EDID data, wrapped in a xmlrpclib.Binary object.
420
421 Returns:
422 An edid_id.
423 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800424 return self._flow_manager.CreateEdid(edid)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800425
426 def DestroyEdid(self, edid_id):
427 """Destroys the internal record of EDID. The internal data will be freed.
428
429 Args:
430 edid_id: The ID of the EDID, which was created by CreateEdid().
431 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800432 self._flow_manager.DestroyEdid(edid_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800433
Tom Wai-Hong Tamae0907d2015-01-14 09:29:50 +0800434 def SetDdcState(self, port_id, enabled):
435 """Sets the enabled/disabled state of DDC bus on the given video input.
436
437 Args:
438 port_id: The ID of the video input port.
439 enabled: True to enable DDC bus due to an user request; False to
440 disable it.
441 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800442 self._flow_manager.SetDdcState(port_id, enabled)
Tom Wai-Hong Tamae0907d2015-01-14 09:29:50 +0800443
Tom Wai-Hong Tamae0907d2015-01-14 09:29:50 +0800444 def IsDdcEnabled(self, port_id):
445 """Checks if the DDC bus is enabled or disabled on the given video input.
446
447 Args:
448 port_id: The ID of the video input port.
449
450 Returns:
451 True if the DDC bus is enabled; False if disabled.
452 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800453 return self._flow_manager.IsDdcEnabled(port_id)
Tom Wai-Hong Tamae0907d2015-01-14 09:29:50 +0800454
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800455 def ReadEdid(self, port_id):
456 """Reads the EDID content of the selected video input on Chameleon.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800457
458 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800459 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800460
461 Returns:
Tom Wai-Hong Tamc78b9eb2014-12-18 07:49:51 +0800462 A byte array of EDID data, wrapped in a xmlrpclib.Binary object,
463 or None if the EDID is disabled.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800464 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800465 return self._flow_manager.ReadEdid(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800466
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800467 def ApplyEdid(self, port_id, edid_id):
468 """Applies the EDID to the selected video input.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800469
470 Note that this method doesn't pulse the HPD line. Should call Plug(),
471 Unplug(), or FireHpdPulse() later.
472
473 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800474 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800475 edid_id: The ID of the EDID.
476 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800477 self._flow_manager.ApplyEdid(port_id, edid_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800478
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800479 def IsPlugged(self, port_id):
480 """Returns true if the port is emulated as plugged.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800481
482 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800483 port_id: The ID of the input/output port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800484
485 Returns:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800486 True if the port is emualted as plugged; otherwise, False.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800487 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800488 return self._flow_manager.IsPlugged(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800489
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800490 def Plug(self, port_id):
491 """Emualtes plug, like asserting HPD line to high on a video port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800492
493 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800494 port_id: The ID of the input/output port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800495 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800496 return self._flow_manager.Plug(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800497
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800498 def Unplug(self, port_id):
499 """Emulates unplug, like deasserting HPD line to low on a video port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800500
501 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800502 port_id: The ID of the input/output port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800503 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800504 return self._flow_manager.Unplug(port_id)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800505
José Roberto de Souza231016d2019-03-13 13:45:00 -0700506 def UnplugHPD(self, port_id):
507 """Only deassert HPD line to low on a video port.
508
509 Args:
510 port_id: The ID of the input/output port.
511 """
512 return self._flow_manager.UnplugHPD(port_id)
513
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800514 def FireHpdPulse(self, port_id, deassert_interval_usec,
Yuan3342be72014-07-21 18:12:00 +0800515 assert_interval_usec=None, repeat_count=1,
516 end_level=1):
517 """Fires one or more HPD pulse (low -> high -> low -> ...).
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800518
519 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800520 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800521 deassert_interval_usec: The time in microsecond of the deassert pulse.
522 assert_interval_usec: The time in microsecond of the assert pulse.
Yuan3342be72014-07-21 18:12:00 +0800523 If None, then use the same value as
524 deassert_interval_usec.
525 repeat_count: The count of HPD pulses to fire.
526 end_level: HPD ends with 0 for LOW (unplugged) or 1 for HIGH (plugged).
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800527 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800528 return self._flow_manager.FireHpdPulse(
529 port_id, deassert_interval_usec, assert_interval_usec, repeat_count,
530 end_level)
Yuan3342be72014-07-21 18:12:00 +0800531
Hung-ying Tyan8ebc5662014-11-20 18:37:00 +0800532 def FireMixedHpdPulses(self, port_id, widths_msec):
Hung-ying Tyan936c83a2014-09-11 17:28:44 +0800533 """Fires one or more HPD pulses, starting at low, of mixed widths.
534
Hung-ying Tyan8ebc5662014-11-20 18:37:00 +0800535 One must specify a list of segment widths in the widths_msec argument where
536 widths_msec[0] is the width of the first low segment, widths_msec[1] is that
537 of the first high segment, widths_msec[2] is that of the second low segment,
538 etc.
Hung-ying Tyan936c83a2014-09-11 17:28:44 +0800539 The HPD line stops at low if even number of segment widths are specified;
540 otherwise, it stops at high.
541
Hung-ying Tyan8ebc5662014-11-20 18:37:00 +0800542 The method is equivalent to a series of calls to Unplug() and Plug()
543 separated by specified pulse widths.
544
Hung-ying Tyan936c83a2014-09-11 17:28:44 +0800545 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800546 port_id: The ID of the video input port.
Hung-ying Tyan8ebc5662014-11-20 18:37:00 +0800547 widths_msec: list of pulse segment widths in milli-second.
Hung-ying Tyan936c83a2014-09-11 17:28:44 +0800548 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800549 return self._flow_manager.FireMixedHpdPulses(port_id, widths_msec)
Hung-ying Tyan936c83a2014-09-11 17:28:44 +0800550
Paul Kocialkowskidf151112017-06-20 10:30:44 +0300551 def ScheduleHpdToggle(self, port_id, delay_ms, rising_edge):
552 """Schedules one HPD Toggle, with a delay between the toggle.
553
554 Args:
555 port_id: The ID of the video input port.
556 delay_ms: Delay in milli-second before the toggle takes place.
557 rising_edge: Whether the toggle should be a rising edge or a falling edge.
558 """
559 return self._flow_manager.ScheduleHpdToggle(port_id, delay_ms, rising_edge)
560
Tom Wai-Hong Tam50bc4c92015-01-09 02:54:36 +0800561 def SetContentProtection(self, port_id, enabled):
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800562 """Sets the content protection state on the port.
563
564 Args:
565 port_id: The ID of the video input port.
Tom Wai-Hong Tam50bc4c92015-01-09 02:54:36 +0800566 enabled: True to enable; False to disable.
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800567 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800568 self._flow_manager.SetContentProtection(port_id, enabled)
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800569
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800570 def IsContentProtectionEnabled(self, port_id):
571 """Returns True if the content protection is enabled on the port.
572
573 Args:
574 port_id: The ID of the video input port.
575
576 Returns:
577 True if the content protection is enabled; otherwise, False.
578 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800579 return self._flow_manager.IsContentProtectionEnabled(port_id)
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800580
Tom Wai-Hong Tamb51a1962014-12-24 02:19:12 +0800581 def IsVideoInputEncrypted(self, port_id):
582 """Returns True if the video input on the port is encrypted.
583
584 Args:
585 port_id: The ID of the video input port.
586
587 Returns:
588 True if the video input is encrypted; otherwise, False.
589 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800590 return self._flow_manager.IsVideoInputEncrypted(port_id)
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +0800591
Chen-Hao Chang@google.com89d88052016-08-16 15:44:23 +0800592 def StartMonitoringAudioVideoCapturingDelay(self):
593 """Starts an audio/video synchronization utility
594
595 The example of usage:
596 chameleon.StartMonitoringAudioVideoCapturingDelay()
597 chameleon.StartCapturingVideo(hdmi_input)
598 chameleon.StartCapturingAudio(hdmi_input)
599 time.sleep(2)
600 chameleon.StopCapturingVideo()
601 chameleon.StopCapturingAudio(hdmi_input)
602 delay = chameleon.GetAudioVideoCapturingDelay()
603 """
604 self._process = system_tools.SystemTools.RunInSubprocess('avsync')
605
606 def GetAudioVideoCapturingDelay(self):
607 """Get the time interval between the first audio/video cpatured data
608
609 Returns:
610 A floating points indicating the time interval between the first
611 audio/video data captured. If the result is negative, then the first
612 video data is earlier, otherwise the first audio data is earlier.
613
614 Raises:
615 DriverError if there is no output from the monitoring process.
616 """
617
Neeraj Poojary8d940f92019-07-01 11:35:36 -0700618 if self._process.poll() is None:
Chen-Hao Chang@google.com89d88052016-08-16 15:44:23 +0800619 self._process.terminate()
620 raise DriverError('The monitoring process has not finished.')
621
622 return_code, out, err = system_tools.SystemTools.GetSubprocessOutput(
623 self._process)
624
625 if return_code != 0 or err:
626 raise DriverError('Runtime error in the monitoring process')
627
628 if not out:
629 raise DriverError('No output from the monitoring process.')
630
631 return float(out)
632
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800633 def DumpPixels(self, port_id, x=None, y=None, width=None, height=None):
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800634 """Dumps the raw pixel array of the selected area.
635
636 If not given the area, default to capture the whole screen.
637
638 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800639 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800640 x: The X position of the top-left corner.
641 y: The Y position of the top-left corner.
642 width: The width of the area.
643 height: The height of the area.
644
645 Returns:
646 A byte-array of the pixels, wrapped in a xmlrpclib.Binary object.
647 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800648 x, y, width, height = self._AutoFillArea(port_id, x, y, width, height)
649 self.CaptureVideo(port_id, self._DEFAULT_FRAME_LIMIT, x, y, width, height)
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800650 return self.ReadCapturedFrame(self._DEFAULT_FRAME_INDEX)
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800651
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800652 def _AutoFillArea(self, port_id, x, y, width, height):
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800653 """Verifies the area argument correctness and fills the default values.
654
Tom Wai-Hong Tam0d490c12014-08-21 09:22:14 +0800655 It keeps x=None and y=None if all of the x, y, width, and height are None.
656 That hints FPGA to use a full-screen capture, not a cropped-sccren capture.
657
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800658 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800659 port_id: The ID of the video input port.
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800660 x: The X position of the top-left corner.
661 y: The Y position of the top-left corner.
662 width: The width of the area.
663 height: The height of the area.
664
665 Returns:
666 A tuple of (x, y, width, height)
667
668 Raises:
669 DriverError if the area is not specified correctly.
670 """
671 if (x, y, width, height) == (None, ) * 4:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800672 return (None, None) + self.DetectResolution(port_id)
Tom Wai-Hong Tam0d490c12014-08-21 09:22:14 +0800673 elif (x, y) == (None, ) * 2 or None not in (x, y, width, height):
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800674 return (x, y, width, height)
675 else:
676 raise DriverError('Some of area arguments are not specified.')
677
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800678 def GetMaxFrameLimit(self, port_id, width, height):
Tom Wai-Hong Tamdc3780d2014-08-15 00:45:13 +0800679 """Gets the maximal number of frames which are accommodated in the buffer.
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800680
681 It depends on the size of the internal buffer on the board and the
Tom Wai-Hong Tamdc3780d2014-08-15 00:45:13 +0800682 size of area to capture (full screen or cropped area).
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800683
684 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800685 port_id: The ID of the video input port.
Tom Wai-Hong Tamdc3780d2014-08-15 00:45:13 +0800686 width: The width of the area to capture.
687 height: The height of the area to capture.
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800688
689 Returns:
690 A number of the frame limit.
691 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800692 return self._flow_manager.GetMaxFrameLimit(port_id, width, height)
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800693
Joseph Hwang4c855d12019-12-23 18:26:09 +0800694 def _PrepareCapturingVideo(self, port_id, width, height):
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800695 """Prepares capturing video on the given video input.
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800696
697 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800698 port_id: The ID of the video input port.
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800699 width: The width of the area of crop.
700 height: The height of the area of crop.
701 """
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +0800702 caching_server.ClearCachedDir()
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800703 self._flow_manager.SelectInput(port_id)
704 if not self._flow_manager.IsPlugged(port_id):
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800705 raise DriverError('HPD is unplugged. No signal is expected.')
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800706 self._captured_params = {
Tom Wai-Hong Tam0d128842015-01-14 07:13:45 +0800707 'port_id': port_id,
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800708 'max_frame_limit': self._flow_manager.GetMaxFrameLimit(port_id,
709 width, height)
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800710 }
711
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800712 def StartCapturingVideo(self, port_id, x=None, y=None, width=None,
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800713 height=None):
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800714 """Starts video capturing continuously on the given video input.
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800715
716 This API is an asynchronous call. It returns after the video starts
717 capturing. The caller should call StopCapturingVideo to stop it.
718
719 The example of usage:
720 chameleon.StartCapturingVideo(hdmi_input)
721 time.sleep(2)
722 chameleon.StopCapturingVideo()
723 for i in xrange(chameleon.GetCapturedFrameCount()):
724 frame = chameleon.ReadCapturedFrame(i, *area).data
725 CompareFrame(frame, golden_frames[i])
726
727 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800728 port_id: The ID of the video input port.
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800729 x: The X position of the top-left corner of crop.
730 y: The Y position of the top-left corner of crop.
731 width: The width of the area of crop.
732 height: The height of the area of crop.
733 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800734 x, y, width, height = self._AutoFillArea(port_id, x, y, width, height)
Joseph Hwang4c855d12019-12-23 18:26:09 +0800735 self._PrepareCapturingVideo(port_id, width, height)
Tom Wai-Hong Tam6bb537f2015-03-19 02:43:08 +0800736 max_frame_limit = self._captured_params['max_frame_limit']
Tom Wai-Hong Tam6b0b3882014-12-17 04:09:29 +0800737 logging.info('Start capturing video from port #%d', port_id)
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800738 self._flow_manager.StartDumpingFrames(
739 port_id, max_frame_limit, x, y, width, height,
740 self._MAX_CAPTURED_FRAME_COUNT)
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800741
Tom Wai-Hong Tamf12bfa12014-12-18 09:20:39 +0800742 def StopCapturingVideo(self, stop_index=None):
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800743 """Stops video capturing which was started previously.
744
Tom Wai-Hong Tamf12bfa12014-12-18 09:20:39 +0800745 Args:
746 stop_index: Wait for the captured frame count to reach this index. If
747 not given, stop immediately. Note that the captured frame of
748 stop_index should not be read.
749
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800750 Raises:
751 DriverError if the capture period is longer than the capture limitation.
752 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800753 port_id = self._captured_params['port_id']
Tom Wai-Hong Tamf12bfa12014-12-18 09:20:39 +0800754 if stop_index:
755 if stop_index >= self._MAX_CAPTURED_FRAME_COUNT:
756 raise DriverError('Exceeded the limit of capture, stop_index >= %d' %
757 self._MAX_CAPTURED_FRAME_COUNT)
758 logging.info('Waiting the captured frame count reaches %d...', stop_index)
759 while self.GetCapturedFrameCount() < stop_index:
760 pass
761
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800762 self._flow_manager.StopDumpingFrames(port_id)
Tom Wai-Hong Tam6b0b3882014-12-17 04:09:29 +0800763 logging.info('Stopped capturing video from port #%d', port_id)
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800764 if self.GetCapturedFrameCount() >= self._MAX_CAPTURED_FRAME_COUNT:
765 raise DriverError('Exceeded the limit of capture, frame_count >= %d' %
766 self._MAX_CAPTURED_FRAME_COUNT)
767
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800768 def CaptureVideo(self, port_id, total_frame, x=None, y=None, width=None,
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800769 height=None):
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800770 """Captures the video stream on the given video input to the buffer.
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800771
772 This API is a synchronous call. It returns after all the frames are
773 captured. The frames can be read using the ReadCapturedFrame API.
774
775 The example of usage:
776 chameleon.CaptureVideo(hdmi_input, total_frame)
777 for i in xrange(total_frame):
778 frame = chameleon.ReadCapturedFrame(i, *area).data
779 CompareFrame(frame, golden_frames[i])
780
781 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800782 port_id: The ID of the video input port.
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800783 total_frame: The total number of frames to capture, should not larger
784 than value of GetMaxFrameLimit.
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800785 x: The X position of the top-left corner of crop.
786 y: The Y position of the top-left corner of crop.
787 width: The width of the area of crop.
788 height: The height of the area of crop.
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800789 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800790 x, y, width, height = self._AutoFillArea(port_id, x, y, width, height)
Tom Wai-Hong Tam6b0b3882014-12-17 04:09:29 +0800791 logging.info('Capture video from port #%d', port_id)
Joseph Hwang4c855d12019-12-23 18:26:09 +0800792 self._PrepareCapturingVideo(port_id, width, height)
Tom Wai-Hong Tam6bb537f2015-03-19 02:43:08 +0800793 max_frame_limit = self._captured_params['max_frame_limit']
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800794 if total_frame > max_frame_limit:
Joseph Hwang4c855d12019-12-23 18:26:09 +0800795 raise DriverError('Exceed the max frame limit %d > %d' %
796 (total_frame, max_frame_limit))
Tom Wai-Hong Tam9ab344a2014-06-17 03:17:36 +0800797
Tom Wai-Hong Tam268def62014-07-09 07:45:33 +0800798 # TODO(waihong): Make the timeout value based on the FPS rate.
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800799 self._flow_manager.DumpFramesToLimit(
800 port_id, total_frame, x, y, width, height,
801 self._TIMEOUT_FRAME_DUMP_PROBE)
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800802
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800803 def GetCapturedFrameCount(self):
804 """Gets the total count of the captured frames.
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800805
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800806 Returns:
807 The number of frames captured.
808 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800809 port_id = self._captured_params['port_id']
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800810 return self._flow_manager.GetDumpedFrameCount(port_id)
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +0800811
812 def GetCapturedResolution(self):
813 """Gets the resolution of the captured frame.
814
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800815 If a cropping area is specified on capturing, returns the cropped
816 resolution.
817
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +0800818 Returns:
819 A (width, height) tuple.
820 """
Tom Wai-Hong Tambe82d372015-03-19 07:31:28 +0800821 port_id = self._captured_params['port_id']
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800822 return self._flow_manager.GetCapturedResolution(port_id)
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +0800823
Tom Wai-Hong Tam97bbdd32014-07-24 05:35:45 +0800824 def ReadCapturedFrame(self, frame_index):
Tom Wai-Hong Tambe82d372015-03-19 07:31:28 +0800825 """Reads the content of the captured frame from the buffer.
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +0800826
827 Args:
828 frame_index: The index of the frame to read.
Tom Wai-Hong Tamf2b1a202014-06-17 03:04:34 +0800829
830 Returns:
831 A byte-array of the pixels, wrapped in a xmlrpclib.Binary object.
832 """
Tom Wai-Hong Tamdd6ce4e2014-12-16 06:34:38 +0800833 port_id = self._captured_params['port_id']
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800834 total_frame = self.GetCapturedFrameCount()
Tom Wai-Hong Tam6bb537f2015-03-19 02:43:08 +0800835 max_frame_limit = self._captured_params['max_frame_limit']
Tom Wai-Hong Tamdd6ce4e2014-12-16 06:34:38 +0800836 # The captured frames are store in a circular buffer. Only the latest
837 # max_frame_limit frames are valid.
838 first_valid_index = max(0, total_frame - max_frame_limit)
839 if not first_valid_index <= frame_index < total_frame:
840 raise DriverError('The frame index is out-of-range: %d not in [%d, %d)' %
841 (frame_index, first_valid_index, total_frame))
842
Tom Wai-Hong Tambe82d372015-03-19 07:31:28 +0800843 # Use the projected index.
844 frame_index = frame_index % max_frame_limit
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800845 screen = self._flow_manager.ReadCapturedFrame(port_id, frame_index)
Yu-Hsuan Hsu16482eb2020-02-13 16:12:12 +0800846 return xmlrpc.client.Binary(screen)
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800847
Tom Wai-Hong Tam3ce649c2016-03-16 03:33:03 +0800848 def CacheFrameThumbnail(self, frame_index, ratio=2):
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +0800849 """Caches the thumbnail of the dumped field to a temp file.
850
851 Args:
852 frame_index: The index of the frame to cache.
Tom Wai-Hong Tam3ce649c2016-03-16 03:33:03 +0800853 ratio: The ratio to scale down the image.
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +0800854
855 Returns:
856 An ID to identify the cached thumbnail.
857 """
858 port_id = self._captured_params['port_id']
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800859 return self._flow_manager.CacheFrameThumbnail(port_id, frame_index, ratio)
Tom Wai-Hong Tam91afcfc2016-03-11 07:26:41 +0800860
Tom Wai-Hong Tamc89c4e82016-03-10 04:41:48 +0800861 def _GetCapturedSignals(self, signal_func_name, start_index=0,
862 stop_index=None):
863 """Gets the list of signals of the captured frames.
Tom Wai-Hong Tam4f769e02014-07-09 07:54:40 +0800864
865 Args:
Tom Wai-Hong Tamc89c4e82016-03-10 04:41:48 +0800866 signal_func_name: The name of the signal function, e.g. 'GetFrameHashes'.
Tom Wai-Hong Tam3610cce2014-12-13 08:57:27 +0800867 start_index: The index of the start frame. Default is 0.
868 stop_index: The index of the stop frame (excluded). Default is the
869 value of GetCapturedFrameCount.
Tom Wai-Hong Tam4f769e02014-07-09 07:54:40 +0800870
871 Returns:
Tom Wai-Hong Tamc89c4e82016-03-10 04:41:48 +0800872 The list of signals.
Tom Wai-Hong Tam4f769e02014-07-09 07:54:40 +0800873 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800874 port_id = self._captured_params['port_id']
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800875 total_frame = self.GetCapturedFrameCount()
Tom Wai-Hong Tam3610cce2014-12-13 08:57:27 +0800876 if stop_index is None:
877 stop_index = total_frame
Tom Wai-Hong Tam12609b22014-08-01 01:36:24 +0800878 if not 0 <= start_index < total_frame:
879 raise DriverError('The start index is out-of-range: %d not in [0, %d)' %
880 (start_index, total_frame))
881 if not 0 < stop_index <= total_frame:
882 raise DriverError('The stop index is out-of-range: %d not in (0, %d]' %
883 (stop_index, total_frame))
Tom Wai-Hong Tamc89c4e82016-03-10 04:41:48 +0800884 signal_func = getattr(self._flows[port_id], signal_func_name)
885 return signal_func(start_index, stop_index)
886
887 def GetCapturedChecksums(self, start_index=0, stop_index=None):
888 """Gets the list of checksums of the captured frames.
889
890 Args:
891 start_index: The index of the start frame. Default is 0.
892 stop_index: The index of the stop frame (excluded). Default is the
893 value of GetCapturedFrameCount.
894
895 Returns:
896 The list of checksums of frames.
897 """
898 return self._GetCapturedSignals('GetFrameHashes', start_index, stop_index)
899
900 def GetCapturedHistograms(self, start_index=0, stop_index=None):
901 """Gets the list of histograms of the captured frames.
902
903 Args:
904 start_index: The index of the start frame. Default is 0.
905 stop_index: The index of the stop frame (excluded). Default is the
906 value of GetCapturedFrameCount.
907
908 Returns:
Chen-Hao Chang@google.com89d88052016-08-16 15:44:23 +0800909 The list of histograms of frames.
Tom Wai-Hong Tamc89c4e82016-03-10 04:41:48 +0800910 """
911 return self._GetCapturedSignals('GetHistograms', start_index, stop_index)
Tom Wai-Hong Tam4f769e02014-07-09 07:54:40 +0800912
Tom Wai-Hong Tam0d128842015-01-14 07:13:45 +0800913 def ComputePixelChecksum(
914 self, port_id, x=None, y=None, width=None, height=None):
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800915 """Computes the checksum of pixels in the selected area.
916
917 If not given the area, default to compute the whole screen.
918
919 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800920 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800921 x: The X position of the top-left corner.
922 y: The Y position of the top-left corner.
923 width: The width of the area.
924 height: The height of the area.
925
926 Returns:
927 The checksum of the pixels.
928 """
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800929 x, y, width, height = self._AutoFillArea(port_id, x, y, width, height)
930 self.CaptureVideo(port_id, self._DEFAULT_FRAME_LIMIT, x, y, width, height)
Tom Wai-Hong Tamd1266e82014-07-26 07:46:02 +0800931 return self.GetCapturedChecksums(self._DEFAULT_FRAME_INDEX,
932 self._DEFAULT_FRAME_INDEX + 1)[0]
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800933
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800934 def DetectResolution(self, port_id):
935 """Detects the video source resolution.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800936
937 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +0800938 port_id: The ID of the video input port.
Tom Wai-Hong Tam0703c542014-05-16 14:13:26 +0800939
940 Returns:
941 A (width, height) tuple.
942 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +0800943 return self._flow_manager.DetectResolution(port_id)
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +0800944
Simon Ser1df012d2019-06-13 16:18:49 +0300945 def GetVideoParams(self, port_id):
946 """Gets video parameters.
947
948 Args:
949 port_id: The ID of the video input port.
950
951 Returns:
952 A dict containing video parameters. Fields are omitted if unknown.
953 """
954 return self._flow_manager.GetVideoParams(port_id)
955
Simon Serc4de51d2019-07-16 17:28:02 +0300956 def GetLastInfoFrame(self, port_id, infoframe_type):
957 """Obtains the last received InfoFrame of the specified type.
958
959 Args:
960 port_id: The ID of the video input port
961 infoframe_type (string): the InfoFrame type
962
963 Returns:
964 A dict containing the InfoFrame.
965 """
966 infoframe = self._flow_manager.GetLastInfoFrame(port_id, infoframe_type)
967 # Wrap the binary blob in an xmlrpclib object so that it can be serialized
968 # in the XML-RPC reply.
Yu-Hsuan Hsu16482eb2020-02-13 16:12:12 +0800969 infoframe['payload'] = xmlrpc.client.Binary(infoframe['payload'])
Simon Serc4de51d2019-07-16 17:28:02 +0300970 return infoframe
971
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +0800972 def HasAudioBoard(self):
973 """Returns True if there is an audio board.
974
975 Returns:
976 True if there is an audio board. False otherwise.
977 """
Moja Hsu28e1aab2017-03-30 15:24:35 +0800978 return self.audio_board is not None
979
980 def HasDevice(self, device_id):
981 """Returns True if there is a device.
982
983 Returns:
984 True if there is a device . False otherwise.
985 """
986 return self._device_manager.GetChameleonDevice(device_id) is not None
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +0800987
Simon Ser403397e2019-04-23 14:06:06 +0300988 def GetAudioChannelMapping(self, port_id):
989 """Obtains the channel mapping for an audio port.
990
991 Args:
992 port_id: The ID of the audio port.
993
994 Returns:
995 An array of integers. There is one element per Chameleon channel.
996 For audio input ports, each element indicates which input channel the
997 capture channel is mapped to. For audio output ports, each element
998 indicates which output channel the playback channel is mapped to. As a
999 special case, -1 means the channel isn't mapped.
1000
1001 Raises:
1002 FlowManagerError: no audio capture in progress
1003 """
1004 return self._flow_manager.GetAudioChannelMapping(port_id)
1005
Simon Sera669e962019-04-11 15:15:05 +03001006 def GetAudioFormat(self, port_id):
1007 """Gets the format currently used by audio capture.
1008
1009 Args:
1010 port_id: The ID of the audio input port.
1011
1012 Returns:
1013 A dict containing the format properties. The keys are:
1014 file_type: 'raw' or 'wav'
1015 sample_format: 'S32_LE' for 32-bit signed integers in little-endian. See
1016 aplay(1) for more formats.
1017 channel: number of channels
1018 rate: sampling rate in Hz (or zero if unknown)
1019
1020 Raises:
1021 FlowManagerError: no audio capture in progress
1022 """
1023 return self._flow_manager.GetAudioFormat(port_id).AsDict()
1024
Simon Ser44866c72019-05-16 15:34:12 +03001025 def TriggerLinkFailure(self, port_id):
1026 """Trigger a link failure on the port.
1027
1028 Args:
1029 port_id: The ID of the input port.
1030 """
1031 self._flow_manager.TriggerLinkFailure(port_id)
1032
Moja Hsu7ef568c2016-12-27 15:58:58 +08001033 def StartCapturingAudio(self, port_id, has_file=True):
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001034 """Starts capturing audio.
1035
Cheng-Yi Chiangb8b247e2015-01-20 11:41:51 +08001036 Refer to the docstring of StartPlayingEcho about the restriction of
1037 capturing and echoing at the same time.
1038
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001039 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +08001040 port_id: The ID of the audio input port.
Moja Hsu7ef568c2016-12-27 15:58:58 +08001041 has_file: True for saving audio data to file. False otherwise.
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001042 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001043 self._flow_manager.StartCapturingAudio(port_id, has_file)
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001044
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +08001045 def StopCapturingAudio(self, port_id):
Cheng-Yi Chiangcdec7ef2015-05-24 15:45:47 +08001046 """Stops capturing audio and returns recorded data path and format.
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001047
1048 Args:
Tom Wai-Hong Tam47ebf232014-10-15 08:06:09 +08001049 port_id: The ID of the audio input port.
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001050
1051 Returns:
Cheng-Yi Chiangcdec7ef2015-05-24 15:45:47 +08001052 A tuple (path, format).
1053 path: The path to the captured audio data.
Simon Sera669e962019-04-11 15:15:05 +03001054 format: The format of the captured data. See GetAudioFormat. Note that
1055 the returned audio frequency may not be correct, for this reason
1056 calling GetAudioFormat during the capture is preferred.
Moja Hsu7ef568c2016-12-27 15:58:58 +08001057 If we assign parameter has_file=False in StartCapturingAudio, we will get
1058 both empty string in path and format.
Cheng-Yi Chiang6048d002014-07-30 17:51:31 +08001059 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001060 return self._flow_manager.StopCapturingAudio(port_id)
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001061
Cheng-Yi Chiang0c742c72015-05-24 15:53:32 +08001062 def StartPlayingAudio(self, port_id, path, data_format):
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001063 """Playing audio data from an output port.
1064
Cheng-Yi Chiang0c742c72015-05-24 15:53:32 +08001065 Play audio data at given path using given format from port_id port.
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001066
1067 Args:
1068 port_id: The ID of the output connector.
Cheng-Yi Chiang0c742c72015-05-24 15:53:32 +08001069 path: The path to the audio data to play.
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001070 data_format: The dict representation of AudioDataFormat.
1071 Refer to docstring of utils.audio.AudioDataFormat for detail.
1072 Currently Chameleon only accepts data format if it meets
1073 dict(file_type='raw', sample_format='S32_LE', channel=8, rate=48000)
1074 Chameleon user should do the format conversion to minimize work load
1075 on Chameleon board.
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001076 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001077 self._flow_manager.StartPlayingAudio(port_id, path, data_format)
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001078
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001079 def StartPlayingEcho(self, port_id, input_id):
1080 """Echoes audio data received from input_id and plays to port_id.
1081
1082 Echoes audio data received from input_id and plays to port_id.
1083
Cheng-Yi Chiangb8b247e2015-01-20 11:41:51 +08001084 Chameleon does not support echoing from HDMI and capturing from LineIn/Mic
1085 at the same time. The echoing/capturing needs to be stop first before
1086 another action starts.
1087
1088 For example, user can call
1089
1090 StartPlayingEcho(3, 7) --> StopPlayingAudio(3) --> StartCapturingAudio(6)
1091
1092 or
1093
1094 StartCapturingAudio(6) --> StopCapturingAudio(6) --> StartPlayingEcho(3, 7)
1095
1096 but user can not call
1097
1098 StartPlayingEcho(3, 7) --> StartCapturingAudio(6)
1099
1100 or
1101
1102 StartCapturingAudio(6) --> StartPlayingEcho(3, 7)
1103
1104 Exception is raised when conflicting actions are performed.
1105
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001106 Args:
1107 port_id: The ID of the output connector. Check the value in ids.py.
1108 input_id: The ID of the input connector. Check the value in ids.py.
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001109 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001110 self._flow_manager.StartPlayingEcho(port_id, input_id)
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001111
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001112 def StopPlayingAudio(self, port_id):
1113 """Stops playing audio from port_id port.
1114
1115 Args:
1116 port_id: The ID of the output connector.
Cheng-Yi Chiang9e62a862014-10-09 19:16:01 +08001117 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001118 self._flow_manager.StopPlayingAudio(port_id)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001119
Cheng-Yi Chiang08eab5f2018-03-08 01:30:55 +08001120 def _ClearPrinterFiles(self):
1121 """Clears temporary printer files.
1122
1123 Chameleon board does not reboot very often. We should clear the temporary
1124 printer files used in capturing printer data.
1125 """
1126 for path in glob.glob('/tmp/printer_*'):
1127 os.unlink(path)
1128
Cheng-Yi Chiang6eac7232015-05-24 15:54:10 +08001129 def _ClearAudioFiles(self):
1130 """Clears temporary audio files.
1131
1132 Chameleon board does not reboot very often. We should clear the temporary
1133 audio files used in capturing audio or playing audio when Reset is called.
1134 """
1135 for path in glob.glob('/tmp/audio_*'):
1136 os.unlink(path)
1137
Moja Hsu28e1aab2017-03-30 15:24:35 +08001138 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001139 def AudioBoardConnect(self, bus_number, endpoint):
1140 """Connects an endpoint to an audio bus.
1141
1142 Args:
1143 bus_number: 1 or 2 for audio bus 1 or bus 2.
1144 endpoint: An endpoint defined in audio_board.AudioBusEndpoint.
1145
1146 Raises:
1147 DriverError: If the endpoint is a source and there is other source
1148 endpoint occupying audio bus.
1149 """
1150 if audio_board.IsSource(endpoint):
Moja Hsu28e1aab2017-03-30 15:24:35 +08001151 current_sources, _ = self.audio_board.GetConnections(bus_number)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001152 if current_sources and endpoint not in current_sources:
1153 raise DriverError(
1154 'Sources %s other than %s are currently occupying audio bus.' %
1155 (current_sources, endpoint))
1156
Moja Hsu28e1aab2017-03-30 15:24:35 +08001157 self.audio_board.SetConnection(
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001158 bus_number, endpoint, True)
1159
Moja Hsu28e1aab2017-03-30 15:24:35 +08001160 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001161 def AudioBoardDisconnect(self, bus_number, endpoint):
1162 """Disconnects an endpoint to an audio bus.
1163
1164 Args:
1165 bus_number: 1 or 2 for audio bus 1 or bus 2.
1166 endpoint: An endpoint defined in audio_board.AudioBusEndpoint.
1167
1168 Raises:
1169 DriverError: If the endpoint is not connected to audio bus.
1170 """
Moja Hsu28e1aab2017-03-30 15:24:35 +08001171 if not self.audio_board.IsConnected(bus_number, endpoint):
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001172 raise DriverError(
1173 'Endpoint %s is not connected to audio bus %d.' %
1174 (endpoint, bus_number))
1175
Moja Hsu28e1aab2017-03-30 15:24:35 +08001176 self.audio_board.SetConnection(
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001177 bus_number, endpoint, False)
1178
Moja Hsu28e1aab2017-03-30 15:24:35 +08001179 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001180 def AudioBoardGetRoutes(self, bus_number):
1181 """Gets a list of routes on audio bus.
1182
1183 Args:
1184 bus_number: 1 or 2 for audio bus 1 or bus 2.
1185
1186 Returns:
1187 A list of tuples (source, sink) that are routed on audio bus
1188 where source and sink are endpoints defined in
1189 audio_board.AudioBusEndpoint.
1190 """
Moja Hsu28e1aab2017-03-30 15:24:35 +08001191 sources, sinks = self.audio_board.GetConnections(bus_number)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001192 routes = []
1193 for source in sources:
1194 for sink in sinks:
1195 logging.info('Route on bus %d: %s ---> %s',
1196 bus_number, source, sink)
1197 routes.append((source, sink))
1198 return routes
1199
Moja Hsu28e1aab2017-03-30 15:24:35 +08001200 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiang32cbd452015-02-22 17:54:35 +08001201 def AudioBoardClearRoutes(self, bus_number):
1202 """Clears routes on an audio bus.
1203
1204 Args:
1205 bus_number: 1 or 2 for audio bus 1 or bus 2.
1206 """
Moja Hsu28e1aab2017-03-30 15:24:35 +08001207 self.audio_board.ResetConnections(bus_number)
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001208
Moja Hsu28e1aab2017-03-30 15:24:35 +08001209 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001210 def AudioBoardHasJackPlugger(self):
1211 """If there is jack plugger on audio board.
1212
1213 Audio board must have the motor cable connected in order to control
1214 jack plugger of audio box.
1215
1216 Returns:
1217 True if there is jack plugger on audio board. False otherwise.
1218 """
Moja Hsu28e1aab2017-03-30 15:24:35 +08001219 return self.audio_board.HasJackPlugger()
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001220
Moja Hsu28e1aab2017-03-30 15:24:35 +08001221 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001222 def AudioBoardAudioJackPlug(self):
1223 """Plugs audio jack to connect audio board and Cros device."""
1224 logging.info('Plug audio jack to connect audio board and Cros device.')
Moja Hsu28e1aab2017-03-30 15:24:35 +08001225 self.audio_board.SetJackPlugger(True)
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001226
Moja Hsu28e1aab2017-03-30 15:24:35 +08001227 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiangcb34f2d2015-04-02 12:38:41 +08001228 def AudioBoardAudioJackUnplug(self):
1229 """Unplugs audio jack to disconnect audio board and Cros device."""
1230 logging.info('Unplug audio jack to disconnect audio board and Cros device.')
Moja Hsu28e1aab2017-03-30 15:24:35 +08001231 self.audio_board.SetJackPlugger(False)
Cheng-Yi Chiange9b00ff2015-04-14 17:07:46 +08001232
Moja Hsu28e1aab2017-03-30 15:24:35 +08001233 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiange9b00ff2015-04-14 17:07:46 +08001234 def AudioBoardResetBluetooth(self):
1235 """Resets bluetooth module on audio board."""
1236 logging.info('Resets bluetooth module on audio board.')
Moja Hsu28e1aab2017-03-30 15:24:35 +08001237 self.audio_board.ResetBluetooth()
Cheng-Yi Chiange9b00ff2015-04-14 17:07:46 +08001238
Moja Hsu28e1aab2017-03-30 15:24:35 +08001239 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiange9b00ff2015-04-14 17:07:46 +08001240 def AudioBoardDisableBluetooth(self):
1241 """Disables bluetooth module on audio board."""
1242 logging.info('Disables bluetooth module on audio board.')
Moja Hsu28e1aab2017-03-30 15:24:35 +08001243 self.audio_board.DisableBluetooth()
Cheng-Yi Chiang00f69842015-05-24 15:30:53 +08001244
Moja Hsu28e1aab2017-03-30 15:24:35 +08001245 @_DeviceMethod(ids.AUDIO_BOARD)
Cheng-Yi Chiang8a45b9c2015-06-01 19:14:13 +08001246 def AudioBoardIsBluetoothEnabled(self):
1247 """Checks if bluetooth module on audio board is enabled.
1248
1249 Returns:
1250 True if bluetooth module is enabled. False otherwise.
1251 """
Moja Hsu28e1aab2017-03-30 15:24:35 +08001252 return self.audio_board.IsBluetoothEnabled()
Cheng-Yi Chiang8a45b9c2015-06-01 19:14:13 +08001253
Selina Liu6096c7a2015-08-19 21:01:03 +08001254 def SetUSBDriverPlaybackConfigs(self, playback_data_format):
1255 """Updates the corresponding playback configurations to argument values.
1256
1257 This provides flexibility for simulating the USB gadget driver using other
1258 configurations different from the default values.
1259
1260 Args:
1261 playback_data_format: The dict form of an AudioDataFormat object. The
1262 'file_type' field will be ignored since for playback, there is no need
1263 for setting file type before playing audio. It is specified by the audio
1264 file passed in for playback. Other fields are used to set USB driver
1265 configurations.
Selina Liu6096c7a2015-08-19 21:01:03 +08001266 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001267 self._flow_manager.SetUSBDriverPlaybackConfigs(playback_data_format)
Selina Liu6096c7a2015-08-19 21:01:03 +08001268
1269 def SetUSBDriverCaptureConfigs(self, capture_data_format):
1270 """Updates the corresponding capture configurations to argument values.
1271
1272 This provides flexibility for simulating the USB gadget driver using other
1273 configurations different from the default values.
1274
1275 Args:
1276 capture_data_format: The dict form of an AudioDataFormat object. The
Johny Linbd1fd902016-03-29 17:00:05 +08001277 'file_type' field will be saved by InputUSBAudioFlow as the file type
1278 for captured data. Other fields are used to set USB driver
1279 configurations.
Selina Liu6096c7a2015-08-19 21:01:03 +08001280 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001281 self._flow_manager.SetUSBDriverCaptureConfigs(capture_data_format)
Selina Liu6096c7a2015-08-19 21:01:03 +08001282
Cheng-Yi Chiang00f69842015-05-24 15:30:53 +08001283 def GetMacAddress(self):
1284 """Gets the MAC address of this Chameleon.
1285
1286 Returns:
1287 A string for MAC address.
1288 """
1289 return open('/sys/class/net/eth0/address').read().strip()
Johny Lin44737f42016-03-29 17:11:30 +08001290
Johny Lin44737f42016-03-29 17:11:30 +08001291 def SendHIDEvent(self, port_id, event_type, *args, **kwargs):
1292 """Sends HID event with event_type and arguments for HID port #port_id.
1293
1294 Args:
1295 port_id: The ID of the HID port.
1296 event_type: Supported event type of string for HID port #port_id.
1297
1298 Returns:
1299 Returns as event function if applicable.
1300 """
Moja Hsu3e2fdcc2017-03-24 18:42:57 +08001301 return self._flow_manager.SendHIDEvent(port_id, event_type, *args, **kwargs)
Neeraj Poojary342e2d22018-10-31 12:21:43 -07001302
1303 def ResetBluetoothRef(self):
1304 """Reset BTREF"""
Shijin Abrahame23f9282019-07-24 10:55:15 -07001305 # Reset the RN52 and wait for it to reset
1306 RN52_RESET_WAIT_SECS = 3
1307 self.bluetooth_a2dp_sink.PowerCycle()
1308 time.sleep(RN52_RESET_WAIT_SECS)
Neeraj Poojary342e2d22018-10-31 12:21:43 -07001309 # Reloads serial port driver if needed
1310 self._bluetooth_a2dp_sink_ctrl.EnableDriver()
1311 # Reads peripheral configuration settings
1312 self.bluetooth_a2dp_sink.GetBasicSettings()
1313
1314 def EnableBluetoothRef(self):
1315 """Enable BTREF"""
1316 self._bluetooth_a2dp_sink_ctrl.EnableDriver()
1317
1318 def DisableBluetoothRef(self):
1319 """Disable BTREF"""
1320 self.bluetooth_a2dp_sink.Close()
1321
1322 def IsBluetoothRefDisabled(self):
1323 """Check if BTREF is enabled"""
1324 raise NotImplementedError('IsBluetoothRefDisabled')