DEV_semaphore/.platformio/packages/tool-esptoolpy/esptool/reset.py

179 lines
5.6 KiB
Python

# SPDX-FileCopyrightText: 2014-2023 Fredrik Ahlberg, Angus Gratton,
# Espressif Systems (Shanghai) CO LTD, other contributors as noted.
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import struct
import time
from .util import FatalError
# Used for resetting into bootloader on Unix-like systems
if os.name != "nt":
import fcntl
import termios
# Constants used for terminal status lines reading/setting.
# Taken from pySerial's backend for IO:
# https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py
TIOCMSET = getattr(termios, "TIOCMSET", 0x5418)
TIOCMGET = getattr(termios, "TIOCMGET", 0x5415)
TIOCM_DTR = getattr(termios, "TIOCM_DTR", 0x002)
TIOCM_RTS = getattr(termios, "TIOCM_RTS", 0x004)
DEFAULT_RESET_DELAY = 0.05 # default time to wait before releasing boot pin after reset
class ResetStrategy(object):
def __init__(self, port, reset_delay=DEFAULT_RESET_DELAY):
self.port = port
self.reset_delay = reset_delay
def __call__():
pass
def _setDTR(self, state):
self.port.setDTR(state)
def _setRTS(self, state):
self.port.setRTS(state)
# Work-around for adapters on Windows using the usbser.sys driver:
# generate a dummy change to DTR so that the set-control-line-state
# request is sent with the updated RTS state and the same DTR state
self.port.setDTR(self.port.dtr)
def _setDTRandRTS(self, dtr=False, rts=False):
status = struct.unpack(
"I", fcntl.ioctl(self.port.fileno(), TIOCMGET, struct.pack("I", 0))
)[0]
if dtr:
status |= TIOCM_DTR
else:
status &= ~TIOCM_DTR
if rts:
status |= TIOCM_RTS
else:
status &= ~TIOCM_RTS
fcntl.ioctl(self.port.fileno(), TIOCMSET, struct.pack("I", status))
class ClassicReset(ResetStrategy):
"""
Classic reset sequence, sets DTR and RTS lines sequentially.
"""
def __call__(self):
self._setDTR(False) # IO0=HIGH
self._setRTS(True) # EN=LOW, chip in reset
time.sleep(0.1)
self._setDTR(True) # IO0=LOW
self._setRTS(False) # EN=HIGH, chip out of reset
time.sleep(self.reset_delay)
self._setDTR(False) # IO0=HIGH, done
class UnixTightReset(ResetStrategy):
"""
UNIX-only reset sequence with custom implementation,
which allows setting DTR and RTS lines at the same time.
"""
def __call__(self):
self._setDTRandRTS(False, False)
self._setDTRandRTS(True, True)
self._setDTRandRTS(False, True) # IO0=HIGH & EN=LOW, chip in reset
time.sleep(0.1)
self._setDTRandRTS(True, False) # IO0=LOW & EN=HIGH, chip out of reset
time.sleep(self.reset_delay)
self._setDTRandRTS(False, False) # IO0=HIGH, done
self._setDTR(False) # Needed in some environments to ensure IO0=HIGH
class USBJTAGSerialReset(ResetStrategy):
"""
Custom reset sequence, which is required when the device
is connecting via its USB-JTAG-Serial peripheral.
"""
def __call__(self):
self._setRTS(False)
self._setDTR(False) # Idle
time.sleep(0.1)
self._setDTR(True) # Set IO0
self._setRTS(False)
time.sleep(0.1)
self._setRTS(True) # Reset. Calls inverted to go through (1,1) instead of (0,0)
self._setDTR(False)
self._setRTS(True) # RTS set as Windows only propagates DTR on RTS setting
time.sleep(0.1)
self._setDTR(False)
self._setRTS(False) # Chip out of reset
class HardReset(ResetStrategy):
"""
Reset sequence for hard resetting the chip.
Can be used to reset out of the bootloader or to restart a running app.
"""
def __init__(self, port, uses_usb_otg=False):
super().__init__(port)
self.uses_usb_otg = uses_usb_otg
def __call__(self):
self._setRTS(True) # EN->LOW
if self.uses_usb_otg:
# Give the chip some time to come out of reset,
# to be able to handle further DTR/RTS transitions
time.sleep(0.2)
self._setRTS(False)
time.sleep(0.2)
else:
time.sleep(0.1)
self._setRTS(False)
class CustomReset(ResetStrategy):
"""
Custom reset strategy defined with a string.
CustomReset object is created as "rst = CustomReset(port, seq_str)"
and can be later executed simply with "rst()"
The seq_str input string consists of individual commands divided by "|".
Commands (e.g. R0) are defined by a code (R) and an argument (0).
The commands are:
D: setDTR - 1=True / 0=False
R: setRTS - 1=True / 0=False
U: setDTRandRTS (Unix-only) - 0,0 / 0,1 / 1,0 / or 1,1
W: Wait (time delay) - positive float number
e.g.
"D0|R1|W0.1|D1|R0|W0.05|D0" represents the ClassicReset strategy
"U1,1|U0,1|W0.1|U1,0|W0.05|U0,0" represents the UnixTightReset strategy
"""
format_dict = {
"D": "self.port.setDTR({})",
"R": "self.port.setRTS({})",
"W": "time.sleep({})",
"U": "self._setDTRandRTS({})",
}
def __call__(self):
exec(self.constructed_strategy)
def __init__(self, port, seq_str):
super().__init__(port)
self.constructed_strategy = self._parse_string_to_seq(seq_str)
def _parse_string_to_seq(self, seq_str):
try:
cmds = seq_str.split("|")
fn_calls_list = [self.format_dict[cmd[0]].format(cmd[1:]) for cmd in cmds]
except Exception as e:
raise FatalError(f'Invalid "custom_reset_sequence" option format: {e}')
return "\n".join(fn_calls_list)