1# Copyright 2020 The Chromium OS 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 5import logging 6import time 7import os 8 9import common 10from autotest_lib.client.common_lib import error 11from autotest_lib.server.cros import servo_keyboard_utils 12 13 14class ServoKeyboardMapFlasher(): 15 """Flash the servo keyboard map on servo.""" 16 17 _ATMEGA_RESET_DELAY = 0.2 18 _ATMEGA_FLASH_TIMEOUT = 120 19 _USB_PRESENT_DELAY = 1 20 21 # Command to detect LUFA Keyboard Demo by VID. 22 LSUSB_CMD = 'lsusb -d %s:' % servo_keyboard_utils.ATMEL_USB_VENDOR_ID 23 LSUSB_TIMEOUT = 30 24 25 def is_image_supported(self, host): 26 """Check if servo keyboard map supported on host 27 28 @param host: CrosHost instance 29 """ 30 if host.run('hash dfu-programmer', ignore_status=True).exit_status: 31 return False 32 return True 33 34 def update(self, host): 35 """Update servo keyboard map firmware on the host if required. 36 37 The process will verify present of the keyboard firmware on the host 38 and flash it if device was not detected. 39 40 @param host: CrosHost instance 41 """ 42 if not self.is_image_supported(host): 43 raise Exception( 44 'The image is too old that does not have dfu-programmer.') 45 46 try: 47 logging.debug('Starting flashing the keyboard map.') 48 host.servo.set_nocheck('init_usb_keyboard', 'on') 49 50 if self._is_keyboard_present(host): 51 logging.info('Already using the new keyboard map.') 52 return 53 54 self._flash_keyboard_map(host) 55 finally: 56 # Restore the default settings. 57 # Select the chip on the USB mux unless using Servo V4 58 if 'servo_v4' not in host.servo.get_servo_type(): 59 host.servo.set('usb_mux_sel4', 'on') 60 61 def _flash_keyboard_map(self, host): 62 """FLash servo keyboard firmware on the host.""" 63 servo = host.servo 64 # Boot AVR into DFU mode by enabling the HardWareBoot mode 65 # strapping and reset. 66 servo.set_get_all([ 67 'at_hwb:on', 'atmega_rst:on', 68 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'atmega_rst:off', 69 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'at_hwb:off' 70 ]) 71 72 time.sleep(self._USB_PRESENT_DELAY) 73 result = host.run(self.LSUSB_CMD, 74 timeout=self.LSUSB_TIMEOUT).stdout.strip() 75 if not 'Atmel Corp. atmega32u4 DFU bootloader' in result: 76 raise Exception('Not an expected chip: %s', result) 77 78 # Update the keyboard map. 79 bindir = os.path.dirname(os.path.realpath(__file__)) 80 local_path = os.path.join(bindir, 'data', 'keyboard.hex') 81 host.send_file(local_path, '/tmp') 82 logging.info('Updating the keyboard map...') 83 host.run('dfu-programmer atmega32u4 erase --force', 84 timeout=self._ATMEGA_FLASH_TIMEOUT) 85 host.run('dfu-programmer atmega32u4 flash /tmp/keyboard.hex', 86 timeout=self._ATMEGA_FLASH_TIMEOUT) 87 88 # Reset the chip. 89 servo.set_get_all([ 90 'atmega_rst:on', 91 'sleep:%f' % self._ATMEGA_RESET_DELAY, 'atmega_rst:off' 92 ]) 93 if self._is_keyboard_present(host): 94 logging.info('Update successfully!') 95 else: 96 raise Exception('Update failed!') 97 98 def _is_keyboard_present(self, host): 99 """Verify if servo keyboard is present on the host. 100 101 The keyboard will be detected as USB device on the host with name: 102 'Atmel Corp. LUFA Keyboard Demo Application' 103 """ 104 time.sleep(self._USB_PRESENT_DELAY) 105 result = host.run(self.LSUSB_CMD, 106 timeout=self.LSUSB_TIMEOUT).stdout.strip() 107 logging.debug('got the result: %s', result) 108 if ('LUFA Keyboard Demo' in result 109 and servo_keyboard_utils.is_servo_usb_wake_capable(host)): 110 return True 111 return False 112