Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ target_include_directories(${DMOD_MODULE_NAME}_if INTERFACE
dmod_link_modules(${DMOD_MODULE_NAME}
dmdrvi
dmini
dmosi
)

add_subdirectory(src/port)
Expand Down
1 change: 1 addition & 0 deletions include/dmdma.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "dmdma_defs.h"
#include "dmdma_types.h"
#include "dmdma_lease.h"

/**
* @brief DMA driver configuration structure
Expand Down
103 changes: 103 additions & 0 deletions include/dmdma_lease.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#ifndef DMDMA_LEASE_H
#define DMDMA_LEASE_H

#include "dmod.h"
#include "dmdma_defs.h"
#include "dmdma_types.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
* @brief DMA lease API
*
* An alternative to opening /dev/dmdmaN/M through dmdrvi, for peripheral
* drivers (dmuart, dmspi, dmsdio, ...) that want to reserve and drive a DMA
* stream directly from their own code instead of going through the VFS path
* lookup. Both reservation paths share the exact same bookkeeping in
* dmdma.c - a stream already open as a device node cannot also be leased,
* and vice versa (see "Streams are devices, not a pool" in docs/README.md).
*
* This is a plain Module API (dmod's README, "Module API" section) - dmdma
* is the one and only implementation of it, not a plugin-style interface
* with several interchangeable backends (that's what DIF is for, e.g.
* dmfsi with dmramfs/dmffs simultaneously). A consumer just includes this
* header and calls the functions directly, e.g. dmdma_lease_acquire(...) -
* no Dmod_GetModuleContext()/Dmod_GetDifFunction() lookup involved, same as
* calling dmdma_port_stream_start() from within dmdma.c itself. Whether a
* given board actually needs DMA is a *build-time* choice (whether "dmdma"
* is declared as a dependency and bundled into that firmware image at
* all - see dmod_link_modules() in the consumer's CMakeLists.txt), not a
* runtime NULL-check.
*/

/**
* @brief Opaque handle to one leased (controller, stream) pair
*/
struct dmdma_lease;
typedef struct dmdma_lease *dmdma_lease_t;

/**
* @brief Lease event callback
*
* Fired from interrupt context for dmdma_event_complete/_half_complete/_error
* (whatever the hardware reported) or dmdma_event_timeout (the
* dmdma_transfer_config_t.timeout_ms watchdog armed at dmdma_lease_start()
* elapsed - see dmdma.c), and fired synchronously (from within the calling
* thread) with dmdma_event_aborted by dmdma_lease_abort() itself.
*
* @param lease The lease the event belongs to (the same value _lease_acquire() returned)
* @param event Which event(s) fired
* @param user_ptr Caller-owned pointer supplied to dmdma_lease_set_callback(), unchanged
*/
typedef void (*dmdma_lease_callback_t)(dmdma_lease_t lease, dmdma_event_t event, void *user_ptr);

/* --- Reservation ---
*
* Fails (returns NULL) if controller/stream is out of range for this build,
* or the stream is already reserved through any API combination.
*/
dmod_dmdma_api(1.0, dmdma_lease_t, _lease_acquire, ( dmdma_controller_t controller, dmdma_stream_t stream ) );

/**
* Release the lease: aborts any transfer in flight, disables the interrupt
* routing for the stream (so a racing ISR cannot fire into it once this
* returns), then returns the stream to the free pool. A driver must release
* every lease it holds before it unloads - dmdma has no way to know when a
* consumer module goes away, so nothing else guarantees a stale callback
* can never be invoked after that point.
*/
dmod_dmdma_api(1.0, void, _lease_release, ( dmdma_lease_t lease ) );

/* --- Transfer control ---
*
* Same semantics and validation as dmdma_ioctl_cmd_start_transfer /
* dmdma_ioctl_cmd_stop_transfer on the device-node path - controller,
* request-line/direction consistency, address alignment, and element count
* are all checked before anything touches the port.
*/
dmod_dmdma_api(1.0, int, _lease_start, ( dmdma_lease_t lease, const dmdma_transfer_config_t *config ) );

/**
* Abort whatever transfer is in flight; safe to call when idle. Fires the
* lease's callback with dmdma_event_aborted, but only if a transfer was
* actually in flight at the time of the call - aborting an already-idle
* (or already-finished) stream is a no-op and does not fire a spurious
* event on top of whatever dmdma_event_complete already fired for it.
*/
dmod_dmdma_api(1.0, void, _lease_abort, ( dmdma_lease_t lease ) );
dmod_dmdma_api(1.0, bool, _lease_is_busy, ( dmdma_lease_t lease ) );
dmod_dmdma_api(1.0, size_t, _lease_get_remaining, ( dmdma_lease_t lease ) );

/**
* Register (callback != NULL) or remove (callback == NULL) this lease's
* event callback.
*/
dmod_dmdma_api(1.0, int, _lease_set_callback, ( dmdma_lease_t lease, dmdma_lease_callback_t callback, void *user_ptr ) );

#ifdef __cplusplus
}
#endif

#endif // DMDMA_LEASE_H
11 changes: 11 additions & 0 deletions include/dmdma_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ typedef struct
bool circular; /**< Wrap back to the start instead of stopping at element_count */
dmdma_priority_t priority; /**< Arbitration priority against other active streams */
size_t element_count; /**< Number of elements (not bytes) to transfer */
uint32_t timeout_ms; /**< Abort the transfer and fire dmdma_event_timeout if it hasn't
* reached dmdma_event_complete within this many milliseconds of
* being started (dmosi_timer-backed, see dmdma.c); 0 = no
* watchdog, wait indefinitely. For a circular transfer this is a
* total-run-time cap (dmdma_event_half_complete wrap points do
* not reset it) rather than a per-element deadline. */
} dmdma_transfer_config_t;

/*
Expand All @@ -110,6 +116,11 @@ typedef enum
dmdma_event_complete = (1 << 0), /**< element_count elements transferred (or a circular wrap point) */
dmdma_event_half_complete = (1 << 1), /**< Half of element_count transferred - circular transfers only */
dmdma_event_error = (1 << 2), /**< Transfer, FIFO, or direct-mode error reported by the hardware */
dmdma_event_aborted = (1 << 3), /**< Fired synchronously by dmdma_lease_abort() / dmdma_ioctl_cmd_stop_transfer
* while a transfer was actually in flight - an explicit, caller-initiated cancel */
dmdma_event_timeout = (1 << 4), /**< dmdma_transfer_config_t.timeout_ms elapsed before the transfer
* completed - dmdma itself stopped the stream via the dmosi_timer
* armed at dmdma_lease_start()/dmdma_ioctl_cmd_start_transfer time */
} dmdma_event_t;

/**
Expand Down
Loading
Loading