From 4ac723a9850d8068f3317da8a7354db4bcfec907 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 13 Jul 2026 19:59:58 +0200 Subject: [PATCH] firmware: Implement debug command via USB control vendor class Allows to collect talloc reports and other debug information from a script while the remsim-client is still running. Change-Id: I1f4d29335eb0d2feef376b8ecdfe57a0162914d6 --- contrib/usb_vendor_request.py | 102 +++++++++++++++++++++ firmware/apps/cardem/main.c | 24 +++++ firmware/libcommon/include/simtrace.h | 3 + firmware/libcommon/include/simtrace_prot.h | 6 ++ firmware/libcommon/source/mode_cardemu.c | 36 ++++++++ 5 files changed, 171 insertions(+) create mode 100644 contrib/usb_vendor_request.py diff --git a/contrib/usb_vendor_request.py b/contrib/usb_vendor_request.py new file mode 100644 index 00000000..120e7f4d --- /dev/null +++ b/contrib/usb_vendor_request.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2026 sysmocom -s.f.m.c. GmbH, Author: Alexander Couzens +# License: MIT + +import sys +import usb.util +from usb.core import USBError + +TIMEOUT = 3 + +# simtrace2 vendor requets +SIMTRACER_VENDOR_REQ_GET_DEBUG_INFO = 0 + +def vendor_get_debug(device: usb.core.Device): + return vendor_req_in(device, bRequest = SIMTRACER_VENDOR_REQ_GET_DEBUG_INFO, wValue = 0, wIndex = 0, length = 32) + +def vendor_req_in(device: usb.core.Device, bRequest: int, wValue: int, wIndex: int, length: int = 8): + """ do a vendor request in (device -> host) + @param bRequest: usb single byte + @param wIndex: short index + """ + return device.ctrl_transfer( + bmRequestType=usb.util.ENDPOINT_IN + | usb.util.CTRL_TYPE_VENDOR + | usb.util.CTRL_RECIPIENT_DEVICE, + bRequest=bRequest, + wValue=wValue, + wIndex=wIndex, + data_or_wLength=length, + timeout=TIMEOUT, + ) + +def find_simtrace(vendor = 0x1d50, product = 0x60e3, find_all = False): + return usb.core.find(find_all=find_all, idVendor=vendor, idProduct=product) + +def get_path(device: usb.core.Device): + ports = ".".join([str(x) for x in device.port_numbers]) + return f"{device.bus}-{ports}" + +def per_device(device: usb.core.Device, args: argparse.Namespace): + if args.get_debug: + result = vendor_get_debug(device) + print(f"{get_path(device)} / {device.serial_number}: {result}") + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(prog='simtrace-util') + # actions + parser.add_argument( + "--get-debug", + action='store_true', + help="Get debug") + + # device selection + parser.add_argument( + "-a", + "--all", + action='store_true', + help="Run the request against all simtrace2 devices.") + parser.add_argument( + "-s", + "--serial", + help="Select the simtrace2 by serial") + parser.add_argument( + "--vendor", + type=int, + help="Use a different USB vendor. Default Openmoko 0x1d50.") + parser.add_argument( + "-p", + "--product", + type=int, + help="Use a different USB product. Default simtrace2 0x60e3.\n" + "qmod: 0x4004") + + args = parser.parse_args() + # no action given + if not args.get_debug: + parser.print_usage() + sys.exit(1) + + vendor = 0x1d50 + product = 0x60e3 + if args.product: + product = args.product + if args.vendor: + vendor = args.vendor + if args.serial: + device = find_simtrace(vendor=vendor, product=product, find_all=args.all, serial_number=args.serial_number) + else: + device = find_simtrace(vendor=vendor, product=product, find_all=args.all) + + if not device: + print("Could not find a single board", file=sys.stderr) + sys.exit(1) + + if type(device) == usb.core.Device: + per_device(device, args) + else: + for dev in device: + per_device(dev, args) diff --git a/firmware/apps/cardem/main.c b/firmware/apps/cardem/main.c index 6372e04f..92a80458 100644 --- a/firmware/apps/cardem/main.c +++ b/firmware/apps/cardem/main.c @@ -19,6 +19,7 @@ #include "board.h" #include "simtrace.h" +#include "talloc.h" #include "utils.h" #include "main_common.h" #include @@ -39,6 +40,8 @@ typedef struct { void (*usart0_irq) (void); /* Interrupt handler for USART1 */ void (*usart1_irq) (void); + /* ctrl vendor request handler */ + void (*ctrl_vendor_req) (const USBGenericRequest *request); } conf_func; static const conf_func config_func_ptrs[] = { @@ -74,6 +77,7 @@ static const conf_func config_func_ptrs[] = { .usart0_irq = mode_cardemu_usart0_irq, .usart1_irq = mode_cardemu_usart1_irq, #endif + .ctrl_vendor_req = mode_cardemu_ctrl_vendor_req, }, #endif #ifdef HAVE_MITM @@ -137,6 +141,26 @@ static void check_exec_dbg_cmd(void) board_exec_dbg_cmd(ch); } +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + if (USBGenericRequest_GetType(request) != USBGenericRequest_VENDOR) + return USBDDriver_RequestHandler(USBD_GetDriver(), request); + + + if (config_func_ptrs[simtrace_config].ctrl_vendor_req) + config_func_ptrs[simtrace_config].ctrl_vendor_req(request); + else { + /* only ctrl in is supported */ + if (USBGenericRequest_GetDirection(request) == USBGenericRequest_IN) + /* Return ZLP */ + USBD_Write(0, NULL, 0, NULL, NULL); + else /* stall Ctrl out vendor requests */ + USBD_Stall(0); + + return; + } +} + /*------------------------------------------------------------------------------ * Main *------------------------------------------------------------------------------*/ diff --git a/firmware/libcommon/include/simtrace.h b/firmware/libcommon/include/simtrace.h index 4a7aca12..b6f8ec4e 100644 --- a/firmware/libcommon/include/simtrace.h +++ b/firmware/libcommon/include/simtrace.h @@ -120,6 +120,9 @@ extern void Sniffer_usart1_irq(void); extern void mode_cardemu_usart0_irq(void); extern void mode_cardemu_usart1_irq(void); +/* Vendor request functions */ +void mode_cardemu_ctrl_vendor_req(const USBGenericRequest *request); + /* Timer helper function */ void Timer_Init( void ); void TC0_Counter_Reset( void ); diff --git a/firmware/libcommon/include/simtrace_prot.h b/firmware/libcommon/include/simtrace_prot.h index 50042a02..66e87245 100644 --- a/firmware/libcommon/include/simtrace_prot.h +++ b/firmware/libcommon/include/simtrace_prot.h @@ -86,6 +86,12 @@ enum simtrace_msg_type_sniff { SIMTRACE_MSGT_SNIFF_TPDU, }; +/* Simtrace ctrl vendor requests */ +enum simtrace_vendor_req { + /* get debug infos */ + SIMTRACER_VENDOR_REQ_GET_DEBUG_INFO = 0, +}; + /* common message header */ struct simtrace_msg_hdr { uint8_t msg_class; /* simtrace_msg_class */ diff --git a/firmware/libcommon/source/mode_cardemu.c b/firmware/libcommon/source/mode_cardemu.c index a0b4aa0f..11b91554 100644 --- a/firmware/libcommon/source/mode_cardemu.c +++ b/firmware/libcommon/source/mode_cardemu.c @@ -689,6 +689,42 @@ void mode_cardemu_exit(void) #endif } +void mode_cardemu_ctrl_vendor_req(const USBGenericRequest *request) +{ + if (USBGenericRequest_GetRecipient(request) != USBGenericRequest_DEVICE) { + USBD_Stall(0); + return; + } + + if (USBGenericRequest_GetDirection(request) != USBGenericRequest_IN) { + USBD_Stall(0); + return; + } + + uint16_t req = USBGenericRequest_GetRequest(request); + uint8_t buf[32] = {}; + uint8_t len = 0; + unsigned int used, i; + switch (req) { + case SIMTRACER_VENDOR_REQ_GET_DEBUG_INFO: + /* don't use msgb here to prevent an additional allocation */ + used = talloc_report_buf(&buf[0], 16); + /* add queues */ + for (i = 0; i < ARRAY_SIZE(cardem_inst); i++) { + struct cardem_inst *ci = &cardem_inst[i]; + buf[used++] = i + 1; + buf[used++] = usb_get_buf_ep(ci->ep_in)->queue_len; + buf[used++] = usb_get_buf_ep(ci->ep_out)->queue_len; + buf[used++] = usb_get_buf_ep(ci->ep_int)->queue_len; + } + USBD_Write(0, &buf, used, 0, 0); + break; + default: + USBD_Stall(0); + break; + } +} + /* handle a single USB command as received from the USB host */ static void dispatch_usb_command_generic(struct msgb *msg, struct cardem_inst *ci) {