Adjust scripts to account for ID normalization
diff --git a/services/i2c/server/src/main.rs b/services/i2c/server/src/main.rs index 1b410d6..5eb309b 100644 --- a/services/i2c/server/src/main.rs +++ b/services/i2c/server/src/main.rs
@@ -61,7 +61,9 @@ // --------------------------------------------------------------------------- fn i2c_server_loop() -> Result<()> { - pw_log::info!("I2C server starting"); + pw_log::info!("+------------------------+"); + pw_log::info!("| I2C Server Starting |"); + pw_log::info!("+------------------------+"); // SAFETY: Called once at server startup, exclusive peripheral access. let mut backend = unsafe { AspeedI2cBackend::new() }; @@ -261,7 +263,11 @@ // ConfigureSlave: set slave address on a bus // ------------------------------------------------------------------ I2cOp::ConfigureSlave => { - pw_log::info!("I2C dispatch configure slave"); + pw_log::info!("+------------------------+"); + pw_log::info!("| I2C Target Configured |"); + pw_log::info!("| Bus: {} |", header.bus as u32); + pw_log::info!("| Addr: 0x{:02X} |", header.address as u32); + pw_log::info!("+------------------------+"); match backend.configure_slave(header.bus, header.address) { Ok(()) => encode_success(response, 0), Err(code) => encode_error(response, code),
diff --git a/services/mctp/server/src/main.rs b/services/mctp/server/src/main.rs index e708180..cfec577 100644 --- a/services/mctp/server/src/main.rs +++ b/services/mctp/server/src/main.rs
@@ -221,13 +221,56 @@ #[cfg(feature = "i2c-polling")] fn mctp_loop() -> Result<()> { + // Print startup banner with configuration #[cfg(feature = "in-process-requester")] - pw_log::info!("MCTP server starting (I2C polling mode, REQUESTER, own: eid=0x{:02x} i2c=0x{:02x}, remote: eid=0x{:02x} i2c=0x{:02x})", - OWN_EID as u32, OWN_I2C_ADDR as u32, REMOTE_EID as u32, REMOTE_I2C_ADDR as u32); + pw_log::info!("+----------------------------+"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| MCTP Server Starting |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| Mode: REQUESTER |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| Own: |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| EID: 0x{:02X} |", OWN_EID as u32); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| I2C: 0x{:02X} |", OWN_I2C_ADDR as u32); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| Remote: |"); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| EID: 0x{:02X} |", REMOTE_EID as u32); + #[cfg(feature = "in-process-requester")] + pw_log::info!("| I2C: 0x{:02X} |", REMOTE_I2C_ADDR as u32); + #[cfg(feature = "in-process-requester")] + pw_log::info!("+----------------------------+"); #[cfg(feature = "in-process-responder")] - pw_log::info!("MCTP server starting (I2C polling mode, RESPONDER, own: eid=0x{:02x} i2c=0x{:02x}, remote: eid=0x{:02x} i2c=0x{:02x})", - OWN_EID as u32, OWN_I2C_ADDR as u32, REMOTE_EID as u32, REMOTE_I2C_ADDR as u32); + pw_log::info!("+----------------------------+"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| MCTP Server Starting |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| Mode: RESPONDER |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| Own: |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| EID: 0x{:02X} |", OWN_EID as u32); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| I2C: 0x{:02X} |", OWN_I2C_ADDR as u32); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| Remote: |"); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| EID: 0x{:02X} |", REMOTE_EID as u32); + #[cfg(feature = "in-process-responder")] + pw_log::info!("| I2C: 0x{:02X} |", REMOTE_I2C_ADDR as u32); + #[cfg(feature = "in-process-responder")] + pw_log::info!("+----------------------------+"); let mut i2c = IpcI2cClient::new(handle::I2C); @@ -618,6 +661,31 @@ &frame_with_dest[..frame_len] ); + // Debug: log full frame being decoded + if frame_len >= 14 { + pw_log::info!( + "Frame[0-7]: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + frame_with_dest[0] as u32, frame_with_dest[1] as u32, + frame_with_dest[2] as u32, frame_with_dest[3] as u32, + frame_with_dest[4] as u32, frame_with_dest[5] as u32, + frame_with_dest[6] as u32, frame_with_dest[7] as u32 + ); + pw_log::info!( + "Frame[8-13]: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + frame_with_dest[8] as u32, frame_with_dest[9] as u32, + frame_with_dest[10] as u32, frame_with_dest[11] as u32, + frame_with_dest[12] as u32, frame_with_dest[13] as u32 + ); + } else if frame_len >= 8 { + pw_log::info!( + "Frame bytes: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + frame_with_dest[0] as u32, frame_with_dest[1] as u32, + frame_with_dest[2] as u32, frame_with_dest[3] as u32, + frame_with_dest[4] as u32, frame_with_dest[5] as u32, + frame_with_dest[6] as u32, frame_with_dest[7] as u32 + ); + } + match receiver.decode(&msg_with_dest) { Ok((pkt, hdr)) => { i2c_pkt = i2c_pkt.wrapping_add(1);
diff --git a/target/ast1060-evb/harness/i2c_requester.py b/target/ast1060-evb/harness/i2c_requester.py index 868ac5e..7386910 100755 --- a/target/ast1060-evb/harness/i2c_requester.py +++ b/target/ast1060-evb/harness/i2c_requester.py
@@ -1,8 +1,8 @@ #!/usr/bin/env python3 """ -Aardvark I2C Multi-Master Transceiver -Sends pre-fabricated I2C payloads as master and receives responses as slave. -Operates in multi-master mode. +Aardvark I2C Multi-Controller Transceiver +Sends pre-fabricated I2C payloads as controller and receives responses as target. +Operates in multi-controller mode. """ import sys @@ -64,13 +64,13 @@ return handle -def configure_device(handle, slave_addr=0x42, bitrate_khz=100, bus_timeout_ms=150): +def configure_device(handle, own_addr=0x42, bitrate_khz=100, bus_timeout_ms=150): """ - Configure the Aardvark for I2C multi-master operation. + Configure the Aardvark for I2C multi-controller operation. Args: handle: Aardvark device handle - slave_addr: Our slave address (default: 0x42) + own_addr: Our I2C address (default: 0x42) bitrate_khz: I2C bus speed in kHz (default: 100) bus_timeout_ms: Bus lock timeout in ms (default: 150) """ @@ -96,32 +96,32 @@ actual_timeout = aa_i2c_bus_timeout(handle, bus_timeout_ms) print(f" Bus Timeout: {actual_timeout} ms") - # Enable slave mode with our address - # Parameters: handle, slave_addr, maxTxBytes, maxRxBytes + # Enable target mode with our address + # Parameters: handle, own_addr, maxTxBytes, maxRxBytes # Use 0 for unlimited buffer sizes - result = aa_i2c_slave_enable(handle, slave_addr, 0, 0) + result = aa_i2c_slave_enable(handle, own_addr, 0, 0) if result < 0: - print(f" Error enabling slave mode: {aa_status_string(result)}") + print(f" Error enabling target mode: {aa_status_string(result)}") sys.exit(1) - print(f" Slave Address: 0x{slave_addr:02X}") - print(" Slave Mode: Enabled") + print(f" Our I2C Address: 0x{own_addr:02X}") + print(" Target Mode: Enabled") - print("\nConfiguration complete. Device ready for multi-master operation.") - print(" Master: Can send to other devices") - print(" Slave: Can receive from other masters\n") + print("\nConfiguration complete. Device ready for multi-controller operation.") + print(" Controller: Can send to other devices") + print(" Target: Can receive from other controllers\n") #========================================================================== # I2C OPERATIONS #========================================================================== -def send_payload(handle, target_addr, payload: List[int], description=""): +def send_payload(handle, remote_addr, payload: List[int], description=""): """ - Send a payload as I2C master. + Send a payload as I2C controller. Args: handle: Aardvark device handle - target_addr: Target device address (7-bit) + remote_addr: Remote device address (7-bit) payload: List of bytes to send description: Optional description of the payload @@ -131,15 +131,15 @@ if description: print(f">>> Sending: {description}") - print(f" Target: 0x{target_addr:02X}") + print(f" Remote: 0x{remote_addr:02X}") print(f" Length: {len(payload)} bytes") print(f" Data: {' '.join(f'{b:02X}' for b in payload)}") # Convert to array data_out = array('B', payload) - # Write to target device - num_written = aa_i2c_write(handle, target_addr, AA_I2C_NO_FLAGS, data_out) + # Write to remote device + num_written = aa_i2c_write(handle, remote_addr, AA_I2C_NO_FLAGS, data_out) if num_written < 0: print(f" Status: ERROR - {aa_status_string(num_written)}") @@ -154,7 +154,7 @@ def poll_for_response(handle, timeout_ms=1000, max_buffer=256): """ - Poll for incoming I2C slave data (response from another master). + Poll for incoming I2C target data (response from another controller). Args: handle: Aardvark device handle @@ -171,11 +171,11 @@ return False, None, [] if result == AA_ASYNC_I2C_READ: - # Data was written to us (we're the slave) + # Data was written to us (we're the target) (num_bytes, addr, data_in) = aa_i2c_slave_read(handle, max_buffer) if num_bytes < 0: - print(f" Error reading slave data: {aa_status_string(num_bytes)}") + print(f" Error reading data: {aa_status_string(num_bytes)}") return False, None, [] # Convert to list @@ -183,9 +183,9 @@ return True, addr, data_list elif result == AA_ASYNC_I2C_WRITE: - # Data was read from us (master read from our slave) + # Data was read from us (controller read from our target) num_bytes = aa_i2c_slave_write_stats(handle) - print(f"<<< Master read {num_bytes} bytes from us") + print(f"<<< Controller read {num_bytes} bytes from us") return False, None, [] else: @@ -195,7 +195,7 @@ def wait_for_response(handle, timeout_ms=1000, description=""): """ - Wait for and display a response from another master. + Wait for and display a response from another controller. Args: handle: Aardvark device handle @@ -226,9 +226,9 @@ return True, data -def set_slave_response(handle, response_data: List[int]): +def set_response(handle, response_data: List[int]): """ - Set the data to send when a master reads from us. + Set the data to send when a controller reads from us. Args: handle: Aardvark device handle @@ -237,23 +237,23 @@ data_array = array('B', response_data) result = aa_i2c_slave_set_response(handle, data_array) if result < 0: - print(f"Error setting slave response: {aa_status_string(result)}") + print(f"Error setting response: {aa_status_string(result)}") else: - print(f"Slave response buffer set ({len(response_data)} bytes)") + print(f"Response buffer set ({len(response_data)} bytes)") #========================================================================== # TRANSACTION SEQUENCES #========================================================================== -def execute_transaction(handle, target_addr, payload, wait_response=True, +def execute_transaction(handle, remote_addr, payload, wait_response=True, response_timeout=1000, description=""): """ Execute a complete transaction: send payload and optionally wait for response. Args: handle: Aardvark device handle - target_addr: Target device address + remote_addr: Remote device address payload: Payload to send wait_response: Whether to wait for a response response_timeout: Response timeout in ms @@ -268,7 +268,7 @@ print("=" * 80) # Send the payload - success, _ = send_payload(handle, target_addr, payload, "Request") + success, _ = send_payload(handle, remote_addr, payload, "Request") response_data = [] if success and wait_response: @@ -292,28 +292,33 @@ def main(): """Main entry point.""" parser = argparse.ArgumentParser( - description='Aardvark I2C Multi-Master Transceiver', + description='Aardvark I2C Multi-Controller Transceiver', formatter_class=argparse.RawDescriptionHelpFormatter, epilog=''' -This tool operates in multi-master mode: - - Sends payloads as I2C master to target devices - - Receives responses as I2C slave from other masters +This tool operates in multi-controller mode: + - Sends payloads as I2C controller to remote devices + - Receives responses as I2C target from other controllers Default configuration: - - Slave address: 0x42 + - Our I2C address: 0x10 + - Remote I2C address: 0x13 - Bus speed: 100 kHz - Bus timeout: 150 ms ''' ) - parser.add_argument('--slave-addr', type=lambda x: int(x, 0), default=0x42, - metavar='ADDR', help='Our slave address (default: 0x42)') + parser.add_argument('--own-addr', type=lambda x: int(x, 0), default=0x10, + metavar='ADDR', help='Our I2C address (default: 0x10)') + parser.add_argument('--own-eid', type=lambda x: int(x, 0), default=0x08, + metavar='EID', help='Our MCTP endpoint ID (default: 0x08)') + parser.add_argument('--remote-addr', type=lambda x: int(x, 0), default=0x13, + metavar='ADDR', help='Remote device address (default: 0x13)') + parser.add_argument('--remote-eid', type=lambda x: int(x, 0), default=0x42, + metavar='EID', help='Remote MCTP endpoint ID (default: 0x42)') parser.add_argument('--bitrate', type=int, default=100, metavar='KHZ', help='I2C bus speed in kHz (default: 100)') parser.add_argument('--bus-timeout', type=int, default=150, metavar='MS', help='Bus lock timeout in ms (default: 150)') - parser.add_argument('--target', type=lambda x: int(x, 0), default=0x10, - metavar='ADDR', help='Target device address (default: 0x10)') parser.add_argument('--response-timeout', type=int, default=1000, metavar='MS', help='Response timeout in ms (default: 1000)') parser.add_argument('--no-wait-response', action='store_true', @@ -328,13 +333,68 @@ try: # Configure device - configure_device(handle, args.slave_addr, args.bitrate, args.bus_timeout) + configure_device(handle, args.own_addr, args.bitrate, args.bus_timeout) + + # Display configuration + print() + print("MCTP Requester Configuration:") + print(f" Our I2C Address: 0x{args.own_addr:02X}") + print(f" Our EID: 0x{args.own_eid:02X}") + print(f" Remote I2C Address: 0x{args.remote_addr:02X}") + print(f" Remote EID: 0x{args.remote_eid:02X}") + print() + + # Build MCTP SPDM GET_VERSION payload using configured addresses + # Format: [CMD][ByteCount][SrcI2C][MCTP_Ver][DestEID][SrcEID][Flags][MsgType][SPDM][PEC] + src_i2c_with_read = (args.own_addr << 1) | 1 # Source I2C with read bit + + # Build the frame (without I2C destination address prefix) + frame = [ + 0x0F, # Command code (MCTP) + 0x0A, # Byte count (10 bytes) + src_i2c_with_read, # Source I2C address with read bit + 0x01, # MCTP version + args.remote_eid, # Destination EID (responder) + args.own_eid, # Source EID (us) + 0xC8, # Flags (SOM=1, EOM=1, Seq=0, TO=1, Tag=0) + 0x05, # Message type (SPDM) + 0x10, # SPDM GET_VERSION + 0x84, # SPDM request code + 0x00, # Param1 + 0x00, # Param2 + ] + + # Calculate PEC (includes destination I2C address) + # For PEC calculation, prepend the destination I2C write address + dest_i2c_write = args.remote_addr << 1 # Destination I2C write address + frame_with_dest = [dest_i2c_write] + frame + + # Calculate CRC-8 PEC + crc = 0 + for byte in frame_with_dest: + crc ^= byte + for _ in range(8): + if crc & 0x80: + crc = (crc << 1) ^ 0x07 + else: + crc <<= 1 + crc &= 0xFF + + frame.append(crc) # Append PEC to payload + + print(f"Generated GET_VERSION payload:") + print(f" Source I2C: 0x{args.own_addr:02X} (frame: 0x{src_i2c_with_read:02X})") + print(f" Source EID: 0x{args.own_eid:02X}") + print(f" Dest I2C: 0x{args.remote_addr:02X}") + print(f" Dest EID: 0x{args.remote_eid:02X}") + print(f" PEC: 0x{crc:02X}") + print() # Define pre-fabricated payloads payloads = [ { 'name': 'MCTP SPDM GET_VERSION', - 'data': [0x0F, 0x0A, 0x85, 0x01, 0x08, 0x30, 0xC8, 0x05, 0x10, 0x84, 0x00, 0x00, 0x65], + 'data': frame, 'description': 'MCTP over SMBus: SPDM GET_VERSION request' }, # Add more payloads here as needed @@ -347,7 +407,7 @@ execute_transaction( handle, - args.target, + args.remote_addr, payload_info['data'], wait_response=not args.no_wait_response, response_timeout=args.response_timeout, @@ -360,7 +420,7 @@ print("\n\nInterrupted by user") finally: - # Disable slave mode and close device + # Disable target mode and close device aa_i2c_slave_disable(handle) aa_close(handle) print("Aardvark device closed")
diff --git a/target/ast1060-evb/harness/i2c_responder.py b/target/ast1060-evb/harness/i2c_responder.py new file mode 100755 index 0000000..02227ad --- /dev/null +++ b/target/ast1060-evb/harness/i2c_responder.py
@@ -0,0 +1,375 @@ +#!/usr/bin/env python3 +""" +Aardvark I2C Responder +Listens for I2C requests and returns a canned response. +""" + +import sys +import os +import argparse +from typing import List + +# Add the Aardvark API to the path +# Assumes script runs at the same level as aardvark-api-linux-x86_64-v6.00 directory +script_dir = os.path.dirname(os.path.abspath(__file__)) +aardvark_lib_path = os.path.join(script_dir, 'aardvark-api-linux-x86_64-v6.00', 'python') +sys.path.insert(0, aardvark_lib_path) +from aardvark_py import * + + +#========================================================================== +# DEVICE MANAGEMENT +#========================================================================== + +def find_and_connect(): + """Find and connect to the first available Aardvark device.""" + print("Searching for Aardvark devices...") + + # Find all attached devices + (num, ports, unique_ids) = aa_find_devices_ext(16, 16) + + if num == 0: + print("Error: No Aardvark devices found!") + sys.exit(1) + + print(f"Found {num} device(s)") + + # Find the first available (not in-use) device + device_port = None + for i in range(num): + port = ports[i] + unique_id = unique_ids[i] + + if not (port & AA_PORT_NOT_FREE): + device_port = port + print(f"Connecting to device on port {port} (S/N: {unique_id:04d}-{unique_id % 1000000:06d})") + break + else: + print(f"Port {port & ~AA_PORT_NOT_FREE} is in use") + + if device_port is None: + print("Error: All devices are in use!") + sys.exit(1) + + # Open the device + handle = aa_open(device_port) + if handle <= 0: + print(f"Error: Unable to open Aardvark device on port {device_port}") + print(f"Error code = {handle}") + sys.exit(1) + + print(f"Successfully opened Aardvark device on port {device_port}") + return handle + + +def configure_device(handle, own_addr=0x42, bitrate_khz=100, bus_timeout_ms=150): + """ + Configure the Aardvark for I2C operation. + + Args: + handle: Aardvark device handle + own_addr: Our I2C address (default: 0x42) + bitrate_khz: I2C bus speed in kHz (default: 100) + bus_timeout_ms: Bus lock timeout in ms (default: 150) + """ + print("\nConfiguring Aardvark I2C interface...") + + # Configure for I2C mode + aa_configure(handle, AA_CONFIG_SPI_I2C) + print(" Mode: I2C") + + # Enable I2C pullup resistors (2.2k) + aa_i2c_pullup(handle, AA_I2C_PULLUP_BOTH) + print(" Pullups: Enabled (both lines)") + + # Enable target power + aa_target_power(handle, AA_TARGET_POWER_BOTH) + print(" Target Power: Enabled") + + # Set the bitrate + actual_bitrate = aa_i2c_bitrate(handle, bitrate_khz) + print(f" Bitrate: {actual_bitrate} kHz (requested: {bitrate_khz} kHz)") + + # Set the bus lock timeout + actual_timeout = aa_i2c_bus_timeout(handle, bus_timeout_ms) + print(f" Bus Timeout: {actual_timeout} ms") + + # Enable target mode with our address + # Parameters: handle, own_addr, maxTxBytes, maxRxBytes + # Use 0 for unlimited buffer sizes + result = aa_i2c_slave_enable(handle, own_addr, 0, 0) + if result < 0: + print(f" Error enabling target mode: {aa_status_string(result)}") + sys.exit(1) + print(f" Our I2C Address: 0x{own_addr:02X}") + print(" Target Mode: Enabled") + + print("\nConfiguration complete. Device ready.") + print(f" Listening on address: 0x{own_addr:02X}\n") + + +def set_response(handle, response_data: List[int]): + """ + Set the data to send when remote device reads from us. + + Args: + handle: Aardvark device handle + response_data: List of bytes to respond with + """ + data_array = array('B', response_data) + result = aa_i2c_slave_set_response(handle, data_array) + if result < 0: + print(f"Error setting response: {aa_status_string(result)}") + sys.exit(1) + + print(f"Canned response configured ({len(response_data)} bytes):") + print(f" Data: {' '.join(f'{b:02X}' for b in response_data)}") + + +#========================================================================== +# I2C OPERATIONS +#========================================================================== + +def listen_for_requests(handle, own_addr, remote_addr, response_data, timeout_ms=500, max_buffer=256, transaction_count=0): + """ + Listen for incoming I2C requests and handle them. + Uses MCTP multi-controller behavior: receive, then respond. + + Args: + handle: Aardvark device handle + own_addr: Our I2C address (needed for re-enabling target mode) + remote_addr: Remote I2C address to send response to + response_data: List of bytes to respond with + timeout_ms: Timeout in milliseconds between requests + max_buffer: Maximum buffer size for receiving data + transaction_count: Max number of transactions (0 = infinite) + + Returns: + Number of transactions handled + """ + print("=" * 80) + print("Listening for I2C requests... (Press Ctrl+C to stop)") + print("=" * 80) + print() + + trans_num = 0 + + try: + while True: + # Check if we've reached transaction limit + if transaction_count > 0 and trans_num >= transaction_count: + print(f"\nReached transaction limit ({transaction_count})") + break + + # Poll for async events + result = aa_async_poll(handle, timeout_ms) + + if result == AA_ASYNC_NO_DATA: + # No data, keep waiting + continue + + if result == AA_ASYNC_I2C_READ: + # Data was written to us (we're receiving a request) + (num_bytes, addr, data_in) = aa_i2c_slave_read(handle, max_buffer) + + if num_bytes < 0: + print(f"Error reading data: {aa_status_string(num_bytes)}") + continue + + # Convert to list + data_list = [data_in[i] for i in range(num_bytes)] + + # Display the request + print(f"<<< Transaction #{trans_num + 1}: Request received") + print(f" Target Address: 0x{addr:02X} (our address)") + print(f" Length: {num_bytes} bytes") + print(f" Data: {' '.join(f'{b:02X}' for b in data_list)}") + print(f" Will respond to: 0x{remote_addr:02X} (remote address)") + print() + + # Delay before responding + import time + print(f" Waiting 1 second before responding...") + time.sleep(1.0) + print() + + # MCTP multi-controller behavior: switch to controller and send response + print(f">>> Switching to controller mode to send response...") + + # Disable target mode + print(f" Disabling target mode...") + result = aa_i2c_slave_disable(handle) + print(f" aa_i2c_slave_disable returned: {result}") + if result < 0: + print(f" ERROR disabling target mode: {aa_status_string(result)}") + continue + print(f" Target mode disabled successfully") + + # Add delay to allow remote to switch to target mode + # and bus to settle after the request transaction + import time + print(f" Waiting for bus to settle and remote to switch to target mode...") + time.sleep(0.05) # 50ms delay + + # Write response as controller to the remote address + response_array = array('B', response_data) + print(f" Preparing to write {len(response_data)} bytes to 0x{remote_addr:02X}") + print(f" Data: {' '.join(f'{b:02X}' for b in response_data)}") + + num_written = aa_i2c_write(handle, remote_addr, AA_I2C_NO_FLAGS, response_array) + + print(f" aa_i2c_write returned: {num_written}") + if num_written < 0: + print(f" ERROR writing response: {aa_status_string(num_written)}") + print(f" Error code: {num_written}") + elif num_written == 0: + print(f" WARNING: 0 bytes written!") + else: + print(f" SUCCESS: Wrote {num_written} bytes to 0x{remote_addr:02X}") + print() + + # Re-enable target mode + print(f" Re-enabling target mode on address 0x{own_addr:02X}...") + result = aa_i2c_slave_enable(handle, own_addr, 0, 0) + print(f" aa_i2c_slave_enable returned: {result}") + if result < 0: + print(f" ERROR re-enabling target mode: {aa_status_string(result)}") + break + + print(f">>> Switched back to target mode (listening on 0x{own_addr:02X})") + print() + + trans_num += 1 + + elif result == AA_ASYNC_I2C_WRITE: + # This shouldn't happen in multi-controller mode, but log it if it does + num_bytes = aa_i2c_slave_write_stats(handle) + print(f"!!! Unexpected passive write event (num_bytes: {num_bytes})") + print() + + else: + print(f"Unexpected async event: {result}") + + except KeyboardInterrupt: + print("\n\nStopped by user") + + return trans_num + + +#========================================================================== +# MAIN +#========================================================================== + +def main(): + """Main entry point.""" + parser = argparse.ArgumentParser( + description='Aardvark I2C Responder', + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=''' +This tool operates with MCTP multi-controller behavior: + - Listens as I2C target for incoming write requests + - Switches to I2C controller to actively send response back to requester + - Returns to target mode for next request + +Default configuration (for in-process-requester mode): + - Responder (us): EID=0x42, I2C address=0x13 + - Requester (peer): EID=0x08, I2C address=0x10 + - Bus speed: 100 kHz + - Bus timeout: 150 ms + +Default canned response (MCTP SPDM VERSION response): + 26 0F 0A 21 01 42 08 D9 05 10 84 00 00 51 + ''' + ) + + parser.add_argument('--own-addr', type=lambda x: int(x, 0), default=0x13, + metavar='ADDR', help='Our I2C address (default: 0x13)') + parser.add_argument('--remote-addr', type=lambda x: int(x, 0), default=0x10, + metavar='ADDR', help='Remote I2C address to respond to (default: 0x10)') + parser.add_argument('--own-eid', type=lambda x: int(x, 0), default=0x42, + metavar='EID', help='Our MCTP endpoint ID (default: 0x42)') + parser.add_argument('--remote-eid', type=lambda x: int(x, 0), default=0x08, + metavar='EID', help='Remote MCTP endpoint ID (default: 0x08)') + parser.add_argument('--bitrate', type=int, default=100, metavar='KHZ', + help='I2C bus speed in kHz (default: 100)') + parser.add_argument('--bus-timeout', type=int, default=150, metavar='MS', + help='Bus lock timeout in ms (default: 150)') + parser.add_argument('--poll-timeout', type=int, default=500, metavar='MS', + help='Poll timeout in ms (default: 500)') + parser.add_argument('--count', type=int, default=0, metavar='N', + help='Number of transactions to handle (0=infinite, default: 0)') + parser.add_argument('--response', type=str, default=None, metavar='HEX', + help='Custom response as hex string (e.g., "01 02 03")') + + args = parser.parse_args() + + # Default canned response (MCTP SPDM VERSION response) + # Captured from actual Rust responder on I2C bus: + # <0x13:W> 0x26 0x0F 0x0A 0x21 0x01 0x42 0x08 0xD9 0x05 0x10 0x84 0x00 0x00 0x51 + # + # Format: [SrcI2C][CMD][ByteCount][DestI2C][MCTP_Ver][DestEID][SrcEID][Flags][MsgType][SPDM][PEC] + # Responder (us): EID=0x42, I2C=0x13 + # Requester (peer): EID=0x08, I2C=0x10 + # Src I2C: 0x26 = 0x13 << 1 (no read bit in this position) + # Dest I2C: 0x21 = 0x10 << 1 | 1 (read bit set) + # MCTP: Dest EID=0x42, Source EID=0x08 (note: different from expected!) + # PEC=0x51 verified from actual capture + canned_response = [ + 0x26, 0x0F, 0x0A, 0x21, 0x01, 0x42, 0x08, 0xD9, + 0x05, 0x10, 0x84, 0x00, 0x00, 0x51, + ] + + # Parse custom response if provided + if args.response: + try: + hex_bytes = args.response.replace(',', ' ').split() + canned_response = [int(b, 16) for b in hex_bytes if b.strip()] + print(f"Using custom response ({len(canned_response)} bytes)") + except ValueError as e: + print(f"Error parsing custom response: {e}") + sys.exit(1) + + # Find and connect to device + handle = find_and_connect() + + try: + # Configure device + configure_device(handle, args.own_addr, args.bitrate, args.bus_timeout) + + # Display configuration + print() + print("MCTP Responder Configuration:") + print(f" Our I2C Address: 0x{args.own_addr:02X}") + print(f" Our EID: 0x{args.own_eid:02X}") + print(f" Remote I2C Address: 0x{args.remote_addr:02X}") + print(f" Remote EID: 0x{args.remote_eid:02X}") + print() + print(f"Active response configured ({len(canned_response)} bytes):") + print(f" Data: {' '.join(f'{b:02X}' for b in canned_response)}") + print(f" Will be sent as I2C controller after receiving requests") + print() + + # Listen for requests + num_transactions = listen_for_requests( + handle, + args.own_addr, + args.remote_addr, + canned_response, + args.poll_timeout, + transaction_count=args.count + ) + + print(f"\nTotal transactions handled: {num_transactions}") + + except KeyboardInterrupt: + print("\n\nInterrupted by user") + + finally: + # Disable target mode and close device + aa_i2c_slave_disable(handle) + aa_close(handle) + print("Aardvark device closed") + + +if __name__ == "__main__": + main()