zephyr/include/zephyr/modem/pipe.h
Bjarki Arge Andreasen 5c24c79a90 modem: pipe: simplify synchronization
The design of the pipe is overly complicated compared to the
in-tree and planned future use of the pipe module.

The pipe is currently designed to protect against multiple
threads calling any API simultaineously. This is not neccesary
as only one thread ever calls open/close/transmit/receive at
once, while the notification APIs are potentially called by a
different thread.

This commit removes the synchronization of calls to the open/
close/receive/transmit APIs. It also uses a k_event for thread
safe event and state handling instead of a k_mutex and k_condvar.

The callback is proteced by a k_sem as it modified using the
attach/release APIs, which can be called simultaneously to a
thread invoking the callback.

Signed-off-by: Bjarki Arge Andreasen <bjarki@arge-andreasen.me>
2024-06-13 16:43:49 -04:00

241 lines
5.5 KiB
C

/*
* Copyright (c) 2022 Trackunit Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/types.h>
#include <zephyr/kernel.h>
#ifndef ZEPHYR_MODEM_PIPE_
#define ZEPHYR_MODEM_PIPE_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Modem Pipe
* @defgroup modem_pipe Modem Pipe
* @ingroup modem
* @{
*/
/** Modem pipe event */
enum modem_pipe_event {
MODEM_PIPE_EVENT_OPENED = 0,
MODEM_PIPE_EVENT_RECEIVE_READY,
MODEM_PIPE_EVENT_TRANSMIT_IDLE,
MODEM_PIPE_EVENT_CLOSED,
};
/**
* @cond INTERNAL_HIDDEN
*/
struct modem_pipe;
/**
* @endcond
*/
typedef void (*modem_pipe_api_callback)(struct modem_pipe *pipe, enum modem_pipe_event event,
void *user_data);
/**
* @cond INTERNAL_HIDDEN
*/
typedef int (*modem_pipe_api_open)(void *data);
typedef int (*modem_pipe_api_transmit)(void *data, const uint8_t *buf, size_t size);
typedef int (*modem_pipe_api_receive)(void *data, uint8_t *buf, size_t size);
typedef int (*modem_pipe_api_close)(void *data);
struct modem_pipe_api {
modem_pipe_api_open open;
modem_pipe_api_transmit transmit;
modem_pipe_api_receive receive;
modem_pipe_api_close close;
};
struct modem_pipe {
void *data;
struct modem_pipe_api *api;
modem_pipe_api_callback callback;
void *user_data;
struct k_spinlock spinlock;
struct k_event event;
};
/**
* @brief Initialize a modem pipe
*
* @param pipe Pipe instance to initialize
* @param data Pipe data to bind to pipe instance
* @param api Pipe API implementation to bind to pipe instance
*/
void modem_pipe_init(struct modem_pipe *pipe, void *data, struct modem_pipe_api *api);
/**
* @endcond
*/
/**
* @brief Open pipe
*
* @param pipe Pipe instance
*
* @retval 0 if pipe was successfully opened or was already open
* @retval -errno code otherwise
*
* @warning Be cautious when using this synchronous version of the call.
* It may block the calling thread, which in the case of the system workqueue
* can result in a deadlock until this call times out waiting for the pipe to be open.
*/
int modem_pipe_open(struct modem_pipe *pipe);
/**
* @brief Open pipe asynchronously
*
* @param pipe Pipe instance
*
* @note The MODEM_PIPE_EVENT_OPENED event is invoked immediately if pipe is
* already opened.
*
* @retval 0 if pipe open was called successfully or pipe was already open
* @retval -errno code otherwise
*/
int modem_pipe_open_async(struct modem_pipe *pipe);
/**
* @brief Attach pipe to callback
*
* @param pipe Pipe instance
* @param callback Callback called when pipe event occurs
* @param user_data Free to use user data passed with callback
*
* @note The MODEM_PIPE_EVENT_RECEIVE_READY event is invoked immediately if pipe has pending
* data ready to receive.
*/
void modem_pipe_attach(struct modem_pipe *pipe, modem_pipe_api_callback callback, void *user_data);
/**
* @brief Transmit data through pipe
*
* @param pipe Pipe to transmit through
* @param buf Data to transmit
* @param size Number of bytes to transmit
*
* @retval Number of bytes placed in pipe
* @retval -EPERM if pipe is closed
* @retval -errno code on error
*
* @warning This call must be non-blocking
*/
int modem_pipe_transmit(struct modem_pipe *pipe, const uint8_t *buf, size_t size);
/**
* @brief Receive data through pipe
*
* @param pipe Pipe to receive from
* @param buf Destination for received data; must not be already in use in a modem module.
* @param size Capacity of destination for received data
*
* @retval Number of bytes received from pipe
* @retval -EPERM if pipe is closed
* @retval -errno code on error
*
* @warning This call must be non-blocking
*/
int modem_pipe_receive(struct modem_pipe *pipe, uint8_t *buf, size_t size);
/**
* @brief Clear callback
*
* @param pipe Pipe instance
*/
void modem_pipe_release(struct modem_pipe *pipe);
/**
* @brief Close pipe
*
* @param pipe Pipe instance
*
* @retval 0 if pipe open was called closed or pipe was already closed
* @retval -errno code otherwise
*
* @warning Be cautious when using this synchronous version of the call.
* It may block the calling thread, which in the case of the system workqueue
* can result in a deadlock until this call times out waiting for the pipe to be closed.
*/
int modem_pipe_close(struct modem_pipe *pipe);
/**
* @brief Close pipe asynchronously
*
* @param pipe Pipe instance
*
* @note The MODEM_PIPE_EVENT_CLOSED event is invoked immediately if pipe is
* already closed.
*
* @retval 0 if pipe close was called successfully or pipe was already closed
* @retval -errno code otherwise
*/
int modem_pipe_close_async(struct modem_pipe *pipe);
/**
* @cond INTERNAL_HIDDEN
*/
/**
* @brief Notify user of pipe that it has opened
*
* @param pipe Pipe instance
*
* @note Invoked from instance which initialized the pipe instance
*/
void modem_pipe_notify_opened(struct modem_pipe *pipe);
/**
* @brief Notify user of pipe that it has closed
*
* @param pipe Pipe instance
*
* @note Invoked from instance which initialized the pipe instance
*/
void modem_pipe_notify_closed(struct modem_pipe *pipe);
/**
* @brief Notify user of pipe that data is ready to be received
*
* @param pipe Pipe instance
*
* @note Invoked from instance which initialized the pipe instance
*/
void modem_pipe_notify_receive_ready(struct modem_pipe *pipe);
/**
* @brief Notify user of pipe that pipe has no more data to transmit
*
* @param pipe Pipe instance
*
* @note Invoked from instance which initialized the pipe instance
*/
void modem_pipe_notify_transmit_idle(struct modem_pipe *pipe);
/**
* @endcond
*/
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_MODEM_PIPE_ */