Connection Layer
The connection layer provides the transport between openocd-python and the OpenOCD process. Most users never interact with these classes directly — the Session facade handles everything. This reference is for contributors, advanced users, and anyone debugging connection issues.
Connection ABC
Section titled “Connection ABC”All connection backends implement the abstract base class in openocd.connection.base:
class Connection(ABC): async def connect(self, host: str, port: int) -> None: ... async def send(self, command: str) -> str: ... async def close(self) -> None: ... async def enable_notifications(self) -> None: ... def on_notification(self, callback: Callable[[str], None]) -> None: ...| Method | Description |
|---|---|
connect(host, port) | Open a TCP connection to the given host and port |
send(command) | Send a command string and return the response |
close() | Close the connection and release resources |
enable_notifications() | Enable asynchronous event notifications from OpenOCD |
on_notification(callback) | Register a callback for incoming notification messages |
TclRpcConnection
Section titled “TclRpcConnection”Module: openocd.connection.tcl_rpc
The primary connection backend. Speaks OpenOCD’s TCL RPC binary protocol on port 6666.
Protocol
Section titled “Protocol”The TCL RPC protocol uses a simple framing scheme:
- Client sends:
command_bytes+\x1a - Server replies:
response_bytes+\x1a
The \x1a byte (ASCII SUB / Ctrl-Z) acts as an unambiguous message delimiter. Commands and responses are UTF-8 encoded strings.
Constructor
Section titled “Constructor”TclRpcConnection(timeout: float = 10.0)| Parameter | Type | Default | Description |
|---|---|---|---|
timeout | float | 10.0 | Timeout in seconds for connect and send operations |
Dual-socket design
Section titled “Dual-socket design”TclRpcConnection maintains two separate TCP connections to OpenOCD:
- Command socket — handles request/response pairs. An async lock serializes all commands to prevent interleaving.
- Notification socket — opened by
enable_notifications(). Sendstcl_notifications onand then exclusively reads unsolicited event messages.
This separation prevents notifications from corrupting the command response stream, which would happen if both shared a single socket with two concurrent readers.
Command flow
Section titled “Command flow”- Acquire the async lock
- Write
command.encode("utf-8") + b"\x1a"to the command socket - Read from the socket until
\x1ais found in the response stream - Any bytes after the separator are preserved in a remainder buffer for the next call
- Release the lock
- Return the response decoded as UTF-8
Constants
Section titled “Constants”| Constant | Value | Description |
|---|---|---|
SEPARATOR | b"\x1a" | Message delimiter byte |
DEFAULT_TIMEOUT | 10.0 | Default timeout in seconds |
MAX_RESPONSE_SIZE | 10 * 1024 * 1024 | 10 MB guard against runaway reads |
Notification loop
Section titled “Notification loop”When enable_notifications() is called:
- A second TCP connection opens to the same host:port
tcl_notifications on\x1ais sent and the acknowledgement consumed- A background
asyncio.Taskenters_notification_loop(), which reads messages delimited by\x1a - Each message is dispatched to all registered callbacks
- If the notification connection drops,
_notification_failedis set toTrueand subsequentsend()calls log a warning
Error conditions
Section titled “Error conditions”- Connection closed: If
send()reads zero bytes,ConnectionErroris raised - Response too large: If the response buffer exceeds
MAX_RESPONSE_SIZEwithout a separator,ConnectionErroris raised (likely connected to the wrong port) - Timeout: If the response does not arrive within the configured timeout,
TimeoutErroris raised
TelnetConnection
Section titled “TelnetConnection”Module: openocd.connection.telnet
A fallback connection backend that speaks to OpenOCD’s human-oriented telnet interface on port 4444.
Protocol
Section titled “Protocol”- Client sends:
command\n - Server replies: response text ending with
"> "prompt
The telnet connection:
- Reads until the
"> "prompt after each command - Strips the echoed command from the first line of the response
- Does not support notifications (
enable_notifications()logs a warning and does nothing)
Constructor
Section titled “Constructor”TelnetConnection(timeout: float = 10.0)Limitations
Section titled “Limitations”| Feature | TclRpcConnection | TelnetConnection |
|---|---|---|
| Default port | 6666 | 4444 |
| Binary framing | \x1a delimiter | "> " prompt |
| Notifications | Supported (dual-socket) | Not supported |
| Output consistency | Structured | Varies by version |
| Recommended | Yes | Fallback only |
OpenOCDProcess
Section titled “OpenOCDProcess”Module: openocd.process
Spawns and manages an OpenOCD subprocess. Used internally by Session.start().
Constructor
Section titled “Constructor”OpenOCDProcess()Properties
Section titled “Properties”| Property | Type | Description |
|---|---|---|
pid | int | None | Process ID, or None if not started |
running | bool | Whether the process is still alive |
tcl_port | int | The TCL RPC port (default 6666) |
start()
Section titled “start()”async def start( config: str | list[str], extra_args: list[str] | None = None, tcl_port: int = 6666, openocd_bin: str | None = None,) -> NoneBuild the command line and spawn openocd as an async subprocess.
The config parameter accepts:
- A string like
"interface/cmsis-dap.cfg -f target/stm32f1x.cfg"(automatically split and prefixed with-fwhere needed) - A pre-split list like
["-f", "my board/config.cfg"](used as-is, preserving paths with spaces)
The method always appends -c "tcl_port <port>" to ensure the TCL RPC port matches what Session will connect to.
OpenOCD binary detection:
- Uses
openocd_binif provided - Otherwise calls
shutil.which("openocd") - Raises
ProcessErrorif not found
wait_ready()
Section titled “wait_ready()”async def wait_ready(timeout: float = 10.0) -> NonePoll the TCL RPC port every 0.25 seconds until it accepts a TCP connection, or raise TimeoutError. Also checks whether the process has died and reports stderr output if so.
stop()
Section titled “stop()”async def stop() -> NoneGraceful shutdown sequence:
- Send
SIGTERM - Wait up to 5 seconds for the process to exit
- If still running after 5 seconds, send
SIGKILL - Wait for final exit