Hoernchen has uploaded this change for review.

View Change

Make ch9 usb tests work

This passes all tests except TD 9.9.
That's is weird, because 9.9 works fine when doing
the same commands on linux. It does not really
matter anyway, since stalling the device ep by
sending a message from the host is is only useful
for the test itself... There is no reasonable
way to continue running after random forced ep
stalls anyway, resetting the device would be the
sane option.

This adds some mildly useful debug python snippets
that can be used to inspect the device state.

Change-Id: Icadaee9d8cbe24bd3cac002cc4f710dcf846a32b
---
M ccid_common/ccid_slot_fsm.c
M ccid_common/cuart.c
A contrib/ch9_epstall.py
A contrib/gdb_clocktree.py
A contrib/gdb_msgb.py
A contrib/gdb_sercom.py
A contrib/gdb_usb.py
M sysmoOCTSIM/hpl/usb/hpl_usb.c
M sysmoOCTSIM/main.c
M sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
M sysmoOCTSIM/usb/device/usbdc.c
11 files changed, 1,242 insertions(+), 50 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/osmo-ccid-firmware refs/changes/59/39759/1
diff --git a/ccid_common/ccid_slot_fsm.c b/ccid_common/ccid_slot_fsm.c
index eb0c145..a521b1d 100644
--- a/ccid_common/ccid_slot_fsm.c
+++ b/ccid_common/ccid_slot_fsm.c
@@ -105,6 +105,23 @@
}
}

+void extern_fsm_reset_hack(struct ccid_slot *cs)
+{
+ struct iso_fsm_slot *ss = ccid_slot2iso_fsm_slot(cs);
+
+ cs->icc_present = false;
+ osmo_fsm_inst_dispatch(ss->fi, ISO7816_E_CARD_REMOVAL, NULL);
+ card_uart_ctrl(ss->cuart, CUART_CTL_RST, true);
+ card_uart_ctrl(ss->cuart, CUART_CTL_POWER_5V0, false);
+ cs->icc_powered = false;
+ cs->cmd_busy = false;
+ cs->icc_present_last = false;
+ cs->icc_in_reset = false;
+ cs->default_pars = &iso_fsm_def_pars;
+ g_si.slot[cs->slot_nr].seq = 0;
+ card_uart_ctrl(ss->cuart, CUART_CTL_RX, true);
+}
+
static void iso_fsm_slot_icc_power_on_async(struct ccid_slot *cs, struct msgb *msg,
const struct ccid_pc_to_rdr_icc_power_on *ipo)
{
diff --git a/ccid_common/cuart.c b/ccid_common/cuart.c
index 9966942..9c96891 100644
--- a/ccid_common/cuart.c
+++ b/ccid_common/cuart.c
@@ -154,6 +154,7 @@
/* we have to reset this somewhere, and powering down loses all state
* this is not hw specific so it belongs here, after handling the hw specific part */
if (!arg) {
+ osmo_timer_del(&cuart->wtime_tmr);
cuart->tx_busy = false;
cuart->rx_threshold = 1;
cuart->wtime_etu = 9600; /* ISO 7816-3 Section 8.1 */
diff --git a/contrib/ch9_epstall.py b/contrib/ch9_epstall.py
new file mode 100644
index 0000000..d407687
--- /dev/null
+++ b/contrib/ch9_epstall.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+"""
+ch9 usb ep halt test
+"""
+
+import sys
+import getopt
+import ctypes
+import libusb
+
+VENDOR = 0x1d50
+PRODUCT = 0x6141
+TIMEOUT = 1000 # milliseconds
+
+def is_myusb(dev):
+ desc = libusb.device_descriptor()
+ ret = libusb.get_device_descriptor(dev, ctypes.byref(desc))
+ if ret != 0:
+ sys.stderr.write("Failed to get device descriptor\n")
+ return False
+ return (desc.idVendor == VENDOR and desc.idProduct == PRODUCT)
+
+
+def print_device_endpoints(dev):
+ """Prints the list of endpoints for each interface from the first configuration."""
+ enum_mapping = {
+ value: name
+ for name, value in vars(libusb).items()
+ if name.startswith("LIBUSB_ENDPOINT_TRANSFER_TYPE_") and isinstance(value, int)
+ }
+
+ def _dir(ep):
+ return "IN" if (ep & libusb.LIBUSB_ENDPOINT_DIR_MASK) == libusb.LIBUSB_ENDPOINT_DIR_MASK else "OUT"
+
+ cfg_ptr = ctypes.POINTER(libusb.config_descriptor)()
+ ret = libusb.get_config_descriptor(dev, 0, ctypes.byref(cfg_ptr))
+ if ret != 0:
+ sys.stderr.write("Error getting config descriptor: error {}\n".format(ret))
+ return
+ cfg = cfg_ptr.contents
+ print("Device Configuration:")
+ print(" Number of interfaces: {}".format(cfg.bNumInterfaces))
+ # Iterate over interfaces
+ for i in range(cfg.bNumInterfaces):
+ iface = cfg.interface[i]
+ print(" Interface {}: {} alternate setting(s)".format(i, iface.num_altsetting))
+ for j in range(iface.num_altsetting):
+ alt = iface.altsetting[j]
+ print(" Interface Altsetting {}: Interface Number {}, Number of Endpoints: {}" .format(j, alt.bInterfaceNumber, alt.bNumEndpoints))
+ for k in range(alt.bNumEndpoints):
+ ep = alt.endpoint[k]
+ print(f" Endpoint {k}: address: 0x{ep.bEndpointAddress:02x} {_dir(ep.bEndpointAddress):3} {enum_mapping[ep.bmAttributes & 0b11]}")
+ libusb.free_config_descriptor(cfg_ptr)
+
+
+def set_endpoint_halt(handle, ep, timeout=TIMEOUT):
+ # bmRequestType: 0x02 (Host-to-device, Standard, Endpoint)
+ # bRequest: 0x03 (SET_FEATURE) with wValue = 0 (ENDPOINT_HALT)
+ ret = libusb.control_transfer(handle, 0x02, 0x03, 0, ep, None, 0, timeout)
+ return ret
+
+
+def get_endpoint_halt(handle, ep, timeout=TIMEOUT):
+ # bmRequestType: 0x82 (Device-to-host, Standard, Endpoint)
+ # bRequest: 0x00 (GET_STATUS) with 2-byte response.
+ buf = (ctypes.c_ubyte * 2)()
+ ret = libusb.control_transfer(handle, 0x82, 0x00, 0, ep, buf, 2, timeout)
+ if ret < 0:
+ return None, ret
+ status = buf[0] | (buf[1] << 8)
+ halted = (status & 1) != 0
+ return halted, 0
+
+
+def clear_endpoint_halt(handle, ep, timeout=TIMEOUT):
+ # bmRequestType: 0x02 (Host-to-device, Standard, Endpoint)
+ # bRequest: 0x01 (CLEAR_FEATURE) with wValue = 0 (ENDPOINT_HALT)
+ # ret = libusb.control_transfer(handle, 0x02, 0x01, 0, ep, None, 0, timeout)
+
+ # use proper clear, because the halt resets the toggle, so the host needs to know
+ ret = libusb.clear_halt(handle, ep)
+ return ret
+
+
+def main():
+ try:
+ opts, _ = getopt.getopt(sys.argv[1:], "e:d:i:")
+ except getopt.GetoptError as err:
+ sys.stderr.write("ERR: Invalid options: {}\n".format(err))
+ sys.exit(1)
+
+ ep = None
+ iface = 0
+ for opt, arg in opts:
+ if opt == '-e':
+ ep = int(arg, 0)
+ elif opt == '-i':
+ iface = int(arg, 0)
+
+ ctx = ctypes.POINTER(libusb.context)()
+ ret = libusb.init(ctypes.byref(ctx))
+ if ret != 0:
+ sys.stderr.write("Failed to initialize libusb, error {}\n".format(ret))
+ sys.exit(1)
+
+ # Get device list.
+ devs = ctypes.POINTER(ctypes.POINTER(libusb.device))()
+ cnt = libusb.get_device_list(ctx, ctypes.byref(devs))
+ print("Number of attached USB devices = {}".format(cnt))
+
+ my_dev = None
+ for i in range(cnt):
+ dev = devs[i]
+ if is_myusb(dev):
+ my_dev = dev
+ break
+
+ if not my_dev:
+ sys.stderr.write("ERR: Device not found\n")
+ libusb.free_device_list(devs, 1)
+ libusb.exit(ctx)
+ sys.exit(1)
+
+ # Print the list of endpoints before performing endpoint halt operations.
+ print("Listing device endpoints:")
+ print_device_endpoints(my_dev)
+
+ if ep is None:
+ sys.stderr.write("ERR: -e: Specify endpoint number\n")
+ libusb.free_device_list(devs, 1)
+ libusb.exit(ctx)
+ sys.exit(1)
+
+ handle = ctypes.POINTER(libusb.device_handle)()
+ ret = libusb.open(my_dev, ctypes.byref(handle))
+ if ret != 0:
+ sys.stderr.write("ERR: Failed to open device, error {}\n".format(libusb.strerror(ret)))
+ libusb.free_device_list(devs, 1)
+ libusb.exit(ctx)
+ sys.exit(1)
+
+ if libusb.kernel_driver_active(handle, iface) == 1:
+ libusb.detach_kernel_driver(handle, iface)
+
+ ret = libusb.claim_interface(handle, iface)
+ if ret != 0:
+ sys.stderr.write("ERR: Failed to claim interface {}: error {}\n".format(iface, libusb.strerror(ret)))
+ libusb.close(handle)
+ libusb.free_device_list(devs, 1)
+ libusb.exit(ctx)
+ sys.exit(1)
+
+ print("Performing halt operations on endpoint 0x{:02x}".format(ep))
+
+ # Set endpoint halt (stall).
+ ret = set_endpoint_halt(handle, ep)
+ if ret < 0:
+ sys.stderr.write("Error setting halt on endpoint 0x{:02x}, error {}\n".format(ep, libusb.strerror(ret)))
+ else:
+ print("Endpoint 0x{:02x} set to halted state.".format(ep))
+
+ # Get the halt status.
+ halted, ret = get_endpoint_halt(handle, ep)
+ if ret < 0:
+ sys.stderr.write("Error getting halt status on endpoint 0x{:02x}, error {}\n".format(ep, libusb.strerror(ret)))
+ else:
+ print("Endpoint 0x{:02x} halt status: {}".format(ep, halted))
+
+ # Clear the endpoint halt.
+ ret = clear_endpoint_halt(handle, ep)
+ if ret < 0:
+ sys.stderr.write("Error clearing halt on endpoint 0x{:02x}, error {}\n".format(ep, libusb.strerror(ret)))
+ else:
+ print("Clear halt command issued for endpoint 0x{:02x}.".format(ep))
+
+ # Verify the status after clearing.
+ halted, ret = get_endpoint_halt(handle, ep)
+ if ret < 0:
+ sys.stderr.write("Error getting halt status after clear on endpoint 0x{:02x}, error {}\n".format(ep, libusb.strerror(ret)))
+ else:
+ print("Endpoint 0x{:02x} halt status after clear: {}".format(ep, halted))
+
+ # Cleanup.
+ libusb.release_interface(handle, iface)
+ libusb.attach_kernel_driver(handle, iface)
+ libusb.close(handle)
+ libusb.free_device_list(devs, 1)
+ libusb.exit(ctx)
+ sys.exit(0)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/gdb_clocktree.py b/contrib/gdb_clocktree.py
new file mode 100644
index 0000000..ba5e06c
--- /dev/null
+++ b/contrib/gdb_clocktree.py
@@ -0,0 +1,455 @@
+import gdb
+
+class ClockTreePrinter(gdb.Command):
+
+ def __init__(self):
+ super(ClockTreePrinter, self).__init__("print-clock-tree", gdb.COMMAND_USER)
+
+ self.known_xosc1_freq = 12e6
+
+ self.OSCCTRL_BASE = 0x40001000
+ self.OSC32KCTRL_BASE = 0x40001400
+ self.GCLK_BASE = 0x40001C00
+ self.MCLK_BASE = 0x40000800
+
+ self.dpll_sources = {
+ 0x0 : "GCLK", # Dedicated GCLK clock reference
+ 0x1 : "XOSC32", # XOSC32K clock reference (default)
+ 0x2 : "XOSC0", # XOSC0 clock reference
+ 0x3 : "XOSC1", # XOSC1 clock reference
+ #Other - Reserved
+ }
+ self.clock_sources = {
+ "XOSC0" : 0x00, # XOSC 0 oscillator output
+ "XOSC1" : 0x01, # XOSC 1 oscillator output
+ "GCLK_IN" : 0x02, # Generator input pad (GCLK_IO)
+ "GCLK_GEN1" : 0x03, # Generic clock generator 1 output
+ "OSCULP32K" : 0x04, # OSCULP32K oscillator output
+ "XOSC32K" : 0x05, # XOSC32K oscillator output
+ "DFLL" : 0x06, # DFLL48M oscillator output
+ "DPLL0" : 0x07, # FDPLL200M0 output
+ "DPLL1" : 0x08, # FDPLL200M1 output
+ # 0x09-0x0F Reserved Reserved
+ }
+
+ self.generic_clocks = range(0, 12) # GCLK0 to GCLK11
+ self.main_clocks = [
+ "CPU", "APBA", "APBB", "APBC", "APBD", "APBE"
+ ]
+
+ self._peripheral_ids = [
+ # grep -rh _GCLK_ID 2>/dev/null | grep define |tr -s ' ' |sort -Vk3 | uniq | sed -E 's/^#define ([^ ]+)[[:space:]]+([0-9]+).*/\2 : "\1"/p' | uniq
+ {0 : "OSCCTRL_GCLK_ID_DFLL48"},
+ {1 : "OSCCTRL_GCLK_ID_FDPLL0"},
+ {2 : "I2S_GCLK_ID_SIZE"},
+ {2 : "OSCCTRL_GCLK_ID_FDPLL1"},
+ {3 : "SERCOM0_GCLK_ID_SLOW"},
+ {3 : "SERCOM1_GCLK_ID_SLOW"},
+ {3 : "SERCOM2_GCLK_ID_SLOW"},
+ {3 : "SERCOM3_GCLK_ID_SLOW"},
+ {3 : "SERCOM4_GCLK_ID_SLOW"},
+ {3 : "SERCOM5_GCLK_ID_SLOW"},
+ {3 : "SERCOM6_GCLK_ID_SLOW"},
+ {3 : "SERCOM7_GCLK_ID_SLOW"},
+ {3 : "SDHC0_GCLK_ID_SLOW"},
+ {3 : "SDHC1_GCLK_ID_SLOW"},
+ {3 : "OSCCTRL_GCLK_ID_FDPLL032K"},
+ {3 : "OSCCTRL_GCLK_ID_FDPLL132K"},
+ {4 : "EIC_GCLK_ID"},
+ {5 : "FREQM_GCLK_ID_MSR"},
+ {7 : "SERCOM0_GCLK_ID_CORE"},
+ {8 : "SERCOM1_GCLK_ID_CORE"},
+ {9 : "TC0_GCLK_ID"},
+ {9 : "TC1_GCLK_ID"},
+ {10 : "USB_GCLK_ID"},
+ {11 : "EVSYS_GCLK_ID_0"},
+ {11 : "EVSYS_GCLK_ID_LSB"},
+ {12 : "EVSYS_GCLK_ID_1"},
+ {12 : "EVSYS_GCLK_ID_SIZE"},
+ {13 : "EVSYS_GCLK_ID_2"},
+ {14 : "EVSYS_GCLK_ID_3"},
+ {15 : "EVSYS_GCLK_ID_4"},
+ {16 : "EVSYS_GCLK_ID_5"},
+ {17 : "EVSYS_GCLK_ID_6"},
+ {18 : "EVSYS_GCLK_ID_7"},
+ {19 : "EVSYS_GCLK_ID_8"},
+ {20 : "EVSYS_GCLK_ID_9"},
+ {21 : "EVSYS_GCLK_ID_10"},
+ {22 : "EVSYS_GCLK_ID_11"},
+ {22 : "EVSYS_GCLK_ID_MSB"},
+ {23 : "SERCOM2_GCLK_ID_CORE"},
+ {24 : "SERCOM3_GCLK_ID_CORE"},
+ {25 : "TCC0_GCLK_ID"},
+ {25 : "TCC1_GCLK_ID"},
+ {26 : "TC2_GCLK_ID"},
+ {26 : "TC3_GCLK_ID"},
+ {27 : "CAN0_GCLK_ID"},
+ {28 : "CAN1_GCLK_ID"},
+ {29 : "TCC2_GCLK_ID"},
+ {29 : "TCC3_GCLK_ID"},
+ {30 : "TC4_GCLK_ID"},
+ {30 : "TC5_GCLK_ID"},
+ {31 : "PDEC_GCLK_ID"},
+ {32 : "AC_GCLK_ID"},
+ {33 : "CCL_GCLK_ID"},
+ {34 : "SERCOM4_GCLK_ID_CORE"},
+ {35 : "SERCOM5_GCLK_ID_CORE"},
+ {36 : "SERCOM6_GCLK_ID_CORE"},
+ {37 : "SERCOM7_GCLK_ID_CORE"},
+ {38 : "TCC4_GCLK_ID"},
+ {39 : "TC6_GCLK_ID"},
+ {39 : "TC7_GCLK_ID"},
+ {40 : "ADC0_GCLK_ID"},
+ {41 : "ADC1_GCLK_ID"},
+ {42 : "DAC_GCLK_ID"},
+ {43 : "I2S_GCLK_ID_0"},
+ {43 : "I2S_GCLK_ID_LSB"},
+ {44 : "I2S_GCLK_ID_1"},
+ {44 : "I2S_GCLK_ID_MSB"},
+ {45 : "SDHC0_GCLK_ID"},
+ {46 : "SDHC1_GCLK_ID"},
+ ]
+ self.peripheral_ids = defaultdict(list)
+ for e in self._peripheral_ids:
+ for k,v in e.items():
+ self.peripheral_ids[k].append(v)
+
+ def read_register(self, address):
+ val = gdb.parse_and_eval(f"*(uint32_t *)({address})")
+ return int(val)
+
+ def read_register_field(self, reg_val, mask, shift):
+ return (reg_val & mask) >> shift
+
+ def get_clock_source_name(self, source_id):
+ for name, id in self.clock_sources.items():
+ if id == source_id:
+ return name
+ return f"UNKNOWN_SOURCE({source_id})"
+
+ def get_xosc_frequency(self, xosc_num):
+ """Get frequency of external oscillator"""
+ xosc_ctrl = self.read_register(self.OSCCTRL_BASE + 0x14 + (xosc_num * 4))
+ if self.read_register_field(xosc_ctrl, 0x2, 1) == 1: # Enabled?#
+ if self.read_register_field(xosc_ctrl, 0x4, 2) == 1: # External crystal
+ freq_range = self.read_register_field(xosc_ctrl, 0x00f00000, 20)
+ if freq_range == 0:
+ return "0.4 to 2 MHz (external crystal)"
+ elif freq_range == 1:
+ return "2 to 4 MHz (external crystal)"
+ elif freq_range == 2:
+ return "4 to 8 MHz (external crystal)"
+ elif freq_range == 3:
+ return "8 to 16 MHz (external crystal)"
+ elif freq_range == 4:
+ return "16 to 32 MHz (external crystal)"
+ else:
+ return "External clock (check configuration)"
+ else: # External clock
+ return "External clock (check configuration)"
+ return f"Disabled {xosc_ctrl:032b}"
+
+ def get_xosc32k_frequency(self):
+ """Get frequency of 32kHz internal oscillator"""
+ osc32k_ctrl = self.read_register(self.OSC32KCTRL_BASE + 0x14)
+ if osc32k_ctrl & 0x02: # Enabled?
+ return "32.768 kHz"
+ return f"Disabled {osc32k_ctrl:b}"
+
+ def get_osculp32k_frequency(self):
+ """Get frequency of ultra low power 32kHz oscillator"""
+ osculp32k_ctrl = self.read_register(self.OSC32KCTRL_BASE + 0x1C)
+ en32k = self.read_register_field(osculp32k_ctrl, 0x02, 1)
+ en1k = self.read_register_field(osculp32k_ctrl, 0x04, 2)
+
+ retstr = f"Disabled"
+ if en32k == 1:
+ retstr = "32.768 kHz (Ultra Low Power)"
+ if en1k == 1:
+ retstr += "+ 1 kHz (Ultra Low Power)"
+ retstr += f" {osculp32k_ctrl:b}"
+ return retstr
+
+ def get_dfll_frequency(self):
+ """Get DFLL frequency and configuration"""
+ dfll_ctrl = self.read_register(self.OSCCTRL_BASE + 0x1c)
+ if dfll_ctrl & 0x02: # Enabled?
+ dfll_config = self.read_register(self.OSCCTRL_BASE + 0x20)
+
+ if dfll_config & 0x1: # Closed loop
+ pchctrl = self.read_register(self.GCLK_BASE + 0x80) # PCHCTRL[0] for DFLL
+ gclk_id = pchctrl & 0xf # self.read_register_field(pchctrl, 0x3e, 1)
+
+ mult = self.read_register(self.OSCCTRL_BASE + 0x28) & 0xfff
+
+ # This is simplified - would need to trace through GCLK configuration
+ return f"Closed loop mode, Ref: GCLK{gclk_id}, Mult: {mult} (~48 MHz)"
+ else:
+ return "Open loop mode (~48 MHz)" + (" USBCRM" if dfll_config & 0x8 else "")
+ return f"Disabled {dfll_ctrl:032b}"
+
+ def get_dpll_frequency(self, dpll_num):
+ """Get DPLL frequency and configuration"""
+ dpll_base = self.OSCCTRL_BASE + 0x30 + (dpll_num * 0x14)
+
+ dpll_ctrl_a = self.read_register(dpll_base)
+ if dpll_ctrl_a & 0x02: # Enabled?
+ dpll_status = self.read_register(dpll_base + 0x10)
+ # if dpll_status & 0x01: # Locked?
+ dpll_ctrl_b = self.read_register(dpll_base + 0x08)
+ refclk = self.read_register_field(dpll_ctrl_b, 0xe0, 5)
+
+ ref_src = ""
+ if refclk == 0:
+ ref_src = "GCLK"
+ elif refclk == 1:
+ ref_src = "XOSC32K"
+ elif refclk == 2:
+ ref_src = "XOSC0"
+ elif refclk == 3:
+ ref_src = "XOSC1"
+
+ ratio = self.read_register(dpll_base + 0x04)
+ ldr = self.read_register_field(ratio, 0x1FFF, 0)
+ ldrfrac = self.read_register_field(ratio, 0xF000, 16)
+
+ # Calculate actual ratio (simplified)
+ actual_ratio = ldr + (ldrfrac / 16.0)
+
+ refdiv = 2 * (self.read_register_field(dpll_ctrl_b, 0xffff0000, 16) +1)
+
+ rets = f"{ "Locked" if dpll_status & 0x1 else "NOT locked"}, Ref: {ref_src}, Ref div: {refdiv}, Ratio: {actual_ratio:.2f}"
+
+ if refclk == 3:
+ rets += f" -> op freq {self.known_xosc1_freq/refdiv * (actual_ratio+1)}"
+
+ return rets
+
+ return f"Disabled {dpll_ctrl_a:032b}"
+
+ def get_clock_frequency(self, source_id):
+ """Get frequency of a clock source"""
+ if source_id == self.clock_sources["XOSC0"]:
+ return self.get_xosc_frequency(0)
+
+ elif source_id == self.clock_sources["XOSC1"]:
+ return self.get_xosc_frequency(1)
+
+ elif source_id == self.clock_sources["XOSC32K"]:
+ return self.get_xosc32k_frequency()
+
+ elif source_id == self.clock_sources["OSCULP32K"]:
+ return self.get_osculp32k_frequency()
+
+ elif source_id == self.clock_sources["DFLL"]:
+ return self.get_dfll_frequency()
+
+ elif source_id == self.clock_sources["DPLL0"]:
+ return self.get_dpll_frequency(0)
+
+ elif source_id == self.clock_sources["DPLL1"]:
+ return self.get_dpll_frequency(1)
+
+ elif source_id == self.clock_sources["GCLK_GEN1"]:
+ # This would be circular, so just indicate it's from another GCLK
+ return "From GCLK Generator 1"
+
+ return "Unknown"
+
+ def print_generic_clock(self, gclk_num):
+ # Each GCLKn has its own GENCTRL register
+ genctrl_addr = self.GCLK_BASE + 0x20 + (gclk_num * 0x04)
+ genctrl = self.read_register(genctrl_addr)
+
+ enabled = genctrl & (0x1 << 8)
+ if not enabled:
+ print(f" GCLK{gclk_num}: Disabled")
+ return
+
+ source_id = self.read_register_field(genctrl, 0x0F, 0)
+ source_name = self.get_clock_source_name(source_id)
+
+ div_val = self.read_register_field(genctrl, 0xFFFF, 16)
+ div_mode = self.read_register_field(genctrl, (0x1 << 12), 8)
+
+ if div_mode: # 50/50 duty cycle divider
+ actual_div = div_val * 2
+ else:
+ actual_div = div_val
+
+ if actual_div == 0 or actual_div == 1:
+ divider_str = "No division"
+ else:
+ divider_str = f"Divide by {actual_div}"
+
+ print(f" GCLK{gclk_num}: Enabled, Source: {source_name}, {divider_str}")
+
+ # Print peripherals using this GCLK
+ print(f" Peripherals using GCLK{gclk_num}:")
+ peripherals_found = False
+ for periph_id in self.peripheral_ids.keys():
+ pchctrl_addr = self.GCLK_BASE + 0x80 + (periph_id * 0x04)
+ pchctrl = self.read_register(pchctrl_addr)
+
+ enabled = pchctrl & (0x1 << 6)
+ if not enabled:
+ continue
+
+ gen = self.read_register_field(pchctrl, 0x0f, 0)
+ if gen == gclk_num:
+ print(f" - {'\n '.join(self.peripheral_ids[periph_id])}")
+ peripherals_found = True
+
+ if not peripherals_found:
+ print(" None")
+
+ def print_main_clocks(self):
+ """Print main clock configuration (CPU, AHB, APBx)"""
+ hsdiv = self.read_register(self.MCLK_BASE + 0x04)
+ cpudiv = self.read_register(self.MCLK_BASE + 0x05)
+ div = cpudiv & 0xFF
+ if div == 0 or div == 1:
+ cpu_div = "No division"
+ else:
+ cpu_div = f"Divide by {div}"
+
+ print(f" CPU Clock: {cpu_div}")
+ print(f" HS div: {hsdiv & 0xFF}")
+
+ bridge_names = ['H', 'A', 'B', 'C', 'D']
+
+ for i, bridge in enumerate(bridge_names):
+ mask = self.read_register(self.MCLK_BASE + 0x10 + (i * 4))
+ print(f" APB{bridge} Bridge: Mask 0x{mask:08X}")
+
+ if mask != 0:
+ print(f" Enabled peripherals:")
+
+ # grep -rh ".*define.*MCLK_A.*MASK.*Pos" 2>/dev/null | grep -v _U_ | tr -s ' ' |sort -Vk3 | uniq | sort -k1.8,1.9 | sed -E 's/^#define ([^ ]+)[[:space:]]+([0-9]+).*/\2 : "\1"/p' | sed -s 's/_Pos//g' | uniq | sort -k2.11,2.13 -Vk1
+ if bridge == 'H':
+ apb_peripherals = {
+ 0 : "MCLK_AHBMASK_HPB0",
+ 1 : "MCLK_AHBMASK_HPB1",
+ 2 : "MCLK_AHBMASK_HPB2",
+ 3 : "MCLK_AHBMASK_HPB3",
+ 4 : "MCLK_AHBMASK_DSU",
+ 5 : "MCLK_AHBMASK_HMATRIX",
+ 6 : "MCLK_AHBMASK_NVMCTRL",
+ 7 : "MCLK_AHBMASK_HSRAM",
+ 8 : "MCLK_AHBMASK_CMCC",
+ 9 : "MCLK_AHBMASK_DMAC",
+ 10 : "MCLK_AHBMASK_USB",
+ 11 : "MCLK_AHBMASK_BKUPRAM",
+ 12 : "MCLK_AHBMASK_PAC",
+ 13 : "MCLK_AHBMASK_QSPI",
+ 14 : "MCLK_AHBMASK_GMAC",
+ 15 : "MCLK_AHBMASK_SDHC0",
+ 16 : "MCLK_AHBMASK_SDHC1",
+ 17 : "MCLK_AHBMASK_CAN0",
+ 18 : "MCLK_AHBMASK_CAN1",
+ 19 : "MCLK_AHBMASK_ICM",
+ 20 : "MCLK_AHBMASK_PUKCC",
+ 21 : "MCLK_AHBMASK_QSPI_2X",
+ 22 : "MCLK_AHBMASK_NVMCTRL_SMEEPROM",
+ 23 : "MCLK_AHBMASK_NVMCTRL_CACHE",
+ }
+ elif bridge == 'A':
+ apb_peripherals = {
+ 0 : "MCLK_APBAMASK_PAC",
+ 1 : "MCLK_APBAMASK_PM",
+ 2 : "MCLK_APBAMASK_MCLK",
+ 3 : "MCLK_APBAMASK_RSTC",
+ 4 : "MCLK_APBAMASK_OSCCTRL",
+ 5 : "MCLK_APBAMASK_OSC32KCTRL",
+ 6 : "MCLK_APBAMASK_SUPC",
+ 7 : "MCLK_APBAMASK_GCLK",
+ 8 : "MCLK_APBAMASK_WDT",
+ 9 : "MCLK_APBAMASK_RTC",
+ 10 : "MCLK_APBAMASK_EIC",
+ 11 : "MCLK_APBAMASK_FREQM",
+ 12 : "MCLK_APBAMASK_SERCOM0",
+ 13 : "MCLK_APBAMASK_SERCOM1",
+ 14 : "MCLK_APBAMASK_TC0",
+ 15 : "MCLK_APBAMASK_TC1",
+ }
+ elif bridge == 'B':
+ apb_peripherals = {
+ 0 : "MCLK_APBBMASK_USB",
+ 1 : "MCLK_APBBMASK_DSU",
+ 2 : "MCLK_APBBMASK_NVMCTRL",
+ 4 : "MCLK_APBBMASK_PORT",
+ 6 : "MCLK_APBBMASK_HMATRIX",
+ 7 : "MCLK_APBBMASK_EVSYS",
+ 9 : "MCLK_APBBMASK_SERCOM2",
+ 10 : "MCLK_APBBMASK_SERCOM3",
+ 11 : "MCLK_APBBMASK_TCC0",
+ 12 : "MCLK_APBBMASK_TCC1",
+ 13 : "MCLK_APBBMASK_TC2",
+ 14 : "MCLK_APBBMASK_TC3",
+ 16 : "MCLK_APBBMASK_RAMECC",
+ }
+ elif bridge == 'C':
+ apb_peripherals = {
+ 2 : "MCLK_APBCMASK_GMAC",
+ 3 : "MCLK_APBCMASK_TCC2",
+ 4 : "MCLK_APBCMASK_TCC3",
+ 5 : "MCLK_APBCMASK_TC4",
+ 6 : "MCLK_APBCMASK_TC5",
+ 7 : "MCLK_APBCMASK_PDEC",
+ 8 : "MCLK_APBCMASK_AC",
+ 9 : "MCLK_APBCMASK_AES",
+ 10 : "MCLK_APBCMASK_TRNG",
+ 11 : "MCLK_APBCMASK_ICM",
+ 13 : "MCLK_APBCMASK_QSPI",
+ 14 : "MCLK_APBCMASK_CCL",
+ }
+ elif bridge == 'D':
+ apb_peripherals = {
+ 0 : "MCLK_APBDMASK_SERCOM4",
+ 1 : "MCLK_APBDMASK_SERCOM5",
+ 2 : "MCLK_APBDMASK_SERCOM6",
+ 3 : "MCLK_APBDMASK_SERCOM7",
+ 4 : "MCLK_APBDMASK_TCC4",
+ 5 : "MCLK_APBDMASK_TC6",
+ 6 : "MCLK_APBDMASK_TC7",
+ 7 : "MCLK_APBDMASK_ADC0",
+ 8 : "MCLK_APBDMASK_ADC1",
+ 9 : "MCLK_APBDMASK_DAC",
+ 10 : "MCLK_APBDMASK_I2S",
+ 11 : "MCLK_APBDMASK_PCC",
+ }
+ else:
+ apb_peripherals = {}
+
+ # Check each bit in the mask
+ for bit in range(32):
+ if mask & (1 << bit):
+ periph_name = apb_peripherals.get(bit, f"Unknown({bit})")
+ print(f" - {periph_name}")
+
+ def print_clock_tree(self):
+ print("SAME54 Clock Tree")
+ print("=================")
+
+ print("\n Clock Sources:")
+ print("----------------")
+ for name, source_id in self.clock_sources.items():
+ freq = self.get_clock_frequency(source_id)
+ print(f" {name}: {freq}")
+
+ print("\n Generic Clocks:")
+ print("----------------")
+ for gclk_num in self.generic_clocks:
+ self.print_generic_clock(gclk_num)
+
+ print("\n Main Clock Buses:")
+ print("------------------")
+ self.print_main_clocks()
+
+ def invoke(self, arg, from_tty):
+ """GDB command invocation"""
+ self.print_clock_tree()
+
+
+
+ClockTreePrinter()
\ No newline at end of file
diff --git a/contrib/gdb_msgb.py b/contrib/gdb_msgb.py
new file mode 100644
index 0000000..4783933
--- /dev/null
+++ b/contrib/gdb_msgb.py
@@ -0,0 +1,67 @@
+import gdb
+
+class MsgbListPrinter(gdb.Command):
+ """Prints all msgb entries in a libosmocore linked list"""
+
+ def __init__(self):
+ super(MsgbListPrinter, self).__init__("print-msgb-list", gdb.COMMAND_USER)
+
+ # prettier full fledged: https://raw.githubusercontent.com/hotsphink/sfink-tools/master/conf/gdbinit.pahole.py
+ def container_of(self, ptr, typename, member):
+ ty = gdb.lookup_type(typename)
+ # first matching field
+ bofs = [f.bitpos//8 for f in ty.strip_typedefs().fields() if f.name == member][0]
+ container_addr = ptr.cast(gdb.lookup_type('unsigned long')) - int(bofs)
+ return container_addr.cast(ty.pointer())
+
+
+ def invoke(self, args, from_tty):
+ try:
+ if not args:
+ # print("Usage: print-msgb-list list_head")
+ # return
+ heads_names = [[f"g_ccid_s.{epn}.list"] for epn in ["in_ep", "irq_ep", "out_ep"]] # f"*(struct msgb*)g_ccid_s.{epn}.in_progress"
+ heads_names = [nested_item for item in heads_names for nested_item in item] + [f"g_ccid_s.free_q"]
+ heads = [gdb.parse_and_eval(i) for i in heads_names]
+
+ else:
+ heads_names = [args]
+ heads = [gdb.parse_and_eval(args)]
+
+ # print(heads)
+
+ for hn, list_head in zip(heads_names, heads):
+ print(hn)
+ # list_head = gdb.parse_and_eval(args)
+ if not list_head.address:
+ continue
+ # print(list_head)
+ try:
+ list_head = list_head["list"]
+ current = list_head
+ except:
+ current = list_head['next']
+
+ head_addr = list_head.address
+ count = 0
+ # print(current, list_head)
+
+ while current != head_addr:
+ msgb = self.container_of(current, "struct msgb", "list")
+ print(f"msgb[{int(count):02}] at {int(current):08x}: <-{int(msgb["list"]["prev"]):08x} {int(msgb["list"]["next"]):08x}-> len: {int(msgb['len'])}")
+ # print(f" data: {msgb['data']}")
+ # print(f" head: {msgb['head']}")
+ # print(f" tail: {msgb['tail']}")
+
+ current = msgb['list']['next']
+ count += 1
+ if current == 0x00100100 or current == 0x00200200: # poison values
+ break
+
+ print(f"\nTotal msgb entries: {count}" if count else "empty!")
+
+ except gdb.error as e:
+ print(f"Error: {str(e)}")
+
+MsgbListPrinter()
+
diff --git a/contrib/gdb_sercom.py b/contrib/gdb_sercom.py
new file mode 100644
index 0000000..fe4e7c9
--- /dev/null
+++ b/contrib/gdb_sercom.py
@@ -0,0 +1,175 @@
+import gdb
+
+
+##grep -A15 name\>SERCOM ATSAME54N19A.svd | grep base
+# <baseAddress>0x40003000</baseAddress>
+# <baseAddress>0x40003400</baseAddress>
+# <baseAddress>0x41012000</baseAddress>
+# <baseAddress>0x41014000</baseAddress>
+# <baseAddress>0x43000000</baseAddress>
+# <baseAddress>0x43000400</baseAddress>
+# <baseAddress>0x43000800</baseAddress>
+# <baseAddress>0x43000C00</baseAddress>
+# <baseAddress>0x40001800</baseAddress>
+
+
+def gdbdl(addr):
+ plong=gdb.lookup_type('long').pointer()
+ val=gdb.Value(addr).cast(plong).dereference()
+ return int(val)&0xffffffff
+
+
+sercbases = [
+0x40003000,
+0x40003400,
+0x41012000,
+0x41014000,
+0x43000000,
+0x43000400,
+0x43000800,
+0x43000C00,
+0x40001800]
+
+USB_EPS=gdb.parse_and_eval (f'usb_d_inst.ep')
+NUM_EPS= USB_EPS.type.sizeof // USB_EPS.type.target().sizeof
+
+# 5 #### {
+# xfer = {
+# hdr = {
+# type = 0 '\000',
+# ep = 255 '\377',
+# state = 0 '\000',
+# status = 0 '\000'
+# },
+
+for iusb in range(0,NUM_EPS):
+ iusbd = gdb.parse_and_eval (f'usb_d_inst.ep[{iusb}]');
+ state = iusbd['xfer']['hdr']['state'].cast(gdb.lookup_type("enum usb_ep_state"))
+ status = iusbd['xfer']['hdr']['status'].cast(gdb.lookup_type("enum usb_xfer_code"))
+ print(iusb, "####", iusbd)
+ print(state)
+ print(status)
+
+def print_usblists():
+ def _print_usblists(structptr):
+ print(structptr)
+ current = gdb.parse_and_eval(structptr)
+ while current != 0:
+ print(current)
+ current = current['next']
+
+ _print_usblists("(struct usbdf_driver *)usbdc.func_list.head")
+ _print_usblists("(struct usbdc_req_handler *)usbdc.handlers.req_list.head")
+ _print_usblists("(struct usbdc_change_handler *)usbdc.handlers.change_list.head")
+ _print_usblists("(struct usbdc_sof_handler *)usbdc.handlers.sof_list.head")
+print_usblists()
+
+
+for i in range(0,7):
+ # >>> py print(gdb.parse_and_eval("((Sercom *)0x40003000)->USART.CTRLA.bit").type.fields()[0].name)
+ #SWRST
+ uartix = gdb.parse_and_eval (f"g_si.slot[{i}].cuart.u.asf4.usa_pd.device.hw")
+ u = gdb.parse_and_eval(f"((Sercom *){uartix})->USART")
+
+ for screg in u.type.fields():
+ if 'reserved' in screg.name.lower():
+ continue
+ scregf = [x.name for x in u[str(screg.name)].type.fields()]
+ # print(screg.name, scregf)
+ if 'bit' in scregf:
+ ctral_f = [fn.name for fn in u[str(screg.name)]["bit"].type.fields()]
+ # print(ctral_f)
+ vals = {x: u[screg.name]["bit"][x] for x in ctral_f}
+ print(uartix, ' '.join([f"{k}: {int(v)}" for k,v in vals.items()]))
+ # ctral_f = [fn.name for fn in u[str(screg.name)]["bit"].type.fields()]
+ # vals = {x: str(u[screg.name]["bit"][x]) for x in ctral_f}
+ # print(' '.join([f"{k}: {v}" for k,v in vals.items()]))
+
+
+for i in range(0,7):
+ uslotdata = gdb.parse_and_eval (f"*g_si.slot[{i}].cuart")
+ print("###################", uslotdata)
+
+
+def _get_enum_flen(ty, v):
+ gdbty = gdb.lookup_type(ty)
+ maxl = len(max(gdb.types.make_enum_dict(gdbty).keys()))
+ return str(v.cast(gdbty)).rjust(20)
+
+# iso7816_3_state_len = "{max(gdb.types.make_enum_dict(gdb.lookup_type("enum iso7816_3_state")).keys())
+# atr_state_len = max(gdb.types.make_enum_dict(gdb.lookup_type("enum atr_state")).keys())
+# tpdu_state_len = max(gdb.types.make_enum_dict(gdb.lookup_type("enum tpdu_state")).keys())
+# pps_state_len = max(gdb.types.make_enum_dict(gdb.lookup_type("enum pps_state")).keys())
+
+# d = gdb.parse_and_eval (f'(enum iso7816_3_state)g_si.slot[{i}].fi.state')
+# a = gdb.parse_and_eval (f'(enum atr_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).atr_fi.state')
+# b = gdb.parse_and_eval (f'(enum tpdu_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).tpdu_fi.state' )
+# c = gdb.parse_and_eval (f'(enum pps_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).pps_fi.state')
+
+for i in range(0,7):
+ # slot = gdb.execute ('p g_ci.slot[%i]' % (i) , False, True)
+ # a = gdb.execute ('p (enum atr_state)((struct iso7816_3_priv*)g_si.slot[%i].fi.priv).atr_fi.state' % (i) , False, True).split('\n')[-2]#.rpartition('\n')
+ # b = gdb.execute ('p (enum tpdu_state)((struct iso7816_3_priv*)g_si.slot[%i].fi.priv).tpdu_fi.state' % (i) , False, True).split('\n')[-2]#.rpartition('\n')
+ # c = gdb.execute ('p (enum pps_state)((struct iso7816_3_priv*)g_si.slot[%i].fi.priv).pps_fi.state' % (i) , False, True).split('\n')[-2]#.rpartition('\n')
+ # d = gdb.execute ('p (enum iso7816_3_state)g_si.slot[%i].fi.state' % (i) , False, True).split('\n')[-2]#.rpartition('\n')
+
+ uslotdata = gdb.parse_and_eval (f"g_si.slot[{i}].cuart.u.asf4.usa_pd.device.hw")
+ uslotdata_str = (f"data_reg: {hex(gdbdl(uslotdata+0x28) & 0xffffffff)}")
+
+
+ g_slot = gdb.parse_and_eval(f'g_si.slot[{i}]')
+
+ # slot = gdb.parse_and_eval(f'g_ci.slot[{i}]')
+ # d = gdb.parse_and_eval (f'(enum iso7816_3_state)g_si.slot[{i}].fi.state')
+ # a = gdb.parse_and_eval (f'(enum atr_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).atr_fi.state')
+ # b = gdb.parse_and_eval (f'(enum tpdu_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).tpdu_fi.state' )
+ # c = gdb.parse_and_eval (f'(enum pps_state)((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).pps_fi.state')
+
+
+ d = gdb.parse_and_eval (f'g_si.slot[{i}].fi')
+ a = gdb.parse_and_eval (f'((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).atr_fi')
+ b = gdb.parse_and_eval (f'((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).tpdu_fi')
+ c = gdb.parse_and_eval (f'((struct iso7816_3_priv*)g_si.slot[{i}].fi.priv).pps_fi')
+
+ # sloevent = gdb.parse_and_eval (f'g_si.slot[{i}].cs.event')
+ # sloeventdata = gdb.parse_and_eval (f'g_si.slot[{i}].cs.event_data')
+
+ print(
+ i,
+ f"seq: {int(g_slot["seq"]):3d}",
+ _get_enum_flen("enum iso7816_3_state", d['state']),
+ _get_enum_flen("enum atr_state", a['state']),
+ _get_enum_flen("enum tpdu_state", b['state']),
+ _get_enum_flen("enum pps_state", c['state']),
+ uslotdata_str,
+ f"ev: {g_slot['cs']['event']} evdata: {g_slot['cs']['event_data']} ", end='', sep=None)
+
+
+ # for i in sercbases:
+ # print(f"data{i} {hex(gdbdl(i+0x28) & 0xffffffff)}")
+
+ # gdb.execute ('x/20b (&SIM%d.rx->buf[SIM%d.rx->write_index & SIM%d.rx->size])-20' % (i,i,i) , True)
+
+ xx = gdb.parse_and_eval ('g_si.slot[%i].cuart.u.asf4.usa_pd.tx_por' % (i))
+ xy = gdb.parse_and_eval ('g_si.slot[%i].cuart.u.asf4.usa_pd.tx_buffer_length' % (i))
+ idx = int(xx)
+ idx2 = int(xy)
+ nval = None
+ bval = None
+
+ if idx != idx2:
+ nval = int(gdb.parse_and_eval ('g_si.slot[%i].cuart.u.asf4.usa_pd.tx_buffer[%i]' % (i, idx)))
+
+ if idx > 0:
+ bval = int(gdb.parse_and_eval ('g_si.slot[%i].cuart.u.asf4.usa_pd.tx_buffer[%i]' % (i, idx-1)))
+
+ #print idx, idx2, bval
+ # print(f"{i}: ", end='', sep=None)
+ if nval is not None and bval is not None:
+ print("last tx:0x" + "{:02x}".format(bval) + " next tx:0x" + "{:02x}".format(nval))
+ elif nval is None and bval is not None:
+ print("last tx:0x" + "{:02x}".format(bval) + " next tx:none" )
+ elif nval is not None and bval is None:
+ print("last tx:none next tx:0x" + "{:02x}".format(nval))
+ else:
+ print(f"wat?! {nval} {bval} {idx} {idx2}")
diff --git a/contrib/gdb_usb.py b/contrib/gdb_usb.py
new file mode 100644
index 0000000..518c3b2
--- /dev/null
+++ b/contrib/gdb_usb.py
@@ -0,0 +1,106 @@
+import gdb
+
+
+EP_N_MASK = 0x0F
+EP_DIR_MASK = 0x80
+USB = 0x41000000
+CONF_USB_D_MAX_EP_N = 4 # !!
+
+eptstr = {
+ 0: "USB_D_EPTYPE_DISABLE",
+ 1: "USB_D_EPTYPE_CTRL",
+ 2: "USB_D_EPTYPE_ISOCH",
+ 3: "USB_D_EPTYPE_INT",
+ 4: "USB_D_EPTYPE_BULK",
+ 5: "USB_D_EPTYPE_DUAL",
+ 0x11: "USB_D_EPCFG_CTRL",
+}
+
+# bmAttributes
+# define USB_EP_XTYPE_CTRL 0x0
+# define USB_EP_XTYPE_ISOCH 0x1
+# define USB_EP_XTYPE_BULK 0x2
+# define USB_EP_XTYPE_INTERRUPT 0x3
+# define USB_EP_XTYPE_MASK 0x3u
+
+
+hwepty1 = {
+ 0x0: "Bank1 is disabled.",
+ 0x1: "Bank1 is enabled and configured as Control IN.",
+ 0x2: "Bank1 is enabled and configured as Isochronous IN.",
+ 0x3: "Bank1 is enabled and configured as Bulk IN.",
+ 0x4: "Bank1 is enabled and configured as Interrupt IN.",
+ 0x5: "Bank1 is enabled and configured as Dual-Bank OUT (Endpoint type is the same as the one defined in EPTYPE0)", }
+# 0x6-0x7 Reserved
+def USB_DEVICE_EPCFG_EPTYPE1(v): return (v >> 4) & 0x7
+
+
+hwepty0 = {
+ 0x0: "Bank0 is disabled.",
+ 0x1: "Bank0 is enabled and configured as Control SETUP / Control OUT.",
+ 0x2: "Bank0 is enabled and configured as Isochronous OUT.",
+ 0x3: "Bank0 is enabled and configured as Bulk OUT.",
+ 0x4: "Bank0 is enabled and configured as Interrupt OUT.",
+ 0x5: "Bank0 is enabled and configured as Dual Bank IN (Endpoint type is the same as the one defined in EPTYPE1)", }
+# 0x6-0x7 Reserved
+def USB_DEVICE_EPCFG_EPTYPE0(v): return v & 0x7
+
+
+def get_epdir(ep):
+ return ep & EP_DIR_MASK
+
+
+def get_epn(ep):
+ return ep & EP_N_MASK
+
+
+def _usb_d_dev_ept(epn, dir):
+ if (epn == 0):
+ ep_index = 0
+ else:
+ if dir:
+ ep_index = (epn + CONF_USB_D_MAX_EP_N)
+ else:
+ ep_index = epn
+ return gdb.parse_and_eval(f'dev_inst.ep[{ep_index}]')
+
+
+def get_epcfg_typ(n, v):
+ if n == 0:
+ return v & 0x7
+ if n == 1:
+ return (v >> 4) & 0x7
+
+
+def hri_usbendpoint_read_EPCFG_reg(epn):
+ addr = USB+0x100+epn*0x20
+ epcfg = gdb.parse_and_eval(f'*{addr}')
+ return epcfg
+
+
+CCID_USB_EPS = gdb.parse_and_eval(f'_ccid_df_funcd')
+# print(USB_EPS["func_iface"])
+for ept in ["func_ep_in", "func_ep_out", "func_ep_irq"]:
+ cept = int(CCID_USB_EPS[ept])
+ dir = 1 if get_epdir(cept) else 0
+ epn = get_epn(cept)
+ softep = gdb.parse_and_eval(f"*_usb_d_dev_ept({epn},{dir})") # _usb_d_dev_ept(epn, dir)
+ # print(epn, dir)
+ # gdb.execute(f"p *_usb_d_dev_ept({epn},{dir})")
+ epcfg = hri_usbendpoint_read_EPCFG_reg(epn)
+ eptype = softep["flags"]["bits"]["eptype"]
+ t0 = int(USB_DEVICE_EPCFG_EPTYPE0(epcfg))
+ t1 = int(USB_DEVICE_EPCFG_EPTYPE1(epcfg))
+
+ print(f"epn:{epn} {ept}:\tccid/soft:{int(cept):02x}:{int(softep["ep"]):02x}\tdir:{int(dir):02x} n:{int(epn):02x}",
+ epcfg, eptstr[int(eptype)], hwepty0[t0], "\t", hwepty1[t1])
+
+USB_EPS = gdb.parse_and_eval(f'usb_d_inst.ep')
+NUM_EPS = USB_EPS.type.sizeof // USB_EPS.type.target().sizeof
+for iusb in range(0, NUM_EPS):
+ iusbd = gdb.parse_and_eval(f'usb_d_inst.ep[{iusb}]')
+ state = iusbd['xfer']['hdr']['state'].cast(gdb.lookup_type("enum usb_ep_state"))
+ status = iusbd['xfer']['hdr']['status'].cast(gdb.lookup_type("enum usb_xfer_code"))
+ print(iusb, "####", iusbd)
+ print(state)
+ print(status)
diff --git a/sysmoOCTSIM/hpl/usb/hpl_usb.c b/sysmoOCTSIM/hpl/usb/hpl_usb.c
index c7e2d33..ed9ef62 100644
--- a/sysmoOCTSIM/hpl/usb/hpl_usb.c
+++ b/sysmoOCTSIM/hpl/usb/hpl_usb.c
@@ -41,11 +41,6 @@
#include <utils_assert.h>
#include <hal_delay.h>

-/* save previous setup state to allow device reset when receving usb reset after the device
- * was previously properly configured, i.e. when powered externally and usb is disconnected and reconnected */
-volatile bool address_was_set = false;
-volatile bool delayed_usb_reset = false;
-
/**
* \brief Dummy callback function
* \return Always false.
@@ -981,11 +976,6 @@

_usb_d_dev_reset_epts();

- if (address_was_set == true) {
- address_was_set = 0;
- delayed_usb_reset = true;
- }
-
dev_inst.callbacks.event(USB_EV_RESET, 0);
}

@@ -1591,7 +1581,6 @@
void _usb_d_dev_set_address(uint8_t addr)
{
hri_usbdevice_write_DADD_reg(USB, USB_DEVICE_DADD_ADDEN | USB_DEVICE_DADD_DADD(addr));
- address_was_set = true;
}

uint8_t _usb_d_dev_get_address(void)
@@ -1732,9 +1721,10 @@
_usb_d_dev_trans_setup(ept);

} else if (dir) {
- if (epcfg & USB_DEVICE_EPCFG_EPTYPE1_Msk) {
- return -USB_ERR_REDO;
- }
+ /* prevents init->enable->disable->enable again from working without reinit...*/
+ // if (epcfg & USB_DEVICE_EPCFG_EPTYPE1_Msk) {
+ // return -USB_ERR_REDO;
+ // }
epcfg |= USB_DEVICE_EPCFG_EPTYPE1(ept->flags.bits.eptype);
hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);

@@ -1746,10 +1736,10 @@
_usbd_ep_clear_bank_status(epn, 1);

} else {
-
- if (epcfg & USB_DEVICE_EPCFG_EPTYPE0_Msk) {
- return -USB_ERR_REDO;
- }
+ /* prevents init->enable->disable->enable again from working without reinit...*/
+ // if (epcfg & USB_DEVICE_EPCFG_EPTYPE0_Msk) {
+ // return -USB_ERR_REDO;
+ // }
epcfg |= USB_DEVICE_EPCFG_EPTYPE0(ept->flags.bits.eptype);
hri_usbendpoint_write_EPCFG_reg(hw, epn, epcfg);

diff --git a/sysmoOCTSIM/main.c b/sysmoOCTSIM/main.c
index f4072d3..e386557 100644
--- a/sysmoOCTSIM/main.c
+++ b/sysmoOCTSIM/main.c
@@ -38,6 +38,12 @@

#include "ccid_device.h"
#include "usb_descriptors.h"
+
+static void bdg_bkptpanic(const char *fmt, va_list args)
+{
+ __asm("BKPT #0");
+}
+
extern struct ccid_slot_ops iso_fsm_slot_ops;
static struct ccid_instance g_ci;

@@ -129,6 +135,10 @@
{
struct msgb *msg;
CRITICAL_SECTION_ENTER()
+ OSMO_ASSERT(q->next != LLIST_POISON1)
+ OSMO_ASSERT(q->next->next != LLIST_POISON1)
+ OSMO_ASSERT(q->prev != LLIST_POISON2)
+ OSMO_ASSERT(q->prev->prev != LLIST_POISON2)
msg = msgb_dequeue(q);
CRITICAL_SECTION_LEAVE()
return msg;
@@ -138,6 +148,10 @@
{
CRITICAL_SECTION_ENTER()
msgb_enqueue(q, msg);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
CRITICAL_SECTION_LEAVE()
}

@@ -158,6 +172,10 @@
ep_q->in_progress = msg;
rc = ccid_df_write_in(msgb_data(msg), msgb_length(msg));
if (rc != ERR_NONE) {
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
printf("EP %s failed: %d\r\n", ep_q->name, rc);
return -1;
}
@@ -183,6 +201,10 @@
rc = ccid_df_write_irq(msgb_data(msg), msgb_length(msg));
/* may return HALTED/ERROR/DISABLED/BUSY/ERR_PARAM/ERR_FUNC/ERR_DENIED */
if (rc != ERR_NONE) {
+ // ep_q->in_progress = msg;
+ // msgb_enqueue_irqsafe(&ep_q->list, msg);
+ // OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ // OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
printf("EP %s failed: %d\r\n", ep_q->name, rc);
return -1;
}
@@ -195,7 +217,9 @@
struct msgb *msg;
int rc;

- OSMO_ASSERT(!ep_q->in_progress);
+ if (ep_q->in_progress)
+ return 0;
+
msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
if (!msg)
return -1;
@@ -206,6 +230,10 @@
if (rc != ERR_NONE) {
/* re-add to the list of free msgb's */
llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
return 0;
}
return 1;
@@ -216,12 +244,32 @@
{
struct msgb *msg = g_ccid_s.out_ep.in_progress;

+ /*
+ reset: resubmit from main loop when ready
+ unhalt: resubmit immediately
+ */
+ if (code == USB_XFER_RESET || code == USB_XFER_UNHALT || code == USB_XFER_HALT) {
+ if (msg) {
+ msgb_reset(msg);
+ llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+ g_ccid_s.out_ep.in_progress = NULL;
+ }
+ if (code == USB_XFER_UNHALT) {
+ submit_next_out();
+ }
+ return;
+ }
+
/* add just-received msg to tail of endpoint queue */
OSMO_ASSERT(msg);
/* update msgb with the amount of data received */
msgb_put(msg, transferred);
/* append to list of pending-to-be-handed messages */
llist_add_tail_at(&msg->list, &g_ccid_s.out_ep.list);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
g_ccid_s.out_ep.in_progress = NULL;

if(code != USB_XFER_DONE)
@@ -236,10 +284,18 @@
{
struct msgb *msg = g_ccid_s.in_ep.in_progress;

- OSMO_ASSERT(msg);
- /* return the message back to the queue of free message buffers */
- llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
- g_ccid_s.in_ep.in_progress = NULL;
+ if (msg) {
+ /* return the message back to the queue of free message buffers */
+ llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
+ g_ccid_s.in_ep.in_progress = NULL;
+ }
+
+ if (code == USB_XFER_UNHALT)
+ submit_next_in();

if(code != USB_XFER_DONE)
return;
@@ -253,10 +309,18 @@
{
struct msgb *msg = g_ccid_s.irq_ep.in_progress;

- OSMO_ASSERT(msg);
- /* return the message back to the queue of free message buffers */
- llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
- g_ccid_s.irq_ep.in_progress = NULL;
+ if (msg) {
+ /* return the message back to the queue of free message buffers */
+ llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
+ g_ccid_s.irq_ep.in_progress = NULL;
+ }
+
+ if (code == USB_XFER_UNHALT)
+ submit_next_irq();

if(code != USB_XFER_DONE)
return;
@@ -451,6 +515,10 @@

/* append to list of pending-to-be-handed messages */
llist_add_tail_at(&msg->list, &g_ccid_s.in_ep.list);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
submit_next_in();
return 0;
}
@@ -472,21 +540,83 @@

char rstcause_buf[RSTCAUSE_STR_SIZE];

-void do_usb_res(void)
+void reset_all_stuff_irq(void)
{
- hri_usbdevice_set_CTRLA_SWRST_bit(USB);
- uint32_t *application_start_address = (uint32_t *)(16384);
- __set_MSP(*application_start_address);
- __DSB();
- __ISB();
- asm("bx %0" ::"r"(*(application_start_address + 1)));
+ SysTick->CTRL = 0; // no clock ticks for osmo timers
+
+ Sercom *const sercom_modules[] = SERCOM_INSTS;
+ for (uint32_t i = 0; i < SERCOM_INST_NUM; i++) {
+ hri_sercomusart_clear_CTRLA_ENABLE_bit(sercom_modules[i]);
+ hri_sercomusart_clear_INTFLAG_reg(sercom_modules[i], 0xff);
+ }
}

-extern volatile bool delayed_usb_reset;
+extern volatile bool was_unconfigured_flag;
+void extern_fsm_reset_hack(struct ccid_slot *cs);
+void ccid_eps_enable(void);
+
+void reset_all_stuff_non_irq(void)
+{
+ if (!was_unconfigured_flag)
+ return;
+
+ CRITICAL_SECTION_ENTER()
+
+ for (int i = 0; i <= usb_fs_descs.ccid.class.bMaxSlotIndex; i++) {
+ g_ci.slot_ops->handle_fsm_events(&g_ci.slot[i], true);
+ }
+
+ for (int i = 0; i <= usb_fs_descs.ccid.class.bMaxSlotIndex; i++) {
+ // g_ci.slot_ops->icc_set_insertion_status(&g_ci.slot[i], false);
+ extern_fsm_reset_hack(&g_ci.slot[i]);
+ g_ci.slot_ops->handle_fsm_events(&g_ci.slot[i], true);
+ }
+
+ for (int i = 0; i <= usb_fs_descs.ccid.class.bMaxSlotIndex; i++) {
+ g_ci.slot_ops->handle_fsm_events(&g_ci.slot[i], true);
+ }
+
+ volatile struct usb_ep_q *all_epqs[] = { &g_ccid_s.in_ep, &g_ccid_s.irq_ep, &g_ccid_s.out_ep };
+ for (int i = 0; i < ARRAY_SIZE(all_epqs); i++) {
+ volatile struct usb_ep_q *cur_epq = all_epqs[i];
+ struct msgb *msg;
+ while ((msg = msgb_dequeue_irqsafe(&cur_epq->list))) {
+ msgb_reset(msg);
+ llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
+ }
+
+ // struct msgb *cur_msg = cur_epq->in_progress;
+ // if (cur_msg) {
+ // // if (i == 2 && usb_d_ep_get_status(/*_ccid_df_funcd.func_ep_out*/ 2, 0) == USB_BUSY)
+ // // continue;
+
+ // msgb_reset(cur_msg);
+ // llist_add_tail_at(&cur_msg->list, &g_ccid_s.free_q);
+ // OSMO_ASSERT(msg->list.next != LLIST_POISON1)
+ // OSMO_ASSERT(msg->list.next->next != LLIST_POISON1)
+ // OSMO_ASSERT(msg->list.prev != LLIST_POISON2)
+ // OSMO_ASSERT(msg->list.prev->prev != LLIST_POISON2)
+ // cur_epq->in_progress = NULL;
+ // }
+ }
+
+ SysTick->CTRL = 1;
+
+ // while (!ccid_df_is_enabled())
+ // ;
+ g_ccid_s.card_insert_mask = 0;
+ was_unconfigured_flag = false;
+ CRITICAL_SECTION_LEAVE()
+ ccid_eps_enable();
+}

int main(void)
{
-
+ osmo_set_panic_handler(&bdg_bkptpanic);
#if 0
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk ; //| /* tracing*/
////CoreDebug_DEMCR_MON_EN_Msk; /* mon interupt catcher */
@@ -566,9 +696,10 @@
struct msgb *msg = msgb_alloc(300, "ccid");
OSMO_ASSERT(msg);
/* return the message back to the queue of free message buffers */
+ OSMO_ASSERT(msg->list.next != LLIST_POISON1)
llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
}
- submit_next_out();
+ // submit_next_out();
CRITICAL_SECTION_LEAVE()
#if 0
/* CAN_RX */
@@ -584,8 +715,10 @@

// command_print_prompt();
while (true) { // main loop
- if (delayed_usb_reset)
- do_usb_res();
+ if (was_unconfigured_flag) {
+ reset_all_stuff_non_irq();
+ submit_next_out();
+ }
poll_extpower_detect();
command_try_recv();
poll_card_detect();
@@ -596,19 +729,18 @@
feed_ccid();
osmo_timers_update();
int qs = llist_count_at(&g_ccid_s.free_q);
- if(qs > NUM_OUT_BUF)
- for (int i= 0; i < qs-NUM_OUT_BUF; i++){
+ if (qs > NUM_OUT_BUF)
+ for (int i = 0; i < qs - NUM_OUT_BUF; i++) {
struct msgb *msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
if (msg)
- msgb_free(msg);
+ msgb_free(msg);
}
- if(qs < NUM_OUT_BUF)
- for (int i= 0; i < NUM_OUT_BUF-qs; i++){
- struct msgb *msg = msgb_alloc(300,"ccid");
+ if (qs < NUM_OUT_BUF)
+ for (int i = 0; i < NUM_OUT_BUF - qs; i++) {
+ struct msgb *msg = msgb_alloc(300, "ccid");
OSMO_ASSERT(msg);
/* return the message back to the queue of free message buffers */
llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
}
-
}
}
diff --git a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
index 2227a23..f9cae34 100644
--- a/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
+++ b/sysmoOCTSIM/usb/class/ccid/device/ccid_df.c
@@ -38,7 +38,7 @@
uint8_t func_ep_in; /*!< IN endpoint number */
uint8_t func_ep_out; /*!< OUT endpoint number */
uint8_t func_ep_irq; /*!< IRQ endpoint number */
- bool enabled; /*!< is this driver/function enabled? */
+ volatile bool enabled; /*!< is this driver/function enabled? */
const struct usb_ccid_class_descriptor *ccid_cd;
};

@@ -47,6 +47,21 @@

extern const struct usb_desc_collection usb_fs_descs;

+void ccid_eps_enable(void)
+{
+ while (!ccid_df_is_enabled())
+ ;
+
+ CRITICAL_SECTION_ENTER()
+ usb_d_ep_disable(_ccid_df_funcd.func_ep_in);
+ usb_d_ep_enable(_ccid_df_funcd.func_ep_in);
+ usb_d_ep_disable(_ccid_df_funcd.func_ep_irq);
+ usb_d_ep_enable(_ccid_df_funcd.func_ep_irq);
+ usb_d_ep_disable(_ccid_df_funcd.func_ep_out);
+ usb_d_ep_enable(_ccid_df_funcd.func_ep_out);
+ CRITICAL_SECTION_LEAVE()
+}
+
/* FIXME: make those configurable, ensure they're sized according to
* bNumClockSupported / bNumDataRatesSupported */
static uint32_t ccid_clock_frequencies[CCID_NUM_CLK_SUPPORTED] = { LE32(2500),LE32(5000),LE32(10000),LE32(20000) };
@@ -93,7 +108,10 @@
} else {
func_data->func_ep_out = ep_desc.bEndpointAddress;
}
+ /*
+ don't do this here: too early for reset, config 0, .. ccid not ready.
usb_d_ep_enable(ep_desc.bEndpointAddress);
+ */
desc->sod = ep;
ep = usb_find_ep_desc(usb_desc_next(desc->sod), desc->eod);
}
@@ -119,16 +137,16 @@

func_data->func_iface = 0xff;
if (func_data->func_ep_in != 0xff) {
- func_data->func_ep_in = 0xff;
usb_d_ep_deinit(func_data->func_ep_in);
+ func_data->func_ep_in = 0xff;
}
if (func_data->func_ep_out != 0xff) {
- func_data->func_ep_out = 0xff;
usb_d_ep_deinit(func_data->func_ep_out);
+ func_data->func_ep_out = 0xff;
}
if (func_data->func_ep_irq != 0xff) {
- func_data->func_ep_irq = 0xff;
usb_d_ep_deinit(func_data->func_ep_irq);
+ func_data->func_ep_irq = 0xff;
}

_ccid_df_funcd.enabled = false;
@@ -200,6 +218,39 @@
/* process a control endpoint request */
static int32_t ccid_df_ctrl_req(uint8_t ep, struct usb_req *req, enum usb_ctrl_stage stage)
{
+ /* ERR_NOT_FOUND defers to default handlers which do the right thing */
+ if (stage == USB_SETUP_STAGE) {
+ if ((req->bmRequestType & USB_REQT_RECIP_MASK) == USB_REQT_RECIP_ENDPOINT) {
+ int32_t st;
+ static uint8_t ctrl_buffer[8] = { 0 };
+ uint8_t epad = req->wIndex & 0xFF;
+ // if (!(_ccid_df_funcd.func_ep_in == epad || _ccid_df_funcd.func_ep_irq == epad || _ccid_df_funcd.func_ep_out == epad))
+ // return ERR_NOT_FOUND;
+
+ switch (req->bRequest) {
+ case USB_REQ_CLEAR_FTR:
+ // usb_d_ep_halt(epad, USB_EP_HALT_CLR);
+ // usbdc_xfer(ep, NULL, 0, true);
+ return ERR_NOT_FOUND;
+ case USB_REQ_SET_FTR:
+ // usb_d_ep_halt(epad, USB_EP_HALT_SET);
+ // usbdc_xfer(ep, NULL, 0, true);
+ return ERR_NOT_FOUND;
+ case USB_REQ_GET_STATUS:
+ // st = usb_d_ep_halt(epad, USB_EP_HALT_GET);
+ // if (st < 0) {
+ // return ERR_NONE;
+ // }
+ // st = st & 0x1;
+ // memcpy(ctrl_buffer, &st, 2);
+ // usbdc_xfer(ep, ctrl_buffer, 2, false);
+ return ERR_NOT_FOUND;
+ default:
+ return ERR_NOT_FOUND;
+ }
+ }
+ }
+
/* verify this is a class-specific request */
if (0x01 != ((req->bmRequestType >> 5) & 0x03))
return ERR_NOT_FOUND;
diff --git a/sysmoOCTSIM/usb/device/usbdc.c b/sysmoOCTSIM/usb/device/usbdc.c
index 4b84937..38aa0e0 100644
--- a/sysmoOCTSIM/usb/device/usbdc.c
+++ b/sysmoOCTSIM/usb/device/usbdc.c
@@ -36,6 +36,8 @@
#define USBDC_VERSION 0x00000001u

extern bool old_extpwer_state;
+volatile bool was_unconfigured_flag = false;
+extern void reset_all_stuff_irq(void);

/**
* \brief USB Device Core Sof Handler
@@ -450,6 +452,9 @@
*/
static void usbdc_unconfig(void)
{
+ reset_all_stuff_irq();
+ was_unconfigured_flag = true;
+
struct usbdf_driver *func = (struct usbdf_driver *)usbdc.func_list.head;
while (NULL != func) {
func->ctrl(func, USBDF_DISABLE, NULL);

To view, visit change 39759. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-MessageType: newchange
Gerrit-Project: osmo-ccid-firmware
Gerrit-Branch: master
Gerrit-Change-Id: Icadaee9d8cbe24bd3cac002cc4f710dcf846a32b
Gerrit-Change-Number: 39759
Gerrit-PatchSet: 1
Gerrit-Owner: Hoernchen <ewild@sysmocom.de>