diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index aaee1a116b8..0127c7c3258 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory_if_kconfig(ieee802154) add_subdirectory_if_kconfig(ipm) add_subdirectory_if_kconfig(led) add_subdirectory_if_kconfig(led_strip) +add_subdirectory_if_kconfig(modem) add_subdirectory_if_kconfig(pci) add_subdirectory_if_kconfig(pinmux) add_subdirectory_if_kconfig(pwm) diff --git a/drivers/Kconfig b/drivers/Kconfig index ef19b270eca..5b4ca81eb2b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -81,4 +81,6 @@ source "drivers/led/Kconfig" source "drivers/can/Kconfig" +source "drivers/modem/Kconfig" + endmenu diff --git a/drivers/modem/CMakeLists.txt b/drivers/modem/CMakeLists.txt new file mode 100644 index 00000000000..44747cb78b4 --- /dev/null +++ b/drivers/modem/CMakeLists.txt @@ -0,0 +1 @@ +zephyr_sources_ifdef(CONFIG_MODEM_RECEIVER modem_receiver.c) diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig new file mode 100644 index 00000000000..8db0dc50302 --- /dev/null +++ b/drivers/modem/Kconfig @@ -0,0 +1,49 @@ +# Kconfig - Modem configuration options + +# +# Copyright (c) 2018 Foundries.io +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig MODEM + bool "Modem Drivers" + help + Enable config options for modem drivers. + +if MODEM + +config MODEM_RECEIVER + bool "Enable modem receiver helper driver" + help + This driver allows modem drivers to communicate over UART with custom + defined protocols. Driver doesn't inspect received data and all + aspects of received protocol data are handled by application via + work method provided. This driver differs from the pipe UART driver + in that callbacks are executed in a different work queue and data is + passed around in k_pipe structures. + +config MODEM_RECEIVER_MAX_CONTEXTS + int "Maximum number of modem receiver contexts" + depends on MODEM_RECEIVER + range 1 10 + default 1 + help + Maximum number of modem receiver contexts to handle. For most + purposes this should stay at 1. + +config SYS_LOG_MODEM_LEVEL + int "Modem driver log level" + depends on SYS_LOG + default 0 + range 0 4 + help + Sets log level for modem drivers. + Levels are: + 0 OFF, do not write + 1 ERROR, only write SYS_LOG_ERR + 2 WARNING, write SYS_LOG_WRN in addition to previous level + 3 INFO, write SYS_LOG_INF in addition to previous levels + 4 DEBUG, write SYS_LOG_DBG in addition to previous levels + +endif # MODEM diff --git a/drivers/modem/modem_receiver.c b/drivers/modem/modem_receiver.c new file mode 100644 index 00000000000..84fc5ddabdf --- /dev/null +++ b/drivers/modem/modem_receiver.c @@ -0,0 +1,192 @@ +/** @file + * @brief Modem receiver driver + * + * A modem receiver driver allowing application to handle all + * aspects of received protocol data. + */ + +/* + * Copyright (c) 2018 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define SYS_LOG_DOMAIN "mdm_receiver" +#define SYS_LOG_LEVEL CONFIG_SYS_LOG_MODEM_LEVEL + +#include +#include +#include + +#include +#include + +#define MAX_MDM_CTX CONFIG_MODEM_RECEIVER_MAX_CONTEXTS +#define MAX_READ_SIZE 128 + +static struct mdm_receiver_context *contexts[MAX_MDM_CTX]; + +struct mdm_receiver_context *mdm_receiver_context_from_id(int id) +{ + if (id >= 0 && id < MAX_MDM_CTX) { + return contexts[id]; + } else { + return NULL; + } +} + +static struct mdm_receiver_context *context_from_dev(struct device *dev) +{ + int i; + + for (i = 0; i < MAX_MDM_CTX; i++) { + if (contexts[i] && contexts[i]->uart_dev == dev) { + return contexts[i]; + } + } + + return NULL; +} + +static int mdm_receiver_get(struct mdm_receiver_context *ctx) +{ + int i; + + /* find a free modem_context */ + for (i = 0; i < MAX_MDM_CTX; i++) { + if (!contexts[i]) { + contexts[i] = ctx; + return 0; + } + } + + return -ENOMEM; +} + +static void mdm_receiver_flush(struct mdm_receiver_context *ctx) +{ + u8_t c; + + if (!ctx) { + return; + } + + /* Drain the fifo */ + while (uart_fifo_read(ctx->uart_dev, &c, 1) > 0) { + continue; + } + + /* clear the UART pipe */ + k_pipe_init(&ctx->uart_pipe, ctx->uart_pipe_buf, ctx->uart_pipe_size); +} + +static void mdm_receiver_isr(struct device *uart_dev) +{ + struct mdm_receiver_context *ctx; + int rx, ret; + size_t bytes_written; + static u8_t read_buf[MAX_READ_SIZE]; + + /* lookup the device */ + ctx = context_from_dev(uart_dev); + if (!ctx) { + return; + } + + /* get all of the data off UART as fast as we can */ + while (uart_irq_update(ctx->uart_dev) && + uart_irq_rx_ready(ctx->uart_dev)) { + rx = uart_fifo_read(ctx->uart_dev, read_buf, sizeof(read_buf)); + if (rx > 0) { + ret = k_pipe_put(&ctx->uart_pipe, read_buf, rx, + &bytes_written, rx, K_NO_WAIT); + if (ret < 0) { + SYS_LOG_ERR("UART buffer write error (%d)! " + "Flushing UART!", ret); + mdm_receiver_flush(ctx); + return; + } + + k_sem_give(&ctx->rx_sem); + } + } +} + +int mdm_receiver_recv(struct mdm_receiver_context *ctx, + u8_t *buf, size_t size, size_t *bytes_read) +{ + if (!ctx) { + return -EINVAL; + } + + return k_pipe_get(&ctx->uart_pipe, buf, size, bytes_read, 1, K_NO_WAIT); +} + +int mdm_receiver_send(struct mdm_receiver_context *ctx, + const u8_t *buf, size_t size) +{ + if (!ctx) { + return -EINVAL; + } + + while (size) { + int written; + + written = uart_fifo_fill(ctx->uart_dev, + (const u8_t *)buf, size); + if (written < 0) { + /* error */ + uart_irq_tx_disable(ctx->uart_dev); + return written; + } else if (written < size) { + k_yield(); + } + + size -= written; + buf += written; + } + + return 0; +} + +static void mdm_receiver_setup(struct mdm_receiver_context *ctx) +{ + if (!ctx) { + return; + } + + uart_irq_rx_disable(ctx->uart_dev); + uart_irq_tx_disable(ctx->uart_dev); + mdm_receiver_flush(ctx); + uart_irq_callback_set(ctx->uart_dev, mdm_receiver_isr); + uart_irq_rx_enable(ctx->uart_dev); +} + +int mdm_receiver_register(struct mdm_receiver_context *ctx, + const char *uart_dev_name, + u8_t *buf, size_t size) +{ + int ret; + + if (!ctx) { + return -EINVAL; + } + + ctx->uart_dev = device_get_binding(uart_dev_name); + if (!ctx->uart_dev) { + return -ENOENT; + } + + /* k_pipe is setup later in mdm_receiver_flush() */ + ctx->uart_pipe_buf = buf; + ctx->uart_pipe_size = size; + k_sem_init(&ctx->rx_sem, 0, 1); + + ret = mdm_receiver_get(ctx); + if (ret < 0) { + return ret; + } + + mdm_receiver_setup(ctx); + return 0; +} diff --git a/include/drivers/modem/modem_receiver.h b/include/drivers/modem/modem_receiver.h new file mode 100644 index 00000000000..632de7e8fb0 --- /dev/null +++ b/include/drivers/modem/modem_receiver.h @@ -0,0 +1,55 @@ +/** @file + * @brief Modem receiver header file. + * + * A modem receiver driver allowing application to handle all + * aspects of received protocol data. + */ + +/* + * Copyright (c) 2018 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _MODEM_RECEIVER_H_ +#define _MODEM_RECEIVER_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct mdm_receiver_context { + struct device *uart_dev; + + /* rx data */ + u8_t *uart_pipe_buf; + size_t uart_pipe_size; + struct k_pipe uart_pipe; + struct k_sem rx_sem; + + /* modem data */ + char *data_manufacturer; + char *data_model; + char *data_revision; + char *data_imei; + int data_rssi; +}; + +struct mdm_receiver_context *mdm_receiver_context_from_id(int id); + +int mdm_receiver_recv(struct mdm_receiver_context *ctx, + u8_t *buf, size_t size, size_t *bytes_read); +int mdm_receiver_send(struct mdm_receiver_context *ctx, + const u8_t *buf, size_t size); +int mdm_receiver_register(struct mdm_receiver_context *ctx, + const char *uart_dev_name, + u8_t *buf, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _MODEM_RECEIVER_H_ */