mirror of
https://github.com/zephyrproject-rtos/zephyr
synced 2025-08-20 07:45:22 +00:00
Constifying global data allows to save lots of RAM. Defining data inside function as 'static const' allows on the other hand to save stack usage and reduced code size (because data doesn't have to be copied on stack in runtime). This improvement allows to save 448 bytes of RAM and 88 bytes of ROM when compiled on ARM, with default configuration. Signed-off-by: Marcin Niestroj <m.niestroj@grinn-global.com>
1802 lines
43 KiB
C
1802 lines
43 KiB
C
/*
|
|
* Copyright (c) 2019-2020 Foundries.io
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT ublox_sara_r4
|
|
|
|
#include <logging/log.h>
|
|
LOG_MODULE_REGISTER(modem_ublox_sara_r4, CONFIG_MODEM_LOG_LEVEL);
|
|
|
|
#include <kernel.h>
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <zephyr.h>
|
|
#include <drivers/gpio.h>
|
|
#include <device.h>
|
|
#include <init.h>
|
|
|
|
#include <net/net_if.h>
|
|
#include <net/net_offload.h>
|
|
#include <net/socket_offload.h>
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
#include "modem_context.h"
|
|
#include "modem_socket.h"
|
|
#include "modem_cmd_handler.h"
|
|
#include "modem_iface_uart.h"
|
|
|
|
#if !defined(CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO)
|
|
#define CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO ""
|
|
#endif
|
|
|
|
/* pin settings */
|
|
enum mdm_control_pins {
|
|
MDM_POWER = 0,
|
|
MDM_RESET,
|
|
#if DT_INST_NODE_HAS_PROP(0, mdm_vint_gpios)
|
|
MDM_VINT,
|
|
#endif
|
|
};
|
|
|
|
static struct modem_pin modem_pins[] = {
|
|
/* MDM_POWER */
|
|
MODEM_PIN(DT_INST_GPIO_LABEL(0, mdm_power_gpios),
|
|
DT_INST_GPIO_PIN(0, mdm_power_gpios),
|
|
DT_INST_GPIO_FLAGS(0, mdm_power_gpios) | GPIO_OUTPUT),
|
|
|
|
/* MDM_RESET */
|
|
MODEM_PIN(DT_INST_GPIO_LABEL(0, mdm_reset_gpios),
|
|
DT_INST_GPIO_PIN(0, mdm_reset_gpios),
|
|
DT_INST_GPIO_FLAGS(0, mdm_reset_gpios) | GPIO_OUTPUT),
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, mdm_vint_gpios)
|
|
/* MDM_VINT */
|
|
MODEM_PIN(DT_INST_GPIO_LABEL(0, mdm_vint_gpios),
|
|
DT_INST_GPIO_PIN(0, mdm_vint_gpios),
|
|
DT_INST_GPIO_FLAGS(0, mdm_vint_gpios) | GPIO_INPUT),
|
|
#endif
|
|
};
|
|
|
|
#define MDM_UART_DEV_NAME DT_INST_BUS_LABEL(0)
|
|
#define MDM_UART_NODE DT_BUS(DT_DRV_INST(0))
|
|
|
|
#define MDM_POWER_ENABLE 1
|
|
#define MDM_POWER_DISABLE 0
|
|
#define MDM_RESET_NOT_ASSERTED 1
|
|
#define MDM_RESET_ASSERTED 0
|
|
|
|
#define MDM_CMD_TIMEOUT K_SECONDS(10)
|
|
#define MDM_DNS_TIMEOUT K_SECONDS(70)
|
|
#define MDM_CMD_CONN_TIMEOUT K_SECONDS(120)
|
|
#define MDM_REGISTRATION_TIMEOUT K_SECONDS(180)
|
|
#define MDM_PROMPT_CMD_DELAY K_MSEC(75)
|
|
#define MDM_SENDMSG_SLEEP K_MSEC(1)
|
|
|
|
#define MDM_MAX_DATA_LENGTH 1024
|
|
#define MDM_RECV_MAX_BUF 30
|
|
#define MDM_RECV_BUF_SIZE 128
|
|
|
|
#define MDM_MAX_SOCKETS 6
|
|
#define MDM_BASE_SOCKET_NUM 0
|
|
|
|
#define MDM_NETWORK_RETRY_COUNT 3
|
|
#define MDM_WAIT_FOR_RSSI_COUNT 10
|
|
#define MDM_WAIT_FOR_RSSI_DELAY K_SECONDS(2)
|
|
|
|
#define MDM_MANUFACTURER_LENGTH 10
|
|
#define MDM_MODEL_LENGTH 16
|
|
#define MDM_REVISION_LENGTH 64
|
|
#define MDM_IMEI_LENGTH 16
|
|
#define MDM_IMSI_LENGTH 16
|
|
#define MDM_APN_LENGTH 32
|
|
|
|
#define RSSI_TIMEOUT_SECS 30
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
#define MDM_VARIANT_UBLOX_R4 4
|
|
#define MDM_VARIANT_UBLOX_U2 2
|
|
#endif
|
|
|
|
NET_BUF_POOL_DEFINE(mdm_recv_pool, MDM_RECV_MAX_BUF, MDM_RECV_BUF_SIZE,
|
|
0, NULL);
|
|
|
|
/* RX thread structures */
|
|
K_KERNEL_STACK_DEFINE(modem_rx_stack,
|
|
CONFIG_MODEM_UBLOX_SARA_R4_RX_STACK_SIZE);
|
|
struct k_thread modem_rx_thread;
|
|
|
|
/* RX thread work queue */
|
|
K_KERNEL_STACK_DEFINE(modem_workq_stack,
|
|
CONFIG_MODEM_UBLOX_SARA_R4_RX_WORKQ_STACK_SIZE);
|
|
static struct k_work_q modem_workq;
|
|
|
|
/* socket read callback data */
|
|
struct socket_read_data {
|
|
char *recv_buf;
|
|
size_t recv_buf_len;
|
|
struct sockaddr *recv_addr;
|
|
uint16_t recv_read_len;
|
|
};
|
|
|
|
/* driver data */
|
|
struct modem_data {
|
|
struct net_if *net_iface;
|
|
uint8_t mac_addr[6];
|
|
|
|
/* modem interface */
|
|
struct modem_iface_uart_data iface_data;
|
|
uint8_t iface_rb_buf[MDM_MAX_DATA_LENGTH];
|
|
|
|
/* modem cmds */
|
|
struct modem_cmd_handler_data cmd_handler_data;
|
|
uint8_t cmd_match_buf[MDM_RECV_BUF_SIZE + 1];
|
|
|
|
/* socket data */
|
|
struct modem_socket_config socket_config;
|
|
struct modem_socket sockets[MDM_MAX_SOCKETS];
|
|
|
|
/* RSSI work */
|
|
struct k_delayed_work rssi_query_work;
|
|
|
|
/* modem data */
|
|
char mdm_manufacturer[MDM_MANUFACTURER_LENGTH];
|
|
char mdm_model[MDM_MODEL_LENGTH];
|
|
char mdm_revision[MDM_REVISION_LENGTH];
|
|
char mdm_imei[MDM_IMEI_LENGTH];
|
|
char mdm_imsi[MDM_IMSI_LENGTH];
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
/* modem variant */
|
|
int mdm_variant;
|
|
#endif
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* APN */
|
|
char mdm_apn[MDM_APN_LENGTH];
|
|
#endif
|
|
|
|
/* modem state */
|
|
int ev_creg;
|
|
|
|
/* bytes written to socket in last transaction */
|
|
int sock_written;
|
|
|
|
/* response semaphore */
|
|
struct k_sem sem_response;
|
|
};
|
|
|
|
static struct modem_data mdata;
|
|
static struct modem_context mctx;
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
static struct zsock_addrinfo result;
|
|
static struct sockaddr result_addr;
|
|
static char result_canonname[DNS_MAX_NAME_SIZE + 1];
|
|
#endif
|
|
|
|
/* helper macro to keep readability */
|
|
#define ATOI(s_, value_, desc_) modem_atoi(s_, value_, desc_, __func__)
|
|
|
|
/**
|
|
* @brief Convert string to long integer, but handle errors
|
|
*
|
|
* @param s: string with representation of integer number
|
|
* @param err_value: on error return this value instead
|
|
* @param desc: name the string being converted
|
|
* @param func: function where this is called (typically __func__)
|
|
*
|
|
* @retval return integer conversion on success, or err_value on error
|
|
*/
|
|
static int modem_atoi(const char *s, const int err_value,
|
|
const char *desc, const char *func)
|
|
{
|
|
int ret;
|
|
char *endptr;
|
|
|
|
ret = (int)strtol(s, &endptr, 10);
|
|
if (!endptr || *endptr != '\0') {
|
|
LOG_ERR("bad %s '%s' in %s", log_strdup(s), log_strdup(desc),
|
|
log_strdup(func));
|
|
return err_value;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
|
|
/* the list of SIM profiles. Global scope, so the app can change it */
|
|
const char *modem_sim_profiles =
|
|
CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN_PROFILES;
|
|
|
|
int find_apn(char *apn, int apnlen, const char *profiles, const char *imsi)
|
|
{
|
|
int rc = -1;
|
|
|
|
/* try to find a match */
|
|
char *s = strstr(profiles, imsi);
|
|
|
|
if (s) {
|
|
char *eos;
|
|
|
|
/* find the assignment operator preceding the match */
|
|
while (s >= profiles && !strchr("=", *s)) {
|
|
s--;
|
|
}
|
|
/* find the apn preceding the assignment operator */
|
|
while (s >= profiles && strchr(" =", *s)) {
|
|
s--;
|
|
}
|
|
|
|
/* mark end of apn string */
|
|
eos = s+1;
|
|
|
|
/* find first character of the apn */
|
|
while (s >= profiles && !strchr(" ,", *s)) {
|
|
s--;
|
|
}
|
|
s++;
|
|
|
|
/* copy the key */
|
|
if (s >= profiles) {
|
|
int len = eos - s;
|
|
|
|
if (len < apnlen) {
|
|
memcpy(apn, s, len);
|
|
apn[len] = '\0';
|
|
rc = 0;
|
|
} else {
|
|
LOG_ERR("buffer overflow");
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/* try to detect APN automatically, based on IMSI */
|
|
int modem_detect_apn(const char *imsi)
|
|
{
|
|
int rc = -1;
|
|
|
|
if (imsi != NULL && strlen(imsi) >= 5) {
|
|
|
|
/* extract MMC and MNC from IMSI */
|
|
char mmcmnc[6];
|
|
*mmcmnc = 0;
|
|
strncat(mmcmnc, imsi, sizeof(mmcmnc)-1);
|
|
|
|
/* try to find a matching IMSI, and assign the APN */
|
|
rc = find_apn(mdata.mdm_apn,
|
|
sizeof(mdata.mdm_apn),
|
|
modem_sim_profiles,
|
|
mmcmnc);
|
|
if (rc < 0) {
|
|
rc = find_apn(mdata.mdm_apn,
|
|
sizeof(mdata.mdm_apn),
|
|
modem_sim_profiles,
|
|
"*");
|
|
}
|
|
}
|
|
|
|
if (rc == 0) {
|
|
LOG_INF("Assign APN: \"%s\"", log_strdup(mdata.mdm_apn));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif
|
|
|
|
/* send binary data via the +USO[ST/WR] commands */
|
|
static ssize_t send_socket_data(struct modem_socket *sock,
|
|
const struct sockaddr *dst_addr,
|
|
const struct modem_cmd *handler_cmds,
|
|
size_t handler_cmds_len,
|
|
const char *buf, size_t buf_len,
|
|
k_timeout_t timeout)
|
|
{
|
|
int ret;
|
|
char send_buf[sizeof("AT+USO**=#,!###.###.###.###!,#####,####\r\n")];
|
|
uint16_t dst_port = 0U;
|
|
|
|
if (!sock) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Binary and ASCII mode allows sending MDM_MAX_DATA_LENGTH bytes to
|
|
* the socket in one command
|
|
*/
|
|
if (buf_len > MDM_MAX_DATA_LENGTH) {
|
|
buf_len = MDM_MAX_DATA_LENGTH;
|
|
}
|
|
|
|
/* The number of bytes written will be reported by the modem */
|
|
mdata.sock_written = 0;
|
|
|
|
if (sock->ip_proto == IPPROTO_UDP) {
|
|
ret = modem_context_get_addr_port(dst_addr, &dst_port);
|
|
snprintk(send_buf, sizeof(send_buf),
|
|
"AT+USOST=%d,\"%s\",%u,%zu", sock->id,
|
|
modem_context_sprint_ip_addr(dst_addr),
|
|
dst_port, buf_len);
|
|
} else {
|
|
snprintk(send_buf, sizeof(send_buf), "AT+USOWR=%d,%zu",
|
|
sock->id, buf_len);
|
|
}
|
|
|
|
k_sem_take(&mdata.cmd_handler_data.sem_tx_lock, K_FOREVER);
|
|
|
|
ret = modem_cmd_send_nolock(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0U, send_buf, NULL, K_NO_WAIT);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
}
|
|
|
|
/* set command handlers */
|
|
ret = modem_cmd_handler_update_cmds(&mdata.cmd_handler_data,
|
|
handler_cmds, handler_cmds_len,
|
|
true);
|
|
if (ret < 0) {
|
|
goto exit;
|
|
}
|
|
|
|
/* slight pause per spec so that @ prompt is received */
|
|
k_sleep(MDM_PROMPT_CMD_DELAY);
|
|
mctx.iface.write(&mctx.iface, buf, buf_len);
|
|
|
|
if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
|
|
ret = 0;
|
|
goto exit;
|
|
}
|
|
|
|
k_sem_reset(&mdata.sem_response);
|
|
ret = k_sem_take(&mdata.sem_response, timeout);
|
|
|
|
if (ret == 0) {
|
|
ret = modem_cmd_handler_get_error(&mdata.cmd_handler_data);
|
|
} else if (ret == -EAGAIN) {
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
|
|
exit:
|
|
/* unset handler commands and ignore any errors */
|
|
(void)modem_cmd_handler_update_cmds(&mdata.cmd_handler_data,
|
|
NULL, 0U, false);
|
|
k_sem_give(&mdata.cmd_handler_data.sem_tx_lock);
|
|
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
return mdata.sock_written;
|
|
}
|
|
|
|
/*
|
|
* Modem Response Command Handlers
|
|
*/
|
|
|
|
/* Handler: OK */
|
|
MODEM_CMD_DEFINE(on_cmd_ok)
|
|
{
|
|
modem_cmd_handler_set_error(data, 0);
|
|
k_sem_give(&mdata.sem_response);
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: ERROR */
|
|
MODEM_CMD_DEFINE(on_cmd_error)
|
|
{
|
|
modem_cmd_handler_set_error(data, -EIO);
|
|
k_sem_give(&mdata.sem_response);
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: +CME Error: <err>[0] */
|
|
MODEM_CMD_DEFINE(on_cmd_exterror)
|
|
{
|
|
/* TODO: map extended error codes to values */
|
|
modem_cmd_handler_set_error(data, -EIO);
|
|
k_sem_give(&mdata.sem_response);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Modem Info Command Handlers
|
|
*/
|
|
|
|
/* Handler: <manufacturer> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_manufacturer)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(mdata.mdm_manufacturer,
|
|
sizeof(mdata.mdm_manufacturer) - 1,
|
|
data->rx_buf, 0, len);
|
|
mdata.mdm_manufacturer[out_len] = '\0';
|
|
LOG_INF("Manufacturer: %s", log_strdup(mdata.mdm_manufacturer));
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <model> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_model)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(mdata.mdm_model,
|
|
sizeof(mdata.mdm_model) - 1,
|
|
data->rx_buf, 0, len);
|
|
mdata.mdm_model[out_len] = '\0';
|
|
LOG_INF("Model: %s", log_strdup(mdata.mdm_model));
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
/* Set modem type */
|
|
if (strstr(mdata.mdm_model, "R4")) {
|
|
mdata.mdm_variant = MDM_VARIANT_UBLOX_R4;
|
|
} else {
|
|
if (strstr(mdata.mdm_model, "U2")) {
|
|
mdata.mdm_variant = MDM_VARIANT_UBLOX_U2;
|
|
}
|
|
}
|
|
LOG_INF("Variant: %d", mdata.mdm_variant);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <rev> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_revision)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(mdata.mdm_revision,
|
|
sizeof(mdata.mdm_revision) - 1,
|
|
data->rx_buf, 0, len);
|
|
mdata.mdm_revision[out_len] = '\0';
|
|
LOG_INF("Revision: %s", log_strdup(mdata.mdm_revision));
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <IMEI> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_imei)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(mdata.mdm_imei, sizeof(mdata.mdm_imei) - 1,
|
|
data->rx_buf, 0, len);
|
|
mdata.mdm_imei[out_len] = '\0';
|
|
LOG_INF("IMEI: %s", log_strdup(mdata.mdm_imei));
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: <IMSI> */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_imsi)
|
|
{
|
|
size_t out_len;
|
|
|
|
out_len = net_buf_linearize(mdata.mdm_imsi, sizeof(mdata.mdm_imsi) - 1,
|
|
data->rx_buf, 0, len);
|
|
mdata.mdm_imsi[out_len] = '\0';
|
|
LOG_INF("IMSI: %s", log_strdup(mdata.mdm_imsi));
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* set the APN automatically */
|
|
modem_detect_apn(mdata.mdm_imsi);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if !defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
/*
|
|
* Handler: +CESQ: <rxlev>[0],<ber>[1],<rscp>[2],<ecn0>[3],<rsrq>[4],<rsrp>[5]
|
|
*/
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_rssi_cesq)
|
|
{
|
|
int rsrp, rxlev;
|
|
|
|
rsrp = ATOI(argv[5], 0, "rsrp");
|
|
rxlev = ATOI(argv[0], 0, "rxlev");
|
|
if (rsrp >= 0 && rsrp <= 97) {
|
|
mctx.data_rssi = -140 + (rsrp - 1);
|
|
LOG_INF("RSRP: %d", mctx.data_rssi);
|
|
} else if (rxlev >= 0 && rxlev <= 63) {
|
|
mctx.data_rssi = -110 + (rxlev - 1);
|
|
LOG_INF("RSSI: %d", mctx.data_rssi);
|
|
} else {
|
|
mctx.data_rssi = -1000;
|
|
LOG_INF("RSRP/RSSI not known");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2) \
|
|
|| defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
/* Handler: +CSQ: <signal_power>[0],<qual>[1] */
|
|
MODEM_CMD_DEFINE(on_cmd_atcmdinfo_rssi_csq)
|
|
{
|
|
int rssi;
|
|
|
|
rssi = ATOI(argv[0], 0, "signal_power");
|
|
if (rssi == 31) {
|
|
mctx.data_rssi = -46;
|
|
} else if (rssi >= 0 && rssi <= 31) {
|
|
/* FIXME: This value depends on the RAT */
|
|
mctx.data_rssi = -110 + ((rssi * 2) + 1);
|
|
} else {
|
|
mctx.data_rssi = -1000;
|
|
}
|
|
|
|
LOG_INF("RSSI: %d", mctx.data_rssi);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Modem Socket Command Handlers
|
|
*/
|
|
|
|
/* Handler: +USOCR: <socket_id>[0] */
|
|
MODEM_CMD_DEFINE(on_cmd_sockcreate)
|
|
{
|
|
struct modem_socket *sock = NULL;
|
|
|
|
/* look up new socket by special id */
|
|
sock = modem_socket_from_newid(&mdata.socket_config);
|
|
if (sock) {
|
|
sock->id = ATOI(argv[0],
|
|
mdata.socket_config.base_socket_num - 1,
|
|
"socket_id");
|
|
/* on error give up modem socket */
|
|
if (sock->id == mdata.socket_config.base_socket_num - 1) {
|
|
modem_socket_put(&mdata.socket_config, sock->sock_fd);
|
|
}
|
|
}
|
|
|
|
/* don't give back semaphore -- OK to follow */
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: +USO[WR|ST]: <socket_id>[0],<length>[1] */
|
|
MODEM_CMD_DEFINE(on_cmd_sockwrite)
|
|
{
|
|
mdata.sock_written = ATOI(argv[1], 0, "length");
|
|
LOG_DBG("bytes written: %d", mdata.sock_written);
|
|
return 0;
|
|
}
|
|
|
|
/* Common code for +USOR[D|F]: "<data>" */
|
|
static int on_cmd_sockread_common(int socket_id,
|
|
struct modem_cmd_handler_data *data,
|
|
int socket_data_length, uint16_t len)
|
|
{
|
|
struct modem_socket *sock = NULL;
|
|
struct socket_read_data *sock_data;
|
|
int ret;
|
|
|
|
if (!len) {
|
|
LOG_ERR("Short +USOR[D|F] value. Aborting!");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/*
|
|
* make sure we still have buf data and next char in the buffer is a
|
|
* quote.
|
|
*/
|
|
if (!data->rx_buf || *data->rx_buf->data != '\"') {
|
|
LOG_ERR("Incorrect format! Ignoring data!");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* zero length */
|
|
if (socket_data_length <= 0) {
|
|
LOG_ERR("Length problem (%d). Aborting!", socket_data_length);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* check to make sure we have all of the data (minus quotes) */
|
|
if ((net_buf_frags_len(data->rx_buf) - 2) < socket_data_length) {
|
|
LOG_DBG("Not enough data -- wait!");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* skip quote */
|
|
len--;
|
|
net_buf_pull_u8(data->rx_buf);
|
|
if (!data->rx_buf->len) {
|
|
data->rx_buf = net_buf_frag_del(NULL, data->rx_buf);
|
|
}
|
|
|
|
sock = modem_socket_from_id(&mdata.socket_config, socket_id);
|
|
if (!sock) {
|
|
LOG_ERR("Socket not found! (%d)", socket_id);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
sock_data = (struct socket_read_data *)sock->data;
|
|
if (!sock_data) {
|
|
LOG_ERR("Socket data not found! Skip handling (%d)", socket_id);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
ret = net_buf_linearize(sock_data->recv_buf, sock_data->recv_buf_len,
|
|
data->rx_buf, 0, (uint16_t)socket_data_length);
|
|
data->rx_buf = net_buf_skip(data->rx_buf, ret);
|
|
sock_data->recv_read_len = ret;
|
|
if (ret != socket_data_length) {
|
|
LOG_ERR("Total copied data is different then received data!"
|
|
" copied:%d vs. received:%d", ret, socket_data_length);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
exit:
|
|
/* remove packet from list (ignore errors) */
|
|
(void)modem_socket_packet_size_update(&mdata.socket_config, sock,
|
|
-socket_data_length);
|
|
|
|
/* don't give back semaphore -- OK to follow */
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Handler: +USORF: <socket_id>[0],<remote_ip_addr>[1],<remote_port>[2],
|
|
* <length>[3],"<data>"
|
|
*/
|
|
MODEM_CMD_DEFINE(on_cmd_sockreadfrom)
|
|
{
|
|
/* TODO: handle remote_ip_addr */
|
|
|
|
return on_cmd_sockread_common(ATOI(argv[0], 0, "socket_id"), data,
|
|
ATOI(argv[3], 0, "length"), len);
|
|
}
|
|
|
|
/* Handler: +USORD: <socket_id>[0],<length>[1],"<data>" */
|
|
MODEM_CMD_DEFINE(on_cmd_sockread)
|
|
{
|
|
return on_cmd_sockread_common(ATOI(argv[0], 0, "socket_id"), data,
|
|
ATOI(argv[1], 0, "length"), len);
|
|
}
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
/* Handler: +UDNSRN: "<resolved_ip_address>"[0], "<resolved_ip_address>"[1] */
|
|
MODEM_CMD_DEFINE(on_cmd_dns)
|
|
{
|
|
/* chop off end quote */
|
|
argv[0][strlen(argv[0]) - 1] = '\0';
|
|
|
|
/* FIXME: Hard-code DNS on SARA-R4 to return IPv4 */
|
|
result_addr.sa_family = AF_INET;
|
|
/* skip beginning quote when parsing */
|
|
(void)net_addr_pton(result.ai_family, &argv[0][1],
|
|
&((struct sockaddr_in *)&result_addr)->sin_addr);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* MODEM UNSOLICITED NOTIFICATION HANDLERS
|
|
*/
|
|
|
|
/* Handler: +UUSOCL: <socket_id>[0] */
|
|
MODEM_CMD_DEFINE(on_cmd_socknotifyclose)
|
|
{
|
|
struct modem_socket *sock;
|
|
|
|
sock = modem_socket_from_id(&mdata.socket_config,
|
|
ATOI(argv[0], 0, "socket_id"));
|
|
if (sock) {
|
|
sock->is_connected = false;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: +UUSOR[D|F]: <socket_id>[0],<length>[1] */
|
|
MODEM_CMD_DEFINE(on_cmd_socknotifydata)
|
|
{
|
|
int ret, socket_id, new_total;
|
|
struct modem_socket *sock;
|
|
|
|
socket_id = ATOI(argv[0], 0, "socket_id");
|
|
new_total = ATOI(argv[1], 0, "length");
|
|
sock = modem_socket_from_id(&mdata.socket_config, socket_id);
|
|
if (!sock) {
|
|
return 0;
|
|
}
|
|
|
|
ret = modem_socket_packet_size_update(&mdata.socket_config, sock,
|
|
new_total);
|
|
if (ret < 0) {
|
|
LOG_ERR("socket_id:%d left_bytes:%d err: %d", socket_id,
|
|
new_total, ret);
|
|
}
|
|
|
|
if (new_total > 0) {
|
|
modem_socket_data_ready(&mdata.socket_config, sock);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Handler: +CREG: <stat>[0] */
|
|
MODEM_CMD_DEFINE(on_cmd_socknotifycreg)
|
|
{
|
|
mdata.ev_creg = ATOI(argv[0], 0, "stat");
|
|
LOG_DBG("CREG:%d", mdata.ev_creg);
|
|
return 0;
|
|
}
|
|
|
|
/* RX thread */
|
|
static void modem_rx(void)
|
|
{
|
|
while (true) {
|
|
/* wait for incoming data */
|
|
k_sem_take(&mdata.iface_data.rx_sem, K_FOREVER);
|
|
|
|
mctx.cmd_handler.process(&mctx.cmd_handler, &mctx.iface);
|
|
|
|
/* give up time if we have a solid stream of data */
|
|
k_yield();
|
|
}
|
|
}
|
|
|
|
static int pin_init(void)
|
|
{
|
|
LOG_INF("Setting Modem Pins");
|
|
|
|
LOG_DBG("MDM_RESET_PIN -> NOT_ASSERTED");
|
|
modem_pin_write(&mctx, MDM_RESET, MDM_RESET_NOT_ASSERTED);
|
|
|
|
LOG_DBG("MDM_POWER_PIN -> ENABLE");
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_ENABLE);
|
|
k_sleep(K_SECONDS(4));
|
|
|
|
LOG_DBG("MDM_POWER_PIN -> DISABLE");
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_DISABLE);
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
k_sleep(K_SECONDS(1));
|
|
#else
|
|
k_sleep(K_SECONDS(4));
|
|
#endif
|
|
LOG_DBG("MDM_POWER_PIN -> ENABLE");
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_ENABLE);
|
|
k_sleep(K_SECONDS(1));
|
|
|
|
/* make sure module is powered off */
|
|
#if DT_INST_NODE_HAS_PROP(0, mdm_vint_gpios)
|
|
LOG_DBG("Waiting for MDM_VINT_PIN = 0");
|
|
|
|
while (modem_pin_read(&mctx, MDM_VINT) > 0) {
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
/* try to power off again */
|
|
LOG_DBG("MDM_POWER_PIN -> DISABLE");
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_DISABLE);
|
|
k_sleep(K_SECONDS(1));
|
|
LOG_DBG("MDM_POWER_PIN -> ENABLE");
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_ENABLE);
|
|
#endif
|
|
k_sleep(K_MSEC(100));
|
|
}
|
|
#else
|
|
k_sleep(K_SECONDS(8));
|
|
#endif
|
|
|
|
LOG_DBG("MDM_POWER_PIN -> DISABLE");
|
|
|
|
unsigned int irq_lock_key = irq_lock();
|
|
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_DISABLE);
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
k_usleep(50); /* 50-80 microseconds */
|
|
#else
|
|
k_sleep(K_SECONDS(1));
|
|
#endif
|
|
modem_pin_write(&mctx, MDM_POWER, MDM_POWER_ENABLE);
|
|
|
|
irq_unlock(irq_lock_key);
|
|
|
|
LOG_DBG("MDM_POWER_PIN -> ENABLE");
|
|
|
|
#if DT_INST_NODE_HAS_PROP(0, mdm_vint_gpios)
|
|
LOG_DBG("Waiting for MDM_VINT_PIN = 1");
|
|
do {
|
|
k_sleep(K_MSEC(100));
|
|
} while (modem_pin_read(&mctx, MDM_VINT) == 0);
|
|
#else
|
|
k_sleep(K_SECONDS(10));
|
|
#endif
|
|
|
|
modem_pin_config(&mctx, MDM_POWER, false);
|
|
|
|
LOG_INF("... Done!");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
static void modem_rssi_query_work(struct k_work *work)
|
|
{
|
|
static const struct modem_cmd cmds[] = {
|
|
MODEM_CMD("+CSQ: ", on_cmd_atcmdinfo_rssi_csq, 2U, ","),
|
|
MODEM_CMD("+CESQ: ", on_cmd_atcmdinfo_rssi_cesq, 6U, ","),
|
|
};
|
|
const char *send_cmd_u2 = "AT+CSQ";
|
|
const char *send_cmd_r4 = "AT+CESQ";
|
|
int ret;
|
|
|
|
/* choose cmd according to variant */
|
|
const char *send_cmd = send_cmd_r4;
|
|
|
|
if (mdata.mdm_variant == MDM_VARIANT_UBLOX_U2) {
|
|
send_cmd = send_cmd_u2;
|
|
}
|
|
|
|
/* query modem RSSI */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
cmds, ARRAY_SIZE(cmds),
|
|
send_cmd,
|
|
&mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("AT+C[E]SQ ret:%d", ret);
|
|
}
|
|
|
|
/* re-start RSSI query work */
|
|
if (work) {
|
|
k_delayed_work_submit_to_queue(&modem_workq,
|
|
&mdata.rssi_query_work,
|
|
K_SECONDS(RSSI_TIMEOUT_SECS));
|
|
}
|
|
}
|
|
#else
|
|
static void modem_rssi_query_work(struct k_work *work)
|
|
{
|
|
static const struct modem_cmd cmd =
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
MODEM_CMD("+CSQ: ", on_cmd_atcmdinfo_rssi_csq, 2U, ",");
|
|
static char *send_cmd = "AT+CSQ";
|
|
#else
|
|
MODEM_CMD("+CESQ: ", on_cmd_atcmdinfo_rssi_cesq, 6U, ",");
|
|
static char *send_cmd = "AT+CESQ";
|
|
#endif
|
|
int ret;
|
|
|
|
/* query modem RSSI */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
&cmd, 1U, send_cmd, &mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("AT+C[E]SQ ret:%d", ret);
|
|
}
|
|
|
|
/* re-start RSSI query work */
|
|
if (work) {
|
|
k_delayed_work_submit_to_queue(&modem_workq,
|
|
&mdata.rssi_query_work,
|
|
K_SECONDS(RSSI_TIMEOUT_SECS));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void modem_reset(void)
|
|
{
|
|
int ret = 0, retry_count = 0, counter = 0;
|
|
static const struct setup_cmd setup_cmds[] = {
|
|
/* turn off echo */
|
|
SETUP_CMD_NOHANDLE("ATE0"),
|
|
/* stop functionality */
|
|
SETUP_CMD_NOHANDLE("AT+CFUN=0"),
|
|
/* extended error numbers */
|
|
SETUP_CMD_NOHANDLE("AT+CMEE=1"),
|
|
#if defined(CONFIG_BOARD_PARTICLE_BORON)
|
|
/* use external SIM */
|
|
SETUP_CMD_NOHANDLE("AT+UGPIOC=23,0,0"),
|
|
#endif
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_R4_NET_STATUS_PIN)
|
|
/* enable the network status indication */
|
|
SETUP_CMD_NOHANDLE("AT+UGPIOC="
|
|
STRINGIFY(CONFIG_MODEM_UBLOX_SARA_R4_NET_STATUS_PIN)
|
|
",2"),
|
|
#endif
|
|
/* UNC messages for registration */
|
|
SETUP_CMD_NOHANDLE("AT+CREG=1"),
|
|
/* query modem info */
|
|
SETUP_CMD("AT+CGMI", "", on_cmd_atcmdinfo_manufacturer, 0U, ""),
|
|
SETUP_CMD("AT+CGMM", "", on_cmd_atcmdinfo_model, 0U, ""),
|
|
SETUP_CMD("AT+CGMR", "", on_cmd_atcmdinfo_revision, 0U, ""),
|
|
SETUP_CMD("AT+CGSN", "", on_cmd_atcmdinfo_imei, 0U, ""),
|
|
SETUP_CMD("AT+CIMI", "", on_cmd_atcmdinfo_imsi, 0U, ""),
|
|
#if !defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* setup PDP context definition */
|
|
SETUP_CMD_NOHANDLE("AT+CGDCONT=1,\"IP\",\""
|
|
CONFIG_MODEM_UBLOX_SARA_R4_APN "\""),
|
|
/* start functionality */
|
|
SETUP_CMD_NOHANDLE("AT+CFUN=1"),
|
|
#endif
|
|
};
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
static const struct setup_cmd post_setup_cmds_u2[] = {
|
|
#if !defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* set the APN */
|
|
SETUP_CMD_NOHANDLE("AT+UPSD=0,1,\""
|
|
CONFIG_MODEM_UBLOX_SARA_R4_APN "\""),
|
|
#endif
|
|
/* set dynamic IP */
|
|
SETUP_CMD_NOHANDLE("AT+UPSD=0,7,\"0.0.0.0\""),
|
|
/* activate the GPRS connection */
|
|
SETUP_CMD_NOHANDLE("AT+UPSDA=0,3"),
|
|
};
|
|
#endif
|
|
|
|
static const struct setup_cmd post_setup_cmds[] = {
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_U2)
|
|
/* set the APN */
|
|
SETUP_CMD_NOHANDLE("AT+UPSD=0,1,\""
|
|
CONFIG_MODEM_UBLOX_SARA_R4_APN "\""),
|
|
/* set dynamic IP */
|
|
SETUP_CMD_NOHANDLE("AT+UPSD=0,7,\"0.0.0.0\""),
|
|
/* activate the GPRS connection */
|
|
SETUP_CMD_NOHANDLE("AT+UPSDA=0,3"),
|
|
#else
|
|
/* activate the PDP context */
|
|
SETUP_CMD_NOHANDLE("AT+CGACT=1,1"),
|
|
#endif
|
|
};
|
|
|
|
restart:
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
mdata.mdm_apn[0] = '\0';
|
|
strncat(mdata.mdm_apn,
|
|
CONFIG_MODEM_UBLOX_SARA_R4_APN,
|
|
sizeof(mdata.mdm_apn)-1);
|
|
#endif
|
|
|
|
/* stop RSSI delay work */
|
|
k_delayed_work_cancel(&mdata.rssi_query_work);
|
|
|
|
pin_init();
|
|
|
|
LOG_INF("Waiting for modem to respond");
|
|
|
|
/* Give the modem a while to start responding to simple 'AT' commands.
|
|
* Also wait for CSPS=1 or RRCSTATE=1 notification
|
|
*/
|
|
ret = -1;
|
|
while (counter++ < 50 && ret < 0) {
|
|
k_sleep(K_SECONDS(2));
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0, "AT", &mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
if (ret < 0 && ret != -ETIMEDOUT) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("MODEM WAIT LOOP ERROR: %d", ret);
|
|
goto error;
|
|
}
|
|
|
|
ret = modem_cmd_handler_setup_cmds(&mctx.iface, &mctx.cmd_handler,
|
|
setup_cmds, ARRAY_SIZE(setup_cmds),
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* autodetect APN from IMSI */
|
|
char cmd[sizeof("AT+CGDCONT=1,\"IP\",\"\"")+MDM_APN_LENGTH];
|
|
|
|
snprintf(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"", mdata.mdm_apn);
|
|
|
|
/* setup PDP context definition */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0,
|
|
(const char *)cmd,
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0,
|
|
"AT+CFUN=1",
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
#endif
|
|
|
|
if (strlen(CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO) > 0) {
|
|
/* use manual MCC/MNO entry */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0,
|
|
"AT+COPS=1,2,\""
|
|
CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO
|
|
"\"",
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
} else {
|
|
/* register operator automatically */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0, "AT+COPS=0,0",
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
}
|
|
|
|
if (ret < 0) {
|
|
LOG_ERR("AT+COPS ret:%d", ret);
|
|
goto error;
|
|
}
|
|
|
|
LOG_INF("Waiting for network");
|
|
|
|
/*
|
|
* TODO: A lot of this should be setup as a 3GPP module to handle
|
|
* basic connection to the network commands / polling
|
|
*/
|
|
|
|
/* wait for +CREG: 1(normal) or 5(roaming) */
|
|
counter = 0;
|
|
while (counter++ < 40 && mdata.ev_creg != 1 && mdata.ev_creg != 5) {
|
|
if (counter == 20) {
|
|
LOG_WRN("Force restart of RF functionality");
|
|
|
|
/* Disable RF temporarily */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0, "AT+CFUN=0", &mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
|
|
k_sleep(K_SECONDS(1));
|
|
|
|
/* Enable RF */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0, "AT+CFUN=1", &mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
}
|
|
|
|
k_sleep(K_SECONDS(1));
|
|
}
|
|
|
|
/* query modem RSSI */
|
|
modem_rssi_query_work(NULL);
|
|
k_sleep(MDM_WAIT_FOR_RSSI_DELAY);
|
|
|
|
counter = 0;
|
|
/* wait for RSSI < 0 and > -1000 */
|
|
while (counter++ < MDM_WAIT_FOR_RSSI_COUNT &&
|
|
(mctx.data_rssi >= 0 ||
|
|
mctx.data_rssi <= -1000)) {
|
|
modem_rssi_query_work(NULL);
|
|
k_sleep(MDM_WAIT_FOR_RSSI_DELAY);
|
|
}
|
|
|
|
if (mctx.data_rssi >= 0 || mctx.data_rssi <= -1000) {
|
|
retry_count++;
|
|
if (retry_count >= MDM_NETWORK_RETRY_COUNT) {
|
|
LOG_ERR("Failed network init. Too many attempts!");
|
|
ret = -ENETUNREACH;
|
|
goto error;
|
|
}
|
|
|
|
LOG_ERR("Failed network init. Restarting process.");
|
|
goto restart;
|
|
}
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
if (mdata.mdm_variant == MDM_VARIANT_UBLOX_U2) {
|
|
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_APN)
|
|
/* setup PDP context definition */
|
|
char cmd[sizeof("AT+UPSD=0,1,\"%s\"")+MDM_APN_LENGTH];
|
|
|
|
snprintf(cmd, sizeof(cmd), "AT+UPSD=0,1,\"%s\"", mdata.mdm_apn);
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0,
|
|
(const char *)cmd,
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
#endif
|
|
ret = modem_cmd_handler_setup_cmds(&mctx.iface,
|
|
&mctx.cmd_handler,
|
|
post_setup_cmds_u2,
|
|
ARRAY_SIZE(post_setup_cmds_u2),
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
} else {
|
|
#endif
|
|
ret = modem_cmd_handler_setup_cmds(&mctx.iface,
|
|
&mctx.cmd_handler,
|
|
post_setup_cmds,
|
|
ARRAY_SIZE(post_setup_cmds),
|
|
&mdata.sem_response,
|
|
MDM_REGISTRATION_TIMEOUT);
|
|
#if defined(CONFIG_MODEM_UBLOX_SARA_AUTODETECT_VARIANT)
|
|
}
|
|
#endif
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
LOG_INF("Network is ready.");
|
|
|
|
/* start RSSI query */
|
|
k_delayed_work_submit_to_queue(&modem_workq,
|
|
&mdata.rssi_query_work,
|
|
K_SECONDS(RSSI_TIMEOUT_SECS));
|
|
|
|
error:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* generic socket creation function
|
|
* which can be called in bind() or connect()
|
|
*/
|
|
static int create_socket(struct modem_socket *sock, const struct sockaddr *addr)
|
|
{
|
|
int ret;
|
|
static const struct modem_cmd cmd =
|
|
MODEM_CMD("+USOCR: ", on_cmd_sockcreate, 1U, "");
|
|
char buf[sizeof("AT+USOCR=#,#####\r")];
|
|
uint16_t local_port = 0U, proto = 6U;
|
|
|
|
if (addr) {
|
|
if (addr->sa_family == AF_INET6) {
|
|
local_port = ntohs(net_sin6(addr)->sin6_port);
|
|
} else if (addr->sa_family == AF_INET) {
|
|
local_port = ntohs(net_sin(addr)->sin_port);
|
|
}
|
|
}
|
|
|
|
if (sock->ip_proto == IPPROTO_UDP) {
|
|
proto = 17U;
|
|
}
|
|
|
|
if (local_port > 0U) {
|
|
snprintk(buf, sizeof(buf), "AT+USOCR=%d,%u", proto, local_port);
|
|
} else {
|
|
snprintk(buf, sizeof(buf), "AT+USOCR=%d", proto);
|
|
}
|
|
|
|
/* create socket */
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
&cmd, 1U, buf,
|
|
&mdata.sem_response, MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s ret:%d", log_strdup(buf), ret);
|
|
modem_socket_put(&mdata.socket_config, sock->sock_fd);
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Socket Offload OPS
|
|
*/
|
|
|
|
static const struct socket_op_vtable offload_socket_fd_op_vtable;
|
|
|
|
static int offload_socket(int family, int type, int proto)
|
|
{
|
|
int ret;
|
|
|
|
/* defer modem's socket create call to bind() */
|
|
ret = modem_socket_get(&mdata.socket_config, family, type, proto);
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int offload_close(void *obj)
|
|
{
|
|
struct modem_socket *sock = (struct modem_socket *)obj;
|
|
char buf[sizeof("AT+USOCL=#\r")];
|
|
int ret;
|
|
|
|
/* make sure we assigned an id */
|
|
if (sock->id < mdata.socket_config.base_socket_num) {
|
|
return 0;
|
|
}
|
|
|
|
if (sock->is_connected || sock->ip_proto == IPPROTO_UDP) {
|
|
snprintk(buf, sizeof(buf), "AT+USOCL=%d", sock->id);
|
|
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0U, buf,
|
|
&mdata.sem_response, MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s ret:%d", log_strdup(buf), ret);
|
|
}
|
|
}
|
|
|
|
modem_socket_put(&mdata.socket_config, sock->sock_fd);
|
|
return 0;
|
|
}
|
|
|
|
static int offload_bind(void *obj, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
struct modem_socket *sock = (struct modem_socket *)obj;
|
|
|
|
/* save bind address information */
|
|
memcpy(&sock->src, addr, sizeof(*addr));
|
|
|
|
/* make sure we've created the socket */
|
|
if (sock->id == mdata.socket_config.sockets_len + 1) {
|
|
if (create_socket(sock, addr) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int offload_connect(void *obj, const struct sockaddr *addr,
|
|
socklen_t addrlen)
|
|
{
|
|
struct modem_socket *sock = (struct modem_socket *)obj;
|
|
int ret;
|
|
char buf[sizeof("AT+USOCO=#,!###.###.###.###!,#####,#\r")];
|
|
uint16_t dst_port = 0U;
|
|
|
|
if (!addr) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (sock->id < mdata.socket_config.base_socket_num - 1) {
|
|
LOG_ERR("Invalid socket_id(%d) from fd:%d",
|
|
sock->id, sock->sock_fd);
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/* make sure we've created the socket */
|
|
if (sock->id == mdata.socket_config.sockets_len + 1) {
|
|
if (create_socket(sock, NULL) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
memcpy(&sock->dst, addr, sizeof(*addr));
|
|
if (addr->sa_family == AF_INET6) {
|
|
dst_port = ntohs(net_sin6(addr)->sin6_port);
|
|
} else if (addr->sa_family == AF_INET) {
|
|
dst_port = ntohs(net_sin(addr)->sin_port);
|
|
} else {
|
|
errno = EAFNOSUPPORT;
|
|
return -1;
|
|
}
|
|
|
|
/* skip socket connect if UDP */
|
|
if (sock->ip_proto == IPPROTO_UDP) {
|
|
errno = 0;
|
|
return 0;
|
|
}
|
|
|
|
snprintk(buf, sizeof(buf), "AT+USOCO=%d,\"%s\",%d", sock->id,
|
|
modem_context_sprint_ip_addr(addr), dst_port);
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
NULL, 0U, buf,
|
|
&mdata.sem_response, MDM_CMD_CONN_TIMEOUT);
|
|
if (ret < 0) {
|
|
LOG_ERR("%s ret:%d", log_strdup(buf), ret);
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
sock->is_connected = true;
|
|
errno = 0;
|
|
return 0;
|
|
}
|
|
|
|
/* support for POLLIN only for now. */
|
|
static int offload_poll(struct zsock_pollfd *fds, int nfds, int msecs)
|
|
{
|
|
int i;
|
|
void *obj;
|
|
|
|
/* Only accept modem sockets. */
|
|
for (i = 0; i < nfds; i++) {
|
|
if (fds[i].fd < 0) {
|
|
continue;
|
|
}
|
|
|
|
/* If vtable matches, then it's modem socket. */
|
|
obj = z_get_fd_obj(fds[i].fd,
|
|
(const struct fd_op_vtable *)
|
|
&offload_socket_fd_op_vtable,
|
|
EINVAL);
|
|
if (obj == NULL) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return modem_socket_poll(&mdata.socket_config, fds, nfds, msecs);
|
|
}
|
|
|
|
static ssize_t offload_recvfrom(void *obj, void *buf, size_t len,
|
|
int flags, struct sockaddr *from,
|
|
socklen_t *fromlen)
|
|
{
|
|
struct modem_socket *sock = (struct modem_socket *)obj;
|
|
int ret, next_packet_size;
|
|
static const struct modem_cmd cmd[] = {
|
|
MODEM_CMD("+USORF: ", on_cmd_sockreadfrom, 4U, ","),
|
|
MODEM_CMD("+USORD: ", on_cmd_sockread, 2U, ","),
|
|
};
|
|
char sendbuf[sizeof("AT+USORF=#,#####\r")];
|
|
struct socket_read_data sock_data;
|
|
|
|
if (!buf || len == 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (flags & ZSOCK_MSG_PEEK) {
|
|
errno = ENOTSUP;
|
|
return -1;
|
|
}
|
|
|
|
next_packet_size = modem_socket_next_packet_size(&mdata.socket_config,
|
|
sock);
|
|
if (!next_packet_size) {
|
|
if (flags & ZSOCK_MSG_DONTWAIT) {
|
|
errno = EAGAIN;
|
|
return -1;
|
|
}
|
|
|
|
if (!sock->is_connected && sock->ip_proto != IPPROTO_UDP) {
|
|
errno = 0;
|
|
return 0;
|
|
}
|
|
|
|
modem_socket_wait_data(&mdata.socket_config, sock);
|
|
next_packet_size = modem_socket_next_packet_size(
|
|
&mdata.socket_config, sock);
|
|
}
|
|
|
|
/*
|
|
* Binary and ASCII mode allows sending MDM_MAX_DATA_LENGTH bytes to
|
|
* the socket in one command
|
|
*/
|
|
if (next_packet_size > MDM_MAX_DATA_LENGTH) {
|
|
next_packet_size = MDM_MAX_DATA_LENGTH;
|
|
}
|
|
|
|
snprintk(sendbuf, sizeof(sendbuf), "AT+USO%s=%d,%d",
|
|
sock->ip_proto == IPPROTO_UDP ? "RF" : "RD", sock->id,
|
|
len < next_packet_size ? len : next_packet_size);
|
|
|
|
/* socket read settings */
|
|
(void)memset(&sock_data, 0, sizeof(sock_data));
|
|
sock_data.recv_buf = buf;
|
|
sock_data.recv_buf_len = len;
|
|
sock_data.recv_addr = from;
|
|
sock->data = &sock_data;
|
|
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
cmd, ARRAY_SIZE(cmd), sendbuf, &mdata.sem_response,
|
|
MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
|
|
/* HACK: use dst address as from */
|
|
if (from && fromlen) {
|
|
*fromlen = sizeof(sock->dst);
|
|
memcpy(from, &sock->dst, *fromlen);
|
|
}
|
|
|
|
/* return length of received data */
|
|
errno = 0;
|
|
ret = sock_data.recv_read_len;
|
|
|
|
exit:
|
|
/* clear socket data */
|
|
sock->data = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t offload_sendto(void *obj, const void *buf, size_t len,
|
|
int flags, const struct sockaddr *to,
|
|
socklen_t tolen)
|
|
{
|
|
int ret;
|
|
struct modem_socket *sock = (struct modem_socket *)obj;
|
|
static const struct modem_cmd cmd[] = {
|
|
MODEM_CMD("+USOST: ", on_cmd_sockwrite, 2U, ","),
|
|
MODEM_CMD("+USOWR: ", on_cmd_sockwrite, 2U, ","),
|
|
};
|
|
|
|
if (!buf || len == 0) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (!sock->is_connected && sock->ip_proto != IPPROTO_UDP) {
|
|
errno = ENOTCONN;
|
|
return -1;
|
|
}
|
|
|
|
if (!to && sock->ip_proto == IPPROTO_UDP) {
|
|
to = &sock->dst;
|
|
}
|
|
|
|
ret = send_socket_data(sock, to, cmd, ARRAY_SIZE(cmd), buf, len,
|
|
MDM_CMD_TIMEOUT);
|
|
if (ret < 0) {
|
|
errno = -ret;
|
|
return -1;
|
|
}
|
|
|
|
errno = 0;
|
|
return ret;
|
|
}
|
|
|
|
static int offload_ioctl(void *obj, unsigned int request, va_list args)
|
|
{
|
|
switch (request) {
|
|
case ZFD_IOCTL_POLL_PREPARE:
|
|
return -EXDEV;
|
|
|
|
case ZFD_IOCTL_POLL_UPDATE:
|
|
return -EOPNOTSUPP;
|
|
|
|
case ZFD_IOCTL_POLL_OFFLOAD: {
|
|
struct zsock_pollfd *fds;
|
|
int nfds;
|
|
int timeout;
|
|
|
|
fds = va_arg(args, struct zsock_pollfd *);
|
|
nfds = va_arg(args, int);
|
|
timeout = va_arg(args, int);
|
|
|
|
return offload_poll(fds, nfds, timeout);
|
|
}
|
|
|
|
default:
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static ssize_t offload_read(void *obj, void *buffer, size_t count)
|
|
{
|
|
return offload_recvfrom(obj, buffer, count, 0, NULL, 0);
|
|
}
|
|
|
|
static ssize_t offload_write(void *obj, const void *buffer, size_t count)
|
|
{
|
|
return offload_sendto(obj, buffer, count, 0, NULL, 0);
|
|
}
|
|
|
|
static ssize_t offload_sendmsg(void *obj, const struct msghdr *msg, int flags)
|
|
{
|
|
ssize_t sent = 0;
|
|
int rc;
|
|
|
|
LOG_DBG("msg_iovlen:%d flags:%d", msg->msg_iovlen, flags);
|
|
|
|
for (int i = 0; i < msg->msg_iovlen; i++) {
|
|
|
|
const char *buf = msg->msg_iov[i].iov_base;
|
|
size_t len = msg->msg_iov[i].iov_len;
|
|
|
|
while (len > 0) {
|
|
rc = offload_sendto(obj, buf, len, flags,
|
|
msg->msg_name,
|
|
msg->msg_namelen);
|
|
if (rc < 0) {
|
|
if (rc == -EAGAIN) {
|
|
k_sleep(MDM_SENDMSG_SLEEP);
|
|
} else {
|
|
sent = rc;
|
|
break;
|
|
}
|
|
} else {
|
|
sent += rc;
|
|
buf += rc;
|
|
len -= rc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return (ssize_t)sent;
|
|
}
|
|
|
|
static const struct socket_op_vtable offload_socket_fd_op_vtable = {
|
|
.fd_vtable = {
|
|
.read = offload_read,
|
|
.write = offload_write,
|
|
.close = offload_close,
|
|
.ioctl = offload_ioctl,
|
|
},
|
|
.bind = offload_bind,
|
|
.connect = offload_connect,
|
|
.sendto = offload_sendto,
|
|
.recvfrom = offload_recvfrom,
|
|
.listen = NULL,
|
|
.accept = NULL,
|
|
.sendmsg = offload_sendmsg,
|
|
.getsockopt = NULL,
|
|
.setsockopt = NULL,
|
|
};
|
|
|
|
static bool offload_is_supported(int family, int type, int proto)
|
|
{
|
|
/* TODO offloading always enabled for now. */
|
|
return true;
|
|
}
|
|
|
|
NET_SOCKET_REGISTER(ublox_sara_r4, AF_UNSPEC, offload_is_supported,
|
|
offload_socket);
|
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
/* TODO: This is a bare-bones implementation of DNS handling
|
|
* We ignore most of the hints like ai_family, ai_protocol and ai_socktype.
|
|
* Later, we can add additional handling if it makes sense.
|
|
*/
|
|
static int offload_getaddrinfo(const char *node, const char *service,
|
|
const struct zsock_addrinfo *hints,
|
|
struct zsock_addrinfo **res)
|
|
{
|
|
static const struct modem_cmd cmd =
|
|
MODEM_CMD("+UDNSRN: ", on_cmd_dns, 1U, ",");
|
|
uint32_t port = 0U;
|
|
int ret;
|
|
/* DNS command + 128 bytes for domain name parameter */
|
|
char sendbuf[sizeof("AT+UDNSRN=#,'[]'\r") + 128];
|
|
|
|
/* init result */
|
|
(void)memset(&result, 0, sizeof(result));
|
|
(void)memset(&result_addr, 0, sizeof(result_addr));
|
|
/* FIXME: Hard-code DNS to return only IPv4 */
|
|
result.ai_family = AF_INET;
|
|
result_addr.sa_family = AF_INET;
|
|
result.ai_addr = &result_addr;
|
|
result.ai_addrlen = sizeof(result_addr);
|
|
result.ai_canonname = result_canonname;
|
|
result_canonname[0] = '\0';
|
|
|
|
if (service) {
|
|
port = ATOI(service, 0U, "port");
|
|
if (port < 1 || port > USHRT_MAX) {
|
|
return DNS_EAI_SERVICE;
|
|
}
|
|
}
|
|
|
|
if (port > 0U) {
|
|
/* FIXME: DNS is hard-coded to return only IPv4 */
|
|
if (result.ai_family == AF_INET) {
|
|
net_sin(&result_addr)->sin_port = htons(port);
|
|
}
|
|
}
|
|
|
|
/* check to see if node is an IP address */
|
|
if (net_addr_pton(result.ai_family, node,
|
|
&((struct sockaddr_in *)&result_addr)->sin_addr)
|
|
== 0) {
|
|
*res = &result;
|
|
return 0;
|
|
}
|
|
|
|
/* user flagged node as numeric host, but we failed net_addr_pton */
|
|
if (hints && hints->ai_flags & AI_NUMERICHOST) {
|
|
return DNS_EAI_NONAME;
|
|
}
|
|
|
|
snprintk(sendbuf, sizeof(sendbuf), "AT+UDNSRN=0,\"%s\"", node);
|
|
ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler,
|
|
&cmd, 1U, sendbuf, &mdata.sem_response,
|
|
MDM_DNS_TIMEOUT);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
|
|
LOG_DBG("DNS RESULT: %s",
|
|
log_strdup(net_addr_ntop(result.ai_family,
|
|
&net_sin(&result_addr)->sin_addr,
|
|
sendbuf, NET_IPV4_ADDR_LEN)));
|
|
|
|
*res = (struct zsock_addrinfo *)&result;
|
|
return 0;
|
|
}
|
|
|
|
static void offload_freeaddrinfo(struct zsock_addrinfo *res)
|
|
{
|
|
/* using static result from offload_getaddrinfo() -- no need to free */
|
|
res = NULL;
|
|
}
|
|
|
|
const struct socket_dns_offload offload_dns_ops = {
|
|
.getaddrinfo = offload_getaddrinfo,
|
|
.freeaddrinfo = offload_freeaddrinfo,
|
|
};
|
|
#endif
|
|
|
|
static int net_offload_dummy_get(sa_family_t family,
|
|
enum net_sock_type type,
|
|
enum net_ip_protocol ip_proto,
|
|
struct net_context **context)
|
|
{
|
|
|
|
LOG_ERR("CONFIG_NET_SOCKETS_OFFLOAD must be enabled for this driver");
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
/* placeholders, until Zepyr IP stack updated to handle a NULL net_offload */
|
|
static struct net_offload modem_net_offload = {
|
|
.get = net_offload_dummy_get,
|
|
};
|
|
|
|
#define HASH_MULTIPLIER 37
|
|
static uint32_t hash32(char *str, int len)
|
|
{
|
|
uint32_t h = 0;
|
|
int i;
|
|
|
|
for (i = 0; i < len; ++i) {
|
|
h = (h * HASH_MULTIPLIER) + str[i];
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
static inline uint8_t *modem_get_mac(const struct device *dev)
|
|
{
|
|
struct modem_data *data = dev->data;
|
|
uint32_t hash_value;
|
|
|
|
data->mac_addr[0] = 0x00;
|
|
data->mac_addr[1] = 0x10;
|
|
|
|
/* use IMEI for mac_addr */
|
|
hash_value = hash32(mdata.mdm_imei, strlen(mdata.mdm_imei));
|
|
|
|
UNALIGNED_PUT(hash_value, (uint32_t *)(data->mac_addr + 2));
|
|
|
|
return data->mac_addr;
|
|
}
|
|
|
|
static void modem_net_iface_init(struct net_if *iface)
|
|
{
|
|
const struct device *dev = net_if_get_device(iface);
|
|
struct modem_data *data = dev->data;
|
|
|
|
/* Direct socket offload used instead of net offload: */
|
|
iface->if_dev->offload = &modem_net_offload;
|
|
net_if_set_link_addr(iface, modem_get_mac(dev),
|
|
sizeof(data->mac_addr),
|
|
NET_LINK_ETHERNET);
|
|
data->net_iface = iface;
|
|
#ifdef CONFIG_DNS_RESOLVER
|
|
socket_offload_dns_register(&offload_dns_ops);
|
|
#endif
|
|
}
|
|
|
|
static struct net_if_api api_funcs = {
|
|
.init = modem_net_iface_init,
|
|
};
|
|
|
|
static const struct modem_cmd response_cmds[] = {
|
|
MODEM_CMD("OK", on_cmd_ok, 0U, ""), /* 3GPP */
|
|
MODEM_CMD("ERROR", on_cmd_error, 0U, ""), /* 3GPP */
|
|
MODEM_CMD("+CME ERROR: ", on_cmd_exterror, 1U, ""),
|
|
};
|
|
|
|
static const struct modem_cmd unsol_cmds[] = {
|
|
MODEM_CMD("+UUSOCL: ", on_cmd_socknotifyclose, 1U, ""),
|
|
MODEM_CMD("+UUSORD: ", on_cmd_socknotifydata, 2U, ","),
|
|
MODEM_CMD("+UUSORF: ", on_cmd_socknotifydata, 2U, ","),
|
|
MODEM_CMD("+CREG: ", on_cmd_socknotifycreg, 1U, ""),
|
|
};
|
|
|
|
static int modem_init(const struct device *dev)
|
|
{
|
|
int ret = 0;
|
|
|
|
ARG_UNUSED(dev);
|
|
|
|
k_sem_init(&mdata.sem_response, 0, 1);
|
|
|
|
/* initialize the work queue */
|
|
k_work_q_start(&modem_workq,
|
|
modem_workq_stack,
|
|
K_KERNEL_STACK_SIZEOF(modem_workq_stack),
|
|
K_PRIO_COOP(7));
|
|
|
|
/* socket config */
|
|
mdata.socket_config.sockets = &mdata.sockets[0];
|
|
mdata.socket_config.sockets_len = ARRAY_SIZE(mdata.sockets);
|
|
mdata.socket_config.base_socket_num = MDM_BASE_SOCKET_NUM;
|
|
ret = modem_socket_init(&mdata.socket_config,
|
|
&offload_socket_fd_op_vtable);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* cmd handler */
|
|
mdata.cmd_handler_data.cmds[CMD_RESP] = response_cmds;
|
|
mdata.cmd_handler_data.cmds_len[CMD_RESP] = ARRAY_SIZE(response_cmds);
|
|
mdata.cmd_handler_data.cmds[CMD_UNSOL] = unsol_cmds;
|
|
mdata.cmd_handler_data.cmds_len[CMD_UNSOL] = ARRAY_SIZE(unsol_cmds);
|
|
mdata.cmd_handler_data.match_buf = &mdata.cmd_match_buf[0];
|
|
mdata.cmd_handler_data.match_buf_len = sizeof(mdata.cmd_match_buf);
|
|
mdata.cmd_handler_data.buf_pool = &mdm_recv_pool;
|
|
mdata.cmd_handler_data.alloc_timeout = K_NO_WAIT;
|
|
mdata.cmd_handler_data.eol = "\r";
|
|
ret = modem_cmd_handler_init(&mctx.cmd_handler,
|
|
&mdata.cmd_handler_data);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* modem interface */
|
|
mdata.iface_data.hw_flow_control = DT_PROP(MDM_UART_NODE,
|
|
hw_flow_control);
|
|
mdata.iface_data.rx_rb_buf = &mdata.iface_rb_buf[0];
|
|
mdata.iface_data.rx_rb_buf_len = sizeof(mdata.iface_rb_buf);
|
|
ret = modem_iface_uart_init(&mctx.iface, &mdata.iface_data,
|
|
MDM_UART_DEV_NAME);
|
|
if (ret < 0) {
|
|
goto error;
|
|
}
|
|
|
|
/* modem data storage */
|
|
mctx.data_manufacturer = mdata.mdm_manufacturer;
|
|
mctx.data_model = mdata.mdm_model;
|
|
mctx.data_revision = mdata.mdm_revision;
|
|
mctx.data_imei = mdata.mdm_imei;
|
|
|
|
/* pin setup */
|
|
mctx.pins = modem_pins;
|
|
mctx.pins_len = ARRAY_SIZE(modem_pins);
|
|
|
|
mctx.driver_data = &mdata;
|
|
|
|
ret = modem_context_register(&mctx);
|
|
if (ret < 0) {
|
|
LOG_ERR("Error registering modem context: %d", ret);
|
|
goto error;
|
|
}
|
|
|
|
/* start RX thread */
|
|
k_thread_create(&modem_rx_thread, modem_rx_stack,
|
|
K_KERNEL_STACK_SIZEOF(modem_rx_stack),
|
|
(k_thread_entry_t) modem_rx,
|
|
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
|
|
|
|
/* init RSSI query */
|
|
k_delayed_work_init(&mdata.rssi_query_work, modem_rssi_query_work);
|
|
|
|
modem_reset();
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
NET_DEVICE_OFFLOAD_INIT(modem_sara, CONFIG_MODEM_UBLOX_SARA_R4_NAME,
|
|
modem_init, device_pm_control_nop, &mdata, NULL,
|
|
CONFIG_MODEM_UBLOX_SARA_R4_INIT_PRIORITY, &api_funcs,
|
|
MDM_MAX_DATA_LENGTH);
|