add some struct doxygen (#3022)

* add some struct doxygen

* add 2 missing group briefs

* revert accidental btsack change

* fix glaring issues

* Fixups from my comments

* Update src/common/boot_picoboot_headers/include/boot/picoboot.h

* Update src/common/boot_picoboot_headers/include/boot/picoboot.h

---------

Co-authored-by: William Vinnicombe <william.vinnicombe@raspberrypi.com>
diff --git a/src/common/boot_picobin_headers/include/boot/picobin.h b/src/common/boot_picobin_headers/include/boot/picobin.h
index 946c78a..3d1b961 100644
--- a/src/common/boot_picobin_headers/include/boot/picobin.h
+++ b/src/common/boot_picobin_headers/include/boot/picobin.h
@@ -147,16 +147,26 @@
 #include <stdbool.h>
 #include <stdint.h>
 
+/*! \brief A single entry in a PICOBIN load map
+ *  \ingroup boot_picobin_headers
+ *
+ * Describes the mapping of one contiguous region from its storage location to its runtime address.
+ */
 typedef struct {
     // these must all be word aligned
-    uint32_t storage_address_rel;
-    uint32_t runtime_address;
-    uint32_t size;
+    uint32_t storage_address_rel; ///< Storage address of the region - if absolute then an absolute address, otherwise relative to the address of the LOAD_MAP item containing this entry
+    uint32_t runtime_address;     ///< Target runtime address to which the region is loaded
+    uint32_t size;                ///< If absolute then the end of the runtime address region, otherwise the size of the region in bytes
 } picobin_load_map_entry;
 
+/*! \brief PICOBIN load map describing regions to copy from storage to RAM
+ *  \ingroup boot_picobin_headers
+ *
+ * Contains a header word followed by a variable-length array of load map entries.
+ */
 typedef struct {
-    uint32_t header;
-    picobin_load_map_entry entries[];
+    uint32_t header;                  ///< Encodes the item type, item size, entry count, and whether addresses are absolute or relative
+    picobin_load_map_entry entries[]; ///< Array of load map entries
 } picobin_load_map;
 
 static inline unsigned int picobin_load_map_entry_count(const picobin_load_map *lm) {
diff --git a/src/common/boot_picoboot_headers/include/boot/picoboot.h b/src/common/boot_picoboot_headers/include/boot/picoboot.h
index a67e427..26f387c 100644
--- a/src/common/boot_picoboot_headers/include/boot/picoboot.h
+++ b/src/common/boot_picoboot_headers/include/boot/picoboot.h
@@ -82,30 +82,48 @@
     PICOBOOT_UNSUPPORTED_MODIFICATION = 17,
 };
 
+/*! \brief Parameters for a reboot command (RP2040 only)
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of a PC_REBOOT command.
+ */
 struct __packed picoboot_reboot_cmd {
-    uint32_t dPC; // 0 means reset into regular boot path
-    uint32_t dSP;
-    uint32_t dDelayMS;
+    uint32_t dPC; ///< Program counter to reboot to; 0 means reset into the regular boot path, otherwise must be a RAM address
+    uint32_t dSP; ///< Stack pointer value at reboot; ignored unless dPC is a RAM address
+    uint32_t dDelayMS; ///< Delay in milliseconds before rebooting
 };
 
-
-// note this (with pc_sp) union member has the same layout as picoboot_reboot_cmd except with extra dFlags
+/*! \brief Parameters for an extended reboot command (not available on RP2040)
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of a PC_REBOOT2 command.
+ */
 struct __packed picoboot_reboot2_cmd {
-    uint32_t dFlags;
-    uint32_t dDelayMS;
-    uint32_t dParam0;
-    uint32_t dParam1;
+    uint32_t dFlags; ///< Reboot flags controlling the boot path
+    uint32_t dDelayMS; ///< Delay in milliseconds before rebooting
+    uint32_t dParam0; ///< First reboot parameter (interpretation depends on flags)
+    uint32_t dParam1; ///< Second reboot parameter (interpretation depends on flags)
 };
 
-// used for EXEC, VECTORIZE_FLASH
+/*! \brief Parameters for a command that operates on a single address (RP2040 only)
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of PC_EXEC and PC_VECTORIZE_FLASH commands,
+ * which are not supported on RP2350.
+ */
 struct __packed picoboot_address_only_cmd {
-    uint32_t dAddr;
+    uint32_t dAddr; ///< Target address
 };
 
 // used for READ, WRITE, FLASH_ERASE
+/*! \brief Parameters for a command that operates on an address range
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of PC_READ, PC_WRITE, and PC_FLASH_ERASE commands.
+ */
 struct __packed picoboot_range_cmd {
-    uint32_t dAddr;
-    uint32_t dSize;
+    uint32_t dAddr; ///< Start address of the range
+    uint32_t dSize; ///< Size of the range in bytes
 };
 
 // remains defined for backwards compatibility with RP2350 bootrom builds
@@ -113,56 +131,88 @@
     uint32_t dummy;
 };
 
+/*! \brief Exclusivity level for a PC_EXCLUSIVE_ACCESS command
+ *  \ingroup boot_picoboot_headers
+ */
 enum picoboot_exclusive_type {
-    NOT_EXCLUSIVE = 0,
-    EXCLUSIVE,
-    EXCLUSIVE_AND_EJECT
+    NOT_EXCLUSIVE = 0,   ///< No restriction on USB Mass Storage operation
+    EXCLUSIVE,           ///< Disable USB Mass Storage writes (any active UF2 download will be aborted)
+    EXCLUSIVE_AND_EJECT  ///< Lock out USB Mass Storage by marking the drive media as not present (eject the drive)
 };
 
+/*! \brief Parameters for an exclusive-access command
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of a PC_EXCLUSIVE_ACCESS command.
+ */
 struct __packed picoboot_exclusive_cmd {
-    uint8_t bExclusive;
+    uint8_t bExclusive; ///< Exclusivity level; one of the picoboot_exclusive_type values
 };
 
+/*! \brief Parameters for an OTP read or write command (not available on RP2040)
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of PC_OTP_READ and PC_OTP_WRITE commands.
+ */
 struct __packed picoboot_otp_cmd {
-    uint16_t wRow; // OTP row
-    uint16_t wRowCount; // number of rows to transfer
-    uint8_t bEcc; // use error correction (16 bit per register vs 24 (stored as 32) bit raw)
+    uint16_t wRow; ///< OTP row index to start from
+    uint16_t wRowCount; ///< Number of rows to transfer
+    uint8_t bEcc; ///< Non-zero to use ECC (16-bit per register); zero for raw 24-bit access (stored as 32-bit)
 };
 
+/*! \brief Parameters for a get-info command (not available on RP2040)
+ *  \ingroup boot_picoboot_headers
+ *
+ * Sent as the argument payload of a PC_GET_INFO command.
+ */
 struct __packed picoboot_get_info_cmd {
-    uint8_t bType;
-    uint8_t bParam;
-    uint16_t wParam;
-    uint32_t dParams[3];
+    uint8_t bType; ///< Info type selector
+    uint8_t bParam; ///< Unused
+    uint16_t wParam; ///< Unused
+    uint32_t dParams[3]; ///< Additional parameters for the selected info type
 };
 
 // little endian
+/*! \brief A PICOBOOT command packet sent to the OUT endpoint
+ *  \ingroup boot_picoboot_headers
+ *
+ * A 32-byte packet written to the PICOBOOT OUT endpoint to initiate any
+ * supported command.  After sending this packet, transfer_length bytes are
+ * exchanged via the IN or OUT endpoint as appropriate, followed by a
+ * zero-length ACK packet.
+ */
 struct __packed __aligned(4) picoboot_cmd {
-    uint32_t dMagic;
-    uint32_t dToken; // an identifier for this token to correlate with a status response
-    uint8_t bCmdId; // top bit set for IN
-    uint8_t bCmdSize; // bytes of actual data in the arg part of this structure
-    uint16_t _unused;
-    uint32_t dTransferLength; // length of IN/OUT transfer (or 0) if none
+    uint32_t dMagic; ///< Must be PICOBOOT_MAGIC
+    uint32_t dToken; ///< Caller-chosen identifier used to correlate this command with its status response
+    uint8_t bCmdId; ///< Command identifier from picoboot_cmd_id; top bit set indicates an IN transfer
+    uint8_t bCmdSize; ///< Number of valid argument bytes within the args union
+    uint16_t _unused; ///< Reserved; must be zero
+    uint32_t dTransferLength; ///< Length of the subsequent IN/OUT data transfer, or 0 if none
     union {
-        uint8_t args[16];
-        struct picoboot_reboot_cmd reboot_cmd;
-        struct picoboot_range_cmd range_cmd;
-        struct picoboot_address_only_cmd address_only_cmd;
-        struct picoboot_exclusive_cmd exclusive_cmd;
-        struct picoboot_reboot2_cmd reboot2_cmd;
-        struct picoboot_otp_cmd otp_cmd;
-        struct picoboot_get_info_cmd get_info_cmd;
+        uint8_t args[16]; ///< Raw argument bytes
+        struct picoboot_reboot_cmd reboot_cmd; ///< Arguments for PC_REBOOT
+        struct picoboot_range_cmd range_cmd; ///< Arguments for PC_READ, PC_WRITE, and PC_FLASH_ERASE
+        struct picoboot_address_only_cmd address_only_cmd; ///< Arguments for PC_EXEC and PC_VECTORIZE_FLASH
+        struct picoboot_exclusive_cmd exclusive_cmd; ///< Arguments for PC_EXCLUSIVE_ACCESS
+        struct picoboot_reboot2_cmd reboot2_cmd; ///< Arguments for PC_REBOOT2
+        struct picoboot_otp_cmd otp_cmd; ///< Arguments for PC_OTP_READ and PC_OTP_WRITE
+        struct picoboot_get_info_cmd get_info_cmd; ///< Arguments for PC_GET_INFO
     };
 };
 static_assert(32 == sizeof(struct picoboot_cmd), "picoboot_cmd must be 32 bytes big");
 
+/*! \brief Status response returned by the PICOBOOT_IF_CMD_STATUS control request
+ *  \ingroup boot_picoboot_headers
+ *
+ * A 16-byte structure read back from the device to determine the outcome of
+ * the most recently issued command.
+ */
 struct __packed __aligned(4) picoboot_cmd_status {
-    uint32_t dToken;
-    uint32_t dStatusCode;
-    uint8_t bCmdId;
-    uint8_t bInProgress;
-    uint8_t _pad[6];
+    uint32_t dToken; ///< Token copied from the corresponding picoboot_cmd
+    uint32_t dStatusCode; ///< Result code; one of the picoboot_status values
+    uint8_t bCmdId; ///< Command identifier of the command this status relates to
+    uint8_t bInProgress; ///< Non-zero if the command is still being processed
+    uint8_t _pad[6]; ///< Padding to reach 16 bytes
 };
 
 static_assert(16 == sizeof(struct picoboot_cmd_status), "picoboot_cmd_status must be 16 bytes big");
diff --git a/src/common/boot_uf2_headers/include/boot/uf2.h b/src/common/boot_uf2_headers/include/boot/uf2.h
index 279d4a1..c62d1cd 100644
--- a/src/common/boot_uf2_headers/include/boot/uf2.h
+++ b/src/common/boot_uf2_headers/include/boot/uf2.h
@@ -45,18 +45,24 @@
 // 04 e3 57 99
 #define UF2_EXTENSION_RP2_IGNORE_BLOCK 0x9957e304
 
+/*! \brief A single 512-byte UF2 block as written to a RP2xxx device in BOOTSEL mode
+ *  \ingroup boot_uf2_headers
+ *
+ * Each block forms one sector of a UF2 file and carries up to 476 bytes of payload data
+ * along with addressing and sequencing metadata in a fixed 32-byte header.
+ */
 struct uf2_block {
     // 32 byte header
-    uint32_t magic_start0;
-    uint32_t magic_start1;
-    uint32_t flags;
-    uint32_t target_addr;
-    uint32_t payload_size;
-    uint32_t block_no;
-    uint32_t num_blocks;
-    uint32_t file_size; // or familyID;
-    uint8_t  data[476];
-    uint32_t magic_end;
+    uint32_t magic_start0; ///< First magic number identifying a UF2 block (UF2_MAGIC_START0)
+    uint32_t magic_start1; ///< Second magic number identifying a UF2 block (UF2_MAGIC_START1)
+    uint32_t flags;        ///< Block flags (e.g. UF2_FLAG_FAMILY_ID_PRESENT)
+    uint32_t target_addr;  ///< Address in flash or memory at which to write this block's payload
+    uint32_t payload_size; ///< Number of valid data bytes in the payload field
+    uint32_t block_no;     ///< Zero-based index of this block within the UF2 file
+    uint32_t num_blocks;   ///< Total number of blocks in the UF2 file
+    uint32_t file_size;    ///< When UF2_FLAG_FAMILY_ID_PRESENT is set in flags, this contains the UF2 family ID
+    uint8_t  data[476];    ///< Raw payload data written to target_addr
+    uint32_t magic_end;    ///< Magic number marking the end of the block (UF2_MAGIC_END)
 };
 
 static_assert(sizeof(struct uf2_block) == 512, "uf2_block not sector sized");
diff --git a/src/common/pico_binary_info/include/pico/binary_info/structure.h b/src/common/pico_binary_info/include/pico/binary_info/structure.h
index 1cda5a9..13e5de3 100644
--- a/src/common/pico_binary_info/include/pico/binary_info/structure.h
+++ b/src/common/pico_binary_info/include/pico/binary_info/structure.h
@@ -64,90 +64,133 @@
 #else
 #define bi_ptr_of(x) uint32_t
 #endif
+
+/*! \brief Common header fields shared by all binary info entries
+ *  \ingroup pico_binary_info
+ *
+ * Every binary info structure begins with this core header, which identifies
+ * the entry type and the tag namespace it belongs to.
+ */
 typedef struct __packed _binary_info_core {
-        uint16_t type;
-        uint16_t tag;
+        uint16_t type; ///< Binary info entry type (one of BINARY_INFO_TYPE_*)
+        uint16_t tag;  ///< Namespace tag identifying the entry's owner
 } binary_info_core_t;
 
+/*! \brief Binary info entry carrying raw, uninterpreted byte data
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_raw_data {
-        struct _binary_info_core core;
-        uint8_t bytes[1];
+        struct _binary_info_core core;  ///< Common binary info header
+        uint8_t bytes[1];               ///< Raw byte payload (variable length)
 } binary_info_raw_data_t;
 
+/*! \brief Binary info entry carrying a length-prefixed byte buffer
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_sized_data {
-        struct _binary_info_core core;
-        uint32_t length;
-        uint8_t bytes[1];
+        struct _binary_info_core core;  ///< Common binary info header
+        uint32_t length;                ///< Number of bytes in the payload
+        uint8_t bytes[1];               ///< Byte payload (variable length)
 } binary_info_sized_data_t;
 
+/*! \brief Binary info entry holding a null-terminated list of binary info pointers
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_list_zero_terminated {
-        struct _binary_info_core core;
-        bi_ptr_of(binary_info_t) list;
+        struct _binary_info_core core;       ///< Common binary info header
+        bi_ptr_of(binary_info_t) list;       ///< Pointer to the null-terminated list of binary info entries
 } binary_info_list_zero_terminated_t;
 
+/*! \brief Binary info entry associating a 32-bit integer value with an ID
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_id_and_int {
-        struct _binary_info_core core;
-        uint32_t id;
-        int32_t value;
+        struct _binary_info_core core;  ///< Common binary info header
+        uint32_t id;                    ///< Identifier for the value (one of BINARY_INFO_ID_*)
+        int32_t value;                  ///< The integer value associated with the ID
 } binary_info_id_and_int_t;
 
+/*! \brief Binary info entry associating a string value with an ID
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_id_and_string {
-        struct _binary_info_core core;
-        uint32_t id;
-        bi_ptr_of(const char) value;
+        struct _binary_info_core core;       ///< Common binary info header
+        uint32_t id;                         ///< Identifier for the value (one of BINARY_INFO_ID_*)
+        bi_ptr_of(const char) value;         ///< Pointer to the null-terminated string value
 } binary_info_id_and_string_t;
 
+/*! \brief Binary info entry holding a pointer to a named 32-bit integer variable
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_ptr_int32_with_name {
-        struct _binary_info_core core;
-        int32_t id;
-        bi_ptr_of(const int) value;
-        bi_ptr_of(const char) label;
+        struct _binary_info_core core;       ///< Common binary info header
+        int32_t id;                          ///< Identifier for the variable
+        bi_ptr_of(const int) value;          ///< Pointer to the integer variable
+        bi_ptr_of(const char) label;         ///< Pointer to the null-terminated display label
 } binary_info_ptr_int32_with_name_t;
 
+/*! \brief Binary info entry holding a pointer to a named string variable
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_ptr_string_with_name {
-        struct _binary_info_core core;
-        int32_t id;
-        bi_ptr_of(const char) value;
-        bi_ptr_of(const char) label;
-        uint32_t len;
+        struct _binary_info_core core;       ///< Common binary info header
+        int32_t id;                          ///< Identifier for the variable
+        bi_ptr_of(const char) value;         ///< Pointer to the string variable
+        bi_ptr_of(const char) label;         ///< Pointer to the null-terminated display label
+        uint32_t len;                        ///< Maximum length of the string buffer
 } binary_info_ptr_string_with_name_t;
 
+/*! \brief Binary info entry describing a block device in the binary
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_block_device {
-        struct _binary_info_core core;
+        struct _binary_info_core core;       ///< Common binary info header
         bi_ptr_of(const char) name; // optional static name (independent of what is formatted)
-        uint32_t address;
-        uint32_t size;
+        uint32_t address;                    ///< Start address of the block device in flash
+        uint32_t size;                       ///< Size of the block device in bytes
         bi_ptr_of(binary_info_t) extra; // additional info
-        uint16_t flags;
+        uint16_t flags;                      ///< Block device capability flags (BINARY_INFO_BLOCK_DEV_FLAG_*)
 } binary_info_block_device_t;
 
 #define BI_PINS_ENCODING_RANGE 1
 #define BI_PINS_ENCODING_MULTI 2
 
+/*! \brief Binary info entry describing one or more GPIO pins and their assigned function
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_pins_with_func {
-    struct _binary_info_core core;
+    struct _binary_info_core core;  ///< Common binary info header
     // p4_5 : p3_5 : p2_5 : p1_5 : p0_5 : func_4 : 010_3 //individual pins p0,p1,p2,p3,p4 ... if fewer than 5 then duplicate p
     //                    phi_5 : plo_5 : func_4 : 001_3 // pin range plo-phi inclusive
-    uint32_t pin_encoding;
+    uint32_t pin_encoding;          ///< Encoded pin numbers and function (see BI_PINS_ENCODING_* for format)
 } binary_info_pins_with_func_t;
 
+/*! \brief Binary info entry describing one or more GPIO pins (up to 64) and their assigned function
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_pins64_with_func {
-    struct _binary_info_core core;
+    struct _binary_info_core core;  ///< Common binary info header
     // p6_8 : p5_8 : p4_8 : p3_8 : p2_8 : p1_8 : p0_8 : func_5 : 010_3 //individual pins p0,p1,p2 ... if fewer than 7 then duplicate p
     //                    phi_8 : plo_8 : func_5 : 001_3 // pin range plo-phi inclusive
-    uint64_t pin_encoding;
+    uint64_t pin_encoding;          ///< Encoded pin numbers and function for up to 64 pins (see BI_PINS_ENCODING_* for format)
 } binary_info_pins64_with_func_t;
 
+/*! \brief Binary info entry associating a human-readable label with a set of GPIO pins
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_pins_with_name {
-    struct _binary_info_core core;
-    uint32_t pin_mask;
-    bi_ptr_of(const char) label;
+    struct _binary_info_core core;       ///< Common binary info header
+    uint32_t pin_mask;                   ///< Bitmask of GPIO pins covered by this entry
+    bi_ptr_of(const char) label;         ///< Pointer to the null-terminated pin label
 } binary_info_pins_with_name_t;
 
+/*! \brief Binary info entry associating a human-readable label with a set of GPIO pins (up to 64)
+ *  \ingroup pico_binary_info
+ */
 typedef struct __packed _binary_info_pins64_with_name {
-    struct _binary_info_core core;
-    uint64_t pin_mask;
-    bi_ptr_of(const char) label;
+    struct _binary_info_core core;       ///< Common binary info header
+    uint64_t pin_mask;                   ///< Bitmask of GPIO pins covered by this entry (up to 64 pins)
+    bi_ptr_of(const char) label;         ///< Pointer to the null-terminated pin label
 } binary_info_pins64_with_name_t;
 
 #define BI_NAMED_GROUP_SHOW_IF_EMPTY   0x0001  // default is to hide
@@ -155,13 +198,19 @@
 #define BI_NAMED_GROUP_SORT_ALPHA      0x0004  // default is no sort
 #define BI_NAMED_GROUP_ADVANCED        0x0008  // if set, then only shown in say info -a
 
+/*! \brief Binary info entry defining a named group of related binary info entries
+ *  \ingroup pico_binary_info
+ *
+ * Named groups allow tools to present related binary info entries together
+ * under a common label, with configurable display behaviour.
+ */
 typedef struct __packed _binary_info_named_group {
-    struct _binary_info_core core;
-    uint32_t parent_id;
-    uint16_t flags;
-    uint16_t group_tag;
-    uint32_t group_id;
-    bi_ptr_of(const char) label;
+    struct _binary_info_core core;       ///< Common binary info header
+    uint32_t parent_id;                  ///< ID of the parent group (0 for top-level)
+    uint16_t flags;                      ///< Display flags for the group (BI_NAMED_GROUP_*)
+    uint16_t group_tag;                  ///< Tag namespace used by entries within this group
+    uint32_t group_id;                   ///< Unique identifier for this group
+    bi_ptr_of(const char) label;         ///< Pointer to the null-terminated group label
 } binary_info_named_group_t;
 
 enum {
diff --git a/src/common/pico_sync/include/pico/critical_section.h b/src/common/pico_sync/include/pico/critical_section.h
index 5dd9547..1da6e92 100644
--- a/src/common/pico_sync/include/pico/critical_section.h
+++ b/src/common/pico_sync/include/pico/critical_section.h
@@ -27,9 +27,15 @@
  *  should be as short as possible.
  */
 
+/*! \brief Critical section instance
+ *  \ingroup critical_section
+ *
+ * Structure holding the state for a single critical section, including
+ * the associated spin lock and the saved interrupt state.
+ */
 typedef struct __packed_aligned critical_section {
-    spin_lock_t *spin_lock;
-    uint32_t save;
+    spin_lock_t *spin_lock; ///< Spin lock used to prevent concurrent access from the other core
+    uint32_t save;          ///< Saved interrupt state, restored on exit
 } critical_section_t;
 
 /*! \brief  Initialise a critical_section structure allowing the system to assign a spin lock number
diff --git a/src/common/pico_sync/include/pico/lock_core.h b/src/common/pico_sync/include/pico/lock_core.h
index babd285..9adbd80 100644
--- a/src/common/pico_sync/include/pico/lock_core.h
+++ b/src/common/pico_sync/include/pico/lock_core.h
@@ -50,9 +50,15 @@
  * access to the remaining lock state (in primitives using lock_core); it is never left locked outside
  * of the function implementations
  */
+
+/*! \brief Core state shared by all lock primitives
+ *  \ingroup lock_core
+ *
+ * Contains the spin lock used to protect the internal state of a locking primitive.
+ * The spin lock is released before any function returns; it is never held on exit.
+ */
 struct lock_core {
-    // spin lock protecting this lock's state
-    spin_lock_t *spin_lock;
+    spin_lock_t *spin_lock; ///< Spin lock protecting this lock's state
 
     // note any lock members in containing structures need not be volatile;
     // they are protected by memory/compiler barriers when gaining and release spin locks
diff --git a/src/common/pico_sync/include/pico/mutex.h b/src/common/pico_sync/include/pico/mutex.h
index 32eb7a0..9c0d0be 100644
--- a/src/common/pico_sync/include/pico/mutex.h
+++ b/src/common/pico_sync/include/pico/mutex.h
@@ -45,11 +45,11 @@
  * \ingroup mutex
  */
 typedef struct {
-    lock_core_t core;
+    lock_core_t core;           ///< Core lock primitive shared by all lock types
     lock_owner_id_t owner;      //! owner id LOCK_INVALID_OWNER_ID for unowned
     uint8_t enter_count;        //! ownership count
 #if PICO_MUTEX_ENABLE_SDK120_COMPATIBILITY
-    bool recursive;
+    bool recursive;             ///< True if this mutex is being used as a recursive mutex
 #endif
 } recursive_mutex_t;
 
@@ -58,7 +58,7 @@
  */
 #if !PICO_MUTEX_ENABLE_SDK120_COMPATIBILITY
 typedef struct mutex {
-    lock_core_t core;
+    lock_core_t core;           ///< Core lock primitive shared by all lock types
     lock_owner_id_t owner;      //! owner id LOCK_INVALID_OWNER_ID for unowned
 } mutex_t;
 #else
diff --git a/src/common/pico_sync/include/pico/sem.h b/src/common/pico_sync/include/pico/sem.h
index 832f149..99bcbdf 100644
--- a/src/common/pico_sync/include/pico/sem.h
+++ b/src/common/pico_sync/include/pico/sem.h
@@ -28,10 +28,16 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
+
+/*! \brief A semaphore for controlling access to a shared resource
+ *  \ingroup sem
+ *
+ * Holds the current number of available permits and the maximum permitted count.
+ */
 typedef struct semaphore {
-    struct lock_core core;
-    int16_t permits;
-    int16_t max_permits;
+    struct lock_core core; ///< Core lock primitive used for blocking and signalling
+    int16_t permits; ///< Current number of available permits
+    int16_t max_permits; ///< Maximum number of permits allowed
 } semaphore_t;
 
 
diff --git a/src/common/pico_time/include/pico/time.h b/src/common/pico_time/include/pico/time.h
index d05ce83..8849931 100644
--- a/src/common/pico_time/include/pico/time.h
+++ b/src/common/pico_time/include/pico/time.h
@@ -746,11 +746,11 @@
  * \return
  */
 struct repeating_timer {
-    int64_t delay_us;
-    alarm_pool_t *pool;
-    alarm_id_t alarm_id;
-    repeating_timer_callback_t callback;
-    void *user_data;
+    int64_t delay_us;                      ///< The delay in microseconds between callbacks (negative if measured between starts)
+    alarm_pool_t *pool;                    ///< The alarm pool used to back this repeating timer
+    alarm_id_t alarm_id;                   ///< The alarm id of the underlying alarm backing this repeating timer
+    repeating_timer_callback_t callback;   ///< The callback to call each time the repeating timer fires
+    void *user_data;                       ///< User data passed to the callback
 };
 
 /*!
diff --git a/src/common/pico_time/include/pico/timeout_helper.h b/src/common/pico_time/include/pico/timeout_helper.h
index 88de8d8..8721f67 100644
--- a/src/common/pico_time/include/pico/timeout_helper.h
+++ b/src/common/pico_time/include/pico/timeout_helper.h
@@ -13,9 +13,15 @@
 extern "C" {
 #endif
 
+/*! \brief State used to track a timeout condition
+ *  \ingroup pico_time
+ *
+ * Holds the deadline and an optional per-iteration parameter used by
+ * the check_timeout_fn callbacks returned by the init functions.
+ */
 typedef struct timeout_state {
-    absolute_time_t next_timeout;
-    uint64_t param;
+    absolute_time_t next_timeout; ///< Absolute time at which the timeout expires
+    uint64_t param;               ///< Optional parameter (e.g. per-iteration duration in microseconds)
 } timeout_state_t;
 
 typedef bool (*check_timeout_fn)(timeout_state_t *ts, bool reset);
@@ -27,4 +33,4 @@
 }
 #endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/common/pico_util/include/pico/util/fixed_bitset.h b/src/common/pico_util/include/pico/util/fixed_bitset.h
index 55dcf8f..d4e3d81 100644
--- a/src/common/pico_util/include/pico/util/fixed_bitset.h
+++ b/src/common/pico_util/include/pico/util/fixed_bitset.h
@@ -20,10 +20,16 @@
 extern "C" {
 #endif
 
+/*! \brief Base type for a fixed-size bitset
+ *  \ingroup fixed_bitset
+ *
+ * This struct holds the metadata and storage for a fixed-size bitset. It is
+ * typically used via the \ref fixed_bitset_type macro rather than directly.
+ */
 typedef struct {
-    uint16_t size;           \
-    uint16_t word_size;     \
-    uint32_t words[];
+    uint16_t size;           ///< Number of bits in the bitset
+    uint16_t word_size;      ///< Number of 32-bit words used to store the bits
+    uint32_t words[];        ///< Storage array for the bitset words
 } fixed_bitset_t;
 
 /*! \brief Macro used to define a fixed-size bitset of a given size
@@ -34,9 +40,9 @@
  * typedef fixed_bitset_type(17) my_bitset_t;
  * ```
  * will define a new bitset type called `my_bitset_t` that can hold 17 boolean values.
- * 
+ *
  * The type can be used as `my_bitset_t bitset;` to declare a new bitset.
- * 
+ *
  * \param N the number of boolean values in the bitset
  */
 #define fixed_bitset_type(N) union { \
diff --git a/src/common/pico_util/include/pico/util/pheap.h b/src/common/pico_util/include/pico/util/pheap.h
index 402d9cb..52b911a 100644
--- a/src/common/pico_util/include/pico/util/pheap.h
+++ b/src/common/pico_util/include/pico/util/pheap.h
@@ -48,8 +48,16 @@
 #error invalid PICO_PHEAP_MAX_ENTRIES
 #endif
 
+/*! \brief A node within a pairing heap
+ *  \ingroup util_pheap
+ *
+ * Stores the linkage indices for a single node in the heap tree.
+ * User state for each node is maintained separately in a companion array.
+ */
 typedef struct pheap_node {
-    pheap_node_id_t child, sibling, parent;
+    pheap_node_id_t child;   ///< Id of the first child node, or 0 if none
+    pheap_node_id_t sibling; ///< Id of the next sibling node, or 0 if none
+    pheap_node_id_t parent;  ///< Id of the parent node, or 0 if this is the root
 } pheap_node_t;
 
 /**
@@ -60,15 +68,21 @@
  */
 typedef bool (*pheap_comparator)(void *user_data, pheap_node_id_t a, pheap_node_id_t b);
 
+/*! \brief A pairing heap instance
+ *  \ingroup util_pheap
+ *
+ * Maintains the state for a pairing heap. Create with ph_create() or initialise
+ * statically with PHEAP_DEFINE_STATIC() and ph_post_alloc_init().
+ */
 typedef struct pheap {
-    pheap_node_t *nodes;
-    pheap_comparator comparator;
-    void *user_data;
-    pheap_node_id_t max_nodes;
-    pheap_node_id_t root_id;
+    pheap_node_t *nodes;          ///< Array of all nodes, indexed by node id minus one
+    pheap_comparator comparator;  ///< Comparator used to determine relative ordering of nodes
+    void *user_data;              ///< User data pointer passed to the comparator
+    pheap_node_id_t max_nodes;    ///< Maximum number of nodes the heap can hold
+    pheap_node_id_t root_id;      ///< Id of the current root (minimum) node, or 0 if the heap is empty
     // we remove from head and add to tail to stop reusing the same ids
-    pheap_node_id_t free_head_id;
-    pheap_node_id_t free_tail_id;
+    pheap_node_id_t free_head_id; ///< Id of the first node in the free list, or 0 if none
+    pheap_node_id_t free_tail_id; ///< Id of the last node in the free list, or 0 if none
 } pheap_t;
 
 /**
diff --git a/src/common/pico_util/include/pico/util/queue.h b/src/common/pico_util/include/pico/util/queue.h
index 93fc4c4..cff31ee 100644
--- a/src/common/pico_util/include/pico/util/queue.h
+++ b/src/common/pico_util/include/pico/util/queue.h
@@ -29,15 +29,22 @@
 
 #include "pico/lock_core.h"
 
+/*! \brief A multi-core and IRQ safe queue instance
+ *  \ingroup queue
+ *
+ * Stores a fixed number of elements of a fixed size. All operations are
+ * protected by a spinlock, making the queue safe to use from multiple cores
+ * and from interrupt handlers.
+ */
 typedef struct {
-    lock_core_t core;
-    uint8_t *data;
-    uint16_t wptr;
-    uint16_t rptr;
-    uint16_t element_size;
-    uint16_t element_count;
+    lock_core_t core; ///< Core lock state used for synchronisation
+    uint8_t *data; ///< Pointer to the backing data buffer
+    uint16_t wptr; ///< Write pointer (index of next slot to write)
+    uint16_t rptr; ///< Read pointer (index of next slot to read)
+    uint16_t element_size; ///< Size in bytes of each element
+    uint16_t element_count; ///< Maximum number of elements the queue can hold
 #if PICO_QUEUE_MAX_LEVEL
-    uint16_t max_level;
+    uint16_t max_level; ///< Highest number of elements seen in the queue since initialisation or last reset
 #endif
 } queue_t;
 
diff --git a/src/rp2_common/hardware_boot_lock/include/hardware/boot_lock.h b/src/rp2_common/hardware_boot_lock/include/hardware/boot_lock.h
index 3244afb..cf1e65f 100644
--- a/src/rp2_common/hardware_boot_lock/include/hardware/boot_lock.h
+++ b/src/rp2_common/hardware_boot_lock/include/hardware/boot_lock.h
@@ -9,6 +9,12 @@
 
 #include "pico.h"
 
+/** \file boot_lock.h
+*  \defgroup hardware_boot_lock hardware_boot_lock
+*
+* \brief Helpers for locking and unlocking hardware boot locks
+*/
+
 #ifdef __cplusplus
 extern "C" {
 #endif
diff --git a/src/rp2_common/hardware_divider/include/hardware/divider.h b/src/rp2_common/hardware_divider/include/hardware/divider.h
index 70746da..f537c5a 100644
--- a/src/rp2_common/hardware_divider/include/hardware/divider.h
+++ b/src/rp2_common/hardware_divider/include/hardware/divider.h
@@ -468,9 +468,15 @@
 #endif
 }
 
+/*! \brief Saved hardware divider state
+ *  \ingroup hardware_divider
+ *
+ * Holds a snapshot of the hardware divider registers so they can be
+ * saved and restored around code that uses the divider.
+ */
 #if !PICO_EMULATE_DIVIDER
 typedef struct {
-    uint32_t values[4];
+    uint32_t values[4]; ///< Saved divider register values (dividend, divisor, quotient, remainder)
 } hw_divider_state_t;
 #else
 typedef uint64_t hw_divider_state_t;
diff --git a/src/rp2_common/hardware_dma/include/hardware/dma.h b/src/rp2_common/hardware_dma/include/hardware/dma.h
index 599f8dd..dd2a611 100644
--- a/src/rp2_common/hardware_dma/include/hardware/dma.h
+++ b/src/rp2_common/hardware_dma/include/hardware/dma.h
@@ -162,7 +162,7 @@
  *  \ingroup channel_config
  */
 typedef struct {
-    uint32_t ctrl;
+    uint32_t ctrl; ///< Raw control register value encoding all channel configuration bits
 } dma_channel_config_t;
 
 // backwards compatibility
diff --git a/src/rp2_common/hardware_i2c/include/hardware/i2c.h b/src/rp2_common/hardware_i2c/include/hardware/i2c.h
index 10df9d6..7d47f0b 100644
--- a/src/rp2_common/hardware_i2c/include/hardware/i2c.h
+++ b/src/rp2_common/hardware_i2c/include/hardware/i2c.h
@@ -148,9 +148,15 @@
 // ----------------------------------------------------------------------------
 // Generic input/output
 
+/*! \brief I2C controller instance
+ *  \ingroup hardware_i2c
+ *
+ * Represents a single I2C hardware controller instance, containing a pointer
+ * to the hardware registers and transfer state.
+ */
 struct i2c_inst {
-    i2c_hw_t *hw;
-    bool restart_on_next;
+    i2c_hw_t *hw;          ///< Pointer to the I2C hardware registers
+    bool restart_on_next;  ///< If true, the next transfer will begin with a Restart rather than a Start
 };
 
 /**
diff --git a/src/rp2_common/hardware_interp/include/hardware/interp.h b/src/rp2_common/hardware_interp/include/hardware/interp.h
index befe1f8..4e6d4dc 100644
--- a/src/rp2_common/hardware_interp/include/hardware/interp.h
+++ b/src/rp2_common/hardware_interp/include/hardware/interp.h
@@ -39,7 +39,7 @@
  * flexible configuration make it possible to optimise many other tasks such as quantization and
  * dithering, table lookup address generation, affine texture mapping, decompression and linear feedback.
  *
- * Please refer to the appropriate RP-series microcontroller datasheet for more information on the HW 
+ * Please refer to the appropriate RP-series microcontroller datasheet for more information on the HW
  * interpolators and how they work.
  */
 
@@ -55,8 +55,15 @@
  *
  */
 
+/*! \brief Holds the configuration for an interpolator lane
+ *  \ingroup interp_config
+ *
+ * Stores the packed control register value for a single interpolator lane.
+ * Use the interp_config_set_* functions to modify the fields, then apply
+ * with interp_set_config().
+ */
 typedef struct {
-    uint32_t ctrl;
+    uint32_t ctrl; ///< Packed control register value for the interpolator lane
 } interp_config;
 
 static inline uint interp_index(interp_hw_t *interp) {
@@ -164,7 +171,7 @@
 /*! \brief Enable cross results
  *  \ingroup interp_config
  *
- *  Allows feeding of the other lane’s result into this lane’s accumulator on a POP operation.
+ *  Allows feeding of the other lane's result into this lane's accumulator on a POP operation.
  *
  * \param c Pointer to interpolation config
  * \param cross_result If true, enables the cross result
@@ -304,10 +311,17 @@
     interp->ctrl[lane] = interp->ctrl[lane] | (bits << SIO_INTERP0_CTRL_LANE0_FORCE_MSB_LSB);
 }
 
+/*! \brief Saved interpolator hardware state
+ *  \ingroup hardware_interp
+ *
+ * Holds a snapshot of all interpolator registers so that the interpolator
+ * state can be saved and restored around code that needs to use it for a
+ * different purpose.
+ */
 typedef struct {
-    uint32_t accum[2];
-    uint32_t base[3];
-    uint32_t ctrl[2];
+    uint32_t accum[2]; ///< Saved accumulator values for lanes 0 and 1
+    uint32_t base[3];  ///< Saved base register values for lanes 0, 1 and 2
+    uint32_t ctrl[2];  ///< Saved control register values for lanes 0 and 1
 } interp_hw_save_t;
 
 /*! \brief Save the specified interpolator state
@@ -355,7 +369,7 @@
  *  \ingroup hardware_interp
  *
  *  The lower 16 bits go to BASE0, upper bits to BASE1 simultaneously.
- *  Each half is sign-extended to 32 bits if that lane’s SIGNED flag is set.
+ *  Each half is sign-extended to 32 bits if that lane's SIGNED flag is set.
  *
  * \param interp Interpolator instance, interp0 or interp1.
  * \param val The value to apply to the register
diff --git a/src/rp2_common/hardware_pio/include/hardware/pio.h b/src/rp2_common/hardware_pio/include/hardware/pio.h
index 2e1b0df..7087a94 100644
--- a/src/rp2_common/hardware_pio/include/hardware/pio.h
+++ b/src/rp2_common/hardware_pio/include/hardware/pio.h
@@ -305,10 +305,10 @@
  * state machine later using pio_sm_set_config() or pio_sm_init().
  */
 typedef struct {
-    uint32_t clkdiv;
-    uint32_t execctrl;
-    uint32_t shiftctrl;
-    uint32_t pinctrl;
+    uint32_t clkdiv;    ///< Clock divider register value
+    uint32_t execctrl;  ///< Execution control register value
+    uint32_t shiftctrl; ///< Shift control register value
+    uint32_t pinctrl;   ///< Pin control register value
 #if PICO_PIO_USE_GPIO_BASE
 #define PINHI_ALL_PINCTRL_LSBS ((1u << PIO_SM0_PINCTRL_IN_BASE_LSB) | (1u << PIO_SM0_PINCTRL_OUT_BASE_LSB) | \
                                (1u << PIO_SM0_PINCTRL_SET_BASE_LSB) | (1u << PIO_SM0_PINCTRL_SIDESET_BASE_LSB))
@@ -321,7 +321,7 @@
     // 0b00000 - pin is in range 0-15
     // 0b00001 - pin is in range 16-31
     // 0b00010 - pin is in range 32-47
-    uint32_t pinhi;
+    uint32_t pinhi;     ///< High bits encoding which 16-pin GPIO range each pin field belongs to (only present when PICO_PIO_USE_GPIO_BASE is set)
 #endif
 } pio_sm_config;
 
diff --git a/src/rp2_common/hardware_pwm/include/hardware/pwm.h b/src/rp2_common/hardware_pwm/include/hardware/pwm.h
index 19258ab..4e111ec 100644
--- a/src/rp2_common/hardware_pwm/include/hardware/pwm.h
+++ b/src/rp2_common/hardware_pwm/include/hardware/pwm.h
@@ -65,10 +65,16 @@
     PWM_CHAN_B = 1
 };
 
+/*! \brief PWM configuration structure
+ *  \ingroup hardware_pwm
+ *
+ * Holds the configuration for a PWM slice. Use \ref pwm_get_default_config() to
+ * initialise and the pwm_config_set_* functions to modify, then pass to \ref pwm_init().
+ */
 typedef struct {
-    uint32_t csr;
-    uint32_t div;
-    uint32_t top;
+    uint32_t csr; ///< Control and status register value
+    uint32_t div; ///< Clock divider register value
+    uint32_t top; ///< Counter wrap (TOP) value
 } pwm_config;
 
 /**
diff --git a/src/rp2_common/hardware_uart/include/hardware/uart.h b/src/rp2_common/hardware_uart/include/hardware/uart.h
index 507c963..d0e5fba 100644
--- a/src/rp2_common/hardware_uart/include/hardware/uart.h
+++ b/src/rp2_common/hardware_uart/include/hardware/uart.h
@@ -75,6 +75,11 @@
  */
 
 // Currently always a pointer to hw but it might not be in the future
+/*! \brief Opaque type representing a UART instance
+ *  \ingroup hardware_uart
+ *
+ * Use \ref uart0 or \ref uart1 rather than constructing this directly.
+ */
 typedef struct uart_inst uart_inst_t;
 
 /** The UART identifiers for use in UART functions.
diff --git a/src/rp2_common/pico_async_context/include/pico/async_context.h b/src/rp2_common/pico_async_context/include/pico/async_context.h
index 8a19b9c..ba64ac8 100644
--- a/src/rp2_common/pico_async_context/include/pico/async_context.h
+++ b/src/rp2_common/pico_async_context/include/pico/async_context.h
@@ -153,21 +153,21 @@
  * \ingroup pico_async_context
  */
 typedef struct async_context_type {
-    uint16_t type;
+    uint16_t type; ///< Numeric type identifier for this async_context implementation
     // see wrapper functions for documentation
-    void (*acquire_lock_blocking)(async_context_t *self);
-    void (*release_lock)(async_context_t *self);
-    void (*lock_check)(async_context_t *self);
-    uint32_t (*execute_sync)(async_context_t *context, uint32_t (*func)(void *param), void *param);
-    bool (*add_at_time_worker)(async_context_t *self, async_at_time_worker_t *worker);
-    bool (*remove_at_time_worker)(async_context_t *self, async_at_time_worker_t *worker);
-    bool (*add_when_pending_worker)(async_context_t *self, async_when_pending_worker_t *worker);
-    bool (*remove_when_pending_worker)(async_context_t *self, async_when_pending_worker_t *worker);
-    void (*set_work_pending)(async_context_t *self, async_when_pending_worker_t *worker);
-    void (*poll)(async_context_t *self); // may be NULL
-    void (*wait_until)(async_context_t *self, absolute_time_t until);
-    void (*wait_for_work_until)(async_context_t *self, absolute_time_t until);
-    void (*deinit)(async_context_t *self);
+    void (*acquire_lock_blocking)(async_context_t *self); ///< Acquire the context lock, blocking until available
+    void (*release_lock)(async_context_t *self); ///< Release the context lock
+    void (*lock_check)(async_context_t *self); ///< Assert that the caller holds the context lock
+    uint32_t (*execute_sync)(async_context_t *context, uint32_t (*func)(void *param), void *param); ///< Execute a function synchronously on the context's core
+    bool (*add_at_time_worker)(async_context_t *self, async_at_time_worker_t *worker); ///< Add an at-time worker to the context
+    bool (*remove_at_time_worker)(async_context_t *self, async_at_time_worker_t *worker); ///< Remove an at-time worker from the context
+    bool (*add_when_pending_worker)(async_context_t *self, async_when_pending_worker_t *worker); ///< Add a when-pending worker to the context
+    bool (*remove_when_pending_worker)(async_context_t *self, async_when_pending_worker_t *worker); ///< Remove a when-pending worker from the context
+    void (*set_work_pending)(async_context_t *self, async_when_pending_worker_t *worker); ///< Mark a when-pending worker as having work to do
+    void (*poll)(async_context_t *self); ///< Poll for work; may be NULL for non-polled contexts
+    void (*wait_until)(async_context_t *self, absolute_time_t until); ///< Sleep until the specified time in a callback-safe manner
+    void (*wait_for_work_until)(async_context_t *self, absolute_time_t until); ///< Block until work is pending or the specified time is reached
+    void (*deinit)(async_context_t *self); ///< Deinitialise and free resources associated with the context
 } async_context_type_t;
 
 /*!
@@ -177,12 +177,12 @@
  * Individual async_context_types with additional state, should contain this structure at the start.
  */
 struct async_context {
-    const async_context_type_t *type;
-    async_when_pending_worker_t *when_pending_list;
-    async_at_time_worker_t *at_time_list;
-    absolute_time_t next_time;
-    uint16_t flags;
-    uint8_t  core_num;
+    const async_context_type_t *type; ///< Pointer to the type implementation for this context
+    async_when_pending_worker_t *when_pending_list; ///< Linked list of registered when-pending workers
+    async_at_time_worker_t *at_time_list; ///< Linked list of registered at-time workers, ordered by next_time
+    absolute_time_t next_time; ///< Time at which the next at-time worker should be called
+    uint16_t flags; ///< Context flags (see ASYNC_CONTEXT_FLAG_* defines)
+    uint8_t core_num; ///< Index of the processor core this context belongs to
 };
 
 /*!
diff --git a/src/rp2_common/pico_async_context/include/pico/async_context_freertos.h b/src/rp2_common/pico_async_context/include/pico/async_context_freertos.h
index 0120410..ba2e735 100644
--- a/src/rp2_common/pico_async_context/include/pico/async_context_freertos.h
+++ b/src/rp2_common/pico_async_context/include/pico/async_context_freertos.h
@@ -71,22 +71,29 @@
 #endif
 } async_context_freertos_config_t;
 
+/*! \brief Internal state for an async_context_freertos instance
+ *  \ingroup async_context_freertos
+ *
+ * Holds all runtime state for an async_context backed by a dedicated FreeRTOS task.
+ * Users should treat this as an opaque type and initialise it via
+ * \ref async_context_freertos_init() or \ref async_context_freertos_init_with_defaults().
+ */
 struct async_context_freertos {
-    async_context_t core;
-    SemaphoreHandle_t lock_mutex;
-    SemaphoreHandle_t work_needed_sem;
-    SemaphoreHandle_t task_complete_sem;
-    TimerHandle_t timer_handle;
-    TaskHandle_t task_handle;
+    async_context_t core; ///< Base async_context structure (must be first)
+    SemaphoreHandle_t lock_mutex; ///< Mutex used to serialise access to the context
+    SemaphoreHandle_t work_needed_sem; ///< Semaphore signalled when work is pending
+    SemaphoreHandle_t task_complete_sem; ///< Semaphore signalled when the task has exited
+    TimerHandle_t timer_handle; ///< FreeRTOS timer used to schedule time-based work
+    TaskHandle_t task_handle; ///< Handle of the worker task
 #if configSUPPORT_STATIC_ALLOCATION
-    StaticSemaphore_t lock_mutex_buf;
-    StaticSemaphore_t work_needed_sem_buf;
-    StaticSemaphore_t task_complete_sem_buf;
-    StaticTimer_t timer_buf;
-    StaticTask_t task_buf;
+    StaticSemaphore_t lock_mutex_buf; ///< Static storage for the lock mutex
+    StaticSemaphore_t work_needed_sem_buf; ///< Static storage for the work semaphore
+    StaticSemaphore_t task_complete_sem_buf; ///< Static storage for the completion semaphore
+    StaticTimer_t timer_buf; ///< Static storage for the timer
+    StaticTask_t task_buf; ///< Static storage for the worker task
 #endif
-    uint8_t nesting;
-    volatile bool task_should_exit;
+    uint8_t nesting; ///< Lock nesting depth
+    volatile bool task_should_exit; ///< Set to true to request the worker task to exit
 };
 
 /*!
diff --git a/src/rp2_common/pico_async_context/include/pico/async_context_poll.h b/src/rp2_common/pico_async_context/include/pico/async_context_poll.h
index 2274576..7d39191 100644
--- a/src/rp2_common/pico_async_context/include/pico/async_context_poll.h
+++ b/src/rp2_common/pico_async_context/include/pico/async_context_poll.h
@@ -25,9 +25,15 @@
 extern "C" {
 #endif
 
+/*! \brief Async context type for a simple single-core polling loop
+ *  \ingroup async_context_poll
+ *
+ * Holds the state for an async_context_poll instance, which processes
+ * asynchronous work by polling rather than using interrupts or threads.
+ */
 typedef struct async_context_poll {
-    async_context_t core;
-    semaphore_t sem;
+    async_context_t core; ///< Core async context state shared by all implementations
+    semaphore_t sem; ///< Semaphore used to signal that work is pending
 } async_context_poll_t;
 
 /*!
@@ -46,4 +52,4 @@
 }
 #endif
 
-#endif
\ No newline at end of file
+#endif
diff --git a/src/rp2_common/pico_async_context/include/pico/async_context_threadsafe_background.h b/src/rp2_common/pico_async_context/include/pico/async_context_threadsafe_background.h
index 1b6b1e0..56a3a22 100644
--- a/src/rp2_common/pico_async_context/include/pico/async_context_threadsafe_background.h
+++ b/src/rp2_common/pico_async_context/include/pico/async_context_threadsafe_background.h
@@ -53,19 +53,25 @@
     alarm_pool_t *custom_alarm_pool;
 } async_context_threadsafe_background_config_t;
 
+/*! \brief Internal state for an async_context_threadsafe_background instance
+ *  \ingroup async_context_threadsafe_background
+ *
+ * Holds all runtime state for the threadsafe background async context implementation.
+ * Users should treat this as an opaque type and not access fields directly.
+ */
 struct async_context_threadsafe_background {
-    async_context_t core;
-    alarm_pool_t *alarm_pool; // this must be on the same core as core_num
-    absolute_time_t last_set_alarm_time;
-    recursive_mutex_t lock_mutex;
-    semaphore_t work_needed_sem;
-    volatile alarm_id_t alarm_id;
+    async_context_t core; ///< Base async_context structure; must be first
+    alarm_pool_t *alarm_pool; ///< Alarm pool used for scheduling; must be on the same core as core_num
+    absolute_time_t last_set_alarm_time; ///< The time at which the alarm was most recently set for
+    recursive_mutex_t lock_mutex; ///< Recursive mutex protecting access to the context
+    semaphore_t work_needed_sem; ///< Semaphore signalled when work is pending
+    volatile alarm_id_t alarm_id; ///< ID of the currently active alarm, or 0 if none
 #if ASYNC_CONTEXT_THREADSAFE_BACKGROUND_MULTI_CORE
-    volatile alarm_id_t force_alarm_id;
-    bool alarm_pool_owned;
+    volatile alarm_id_t force_alarm_id; ///< Alarm ID used to force execution on the correct core
+    bool alarm_pool_owned; ///< True if this instance owns the alarm pool and must free it on deinitialisation
 #endif
-    uint8_t low_priority_irq_num;
-    volatile bool alarm_pending;
+    uint8_t low_priority_irq_num; ///< IRQ number of the low-priority IRQ used to process work
+    volatile bool alarm_pending; ///< True if an alarm is currently pending
 };
 
 /*!
diff --git a/src/rp2_common/pico_bootrom/include/pico/bootrom.h b/src/rp2_common/pico_bootrom/include/pico/bootrom.h
index a3dbc9c..3e9e2c3 100644
--- a/src/rp2_common/pico_bootrom/include/pico/bootrom.h
+++ b/src/rp2_common/pico_bootrom/include/pico/bootrom.h
@@ -95,9 +95,15 @@
 #endif
 
 #ifdef __riscv
+/*! \brief Stack descriptor passed to the bootrom for RISC-V Arm emulation
+ *  \ingroup pico_bootrom
+ *
+ * Specifies a region of RAM to use as the Arm emulation stack for the current core.
+ * The previous values are written back into this struct before the call returns.
+ */
 typedef struct {
-    uint32_t *base;
-    uint32_t size;
+    uint32_t *base; ///< Word-aligned base address of the stack region
+    uint32_t size;  ///< Size of the stack region in bytes (must be a multiple of 4)
 } bootrom_stack_t;
 // passed in, and out.
 typedef int (*rom_set_bootrom_stack_fn)(bootrom_stack_t *stack);
@@ -586,12 +592,18 @@
     func(mode, clkdiv);
 }
 
+/*! \brief Parameters for the flash operation helper used with flash_safe_execute
+ *  \ingroup pico_bootrom
+ *
+ * Bundles the arguments for rom_flash_op so they can be passed through the
+ * flash_safe_execute callback interface as a single pointer.
+ */
 typedef struct {
-    cflash_flags_t flags;
-    uintptr_t addr;
-    uint32_t size_bytes;
-    uint8_t *buf;
-    int *res;
+    cflash_flags_t flags;  ///< Flags controlling the security level, address space, and flash operation
+    uintptr_t addr;        ///< Address of the first flash byte to be accessed
+    uint32_t size_bytes;   ///< Size of the buffer in bytes
+    uint8_t *buf;          ///< Buffer for data to be written to or read from flash
+    int *res;              ///< Pointer to store the return code from the flash operation
 } rom_helper_flash_op_params_t;
 
 static inline void rom_helper_flash_op(void *param) {
@@ -915,10 +927,16 @@
     return rc;
 }
 
+/*! \brief Parameters for the explicit buy helper used with flash_safe_execute
+ *  \ingroup pico_bootrom
+ *
+ * Bundles the arguments for rom_explicit_buy so they can be passed through the
+ * flash_safe_execute callback interface as a single pointer.
+ */
 typedef struct {
-    uint8_t *buffer;
-    uint32_t buffer_size;
-    int *res;
+    uint8_t *buffer;      ///< Word-aligned base address of the scratch space buffer
+    uint32_t buffer_size; ///< Size of the scratch space buffer in bytes (at least 4 KiB)
+    int *res;             ///< Pointer to store the return code from the explicit buy operation
 } rom_helper_explicit_buy_params_t;
 
 static inline void rom_helper_explicit_buy(void *param) {
@@ -1070,18 +1088,24 @@
     return func(out_buffer, out_buffer_word_size, flags);
 }
 
+/*! \brief Boot information returned by the bootrom SYS_INFO_BOOT_INFO query
+ *  \ingroup pico_bootrom
+ *
+ * Contains details about the most recent boot, including the boot type, the
+ * partition that was booted, and any diagnostic or reboot parameters.
+ */
 typedef struct {
     union {
         struct __packed {
-            int8_t diagnostic_partition_index; // used BOOT_PARTITION constants
-            uint8_t boot_type;
-            int8_t partition;
-            uint8_t tbyb_and_update_info;
+            int8_t diagnostic_partition_index; ///< Partition index used for diagnostics; uses BOOT_PARTITION constants
+            uint8_t boot_type;                 ///< The type of boot that occurred (e.g. BOOT_TYPE_NORMAL, BOOT_TYPE_BOOTSEL)
+            int8_t partition;                  ///< The partition that was booted, or -1 if not applicable
+            uint8_t tbyb_and_update_info;      ///< Try-before-you-buy and flash update status flags
         };
-        uint32_t boot_word;
+        uint32_t boot_word; ///< The four boot fields packed into a single 32-bit word
     };
-    uint32_t boot_diagnostic;
-    uint32_t reboot_params[2];
+    uint32_t boot_diagnostic;   ///< Diagnostic word describing the outcome of the most recent boot attempt
+    uint32_t reboot_params[2];  ///< Parameters passed to the reboot call that initiated this boot
 } boot_info_t;
 
 static inline int rom_get_boot_info(boot_info_t *info) {
diff --git a/src/rp2_common/pico_fix/doc.h b/src/rp2_common/pico_fix/doc.h
new file mode 100644
index 0000000..0faccf0
--- /dev/null
+++ b/src/rp2_common/pico_fix/doc.h
@@ -0,0 +1,5 @@
+/**
+*  \defgroup pico_fix pico_fix
+*
+* \brief Miscellaneous device specifc workarounds/fixes
+*/
\ No newline at end of file
diff --git a/src/rp2_common/pico_flash/include/pico/flash.h b/src/rp2_common/pico_flash/include/pico/flash.h
index ae16d76..fd43946 100644
--- a/src/rp2_common/pico_flash/include/pico/flash.h
+++ b/src/rp2_common/pico_flash/include/pico/flash.h
@@ -115,10 +115,16 @@
 #endif
 #endif
 
+/*! \brief Helper interface for coordinating safe flash access across cores
+ *  \ingroup pico_flash
+ *
+ * Provides the callbacks used by \ref flash_safe_execute to initialize per-core
+ * state and to enter/exit the safe zone in which flash may be written or erased.
+ */
 typedef struct {
-    bool (*core_init_deinit)(bool init);
-    int (*enter_safe_zone_timeout_ms)(uint32_t timeout_ms);
-    int (*exit_safe_zone_timeout_ms)(uint32_t timeout_ms);
+    bool (*core_init_deinit)(bool init); ///< Initialize (init=true) or de-initialise (init=false) this core's participation in flash safety
+    int (*enter_safe_zone_timeout_ms)(uint32_t timeout_ms); ///< Enter the safe zone, blocking until safe or until timeout_ms milliseconds elapse
+    int (*exit_safe_zone_timeout_ms)(uint32_t timeout_ms); ///< Exit the safe zone, unblocking the other core, waiting up to timeout_ms milliseconds
 } flash_safety_helper_t;
 
 /**
diff --git a/src/rp2_common/pico_rand/include/pico/rand.h b/src/rp2_common/pico_rand/include/pico/rand.h
index e3bbe2d..00ba35e 100644
--- a/src/rp2_common/pico_rand/include/pico/rand.h
+++ b/src/rp2_common/pico_rand/include/pico/rand.h
@@ -175,9 +175,14 @@
 #define PICO_RAND_RAM_HASH_START   (PICO_RAND_RAM_HASH_END - 1024u)
 #endif
 
+/*! \brief A 128-bit random number value
+ *  \ingroup pico_rand
+ *
+ * Holds up to 128 bits of entropy returned by \ref get_rand_128.
+ */
 // We provide a maximum of 128 bits entropy in one go
 typedef struct rng_128 {
-    uint64_t r[2];
+    uint64_t r[2]; ///< Two 64-bit words comprising the 128-bit random value
 } rng_128_t;
 
 /*! \brief Get 128-bit random number
diff --git a/src/rp2_common/pico_sha256/include/pico/sha256.h b/src/rp2_common/pico_sha256/include/pico/sha256.h
index ab0c81a..166059e 100644
--- a/src/rp2_common/pico_sha256/include/pico/sha256.h
+++ b/src/rp2_common/pico_sha256/include/pico/sha256.h
@@ -46,16 +46,16 @@
  *  \ingroup pico_sha256
  */
 typedef struct pico_sha256_state {
-    enum sha256_endianness endianness;
-    int8_t channel;
-    bool locked;
-    uint8_t cache_used;
+    enum sha256_endianness endianness; ///< Endianness for data in and data out
+    int8_t channel;                    ///< DMA channel in use, or -1 if DMA is not being used
+    bool locked;                       ///< Whether the SHA-256 hardware lock is currently held
+    uint8_t cache_used;                ///< Number of bytes currently stored in the partial-word cache
     union {
-        uint32_t word;
-        uint8_t bytes[4];
-    } cache;
-    dma_channel_config config;
-    size_t total_data_size;
+        uint32_t word;                 ///< Cache contents as a 32-bit word
+        uint8_t bytes[4];              ///< Cache contents as individual bytes
+    } cache;                           ///< Partial-word cache for data not yet written to hardware
+    dma_channel_config config;         ///< DMA channel configuration
+    size_t total_data_size;            ///< Total number of bytes added to the calculation so far
 } pico_sha256_state_t;
 
 /*! \brief Release the internal lock on the SHA-256 hardware
diff --git a/src/rp2_common/pico_stdio/include/pico/stdio/driver.h b/src/rp2_common/pico_stdio/include/pico/stdio/driver.h
index 9ed1ebd..6153f11 100644
--- a/src/rp2_common/pico_stdio/include/pico/stdio/driver.h
+++ b/src/rp2_common/pico_stdio/include/pico/stdio/driver.h
@@ -9,15 +9,22 @@
 
 #include "pico/stdio.h"
 
+/*! \brief A stdio driver that can be registered with the pico_stdio subsystem
+ *  \ingroup pico_stdio
+ *
+ * Drivers are linked into a list and called in turn for each I/O operation.
+ * Implement only the callbacks relevant to your driver; unused callbacks may
+ * be left as NULL.
+ */
 struct stdio_driver {
-    void (*out_chars)(const char *buf, int len);
-    void (*out_flush)(void);
-    int (*in_chars)(char *buf, int len);
-    void (*set_chars_available_callback)(void (*fn)(void*), void *param);
-    stdio_driver_t *next;
+    void (*out_chars)(const char *buf, int len); ///< Output characters to the driver; may be NULL if output is not supported
+    void (*out_flush)(void); ///< Flush any buffered output; may be NULL
+    int (*in_chars)(char *buf, int len); ///< Read available input characters; returns the number read, or may be NULL if input is not supported
+    void (*set_chars_available_callback)(void (*fn)(void*), void *param); ///< Register a callback to be invoked when input characters become available; may be NULL
+    stdio_driver_t *next; ///< Next driver in the linked list; managed by the stdio subsystem
 #if PICO_STDIO_ENABLE_CRLF_SUPPORT
-    bool last_ended_with_cr;
-    bool crlf_enabled;
+    bool last_ended_with_cr; ///< Private implementation detail
+    bool crlf_enabled; ///< Whether CR/LF translation is enabled for this driver
 #endif
 };
 
diff --git a/src/rp2_common/pico_unique_id/include/pico/unique_id.h b/src/rp2_common/pico_unique_id/include/pico/unique_id.h
index ac4fc37..3af23d5 100644
--- a/src/rp2_common/pico_unique_id/include/pico/unique_id.h
+++ b/src/rp2_common/pico_unique_id/include/pico/unique_id.h
@@ -81,7 +81,7 @@
  *
  */
 typedef struct {
-	uint8_t id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES];
+	uint8_t id[PICO_UNIQUE_BOARD_ID_SIZE_BYTES]; ///< The identifier bytes
 } pico_unique_board_id_t;
 
 /*! \brief Get unique ID