ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_dfu_device.c
Go to the documentation of this file.
1
21
22#include "ra8_dfu_device.h"
23
24/* Firmware + USBX only. The host test build defines RA8_OFF_TARGET (no USBX),
25 * and a firmware build that does not pull in USBX -- e.g. dfu_copy_to_run, which
26 * only needs the ra8_dfu core (boot/program/launch) -- has no ux_api.h on the
27 * include path. In both cases this whole TU is empty; the only consumers of the
28 * DFU device API are apps that `USES usbx`. */
29#if !defined(RA8_OFF_TARGET) && __has_include("ux_api.h")
30
31#include <string.h>
32
33#include "ra8_attributes.h"
34#include "ra8_dfu.h"
35#include "ra8_usb.h"
36#include "ux_api.h"
37#include "ux_dcd_ra8_usb.h"
38#include "ux_device_class_dfu.h"
39#include "ux_device_stack.h"
40
42typedef enum : uint32_t {
43 k_ra8_dfu_dev_block_bytes = 64U,
44 k_ra8_dfu_dev_page_mask = 0x0000001FU,
45} ra8_dfu_dev_geom_t;
46
48typedef enum : uint8_t {
49 k_ra8_dfu_dev_reg_interface = 1U,
50 k_ra8_dfu_dev_reg_config = 0U,
51} ra8_dfu_dev_reg_t;
52
54typedef enum : uint8_t {
55 k_ra8_dfu_dev_erased_byte = 0xFFU,
56} ra8_dfu_dev_fill_t;
57
63typedef struct {
64 ra8_dfu_slot_t target;
65 ra8_usb_speed_t speed;
66 volatile bool prepared;
67 volatile bool manifest;
68 volatile bool committed;
69 volatile uint32_t img_len;
70 volatile uint32_t writes;
71 volatile ra8_err_t prog_err;
72 uint8_t stage[k_ra8_dfu_dev_block_bytes];
73} ra8_dfu_dev_ctx_t;
74
76static ra8_dfu_dev_ctx_t s_dev = {
77 .target = k_ra8_dfu_slot_b,
78};
79
81{
82 if ((target_slot == k_ra8_dfu_slot_a) || (target_slot == k_ra8_dfu_slot_b)) {
83 s_dev.target = target_slot;
84 }
85}
86
108RA8_INTERNAL static VOID internal_dfu_activate(VOID* dfu)
109{
110 (void)dfu;
111}
112
135RA8_INTERNAL static VOID internal_dfu_deactivate(VOID* dfu)
136{
137 (void)dfu;
138}
139
177RA8_INTERNAL static UINT
178internal_dfu_write(VOID* dfu, ULONG block_number, UCHAR* data, ULONG length, ULONG* media_status)
179{
180 (void)dfu;
181 if (length == 0UL) {
182 s_dev.manifest = true;
183 } else {
184 uint32_t len = (uint32_t)length;
185 if (len > (uint32_t)k_ra8_dfu_dev_block_bytes) {
186 len = (uint32_t)k_ra8_dfu_dev_block_bytes;
187 }
188 (void)memcpy(s_dev.stage, data, (size_t)len);
189 /* Pad the tail of a short final block to a 32-byte page with the erased
190 * value so the 32-byte program granularity is satisfied. */
191 const uint32_t plen =
192 (uint32_t)((len + (uint32_t)k_ra8_dfu_dev_page_mask) & ~(uint32_t)k_ra8_dfu_dev_page_mask);
193 for (uint32_t i = len; i < plen; i++) {
194 s_dev.stage[i] = (uint8_t)k_ra8_dfu_dev_erased_byte;
195 }
196 const uint32_t off = (uint32_t)block_number * (uint32_t)k_ra8_dfu_dev_block_bytes;
197 const ra8_err_t we = ra8_dfu_program_image(s_dev.target, off, s_dev.stage, plen);
198 if (we != k_ra8_ok) {
199 s_dev.prog_err = we;
200 } else {
201 s_dev.writes++;
202 if ((off + plen) > s_dev.img_len) {
203 s_dev.img_len = off + plen;
204 }
205 }
206 }
207 *media_status = (s_dev.prog_err == k_ra8_ok) ? (ULONG)UX_SLAVE_CLASS_DFU_MEDIA_STATUS_OK
208 : (ULONG)UX_SLAVE_CLASS_DFU_MEDIA_STATUS_ERROR;
209 return UX_SUCCESS;
210}
211
241RA8_INTERNAL static UINT
242internal_dfu_read(VOID* dfu, ULONG block_number, UCHAR* data, ULONG length, ULONG* actual_length)
243{
244 (void)dfu;
245 const uint32_t off = (uint32_t)block_number * (uint32_t)k_ra8_dfu_dev_block_bytes;
246 if (off >= s_dev.img_len) {
247 *actual_length = 0UL;
248 return UX_SUCCESS;
249 }
250 uint32_t remain = s_dev.img_len - off;
251 if (remain > (uint32_t)length) {
252 remain = (uint32_t)length;
253 }
254 const uintptr_t src = ra8_dfu_slot_base(s_dev.target) + (uintptr_t)off;
255 (void)memcpy(data, (const void*)src, (size_t)remain);
256 *actual_length = (ULONG)remain;
257 return UX_SUCCESS;
258}
259
286RA8_INTERNAL static UINT internal_dfu_get_status(VOID* dfu, ULONG* media_status)
287{
288 (void)dfu;
289 *media_status = (s_dev.prog_err == k_ra8_ok) ? (ULONG)UX_SLAVE_CLASS_DFU_MEDIA_STATUS_OK
290 : (ULONG)UX_SLAVE_CLASS_DFU_MEDIA_STATUS_ERROR;
291 return UX_SUCCESS;
292}
293
319RA8_INTERNAL static UINT internal_dfu_notify(VOID* dfu, ULONG notification)
320{
321 (void)dfu;
322 if (notification == (ULONG)UX_SLAVE_CLASS_DFU_NOTIFICATION_END_DOWNLOAD) {
323 s_dev.manifest = true;
324 }
325 return UX_SUCCESS;
326}
327
358RA8_INTERNAL static UINT internal_class_register(unsigned char* framework, uint32_t framework_len)
359{
360 static UCHAR s_class_name[] = "ux_slave_class_dfu";
361
362 UX_SLAVE_CLASS_DFU_PARAMETER p = {
363 .ux_slave_class_dfu_parameter_will_detach = 0UL,
364 .ux_slave_class_dfu_parameter_capabilities =
365 (ULONG)(UX_SLAVE_CLASS_DFU_CAPABILITY_CAN_DOWNLOAD |
366 UX_SLAVE_CLASS_DFU_CAPABILITY_CAN_UPLOAD),
367 .ux_slave_class_dfu_parameter_instance_activate = internal_dfu_activate,
368 .ux_slave_class_dfu_parameter_instance_deactivate = internal_dfu_deactivate,
369 .ux_slave_class_dfu_parameter_read = internal_dfu_read,
370 .ux_slave_class_dfu_parameter_write = internal_dfu_write,
371 .ux_slave_class_dfu_parameter_get_status = internal_dfu_get_status,
372 .ux_slave_class_dfu_parameter_notify = internal_dfu_notify,
373 .ux_slave_class_dfu_parameter_framework = (UCHAR*)framework,
374 .ux_slave_class_dfu_parameter_framework_length = (ULONG)framework_len,
375 };
376 return _ux_device_stack_class_register(s_class_name,
377 _ux_device_class_dfu_entry,
378 (ULONG)k_ra8_dfu_dev_reg_interface,
379 (ULONG)k_ra8_dfu_dev_reg_config,
380 &p);
381}
382
384 void* usbx_pool,
385 uint32_t pool_bytes,
386 unsigned char* framework,
387 uint32_t framework_len,
388 unsigned char* strings,
389 uint32_t strings_len,
390 unsigned char* langids,
391 uint32_t langids_len)
392{
393 if ((usbx_pool == nullptr) || (framework == nullptr) || (strings == nullptr) ||
394 (langids == nullptr)) {
395 return k_ra8_err_null_ptr;
396 }
397 if (_ux_system_initialize(usbx_pool, (ULONG)pool_bytes, UX_NULL, 0) != UX_SUCCESS) {
399 }
400 if (_ux_device_stack_initialize((UCHAR*)UX_NULL,
401 0,
402 (UCHAR*)framework,
403 (ULONG)framework_len,
404 (UCHAR*)strings,
405 (ULONG)strings_len,
406 (UCHAR*)langids,
407 (ULONG)langids_len,
408 UX_NULL) != UX_SUCCESS) {
410 }
411 if (internal_class_register(framework, framework_len) != UX_SUCCESS) {
413 }
414 s_dev.speed = speed;
415 ra8_err_t dcd_err = ux_dcd_ra8_usb_initialize(speed);
416 if (dcd_err != k_ra8_ok) {
417 return dcd_err;
418 }
419 /* Open the target slot once, here in thread context, so the synchronous
420 * per-block program path in internal_dfu_write never runs the controller
421 * bring-up (which logs over UART) from the USB control-request context. */
422 const ra8_err_t pe = ra8_dfu_program_prepare(s_dev.target);
423 if (pe != k_ra8_ok) {
424 return pe;
425 }
426 s_dev.prepared = true;
427 return ra8_usb_device_attach(speed, true);
428}
429
431{
432 /* Per-block programming now happens synchronously in internal_dfu_write, so
433 * the worker only finalizes the image header once the host signals
434 * end-of-download (zero-length DFU_DNLOAD). The self-test twins use DFU_ABORT
435 * instead of a manifest, so they never reach this branch -- only the
436 * bootloader's real download path does. */
437 if (s_dev.manifest && !s_dev.committed && s_dev.prepared && (s_dev.prog_err == k_ra8_ok)) {
438 uint32_t other_seq = 0U;
439 (void)ra8_dfu_slot_seq(ra8_dfu_other_slot(s_dev.target), &other_seq);
440 const ra8_err_t ce = ra8_dfu_program_commit(s_dev.target, s_dev.img_len, other_seq + 1U);
441 if (ce != k_ra8_ok) {
442 s_dev.prog_err = ce;
443 }
444 s_dev.committed = true;
445 return ce;
446 }
447 return s_dev.prog_err;
448}
449
450uint32_t ra8_dfu_device_image_len(void)
451{
452 return s_dev.img_len;
453}
454
455uint32_t ra8_dfu_device_block_writes(void)
456{
457 return s_dev.writes;
458}
459
461{
462 return s_dev.manifest;
463}
464
466{
467 return s_dev.prog_err;
468}
469
471{
472 return s_dev.committed;
473}
474
475#endif /* !RA8_OFF_TARGET */
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Controller-agnostic USB-DFU MRAM bootloader core for the RA8D2.
ra8_err_t ra8_dfu_program_commit(ra8_dfu_slot_t inactive, uint32_t img_len, uint32_t seq)
Finalize a slot: CRC the programmed body, then program the header.
ra8_err_t ra8_dfu_slot_seq(ra8_dfu_slot_t slot, uint32_t *out_seq)
Read a slot header's sequence number (0 if the magic is wrong).
ra8_dfu_slot_t
Application-slot identifier.
Definition ra8_dfu.h:133
@ k_ra8_dfu_slot_b
Slot B (0x02090000).
Definition ra8_dfu.h:135
@ k_ra8_dfu_slot_a
Slot A (0x02020000).
Definition ra8_dfu.h:134
ra8_err_t ra8_dfu_program_image(ra8_dfu_slot_t inactive, uint32_t img_offset, const uint8_t *data, uint32_t len)
Program one image chunk into the inactive slot's body.
uintptr_t ra8_dfu_slot_base(ra8_dfu_slot_t slot)
Return the MRAM base address of a slot.
ra8_err_t ra8_dfu_program_prepare(ra8_dfu_slot_t inactive)
Open ra8_flash and fence all writes to one slot's window.
ra8_dfu_slot_t ra8_dfu_other_slot(ra8_dfu_slot_t slot)
Return the opposite slot (A<->B).
USBX DFU device class wired to real MRAM, bound to either controller.
bool ra8_dfu_device_manifested(void)
Whether the host has signalled end-of-download / manifest.
ra8_err_t ra8_dfu_device_last_error(void)
Latched last program error (diagnostic).
ra8_err_t ra8_dfu_device_start(ra8_usb_speed_t speed, void *usbx_pool, uint32_t pool_bytes, unsigned char *framework, uint32_t framework_len, unsigned char *strings, uint32_t strings_len, unsigned char *langids, uint32_t langids_len)
Bring up USBX + the DFU class on one controller and raise D+.
void ra8_dfu_device_set_target(ra8_dfu_slot_t target_slot)
Select the slot DFU_DNLOAD programs into / DFU_UPLOAD reads from.
ra8_err_t ra8_dfu_device_worker_step(void)
Program any pending DNLOAD block into MRAM; commit on end-of-download.
uint32_t ra8_dfu_device_image_len(void)
Total image bytes accepted so far (diagnostic).
uint32_t ra8_dfu_device_block_writes(void)
Count of DNLOAD blocks programmed into MRAM (diagnostic).
bool ra8_dfu_device_committed(void)
Whether the image header has been committed (slot now bootable).
@ k_ra8_err_invalid_state
Module in wrong state for requested operation.
Definition ra8_err.h:161
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
@ k_ra8_err_null_ptr
Pointer was NULL where a valid pointer was required.
Definition ra8_err.h:478
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
Native USB controller driver public API (device + host modes).
ra8_err_t ra8_usb_device_attach(ra8_usb_speed_t speed, bool attached)
Raise / drop the D+ pull-up to advertise the device to the host.
ra8_usb_speed_t
Selects which controller instance the call targets.
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
USBX device-controller-driver (DCD) bridge to ra8_usb.
ra8_err_t ux_dcd_ra8_usb_initialize(ra8_usb_speed_t speed)
Register the ra8_usb bridge as the active USBX DCD.