1# Copyright 2015 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 7 8from autotest_lib.client.common_lib import error 9 10class ConnectionError(Exception): 11 """Raised on an error of connecting DUT.""" 12 pass 13 14 15class _BaseFwBypasser(object): 16 """Base class that controls bypass logic for firmware screens.""" 17 18 # Duration of holding Volume down button to quickly bypass the developer 19 # warning screen in tablets/detachables. 20 HOLD_VOL_DOWN_BUTTON_BYPASS = 3 21 22 def __init__(self, faft_framework): 23 self.servo = faft_framework.servo 24 self.faft_config = faft_framework.faft_config 25 self.client_host = faft_framework._client 26 27 28 def bypass_dev_mode(self): 29 """Bypass the dev mode firmware logic to boot internal image.""" 30 raise NotImplementedError 31 32 33 def bypass_dev_boot_usb(self): 34 """Bypass the dev mode firmware logic to boot USB.""" 35 raise NotImplementedError 36 37 38 def bypass_rec_mode(self): 39 """Bypass the rec mode firmware logic to boot USB.""" 40 raise NotImplementedError 41 42 43 def trigger_dev_to_rec(self): 44 """Trigger to the rec mode from the dev screen.""" 45 raise NotImplementedError 46 47 48 def trigger_rec_to_dev(self): 49 """Trigger to the dev mode from the rec screen.""" 50 raise NotImplementedError 51 52 53 def trigger_dev_to_normal(self): 54 """Trigger to the normal mode from the dev screen.""" 55 raise NotImplementedError 56 57 58class _CtrlDBypasser(_BaseFwBypasser): 59 """Controls bypass logic via Ctrl-D combo.""" 60 61 def bypass_dev_mode(self): 62 """Bypass the dev mode firmware logic to boot internal image. 63 64 Press Ctrl-D repeatedly. To obtain a low firmware boot time, pressing 65 Ctrl+D for every half second until firmware_screen delay has been 66 reached. 67 """ 68 logging.info("Pressing Ctrl-D.") 69 # At maximum, device waits for twice of firmware_screen delay to 70 # bypass the Dev screen. 71 timeout = time.time() + (self.faft_config.firmware_screen * 2) 72 while time.time() < timeout: 73 self.servo.ctrl_d() 74 time.sleep(0.5) 75 if self.client_host.ping_wait_up(timeout=0.1): 76 break 77 78 79 def bypass_dev_boot_usb(self): 80 """Bypass the dev mode firmware logic to boot USB.""" 81 time.sleep(self.faft_config.firmware_screen) 82 self.servo.ctrl_u() 83 84 85 def bypass_rec_mode(self): 86 """Bypass the rec mode firmware logic to boot USB.""" 87 self.servo.switch_usbkey('host') 88 time.sleep(self.faft_config.usb_plug) 89 self.servo.switch_usbkey('dut') 90 logging.info('Enabled dut_sees_usb') 91 if not self.client_host.ping_wait_up( 92 timeout=self.faft_config.delay_reboot_to_ping): 93 logging.info('ping timed out, try REC_ON') 94 psc = self.servo.get_power_state_controller() 95 psc.power_on(psc.REC_ON) 96 97 98 def trigger_dev_to_rec(self): 99 """Trigger to the rec mode from the dev screen.""" 100 time.sleep(self.faft_config.firmware_screen) 101 self.servo.enter_key() 102 103 104 def trigger_rec_to_dev(self): 105 """Trigger to the dev mode from the rec screen.""" 106 time.sleep(self.faft_config.firmware_screen) 107 self.servo.ctrl_d() 108 time.sleep(self.faft_config.confirm_screen) 109 if self.faft_config.rec_button_dev_switch: 110 logging.info('RECOVERY button pressed to switch to dev mode') 111 self.servo.toggle_recovery_switch() 112 elif self.faft_config.power_button_dev_switch: 113 logging.info('POWER button pressed to switch to dev mode') 114 self.servo.power_normal_press() 115 else: 116 logging.info('ENTER pressed to switch to dev mode') 117 self.servo.enter_key() 118 119 120 def trigger_dev_to_normal(self): 121 """Trigger to the normal mode from the dev screen.""" 122 time.sleep(self.faft_config.firmware_screen) 123 self.servo.enter_key() 124 time.sleep(self.faft_config.confirm_screen) 125 self.servo.enter_key() 126 127 128class _JetstreamBypasser(_BaseFwBypasser): 129 """Controls bypass logic of Jetstream devices.""" 130 131 def bypass_dev_mode(self): 132 """Bypass the dev mode firmware logic to boot internal image.""" 133 # Jetstream does nothing to bypass. 134 pass 135 136 137 def bypass_dev_boot_usb(self): 138 """Bypass the dev mode firmware logic to boot USB.""" 139 self.servo.switch_usbkey('dut') 140 time.sleep(self.faft_config.firmware_screen) 141 self.servo.toggle_development_switch() 142 143 144 def bypass_rec_mode(self): 145 """Bypass the rec mode firmware logic to boot USB.""" 146 self.servo.switch_usbkey('host') 147 time.sleep(self.faft_config.usb_plug) 148 self.servo.switch_usbkey('dut') 149 if not self.client_host.ping_wait_up( 150 timeout=self.faft_config.delay_reboot_to_ping): 151 psc = self.servo.get_power_state_controller() 152 psc.power_on(psc.REC_ON) 153 154 155 def trigger_dev_to_rec(self): 156 """Trigger to the rec mode from the dev screen.""" 157 # Jetstream does not have this triggering logic. 158 raise NotImplementedError 159 160 161 def trigger_rec_to_dev(self): 162 """Trigger to the dev mode from the rec screen.""" 163 self.servo.disable_development_mode() 164 time.sleep(self.faft_config.firmware_screen) 165 self.servo.toggle_development_switch() 166 167 168 def trigger_dev_to_normal(self): 169 """Trigger to the normal mode from the dev screen.""" 170 # Jetstream does not have this triggering logic. 171 raise NotImplementedError 172 173 174class _TabletDetachableBypasser(_BaseFwBypasser): 175 """Controls bypass logic of tablet/ detachable chromebook devices.""" 176 177 def set_button(self, button, duration, info): 178 """Helper method that sets the button hold time for UI selections""" 179 self.servo.set_nocheck(button, duration) 180 time.sleep(self.faft_config.confirm_screen) 181 logging.info(info) 182 183 184 def bypass_dev_boot_usb(self): 185 """Bypass the dev mode firmware logic to boot USB. 186 187 On tablets/ detachables, recovery entered by pressing pwr, vol up 188 & vol down buttons for 10s. 189 Menu options seen in DEVELOPER WARNING screen: 190 Developer Options 191 Show Debug Info 192 Enable Root Verification 193 Power Off* 194 Language 195 Menu options seen in DEV screen: 196 Boot legacy BIOS 197 Boot USB image 198 Boot developer image* 199 Cancel 200 Power off 201 Language 202 Vol up button selects previous item, vol down button selects 203 next item and pwr button selects current activated item. 204 """ 205 self.trigger_dev_screen() 206 time.sleep(self.faft_config.firmware_screen) 207 self.set_button('volume_up_hold', 100, ('Selecting power as' 208 ' enter key to select Boot USB Image')) 209 self.servo.power_short_press() 210 211 212 def bypass_rec_mode(self): 213 """Bypass the rec mode firmware logic to boot USB.""" 214 self.servo.switch_usbkey('host') 215 time.sleep(self.faft_config.usb_plug) 216 self.servo.switch_usbkey('dut') 217 logging.info('Enabled dut_sees_usb') 218 if not self.client_host.ping_wait_up( 219 timeout=self.faft_config.delay_reboot_to_ping): 220 logging.info('ping timed out, try REC_ON') 221 psc = self.servo.get_power_state_controller() 222 psc.power_on(psc.REC_ON) 223 224 225 def bypass_dev_mode(self): 226 """Bypass the developer warning screen immediately to boot into 227 internal disk. 228 229 On tablets/detachables, press & holding the Volume down button for 230 3-seconds will quickly bypass the developer warning screen. 231 """ 232 # Unit for the "volume_down_hold" console command is msec. 233 duration = (self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) * 1000 234 logging.info("Press and hold volume down button for %.1f seconds to " 235 "immediately bypass the Developer warning screen.", 236 self.HOLD_VOL_DOWN_BUTTON_BYPASS + 0.1) 237 # At maximum, device waits for twice of firmware_screen delay to 238 # bypass the Dev screen. 239 timeout = time.time() + (self.faft_config.firmware_screen * 2) 240 # To obtain a low firmware boot time, volume_down button pressed for 241 # every 3.1 seconds until firmware_screen delay has been reached. 242 while time.time() < timeout: 243 self.servo.set_nocheck('volume_down_hold', duration) 244 # After pressing 'volume_down_hold' button, wait for 0.1 seconds 245 # before start pressing the button for next iteration. 246 time.sleep(0.1) 247 if self.client_host.ping_wait_up(timeout=0.1): 248 break 249 250 251 def trigger_dev_screen(self): 252 """Helper method that transitions from DEVELOPER WARNING to DEV screen 253 254 Menu options seen in DEVELOPER WARNING screen: 255 Developer Options 256 Show Debug Info 257 Enable Root Verification 258 Power Off* 259 Language 260 Menu options seen in DEV screen: 261 Boot legacy BIOS 262 Boot USB image 263 Boot developer image* 264 Cancel 265 Power off 266 Language 267 Vol up button selects previous item, vol down button selects 268 next item and pwr button selects current activated item. 269 """ 270 time.sleep(self.faft_config.firmware_screen) 271 self.servo.set_nocheck('volume_up_hold', 100) 272 time.sleep(self.faft_config.confirm_screen) 273 self.servo.set_nocheck('volume_up_hold', 100) 274 time.sleep(self.faft_config.confirm_screen) 275 self.set_button('volume_up_hold', 100, ('Selecting power ' 276 'as enter key to select Developer Options')) 277 self.servo.power_short_press() 278 279 280 def trigger_rec_to_dev(self): 281 """Trigger to the dev mode from the rec screen using vol up button. 282 283 On tablets/ detachables, recovery entered by pressing pwr, vol up 284 & vol down buttons for 10s. TO_DEV screen is entered by pressing 285 vol up & vol down buttons together on the INSERT screen. 286 Menu options seen in TO_DEV screen: 287 Confirm enabling developer mode 288 Cancel* 289 Power off 290 Language 291 Vol up button selects previous item, vol down button selects 292 next item and pwr button selects current activated item. 293 """ 294 time.sleep(self.faft_config.firmware_screen) 295 self.set_button('volume_up_down_hold', 100, ('Enter Recovery Menu.')) 296 time.sleep(self.faft_config.confirm_screen) 297 self.set_button('volume_up_hold', 100, ('Selecting power as ' 298 'enter key to select Confirm Enabling Developer Mode')) 299 self.servo.power_short_press() 300 time.sleep(self.faft_config.firmware_screen) 301 302 303 def trigger_dev_to_normal(self): 304 """Trigger to the normal mode from the dev screen. 305 306 Menu options seen in DEVELOPER WARNING screen: 307 Developer Options 308 Show Debug Info 309 Enable Root Verification 310 Power Off* 311 Language 312 Menu options seen in TO_NORM screen: 313 Confirm Enabling Verified Boot* 314 Cancel 315 Power off 316 Language 317 Vol up button selects previous item, vol down button selects 318 next item and pwr button selects current activated item. 319 """ 320 time.sleep(self.faft_config.firmware_screen) 321 self.set_button('volume_up_hold', 100, ('Selecting ' 322 'Enable Root Verification using pwr ' 323 'button to enter TO_NORM screen')) 324 self.servo.power_short_press() 325 logging.info('Transitioning from DEV to TO_NORM screen.') 326 time.sleep(self.faft_config.firmware_screen) 327 logging.info('Selecting Confirm Enabling Verified ' 328 'Boot using pwr button in ' 329 'TO_NORM screen') 330 self.servo.power_short_press() 331 332 def trigger_dev_to_rec(self): 333 """Trigger to the TO_NORM screen from the dev screen. 334 Menu options seen in DEVELOPER WARNING screen: 335 Developer Options 336 Show Debug Info 337 Enable Root Verification 338 Power Off* 339 Language 340 Menu options seen in TO_NORM screen: 341 Confirm Enabling Verified Boot* 342 Cancel 343 Power off 344 Language 345 Vol up button selects previous item, vol down button selects 346 next item and pwr button selects current activated item. 347 """ 348 time.sleep(self.faft_config.firmware_screen) 349 self.set_button('volume_up_hold', 100, ('Selecting ' 350 'Enable Root Verification using pwr ' 351 'button to enter TO_NORM screen')) 352 self.servo.power_short_press() 353 logging.info('Transitioning from DEV to TO_NORM screen.') 354 time.sleep(self.faft_config.firmware_screen) 355 356 # In firmware_FwScreenPressPower, test will power off the DUT using 357 # Power button in second screen (TO_NORM screen) so scrolling to 358 # Power-off is necessary in this case. Hence scroll to Power-off as 359 # a generic action and wait for next action of either Lid close or 360 # power button press. 361 self.servo.set_nocheck('volume_down_hold', 100) 362 time.sleep(self.faft_config.confirm_screen) 363 self.servo.set_nocheck('volume_down_hold', 100) 364 time.sleep(self.faft_config.confirm_screen) 365 366 367class _BaseModeSwitcher(object): 368 """Base class that controls firmware mode switching.""" 369 370 HOLD_VOL_DOWN_BUTTON_BYPASS = _BaseFwBypasser.HOLD_VOL_DOWN_BUTTON_BYPASS 371 372 FW_BYPASSER_CLASS = _BaseFwBypasser 373 374 def __init__(self, faft_framework): 375 self.faft_framework = faft_framework 376 self.client_host = faft_framework._client 377 self.faft_client = faft_framework.faft_client 378 self.servo = faft_framework.servo 379 self.faft_config = faft_framework.faft_config 380 self.checkers = faft_framework.checkers 381 self.bypasser = self._create_fw_bypasser() 382 self._backup_mode = None 383 384 def _create_fw_bypasser(self): 385 """Creates a proper firmware bypasser.""" 386 return self.FW_BYPASSER_CLASS(self.faft_framework) 387 388 def setup_mode(self, mode): 389 """Setup for the requested mode. 390 391 It makes sure the system in the requested mode. If not, it tries to 392 do so. 393 394 @param mode: A string of mode, one of 'normal', 'dev', or 'rec'. 395 @raise TestFail: If the system not switched to expected mode after 396 reboot_to_mode. 397 398 """ 399 if not self.checkers.mode_checker(mode): 400 logging.info('System not in expected %s mode. Reboot into it.', 401 mode) 402 if self._backup_mode is None: 403 # Only resume to normal/dev mode after test, not recovery. 404 self._backup_mode = 'dev' if mode == 'normal' else 'normal' 405 self.reboot_to_mode(mode) 406 if not self.checkers.mode_checker(mode): 407 raise error.TestFail('System not switched to expected %s' 408 ' mode after setup_mode.' % mode) 409 410 def restore_mode(self): 411 """Restores original dev mode status if it has changed. 412 413 @raise TestFail: If the system not restored to expected mode. 414 """ 415 if (self._backup_mode is not None and 416 not self.checkers.mode_checker(self._backup_mode)): 417 self.reboot_to_mode(self._backup_mode) 418 if not self.checkers.mode_checker(self._backup_mode): 419 raise error.TestFail('System not restored to expected %s' 420 ' mode in cleanup.' % self._backup_mode) 421 422 423 424 def reboot_to_mode(self, to_mode, from_mode=None, sync_before_boot=True, 425 wait_for_dut_up=True): 426 """Reboot and execute the mode switching sequence. 427 428 @param to_mode: The target mode, one of 'normal', 'dev', or 'rec'. 429 @param from_mode: The original mode, optional, one of 'normal, 'dev', 430 or 'rec'. 431 @param sync_before_boot: True to sync to disk before booting. 432 @param wait_for_dut_up: True to wait DUT online again. False to do the 433 reboot and mode switching sequence only and may 434 need more operations to pass the firmware 435 screen. 436 """ 437 logging.info('-[ModeSwitcher]-[ start reboot_to_mode(%r, %r, %r) ]-', 438 to_mode, from_mode, wait_for_dut_up) 439 if sync_before_boot: 440 self.faft_framework.blocking_sync() 441 if to_mode == 'rec': 442 self.enable_rec_mode_and_reboot(usb_state='dut') 443 if wait_for_dut_up: 444 self.wait_for_client() 445 446 elif to_mode == 'rec_force_mrc': 447 self._enable_rec_mode_force_mrc_and_reboot(usb_state='dut') 448 if wait_for_dut_up: 449 self.wait_for_client() 450 451 elif to_mode == 'dev': 452 self._enable_dev_mode_and_reboot() 453 if wait_for_dut_up: 454 self.bypass_dev_mode() 455 self.wait_for_client() 456 457 elif to_mode == 'normal': 458 self._enable_normal_mode_and_reboot() 459 if wait_for_dut_up: 460 self.wait_for_client() 461 462 else: 463 raise NotImplementedError( 464 'Not supported mode switching from %s to %s' % 465 (str(from_mode), to_mode)) 466 logging.info('-[ModeSwitcher]-[ end reboot_to_mode(%r, %r, %r) ]-', 467 to_mode, from_mode, wait_for_dut_up) 468 469 def simple_reboot(self, reboot_type='warm', sync_before_boot=True): 470 """Simple reboot method 471 472 Just reboot the DUT using either cold or warm reset. Does not wait for 473 DUT to come back online. Will wait for test to handle this. 474 475 @param reboot_type: A string of reboot type, 'warm' or 'cold'. 476 If reboot_type != warm/cold, raise exception. 477 @param sync_before_boot: True to sync to disk before booting. 478 If sync_before_boot=False, DUT offline before 479 calling mode_aware_reboot. 480 """ 481 if reboot_type == 'warm': 482 reboot_method = self.servo.get_power_state_controller().warm_reset 483 elif reboot_type == 'cold': 484 reboot_method = self.servo.get_power_state_controller().reset 485 else: 486 raise NotImplementedError('Not supported reboot_type: %s', 487 reboot_type) 488 if sync_before_boot: 489 boot_id = self.faft_framework.get_bootid() 490 self.faft_framework.blocking_sync() 491 logging.info("-[ModeSwitcher]-[ start simple_reboot(%r) ]-", 492 reboot_type) 493 reboot_method() 494 if sync_before_boot: 495 self.wait_for_client_offline(orig_boot_id=boot_id) 496 logging.info("-[ModeSwitcher]-[ end simple_reboot(%r) ]-", 497 reboot_type) 498 499 def mode_aware_reboot(self, reboot_type=None, reboot_method=None, 500 sync_before_boot=True, wait_for_dut_up=True): 501 """Uses a mode-aware way to reboot DUT. 502 503 For example, if DUT is in dev mode, it requires pressing Ctrl-D to 504 bypass the developer screen. 505 506 @param reboot_type: A string of reboot type, one of 'warm', 'cold', or 507 'custom'. Default is a warm reboot. 508 @param reboot_method: A custom method to do the reboot. Only use it if 509 reboot_type='custom'. 510 @param sync_before_boot: True to sync to disk before booting. 511 If sync_before_boot=False, DUT offline before 512 calling mode_aware_reboot. 513 @param wait_for_dut_up: True to wait DUT online again. False to do the 514 reboot only. 515 """ 516 if reboot_type is None or reboot_type == 'warm': 517 reboot_method = self.servo.get_power_state_controller().warm_reset 518 elif reboot_type == 'cold': 519 reboot_method = self.servo.get_power_state_controller().reset 520 elif reboot_type != 'custom': 521 raise NotImplementedError('Not supported reboot_type: %s', 522 reboot_type) 523 524 logging.info("-[ModeSwitcher]-[ start mode_aware_reboot(%r, %s, ..) ]-", 525 reboot_type, reboot_method.__name__) 526 is_dev = is_rec = is_devsw_boot = False 527 if sync_before_boot: 528 is_dev = self.checkers.mode_checker('dev') 529 is_rec = self.checkers.mode_checker('rec') 530 is_devsw_boot = self.checkers.crossystem_checker( 531 {'devsw_boot': '1'}, True) 532 boot_id = self.faft_framework.get_bootid() 533 self.faft_framework.blocking_sync() 534 if is_rec: 535 logging.info("-[mode_aware_reboot]-[ is_rec=%s is_dev_switch=%s ]-", 536 is_rec, is_devsw_boot) 537 else: 538 logging.info("-[mode_aware_reboot]-[ is_dev=%s ]-", is_dev) 539 reboot_method() 540 if sync_before_boot: 541 self.wait_for_client_offline(orig_boot_id=boot_id) 542 # Encapsulating the behavior of skipping dev firmware screen, 543 # hitting ctrl-D 544 # Note that if booting from recovery mode, we can predict the next 545 # boot based on the developer switch position at boot (devsw_boot). 546 # If devsw_boot is True, we will call bypass_dev_mode after reboot. 547 if is_dev or is_devsw_boot: 548 self.bypass_dev_mode() 549 if wait_for_dut_up: 550 self.wait_for_client() 551 logging.info("-[ModeSwitcher]-[ end mode_aware_reboot(%r, %s, ..) ]-", 552 reboot_type, reboot_method.__name__) 553 554 555 def enable_rec_mode_and_reboot(self, usb_state=None): 556 """Switch to rec mode and reboot. 557 558 This method emulates the behavior of the old physical recovery switch, 559 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 560 recovery mode, i.e. just press Power + Esc + Refresh. 561 562 @param usb_state: A string, one of 'dut', 'host', or 'off'. 563 """ 564 psc = self.servo.get_power_state_controller() 565 psc.power_off() 566 if usb_state: 567 self.servo.switch_usbkey(usb_state) 568 psc.power_on(psc.REC_ON) 569 570 571 def _enable_rec_mode_force_mrc_and_reboot(self, usb_state=None): 572 """Switch to rec mode, enable force mrc cache retraining, and reboot. 573 574 This method emulates the behavior of the old physical recovery switch, 575 i.e. switch ON + reboot + switch OFF, and the new keyboard controlled 576 recovery mode, i.e. just press Power + Esc + Refresh. 577 578 @param usb_state: A string, one of 'dut', 'host', or 'off'. 579 """ 580 psc = self.servo.get_power_state_controller() 581 psc.power_off() 582 if usb_state: 583 self.servo.switch_usbkey(usb_state) 584 psc.power_on(psc.REC_ON_FORCE_MRC) 585 586 def _disable_rec_mode_and_reboot(self, usb_state=None): 587 """Disable the rec mode and reboot. 588 589 It is achieved by calling power state controller to do a normal 590 power on. 591 """ 592 psc = self.servo.get_power_state_controller() 593 psc.power_off() 594 time.sleep(self.faft_config.ec_boot_to_pwr_button) 595 psc.power_on(psc.REC_OFF) 596 597 598 def _enable_dev_mode_and_reboot(self): 599 """Switch to developer mode and reboot.""" 600 raise NotImplementedError 601 602 603 def _enable_normal_mode_and_reboot(self): 604 """Switch to normal mode and reboot.""" 605 raise NotImplementedError 606 607 608 # Redirects the following methods to FwBypasser 609 def bypass_dev_mode(self): 610 """Bypass the dev mode firmware logic to boot internal image.""" 611 logging.info("-[bypass_dev_mode]-") 612 self.bypasser.bypass_dev_mode() 613 614 615 def bypass_dev_boot_usb(self): 616 """Bypass the dev mode firmware logic to boot USB.""" 617 logging.info("-[bypass_dev_boot_usb]-") 618 self.bypasser.bypass_dev_boot_usb() 619 620 621 def bypass_rec_mode(self): 622 """Bypass the rec mode firmware logic to boot USB.""" 623 logging.info("-[bypass_rec_mode]-") 624 self.bypasser.bypass_rec_mode() 625 626 627 def trigger_dev_to_rec(self): 628 """Trigger to the rec mode from the dev screen.""" 629 self.bypasser.trigger_dev_to_rec() 630 631 632 def trigger_rec_to_dev(self): 633 """Trigger to the dev mode from the rec screen.""" 634 self.bypasser.trigger_rec_to_dev() 635 636 637 def trigger_dev_to_normal(self): 638 """Trigger to the normal mode from the dev screen.""" 639 self.bypasser.trigger_dev_to_normal() 640 641 642 def wait_for_client(self, timeout=180): 643 """Wait for the client to come back online. 644 645 New remote processes will be launched if their used flags are enabled. 646 647 @param timeout: Time in seconds to wait for the client SSH daemon to 648 come up. 649 @raise ConnectionError: Failed to connect DUT. 650 """ 651 logging.info("-[FAFT]-[ start wait_for_client ]---") 652 # Wait for the system to respond to ping before attempting ssh 653 if not self.client_host.ping_wait_up(timeout): 654 logging.warning("-[FAFT]-[ system did not respond to ping ]") 655 if self.client_host.wait_up(timeout): 656 # Check the FAFT client is avaiable. 657 self.faft_client.system.is_available() 658 # Stop update-engine as it may change firmware/kernel. 659 self.faft_framework.faft_client.updater.stop_daemon() 660 else: 661 logging.error('wait_for_client() timed out.') 662 raise ConnectionError('DUT is still down unexpectedly') 663 logging.info("-[FAFT]-[ end wait_for_client ]-----") 664 665 666 def wait_for_client_offline(self, timeout=60, orig_boot_id=None): 667 """Wait for the client to come offline. 668 669 @param timeout: Time in seconds to wait the client to come offline. 670 @param orig_boot_id: A string containing the original boot id. 671 @raise ConnectionError: Failed to wait DUT offline. 672 """ 673 # When running against panther, we see that sometimes 674 # ping_wait_down() does not work correctly. There needs to 675 # be some investigation to the root cause. 676 # If we sleep for 120s before running get_boot_id(), it 677 # does succeed. But if we change this to ping_wait_down() 678 # there are implications on the wait time when running 679 # commands at the fw screens. 680 if not self.client_host.ping_wait_down(timeout): 681 if orig_boot_id and self.client_host.get_boot_id() != orig_boot_id: 682 logging.warn('Reboot done very quickly.') 683 return 684 raise ConnectionError('DUT is still up unexpectedly') 685 686 687class _KeyboardDevSwitcher(_BaseModeSwitcher): 688 """Class that switches firmware mode via keyboard combo.""" 689 690 FW_BYPASSER_CLASS = _CtrlDBypasser 691 692 def _enable_dev_mode_and_reboot(self): 693 """Switch to developer mode and reboot.""" 694 logging.info("Enabling keyboard controlled developer mode") 695 # Rebooting EC with rec mode on. Should power on AP. 696 # Plug out USB disk for preventing recovery boot without warning 697 self.enable_rec_mode_and_reboot(usb_state='host') 698 self.wait_for_client_offline() 699 self.bypasser.trigger_rec_to_dev() 700 701 702 def _enable_normal_mode_and_reboot(self): 703 """Switch to normal mode and reboot.""" 704 logging.info("Disabling keyboard controlled developer mode") 705 self._disable_rec_mode_and_reboot() 706 self.wait_for_client_offline() 707 self.bypasser.trigger_dev_to_normal() 708 709 710class _JetstreamSwitcher(_BaseModeSwitcher): 711 """Class that switches firmware mode in Jetstream devices.""" 712 713 FW_BYPASSER_CLASS = _JetstreamBypasser 714 715 def _enable_dev_mode_and_reboot(self): 716 """Switch to developer mode and reboot.""" 717 logging.info("Enabling Jetstream developer mode") 718 self.enable_rec_mode_and_reboot(usb_state='host') 719 self.wait_for_client_offline() 720 self.bypasser.trigger_rec_to_dev() 721 722 723 def _enable_normal_mode_and_reboot(self): 724 """Switch to normal mode and reboot.""" 725 logging.info("Disabling Jetstream developer mode") 726 self.servo.disable_development_mode() 727 self.enable_rec_mode_and_reboot(usb_state='host') 728 time.sleep(self.faft_config.firmware_screen) 729 self._disable_rec_mode_and_reboot(usb_state='host') 730 731 732class _TabletDetachableSwitcher(_BaseModeSwitcher): 733 """Class that switches fw mode in tablets/detachables with fw menu UI.""" 734 735 FW_BYPASSER_CLASS = _TabletDetachableBypasser 736 737 def _enable_dev_mode_and_reboot(self): 738 """Switch to developer mode and reboot. 739 740 On tablets/ detachables, recovery entered by pressing pwr, vol up 741 & vol down buttons for 10s. 742 Menu options seen in RECOVERY screen: 743 Enable Developer Mode 744 Show Debug Info 745 Power off* 746 Language 747 """ 748 logging.info('Enabling tablets/detachable recovery mode') 749 self.enable_rec_mode_and_reboot(usb_state='host') 750 self.wait_for_client_offline() 751 self.bypasser.trigger_rec_to_dev() 752 753 754 def _enable_normal_mode_and_reboot(self): 755 """Switch to normal mode and reboot. 756 757 Menu options seen in DEVELOPER WARNING screen: 758 Developer Options 759 Show Debug Info 760 Enable Root Verification 761 Power Off* 762 Language 763 Menu options seen in TO_NORM screen: 764 Confirm Enabling Verified Boot 765 Cancel 766 Power off* 767 Language 768 Vol up button selects previous item, vol down button selects 769 next item and pwr button selects current activated item. 770 """ 771 self._disable_rec_mode_and_reboot() 772 self.wait_for_client_offline() 773 self.bypasser.trigger_dev_to_normal() 774 775 776_SWITCHER_CLASSES = { 777 'keyboard_dev_switcher': _KeyboardDevSwitcher, 778 'jetstream_switcher': _JetstreamSwitcher, 779 'tablet_detachable_switcher': _TabletDetachableSwitcher} 780 781 782def create_mode_switcher(faft_framework): 783 """Creates a proper mode switcher. 784 785 @param faft_framework: The main FAFT framework object. 786 """ 787 switcher_type = faft_framework.faft_config.mode_switcher_type 788 switcher_class = _SWITCHER_CLASSES.get(switcher_type, None) 789 if switcher_class is None: 790 raise NotImplementedError('Not supported mode_switcher_type: %s', 791 switcher_type) 792 else: 793 return switcher_class(faft_framework)