xref: /aosp_15_r20/external/autotest/server/cros/servo/keyboard/servo_keyboard_flasher.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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