| /* |
| * Copyright (c) 2022 Trackunit Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/modem/pipe.h> |
| |
| #define PIPE_EVENT_OPENED_BIT BIT(0) |
| #define PIPE_EVENT_CLOSED_BIT BIT(1) |
| #define PIPE_EVENT_RECEIVE_READY_BIT BIT(2) |
| #define PIPE_EVENT_TRANSMIT_IDLE_BIT BIT(3) |
| |
| static void pipe_set_callback(struct modem_pipe *pipe, modem_pipe_api_callback callback, |
| void *user_data) |
| { |
| K_SPINLOCK(&pipe->spinlock) { |
| pipe->callback = callback; |
| pipe->user_data = user_data; |
| } |
| } |
| |
| static void pipe_call_callback(struct modem_pipe *pipe, enum modem_pipe_event event) |
| { |
| K_SPINLOCK(&pipe->spinlock) { |
| if (pipe->callback != NULL) { |
| pipe->callback(pipe, event, pipe->user_data); |
| } |
| } |
| } |
| |
| static uint32_t pipe_test_events(struct modem_pipe *pipe, uint32_t events) |
| { |
| return k_event_test(&pipe->event, events); |
| } |
| |
| static uint32_t pipe_await_events(struct modem_pipe *pipe, uint32_t events, k_timeout_t timeout) |
| { |
| return k_event_wait(&pipe->event, events, false, timeout); |
| } |
| |
| static void pipe_post_events(struct modem_pipe *pipe, uint32_t events) |
| { |
| k_event_post(&pipe->event, events); |
| } |
| |
| static void pipe_clear_events(struct modem_pipe *pipe, uint32_t events) |
| { |
| k_event_clear(&pipe->event, events); |
| } |
| |
| static void pipe_set_events(struct modem_pipe *pipe, uint32_t events) |
| { |
| k_event_set(&pipe->event, events); |
| } |
| |
| static int pipe_call_open(struct modem_pipe *pipe) |
| { |
| return pipe->api->open(pipe->data); |
| } |
| |
| static int pipe_call_transmit_chain(struct modem_pipe *pipe, |
| const struct modem_pipe_data_fragment *frags, size_t num_frags) |
| { |
| if (pipe->api->transmit_chain) { |
| /* Native support for `transmit_chain` */ |
| return pipe->api->transmit_chain(pipe->data, frags, num_frags); |
| } |
| /* Fallback to legacy `transmit` (send first part of chain only). |
| * This is valid because the `transmit_chain` API is documented as queuing as many bytes as |
| * it can, not necessarily all the bytes provided. |
| */ |
| return pipe->api->transmit(pipe->data, frags[0].data, frags[0].size); |
| } |
| |
| static int pipe_call_receive(struct modem_pipe *pipe, uint8_t *buf, size_t size) |
| { |
| return pipe->api->receive(pipe->data, buf, size); |
| } |
| |
| static int pipe_call_close(struct modem_pipe *pipe) |
| { |
| return pipe->api->close(pipe->data); |
| } |
| |
| void modem_pipe_init(struct modem_pipe *pipe, void *data, const struct modem_pipe_api *api) |
| { |
| __ASSERT_NO_MSG(pipe != NULL); |
| __ASSERT_NO_MSG(data != NULL); |
| __ASSERT_NO_MSG(api != NULL); |
| |
| pipe->data = data; |
| pipe->api = api; |
| pipe->callback = NULL; |
| pipe->user_data = NULL; |
| k_event_init(&pipe->event); |
| } |
| |
| int modem_pipe_open(struct modem_pipe *pipe, k_timeout_t timeout) |
| { |
| int ret; |
| |
| if (pipe_test_events(pipe, PIPE_EVENT_OPENED_BIT)) { |
| return 0; |
| } |
| |
| ret = pipe_call_open(pipe); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (!pipe_await_events(pipe, PIPE_EVENT_OPENED_BIT, timeout)) { |
| return -EAGAIN; |
| } |
| |
| return 0; |
| } |
| |
| int modem_pipe_open_async(struct modem_pipe *pipe) |
| { |
| if (pipe_test_events(pipe, PIPE_EVENT_OPENED_BIT)) { |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_OPENED); |
| return 0; |
| } |
| |
| return pipe_call_open(pipe); |
| } |
| |
| void modem_pipe_attach(struct modem_pipe *pipe, modem_pipe_api_callback callback, void *user_data) |
| { |
| pipe_set_callback(pipe, callback, user_data); |
| |
| if (pipe_test_events(pipe, PIPE_EVENT_RECEIVE_READY_BIT)) { |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_RECEIVE_READY); |
| } |
| |
| if (pipe_test_events(pipe, PIPE_EVENT_TRANSMIT_IDLE_BIT)) { |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_TRANSMIT_IDLE); |
| } |
| } |
| |
| int modem_pipe_transmit_chain(struct modem_pipe *pipe, const struct modem_pipe_data_fragment *frags, |
| size_t num_frags) |
| { |
| if (!pipe_test_events(pipe, PIPE_EVENT_OPENED_BIT) || (num_frags == 0)) { |
| return 0; |
| } |
| |
| pipe_clear_events(pipe, PIPE_EVENT_TRANSMIT_IDLE_BIT); |
| return pipe_call_transmit_chain(pipe, frags, num_frags); |
| } |
| |
| int modem_pipe_receive(struct modem_pipe *pipe, uint8_t *buf, size_t size) |
| { |
| if (!pipe_test_events(pipe, PIPE_EVENT_OPENED_BIT)) { |
| return 0; |
| } |
| |
| pipe_clear_events(pipe, PIPE_EVENT_RECEIVE_READY_BIT); |
| return pipe_call_receive(pipe, buf, size); |
| } |
| |
| void modem_pipe_release(struct modem_pipe *pipe) |
| { |
| pipe_set_callback(pipe, NULL, NULL); |
| } |
| |
| int modem_pipe_close(struct modem_pipe *pipe, k_timeout_t timeout) |
| { |
| int ret; |
| |
| if (pipe_test_events(pipe, PIPE_EVENT_CLOSED_BIT)) { |
| return 0; |
| } |
| |
| ret = pipe_call_close(pipe); |
| if (ret < 0) { |
| return ret; |
| } |
| |
| if (!pipe_await_events(pipe, PIPE_EVENT_CLOSED_BIT, timeout)) { |
| return -EAGAIN; |
| } |
| |
| return 0; |
| } |
| |
| int modem_pipe_close_async(struct modem_pipe *pipe) |
| { |
| if (pipe_test_events(pipe, PIPE_EVENT_CLOSED_BIT)) { |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_CLOSED); |
| return 0; |
| } |
| |
| return pipe_call_close(pipe); |
| } |
| |
| void modem_pipe_notify_opened(struct modem_pipe *pipe) |
| { |
| pipe_set_events(pipe, PIPE_EVENT_OPENED_BIT | PIPE_EVENT_TRANSMIT_IDLE_BIT); |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_OPENED); |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_TRANSMIT_IDLE); |
| } |
| |
| void modem_pipe_notify_closed(struct modem_pipe *pipe) |
| { |
| pipe_set_events(pipe, PIPE_EVENT_TRANSMIT_IDLE_BIT | PIPE_EVENT_CLOSED_BIT); |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_CLOSED); |
| } |
| |
| void modem_pipe_notify_receive_ready(struct modem_pipe *pipe) |
| { |
| pipe_post_events(pipe, PIPE_EVENT_RECEIVE_READY_BIT); |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_RECEIVE_READY); |
| } |
| |
| void modem_pipe_notify_transmit_idle(struct modem_pipe *pipe) |
| { |
| pipe_post_events(pipe, PIPE_EVENT_TRANSMIT_IDLE_BIT); |
| pipe_call_callback(pipe, MODEM_PIPE_EVENT_TRANSMIT_IDLE); |
| } |