| # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import logging |
| import time |
| import os |
| |
| import common |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.server.cros import servo_keyboard_utils |
| |
| |
| class ServoKeyboardMapFlasher(): |
| """Flash the servo keyboard map on servo.""" |
| |
| _ATMEGA_RESET_DELAY = 0.2 |
| _ATMEGA_FLASH_TIMEOUT = 120 |
| _USB_PRESENT_DELAY = 1 |
| |
| # Command to detect LUFA Keyboard Demo by VID. |
| LSUSB_CMD = 'lsusb -d %s:' % servo_keyboard_utils.ATMEL_USB_VENDOR_ID |
| LSUSB_TIMEOUT = 30 |
| |
| def is_image_supported(self, host): |
| """Check if servo keyboard map supported on host |
| |
| @param host: CrosHost instance |
| """ |
| if host.run('hash dfu-programmer', ignore_status=True).exit_status: |
| return False |
| return True |
| |
| def update(self, host): |
| """Update servo keyboard map firmware on the host if required. |
| |
| The process will verify present of the keyboard firmware on the host |
| and flash it if device was not detected. |
| |
| @param host: CrosHost instance |
| """ |
| if not self.is_image_supported(host): |
| raise Exception( |
| 'The image is too old that does not have dfu-programmer.') |
| |
| try: |
| logging.debug('Starting flashing the keyboard map.') |
| host.servo.set_nocheck('init_usb_keyboard', 'on') |
| |
| if self._is_keyboard_present(host): |
| logging.info('Already using the new keyboard map.') |
| return |
| |
| self._flash_keyboard_map(host) |
| finally: |
| # Restore the default settings. |
| # Select the chip on the USB mux unless using Servo V4 |
| if 'servo_v4' not in host.servo.get_servo_type(): |
| host.servo.set('usb_mux_sel4', 'on') |
| |
| def _flash_keyboard_map(self, host): |
| """FLash servo keyboard firmware on the host.""" |
| servo = host.servo |
| # Boot AVR into DFU mode by enabling the HardWareBoot mode |
| # strapping and reset. |
| servo.set_get_all([ |
| 'at_hwb:on', 'atmega_rst:on', |
| 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'atmega_rst:off', |
| 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'at_hwb:off' |
| ]) |
| |
| time.sleep(self._USB_PRESENT_DELAY) |
| result = host.run(self.LSUSB_CMD, |
| timeout=self.LSUSB_TIMEOUT).stdout.strip() |
| if not 'Atmel Corp. atmega32u4 DFU bootloader' in result: |
| raise Exception('Not an expected chip: %s', result) |
| |
| # Update the keyboard map. |
| bindir = os.path.dirname(os.path.realpath(__file__)) |
| local_path = os.path.join(bindir, 'data', 'keyboard.hex') |
| host.send_file(local_path, '/tmp') |
| logging.info('Updating the keyboard map...') |
| host.run('dfu-programmer atmega32u4 erase --force', |
| timeout=self._ATMEGA_FLASH_TIMEOUT) |
| host.run('dfu-programmer atmega32u4 flash /tmp/keyboard.hex', |
| timeout=self._ATMEGA_FLASH_TIMEOUT) |
| |
| # Reset the chip. |
| servo.set_get_all([ |
| 'atmega_rst:on', |
| 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'atmega_rst:off' |
| ]) |
| if self._is_keyboard_present(host): |
| logging.info('Update successfully!') |
| else: |
| raise Exception('Update failed!') |
| |
| def _is_keyboard_present(self, host): |
| """Verify if servo keyboard is present on the host. |
| |
| The keyboard will be detected as USB device on the host with name: |
| 'Atmel Corp. LUFA Keyboard Demo Application' |
| """ |
| time.sleep(self._USB_PRESENT_DELAY) |
| result = host.run(self.LSUSB_CMD, |
| timeout=self.LSUSB_TIMEOUT).stdout.strip() |
| logging.debug('got the result: %s', result) |
| if ('LUFA Keyboard Demo' in result |
| and servo_keyboard_utils.is_servo_usb_wake_capable(host)): |
| return True |
| return False |