diff --git a/drivers/modem/CMakeLists.txt b/drivers/modem/CMakeLists.txt index 20af808df69..e2246e0b422 100644 --- a/drivers/modem/CMakeLists.txt +++ b/drivers/modem/CMakeLists.txt @@ -3,6 +3,11 @@ zephyr_sources_ifdef(CONFIG_MODEM_RECEIVER modem_receiver.c) zephyr_sources_ifdef(CONFIG_MODEM_SHELL modem_shell.c) +zephyr_sources_ifdef(CONFIG_MODEM_CONTEXT + modem_context.c + modem_pin.c +) + if(CONFIG_MODEM_UBLOX_SARA_R4) zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) zephyr_library_sources(ublox-sara-r4.c) diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index d3adb31aaab..3a553f8c9c2 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -39,6 +39,33 @@ config MODEM_RECEIVER_MAX_CONTEXTS Maximum number of modem receiver contexts to handle. For most purposes this should stay at 1. +config MODEM_CONTEXT + bool "Modem context helper driver [EXPERIMENTAL]" + help + This driver allows modem drivers to communicate with an interface + using custom defined protocols. Driver doesn't inspect received data + and all aspects of received protocol data are handled by application + work method provided. This driver combines abstractions for: + modem interface, command handler, pin config and socket handling each + of which will need to be configured. + +if MODEM_CONTEXT + +config MODEM_CONTEXT_MAX_NUM + int "Maximum number of modem contexts" + default 1 + help + Maximum number of modem contexts to handle. For most + purposes this should stay at 1. + +config MODEM_CONTEXT_VERBOSE_DEBUG + bool "Verbose debug output in the modem context" + help + Enabling this setting will turn on VERY heavy debugging from the + modem context helper. Do NOT leave on for production. + +endif # MODEM_CONTEXT + config MODEM_SHELL bool "Enable modem shell utilities" select SHELL diff --git a/drivers/modem/modem_context.c b/drivers/modem/modem_context.c new file mode 100644 index 00000000000..7705ffbd14c --- /dev/null +++ b/drivers/modem/modem_context.c @@ -0,0 +1,132 @@ +/** @file + * @brief Modem context helper driver + * + * A modem context driver allowing application to handle all + * aspects of received protocol data. + */ + +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(modem_context, CONFIG_MODEM_LOG_LEVEL); + +#include + +#include "modem_context.h" + +static struct modem_context *contexts[CONFIG_MODEM_CONTEXT_MAX_NUM]; + +char *modem_context_sprint_ip_addr(const struct sockaddr *addr) +{ + static char buf[NET_IPV6_ADDR_LEN]; + + if (addr->sa_family == AF_INET6) { + return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, + buf, sizeof(buf)); + } + + if (addr->sa_family == AF_INET) { + return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, + buf, sizeof(buf)); + } + + LOG_ERR("Unknown IP address family:%d", addr->sa_family); + strcpy(buf, "unk"); + return buf; +} + +int modem_context_get_addr_port(const struct sockaddr *addr, u16_t *port) +{ + if (!addr || !port) { + return -EINVAL; + } + + if (addr->sa_family == AF_INET6) { + *port = ntohs(net_sin6(addr)->sin6_port); + return 0; + } else if (addr->sa_family == AF_INET) { + *port = ntohs(net_sin(addr)->sin_port); + return 0; + } + + return -EPROTONOSUPPORT; +} + +/** + * @brief Finds modem context which owns the iface device. + * + * @param *dev: device used by the modem iface. + * + * @retval Modem context or NULL. + */ +struct modem_context *modem_context_from_iface_dev(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(contexts); i++) { + if (contexts[i] && contexts[i]->iface.dev == dev) { + return contexts[i]; + } + } + + return NULL; +} + +/** + * @brief Assign a modem context if there is free space. + * + * @note Amount of stored modem contexts is determined by + * CONFIG_MODEM_CONTEXT_MAX_NUM. + * + * @param *ctx: modem context to persist. + * + * @retval 0 if ok, < 0 if error. + */ +static int modem_context_get(struct modem_context *ctx) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(contexts); i++) { + if (!contexts[i]) { + contexts[i] = ctx; + return 0; + } + } + + return -ENOMEM; +} + +struct modem_context *modem_context_from_id(int id) +{ + if (id >= 0 && id < ARRAY_SIZE(contexts)) { + return contexts[id]; + } else { + return NULL; + } +} + +int modem_context_register(struct modem_context *ctx) +{ + int ret; + + if (!ctx) { + return -EINVAL; + } + + ret = modem_context_get(ctx); + if (ret < 0) { + return ret; + } + + ret = modem_pin_init(ctx); + if (ret < 0) { + LOG_ERR("modem pin init error: %d", ret); + return ret; + } + + return 0; +} diff --git a/drivers/modem/modem_context.h b/drivers/modem/modem_context.h new file mode 100644 index 00000000000..3cf11593c0a --- /dev/null +++ b/drivers/modem/modem_context.h @@ -0,0 +1,138 @@ +/** @file + * @brief Modem context header file. + * + * A modem context driver allowing application to handle all + * aspects of received protocol data. + */ + +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CONTEXT_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CONTEXT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MODEM_PIN(name_, pin_, flags_) { \ + .dev_name = name_, \ + .pin = pin_, \ + .init_flags = flags_ \ +} + +struct modem_iface { + struct device *dev; + + int (*read)(struct modem_iface *iface, u8_t *buf, size_t size, + size_t *bytes_read); + int (*write)(struct modem_iface *iface, const u8_t *buf, size_t size); + + /* implementation data */ + void *iface_data; +}; + +struct modem_cmd_handler { + void (*process)(struct modem_cmd_handler *cmd_handler, + struct modem_iface *iface); + + /* implementation data */ + void *cmd_handler_data; +}; + +struct modem_pin { + struct device *gpio_port_dev; + char *dev_name; + u32_t pin; + int init_flags; +}; + +struct modem_context { + /* modem data */ + char *data_manufacturer; + char *data_model; + char *data_revision; + char *data_imei; + int data_rssi; + + /* pin config */ + struct modem_pin *pins; + size_t pins_len; + + /* interface config */ + struct modem_iface iface; + + /* command handler config */ + struct modem_cmd_handler cmd_handler; + + /* driver data */ + void *driver_data; +}; + +/** + * @brief IP address to string + * + * @param addr: sockaddr to be converted + * + * @retval Buffer with IP in string form + */ +char *modem_context_sprint_ip_addr(const struct sockaddr *addr); + +/** + * @brief Get port from IP address + * + * @param addr: sockaddr + * @param port: store port + * + * @retval 0 if ok, < 0 if error. + */ +int modem_context_get_addr_port(const struct sockaddr *addr, u16_t *port); + +/** + * @brief Gets modem context by id. + * + * @param id: modem context id. + * + * @retval modem context or NULL. + */ +struct modem_context *modem_context_from_id(int id); + +/** + * @brief Finds modem context which owns the iface device. + * + * @param *dev: device used by the modem iface. + * + * @retval Modem context or NULL. + */ +struct modem_context *modem_context_from_iface_dev(struct device *dev); + +/** + * @brief Registers modem context. + * + * @note Prepares modem context to be used. + * + * @param *ctx: modem context to register. + * + * @retval 0 if ok, < 0 if error. + */ +int modem_context_register(struct modem_context *ctx); + +/* pin config functions */ +int modem_pin_read(struct modem_context *ctx, u32_t pin); +int modem_pin_write(struct modem_context *ctx, u32_t pin, u32_t value); +int modem_pin_config(struct modem_context *ctx, u32_t pin, int flags); +int modem_pin_init(struct modem_context *ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_CONTEXT_H_ */ diff --git a/drivers/modem/modem_pin.c b/drivers/modem/modem_pin.c new file mode 100644 index 00000000000..f255e2a919a --- /dev/null +++ b/drivers/modem/modem_pin.c @@ -0,0 +1,78 @@ +/** @file + * @brief Modem pin setup for modem context driver + * + * GPIO-based pin handling for the modem context driver + */ + +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include "modem_context.h" + +int modem_pin_read(struct modem_context *ctx, u32_t pin) +{ + int ret = 0; + u32_t value = 0; + + if (pin < 0 || pin >= ctx->pins_len) { + return -ENODEV; + } + + ret = gpio_pin_read(ctx->pins[pin].gpio_port_dev, ctx->pins[pin].pin, + &value); + if (ret < 0) { + return ret; + } + + return (int)value; +} + +int modem_pin_write(struct modem_context *ctx, u32_t pin, u32_t value) +{ + if (pin < 0 || pin >= ctx->pins_len) { + return -ENODEV; + } + + return gpio_pin_write(ctx->pins[pin].gpio_port_dev, ctx->pins[pin].pin, + value); +} + +int modem_pin_config(struct modem_context *ctx, u32_t pin, int flags) +{ + if (pin < 0 || pin >= ctx->pins_len) { + return -ENODEV; + } + + return gpio_pin_configure(ctx->pins[pin].gpio_port_dev, + ctx->pins[pin].pin, flags); +} + +int modem_pin_init(struct modem_context *ctx) +{ + int i, ret; + + /* setup port devices and pin directions */ + for (i = 0; i < ctx->pins_len; i++) { + ctx->pins[i].gpio_port_dev = + device_get_binding(ctx->pins[i].dev_name); + if (!ctx->pins[i].gpio_port_dev) { + return -ENODEV; + } + + ret = gpio_pin_configure(ctx->pins[i].gpio_port_dev, + ctx->pins[i].pin, + ctx->pins[i].init_flags); + if (ret < 0) { + return ret; + } + } + + return 0; +} diff --git a/drivers/modem/modem_shell.c b/drivers/modem/modem_shell.c index 0a20351c93f..fc2599e3cee 100644 --- a/drivers/modem/modem_shell.c +++ b/drivers/modem/modem_shell.c @@ -20,28 +20,45 @@ #include +#if defined(CONFIG_MODEM_CONTEXT) +#include "modem_context.h" +#define ms_context modem_context +#define ms_max_context CONFIG_MODEM_CONTEXT_MAX_NUM +#define ms_send(ctx_, buf_, size_) \ + (ctx_->iface.write(&ctx_->iface, buf_, size_)) +#define ms_context_from_id modem_context_from_id +#define UART_DEV_NAME(ctx) (ctx->iface.dev->config->name) +#elif defined(CONFIG_MODEM_RECEIVER) #include "modem_receiver.h" +#define ms_context mdm_receiver_context +#define ms_max_context CONFIG_MODEM_RECEIVER_MAX_CONTEXTS +#define ms_send mdm_receiver_send +#define ms_context_from_id mdm_receiver_context_from_id +#define UART_DEV_NAME(ctx_) (ctx_->uart_dev->config->name) +#else +#error "MODEM_CONTEXT or MODEM_RECEIVER need to be enabled" +#endif static int cmd_modem_list(const struct shell *shell, size_t argc, char *argv[]) { - struct mdm_receiver_context *mdm_ctx; + struct ms_context *mdm_ctx; int i, count = 0; shell_fprintf(shell, SHELL_NORMAL, "Modem receivers:\n"); - for (i = 0; i < CONFIG_MODEM_RECEIVER_MAX_CONTEXTS; i++) { - mdm_ctx = mdm_receiver_context_from_id(i); + for (i = 0; i < ms_max_context; i++) { + mdm_ctx = ms_context_from_id(i); if (mdm_ctx) { count++; shell_fprintf(shell, SHELL_NORMAL, - "%d:\tUART Name: %s\n" + "%d:\tIface Device: %s\n" "\tManufacturer: %s\n" "\tModel: %s\n" "\tRevision: %s\n" "\tIMEI: %s\n" "\tRSSI: %d\n", i, - mdm_ctx->uart_dev->config->name, + UART_DEV_NAME(mdm_ctx), mdm_ctx->data_manufacturer, mdm_ctx->data_model, mdm_ctx->data_revision, @@ -60,7 +77,7 @@ static int cmd_modem_list(const struct shell *shell, size_t argc, static int cmd_modem_send(const struct shell *shell, size_t argc, char *argv[]) { - struct mdm_receiver_context *mdm_ctx; + struct ms_context *mdm_ctx; char *endptr; int ret, i, arg = 1; @@ -79,14 +96,14 @@ static int cmd_modem_send(const struct shell *shell, size_t argc, return -EINVAL; } - mdm_ctx = mdm_receiver_context_from_id(i); + mdm_ctx = ms_context_from_id(i); if (!mdm_ctx) { shell_fprintf(shell, SHELL_ERROR, "Modem receiver not found!"); return 0; } for (i = arg + 1; i < argc; i++) { - ret = mdm_receiver_send(mdm_ctx, argv[i], strlen(argv[i])); + ret = ms_send(mdm_ctx, argv[i], strlen(argv[i])); if (ret < 0) { shell_fprintf(shell, SHELL_ERROR, "Error sending '%s': %d\n", argv[i], ret); @@ -94,9 +111,9 @@ static int cmd_modem_send(const struct shell *shell, size_t argc, } if (i == argc - 1) { - ret = mdm_receiver_send(mdm_ctx, "\r\n", 2); + ret = ms_send(mdm_ctx, "\r", 2); } else { - ret = mdm_receiver_send(mdm_ctx, " ", 1); + ret = ms_send(mdm_ctx, " ", 1); } if (ret < 0) {