diff --git a/boards/arm/disco_l475_iot1/disco_l475_iot1.dts b/boards/arm/disco_l475_iot1/disco_l475_iot1.dts index 99b3ae66d1ff4..19ed0aab9480b 100644 --- a/boards/arm/disco_l475_iot1/disco_l475_iot1.dts +++ b/boards/arm/disco_l475_iot1/disco_l475_iot1.dts @@ -45,6 +45,33 @@ sw0 = &user_button; eswifi0 = &wifi0; }; + + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map = <0 0 &gpioc 5 0>, /* A0 */ + <1 0 &gpioc 4 0>, /* A1 */ + <2 0 &gpioc 3 0>, /* A2 */ + <3 0 &gpioc 2 0>, /* A3 */ + <4 0 &gpioc 1 0>, /* A4 */ + <5 0 &gpioc 0 0>, /* A5 */ + <6 0 &gpioa 1 0>, /* D0 */ + <7 0 &gpioa 0 0>, /* D1 */ + <8 0 &gpiod 14 0>, /* D2 */ + <9 0 &gpiob 0 0>, /* D3 */ + <10 0 &gpioa 3 0>, /* D4 */ + <11 0 &gpiob 4 0>, /* D5 */ + <12 0 &gpiob 1 0>, /* D6 */ + <13 0 &gpioa 4 0>, /* D7 */ + <14 0 &gpiob 2 0>, /* D8 */ + <15 0 &gpioa 15 0>, /* D9 */ + <16 0 &gpioa 2 0>, /* D10 */ + <17 0 &gpioa 7 0>, /* D11 */ + <18 0 &gpioa 6 0>, /* D12 */ + <19 0 &gpioa 5 0>, /* D13 */ + <20 0 &gpiob 9 0>, /* D14 */ + <21 0 &gpiob 8 0>; /* D15 */ + }; }; arduino_i2c: &i2c1 {}; diff --git a/boards/arm/frdm_k64f/Kconfig.defconfig b/boards/arm/frdm_k64f/Kconfig.defconfig index 7650999bc1b86..7a2926294e286 100644 --- a/boards/arm/frdm_k64f/Kconfig.defconfig +++ b/boards/arm/frdm_k64f/Kconfig.defconfig @@ -101,7 +101,7 @@ endif # SPI if NETWORKING config NET_L2_ETHERNET - default y if !MODEM_WNCM14A2A + default y if !MODEM config ETH_MCUX_0 default y if NET_L2_ETHERNET diff --git a/boards/arm/frdm_k64f/frdm_k64f.dts b/boards/arm/frdm_k64f/frdm_k64f.dts index d1691d745eec6..323300ba5eabf 100644 --- a/boards/arm/frdm_k64f/frdm_k64f.dts +++ b/boards/arm/frdm_k64f/frdm_k64f.dts @@ -74,6 +74,36 @@ gpios = <&gpioa 4 GPIO_INT_ACTIVE_LOW>; }; }; + + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map = <0 0 &gpiob 2 0>, /* A0 */ + <1 0 &gpiob 3 0>, /* A1 */ + <2 0 &gpiob 10 0>, /* A2 */ + <3 0 &gpiob 11 0>, /* A3 */ + <4 0 &gpioc 11 0>, /* A4 */ + <5 0 &gpioc 10 0>, /* A5 */ + <6 0 &gpioc 16 0>, /* D0 */ + <7 0 &gpioc 17 0>, /* D1 */ + <8 0 &gpiob 9 0>, /* D2 */ + <9 0 &gpioa 1 0>, /* D3 */ + <10 0 &gpiob 23 0>, /* D4 */ + <11 0 &gpioa 2 0>, /* D5 */ + <12 0 &gpioc 2 0>, /* D6 */ + <13 0 &gpioc 3 0>, /* D7 */ + /* NOTE: HW Rev D and below use: */ + /* <14 0 &gpioa 0 0>, */ + /* NOTE: HW Rev E and on use: */ + <14 0 &gpioc 12 0>, /* D8 */ + <15 0 &gpioc 4 0>, /* D9 */ + <16 0 &gpiod 0 0>, /* D10 */ + <17 0 &gpiod 2 0>, /* D11 */ + <18 0 &gpiod 3 0>, /* D12 */ + <19 0 &gpiod 1 0>, /* D13 */ + <20 0 &gpioe 25 0>, /* D14 */ + <21 0 &gpioe 24 0>; /* D15 */ + }; }; arduino_serial: &uart3 {}; diff --git a/boards/arm/frdm_k64f/pinmux.c b/boards/arm/frdm_k64f/pinmux.c index 23bbc7bce0e9a..c9672ae1ae9c2 100644 --- a/boards/arm/frdm_k64f/pinmux.c +++ b/boards/arm/frdm_k64f/pinmux.c @@ -87,6 +87,13 @@ static int frdm_k64f_pinmux_init(struct device *dev) pinmux_pin_set(portc, 17, PORT_PCR_MUX(kPORT_MuxAsGpio)); #endif +#ifdef CONFIG_MODEM_UBLOX_SARA_R4 + /* Modem RESET */ + pinmux_pin_set(portc, 2, PORT_PCR_MUX(kPORT_MuxAsGpio)); + /* Modem POWER_ON */ + pinmux_pin_set(porta, 2, PORT_PCR_MUX(kPORT_MuxAsGpio)); +#endif + #ifdef CONFIG_IEEE802154_MCR20A /* FRDM-MCR20A Reset (D5) */ pinmux_pin_set(porta, 2, PORT_PCR_MUX(kPORT_MuxAsGpio)); diff --git a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts index 4ce6e1e54d1da..e4ac18f796f17 100644 --- a/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts +++ b/boards/arm/nrf52840_pca10056/nrf52840_pca10056.dts @@ -62,6 +62,33 @@ }; }; + arduino_header: connector { + compatible = "arduino-header-r3"; + #gpio-cells = <2>; + gpio-map = <0 0 &gpio0 3 0>, /* A0 */ + <1 0 &gpio0 4 0>, /* A1 */ + <2 0 &gpio0 28 0>, /* A2 */ + <3 0 &gpio0 29 0>, /* A3 */ + <4 0 &gpio0 30 0>, /* A4 */ + <5 0 &gpio0 31 0>, /* A5 */ + <6 0 &gpio1 1 0>, /* D0 */ + <7 0 &gpio1 2 0>, /* D1 */ + <8 0 &gpio1 3 0>, /* D2 */ + <9 0 &gpio1 4 0>, /* D3 */ + <10 0 &gpio1 5 0>, /* D4 */ + <11 0 &gpio1 6 0>, /* D5 */ + <12 0 &gpio1 7 0>, /* D6 */ + <13 0 &gpio1 8 0>, /* D7 */ + <14 0 &gpio1 10 0>, /* D8 */ + <15 0 &gpio1 11 0>, /* D9 */ + <16 0 &gpio1 12 0>, /* D10 */ + <17 0 &gpio1 13 0>, /* D11 */ + <18 0 &gpio1 14 0>, /* D12 */ + <19 0 &gpio1 15 0>, /* D13 */ + <20 0 &gpio0 26 0>, /* D14 */ + <21 0 &gpio0 27 0>; /* D15 */ + }; + /* These aliases are provided for compatibility with samples */ aliases { led0 = &led0; diff --git a/boards/arm/particle_boron/CMakeLists.txt b/boards/arm/particle_boron/CMakeLists.txt new file mode 100644 index 0000000000000..218a0602483ab --- /dev/null +++ b/boards/arm/particle_boron/CMakeLists.txt @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() +zephyr_library_sources(board.c) diff --git a/boards/arm/particle_boron/Kconfig.defconfig b/boards/arm/particle_boron/Kconfig.defconfig index 9c7427ddd0a87..8bc3d28058e4f 100644 --- a/boards/arm/particle_boron/Kconfig.defconfig +++ b/boards/arm/particle_boron/Kconfig.defconfig @@ -50,4 +50,20 @@ endif # IEEE802154 config BT_CTLR default BT +if MODEM + +config UART_1_NRF_UARTE + default y + +config UART_1_NRF_FLOW_CONTROL + default y + +config MODEM_UBLOX_SARA_R4 + default y + +config UART_INTERRUPT_DRIVEN + default y + +endif # MODEM + endif # BOARD_PARTICLE_BORON diff --git a/boards/arm/particle_boron/board.c b/boards/arm/particle_boron/board.c new file mode 100644 index 0000000000000..23f1cbb77317a --- /dev/null +++ b/boards/arm/particle_boron/board.c @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "board.h" +#include + +static int particle_boron_init(struct device *dev) +{ + + ARG_UNUSED(dev); + +#if defined(CONFIG_MODEM_UBLOX_SARA_R4) + struct device *gpio_dev; + + /* Enable the serial buffer for SARA-R4 modem */ + gpio_dev = device_get_binding(SERIAL_BUFFER_ENABLE_GPIO_NAME); + if (!gpio_dev) { + return -ENODEV; + } + + gpio_pin_configure(gpio_dev, V_INT_DETECT_GPIO_PIN, GPIO_DIR_IN); + + gpio_pin_configure(gpio_dev, SERIAL_BUFFER_ENABLE_GPIO_PIN, + GPIO_DIR_OUT); +#endif + + return 0; +} + +/* needs to be done after GPIO driver init */ +SYS_INIT(particle_boron_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); diff --git a/boards/arm/particle_boron/board.h b/boards/arm/particle_boron/board.h new file mode 100644 index 0000000000000..566c653f4d7b0 --- /dev/null +++ b/boards/arm/particle_boron/board.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __INC_BOARD_H +#define __INC_BOARD_H + +/* pin used to enable the buffer power */ +#define SERIAL_BUFFER_ENABLE_GPIO_NAME DT_NORDIC_NRF_GPIO_0_LABEL +#define SERIAL_BUFFER_ENABLE_GPIO_PIN 25 + +/* pin used to detect V_INT (buffer power) */ +#define V_INT_DETECT_GPIO_PIN 2 + +#endif /* __INC_BOARD_H */ diff --git a/boards/arm/particle_boron/particle_boron.dts b/boards/arm/particle_boron/particle_boron.dts index f442e4b698b3c..1c4b3256164dc 100644 --- a/boards/arm/particle_boron/particle_boron.dts +++ b/boards/arm/particle_boron/particle_boron.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include "mesh_feather.dtsi" / { @@ -23,10 +24,20 @@ &uart1 { /* u-blox SARA-U2 or SARA-R4 */ compatible = "nordic,nrf-uarte"; - current-speed = <921600>; + current-speed = <115200>; status = "ok"; + tx-pin = <37>; rx-pin = <36>; rts-pin = <39>; cts-pin = <38>; + + sara_r4 { + compatible = "ublox,sara-r4"; + label = "ublox-sara-r4"; + status = "ok"; + + mdm-power-gpios-gpio = <&gpio0 16 0>; + mdm-reset-gpios-gpio = <&gpio0 12 0>; + }; }; diff --git a/boards/arm/particle_boron/particle_boron_defconfig b/boards/arm/particle_boron/particle_boron_defconfig index baa4c16a99c10..9c04e10772832 100644 --- a/boards/arm/particle_boron/particle_boron_defconfig +++ b/boards/arm/particle_boron/particle_boron_defconfig @@ -18,7 +18,6 @@ CONFIG_GPIO=y # enable uart driver CONFIG_SERIAL=y CONFIG_UART_0_NRF_UARTE=y -CONFIG_UART_1_NRF_UARTE=y # enable power I2C interface CONFIG_I2C=y diff --git a/boards/shields/sparkfun_sara_r4/boards/disco_l475_iot1.conf b/boards/shields/sparkfun_sara_r4/boards/disco_l475_iot1.conf new file mode 100644 index 0000000000000..13237d8cdd255 --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/boards/disco_l475_iot1.conf @@ -0,0 +1 @@ +CONFIG_UART_4=y diff --git a/boards/shields/sparkfun_sara_r4/boards/frdm_k64f.conf b/boards/shields/sparkfun_sara_r4/boards/frdm_k64f.conf new file mode 100644 index 0000000000000..725de944d7130 --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/boards/frdm_k64f.conf @@ -0,0 +1 @@ +CONFIG_UART_MCUX_3=y diff --git a/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.conf b/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.conf new file mode 100644 index 0000000000000..23ce9d5ef2070 --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.conf @@ -0,0 +1,2 @@ +CONFIG_GPIO_NRF_P1=y +CONFIG_UART_1_NRF_UARTE=y diff --git a/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.overlay b/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.overlay new file mode 100644 index 0000000000000..bff47c606c6ae --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/boards/nrf52840_pca10056.overlay @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&arduino_serial { + tx-pin = <34>; + rx-pin = <33>; +}; diff --git a/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.conf b/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.conf new file mode 100644 index 0000000000000..086de69c9867c --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.conf @@ -0,0 +1,12 @@ +# Enable u-blox SARA-R4 modem +CONFIG_MODEM=y +CONFIG_MODEM_SHELL=y +CONFIG_MODEM_UBLOX_SARA_R4=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# extend retry timing to 20 seconds for LTE/LTE-M +CONFIG_COAP_INIT_ACK_TIMEOUT_MS=20000 + +# Hack: disable IPv6 for now +# CONFIG_NET_IPV6 is not set +# CONFIG_NET_CONFIG_NEED_IPV6 is not set diff --git a/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.overlay b/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.overlay new file mode 100644 index 0000000000000..ba9da11569a66 --- /dev/null +++ b/boards/shields/sparkfun_sara_r4/sparkfun_sara_r4.overlay @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&arduino_serial { + current-speed = <115200>; + status = "ok"; + + sara_r4 { + compatible = "ublox,sara-r4"; + label = "ublox-sara-r4"; + mdm-power-gpios = <&arduino_header 11 0>; /* D5 */ + mdm-reset-gpios = <&arduino_header 12 0>; /* D6 */ + status = "ok"; + }; +}; diff --git a/boards/shields/wnc_m14a2a/boards/frdm_k64f.conf b/boards/shields/wnc_m14a2a/boards/frdm_k64f.conf new file mode 100644 index 0000000000000..c89c13eaaa8ce --- /dev/null +++ b/boards/shields/wnc_m14a2a/boards/frdm_k64f.conf @@ -0,0 +1 @@ +CONFIG_UART_MCUX_2=y diff --git a/boards/shields/wnc_m14a2a/boards/frdm_k64f.overlay b/boards/shields/wnc_m14a2a/boards/frdm_k64f.overlay new file mode 100644 index 0000000000000..8bef00ba5f531 --- /dev/null +++ b/boards/shields/wnc_m14a2a/boards/frdm_k64f.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * WNC-M14A2A shield uses an odd UART available on *some* Arduino-R3-compatible + * headers. It needs to be defined individually. + */ +&uart2 { + current-speed = <115200>; + hw-flow-control; + status = "ok"; + + wnc_m14a2a: wncm14a2a { + status = "ok"; + compatible = "wnc,m14a2a"; + label = "wnc-m14a2a"; + mdm-boot-mode-sel-gpios = <&arduino_header 7 0>; /* D1 */ + mdm-power-gpios = <&arduino_header 8 0>; /* D2 */ + mdm-keep-awake-gpios = <&arduino_header 12 0>; /* D6 */ + mdm-reset-gpios = <&arduino_header 14 0>; /* D8 */ + mdm-shld-trans-ena-gpios = <&arduino_header 15 0>; /* D9 */ + }; +}; diff --git a/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.conf b/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.conf new file mode 100644 index 0000000000000..a60d7cd743d3c --- /dev/null +++ b/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.conf @@ -0,0 +1,3 @@ +CONFIG_GPIO_NRF_P1=y +CONFIG_UART_1_NRF_UARTE=y +CONFIG_UART_1_NRF_FLOW_CONTROL=y diff --git a/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.overlay b/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.overlay new file mode 100644 index 0000000000000..d31581fc4dffc --- /dev/null +++ b/boards/shields/wnc_m14a2a/boards/nrf52840_pca10056.overlay @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * WNC-M14A2A shield uses an odd UART available on *some* Arduino-R3-compatible + * headers. It needs to be defined individually. + */ +&uart1 { + current-speed = <115200>; + hw-flow-control; + status = "ok"; + + tx-pin = <46>; + rx-pin = <45>; + rts-pin = <44>; + cts-pin = <47>; + + wnc_m14a2a: wncm14a2a { + status = "ok"; + compatible = "wnc,m14a2a"; + label = "wnc-m14a2a"; + mdm-boot-mode-sel-gpios = <&arduino_header 7 0>; /* D1 */ + mdm-power-gpios = <&arduino_header 8 0>; /* D2 */ + mdm-keep-awake-gpios = <&arduino_header 12 0>; /* D6 */ + mdm-reset-gpios = <&arduino_header 14 0>; /* D8 */ + mdm-shld-trans-ena-gpios = <&arduino_header 15 0>; /* D9 */ + }; +}; diff --git a/samples/net/lwm2m_client/overlay-wncm14a2a.conf b/boards/shields/wnc_m14a2a/wnc_m14a2a.conf similarity index 74% rename from samples/net/lwm2m_client/overlay-wncm14a2a.conf rename to boards/shields/wnc_m14a2a/wnc_m14a2a.conf index c8ef0a411738c..a9fe6139dc5d7 100644 --- a/samples/net/lwm2m_client/overlay-wncm14a2a.conf +++ b/boards/shields/wnc_m14a2a/wnc_m14a2a.conf @@ -2,9 +2,7 @@ CONFIG_MODEM=y CONFIG_MODEM_SHELL=y CONFIG_MODEM_WNCM14A2A=y - -# Connect to public LwM2M server (leshan.ecplise.org) -CONFIG_NET_CONFIG_PEER_IPV4_ADDR="5.39.83.206" +CONFIG_UART_INTERRUPT_DRIVEN=y # extend retry timing to 20 seconds for LTE/LTE-M CONFIG_COAP_INIT_ACK_TIMEOUT_MS=20000 @@ -12,4 +10,3 @@ CONFIG_COAP_INIT_ACK_TIMEOUT_MS=20000 # Hack: disable IPv6 for now CONFIG_NET_IPV6=n CONFIG_NET_CONFIG_NEED_IPV6=n -CONFIG_UART_INTERRUPT_DRIVEN=y diff --git a/boards/shields/wnc_m14a2a/wnc_m14a2a.overlay b/boards/shields/wnc_m14a2a/wnc_m14a2a.overlay new file mode 100644 index 0000000000000..4aa6b706b59d5 --- /dev/null +++ b/boards/shields/wnc_m14a2a/wnc_m14a2a.overlay @@ -0,0 +1,7 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* File meant to be empty */ diff --git a/cmake/app/boilerplate.cmake b/cmake/app/boilerplate.cmake index f95fa48fb1e12..16840615078c1 100644 --- a/cmake/app/boilerplate.cmake +++ b/cmake/app/boilerplate.cmake @@ -313,6 +313,33 @@ foreach(root ${BOARD_ROOT}) else() list(APPEND NOT_FOUND_SHIELD_LIST ${s}) endif() + + # search for shield/boards/board.overlay file + if(EXISTS ${shield_dir}/${s}/boards/${BOARD}.overlay) + # add shield/board overlay to the shield overlays list + list(APPEND + shield_dts_files + ${shield_dir}/${s}/boards/${BOARD}.overlay + ) + endif() + + # search for shield/shield.conf file + if(EXISTS ${shield_dir}/${s}/${s}.conf) + # add shield.conf to the shield config list + list(APPEND + shield_conf_files + ${shield_dir}/${s}/${s}.conf + ) + endif() + + # search for shield/boards/board.conf file + if(EXISTS ${shield_dir}/${s}/boards/${BOARD}.conf) + # add HW specific board.conf to the shield config list + list(APPEND + shield_conf_files + ${shield_dir}/${s}/boards/${BOARD}.conf + ) + endif() endforeach() endif() endforeach() diff --git a/cmake/kconfig.cmake b/cmake/kconfig.cmake index 7c72e080fd5cd..7bb0c7c8320cb 100644 --- a/cmake/kconfig.cmake +++ b/cmake/kconfig.cmake @@ -112,6 +112,7 @@ set( merge_config_files ${BOARD_DEFCONFIG} ${CONF_FILE_AS_LIST} + ${shield_conf_files} ${OVERLAY_CONFIG_AS_LIST} ${EXTRA_KCONFIG_OPTIONS_FILE} ${config_files} diff --git a/drivers/modem/CMakeLists.txt b/drivers/modem/CMakeLists.txt index 482698a68af22..20af808df6908 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) +if(CONFIG_MODEM_UBLOX_SARA_R4) + zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) + zephyr_library_sources(ublox-sara-r4.c) +endif() + if(CONFIG_MODEM_WNCM14A2A) zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) zephyr_library_sources(wncm14a2a.c) diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index 4d43a5d52c62f..d3adb31aaab7b 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -46,6 +46,7 @@ config MODEM_SHELL Activate shell module that provides modem utilities like sending a command to the modem UART. +source "drivers/modem/Kconfig.ublox-sara-r4" source "drivers/modem/Kconfig.wncm14a2a" endif # MODEM diff --git a/drivers/modem/Kconfig.ublox-sara-r4 b/drivers/modem/Kconfig.ublox-sara-r4 new file mode 100644 index 0000000000000..4c0f674f1a13a --- /dev/null +++ b/drivers/modem/Kconfig.ublox-sara-r4 @@ -0,0 +1,59 @@ +# Kconfig - u-blox SARA R4 driver options + +# +# Copyright (c) 2019 Foundries.io +# +# SPDX-License-Identifier: Apache-2.0 +# + +config MODEM_UBLOX_SARA_R4 + bool "Enable u-blox SARA-R4 LTE-CatM1/NB-IoT modem driver" + select MODEM_RECEIVER + select NET_OFFLOAD + imply GPIO + help + Choose this setting to enable u-blox SARA-R4 LTE-CatM1/NB-IoT modem + driver. + +if MODEM_UBLOX_SARA_R4 + +config MODEM_UBLOX_SARA_R4_RX_STACK_SIZE + int "Stack size for the u-blox SARA-R4 modem driver RX thread" + default 1028 + help + This stack is used by the u-blox SARA-R4 RX thread. + +config MODEM_UBLOX_SARA_R4_RX_WORKQ_STACK_SIZE + int "Stack size for the u-blox SARA-R4 modem driver work queue" + default 2048 + help + This stack is used by the work queue to pass off net_pkt data + to the rest of the network stack, letting the rx thread continue + processing data. + +config MODEM_UBLOX_SARA_R4_APN + string "APN for establishing network connection" + default "hologram" + help + This setting is used in the AT+CGDCONT command to set the APN name + for the network connection context. This value is specific to + the network provider and may need to be changed. + +config MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO + string "MCC/MNOfor establishing network connection" + help + This setting is used in the AT+COPS command to set the MCC/MNO + for the network connection context. This value is specific to + the network provider and may need to be changed if auto is not + selected. + +config MODEM_UBLOX_SARA_R4_INIT_PRIORITY + int "u-blox SARA-R4 driver init priority" + default 80 + help + u-blox SARA-R4 device driver initialization priority. + Do not mess with it unless you know what you are doing. + Note that the priority needs to be lower than the net stack + so that it can start before the networking sub-system. + +endif # MODEM_UBLOX_SARA_R4 diff --git a/drivers/modem/Kconfig.wncm14a2a b/drivers/modem/Kconfig.wncm14a2a index 2d8bfa3f4f7c1..f41791e9645e9 100644 --- a/drivers/modem/Kconfig.wncm14a2a +++ b/drivers/modem/Kconfig.wncm14a2a @@ -11,10 +11,6 @@ menuconfig MODEM_WNCM14A2A select MODEM_RECEIVER select NET_OFFLOAD imply GPIO - select UART_MCUX_2 if BOARD_FRDM_K64F - select GPIO_NRF_P1 if SOC_NRF52840 - select UART_1_NRF_UARTE if SOC_NRF52840 - select UART_1_NRF_FLOW_CONTROL if SOC_NRF52840 help Choose this setting to enable Wistron WNC-M14A2A LTE-M modem driver. NOTE: Currently the pin settings only work with FRDM K64F shield. diff --git a/drivers/modem/ublox-sara-r4.c b/drivers/modem/ublox-sara-r4.c new file mode 100644 index 0000000000000..b01d83a67c350 --- /dev/null +++ b/drivers/modem/ublox-sara-r4.c @@ -0,0 +1,1857 @@ +/* + * Copyright (c) 2019 Foundries.io + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_DOMAIN modem_ublox_sara_r4 +#define LOG_LEVEL CONFIG_MODEM_LOG_LEVEL +#include +LOG_MODULE_REGISTER(LOG_DOMAIN); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#if defined(CONFIG_NET_IPV6) +#include "ipv6.h" +#endif +#if defined(CONFIG_NET_IPV4) +#include "ipv4.h" +#endif +#if defined(CONFIG_NET_UDP) +#include "udp_internal.h" +#endif + +#include + +#if !defined(CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO) +#define CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO "" +#endif + +/* Uncomment the #define below to enable a hexdump of all incoming + * data from the modem receiver + */ +/* #define ENABLE_VERBOSE_MODEM_RECV_HEXDUMP 1 */ + +struct mdm_control_pinconfig { + char *dev_name; + u32_t pin; +}; + +#define PINCONFIG(name_, pin_) { \ + .dev_name = name_, \ + .pin = pin_ \ +} + +/* pin settings */ +enum mdm_control_pins { + MDM_POWER = 0, + MDM_RESET, + MAX_MDM_CONTROL_PINS, +}; + +static const struct mdm_control_pinconfig pinconfig[] = { + /* MDM_POWER */ + PINCONFIG(DT_UBLOX_SARA_R4_0_MDM_POWER_GPIOS_CONTROLLER, + DT_UBLOX_SARA_R4_0_MDM_POWER_GPIOS_PIN), + + /* MDM_RESET */ + PINCONFIG(DT_UBLOX_SARA_R4_0_MDM_RESET_GPIOS_CONTROLLER, + DT_UBLOX_SARA_R4_0_MDM_RESET_GPIOS_PIN), +}; + +#define MDM_UART_DEV_NAME DT_UBLOX_SARA_R4_0_BUS_NAME + +#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(5) +#define MDM_CMD_SEND_TIMEOUT K_SECONDS(10) +#define MDM_CMD_CONN_TIMEOUT K_SECONDS(31) +#define MDM_REGISTRATION_TIMEOUT K_SECONDS(180) +#define MDM_PROMPT_CMD_DELAY K_MSEC(10) + +#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 BUF_ALLOC_TIMEOUT K_SECONDS(1) + +#define CMD_HANDLER(cmd_, cb_) { \ + .cmd = cmd_, \ + .cmd_len = (u16_t)sizeof(cmd_)-1, \ + .func = on_cmd_ ## cb_ \ +} + +#define MDM_MANUFACTURER_LENGTH 10 +#define MDM_MODEL_LENGTH 16 +#define MDM_REVISION_LENGTH 64 +#define MDM_IMEI_LENGTH 16 + +#define RSSI_TIMEOUT_SECS 30 + +NET_BUF_POOL_DEFINE(mdm_recv_pool, MDM_RECV_MAX_BUF, MDM_RECV_BUF_SIZE, + 0, NULL); + +static u8_t mdm_recv_buf[MDM_MAX_DATA_LENGTH]; + +/* RX thread structures */ +K_THREAD_STACK_DEFINE(modem_rx_stack, + CONFIG_MODEM_UBLOX_SARA_R4_RX_STACK_SIZE); +struct k_thread modem_rx_thread; + +/* RX thread work queue */ +K_THREAD_STACK_DEFINE(modem_workq_stack, + CONFIG_MODEM_UBLOX_SARA_R4_RX_WORKQ_STACK_SIZE); +static struct k_work_q modem_workq; + +struct modem_socket { + struct net_context *context; + sa_family_t family; + enum net_sock_type type; + enum net_ip_protocol ip_proto; + struct sockaddr src; + struct sockaddr dst; + int dst_port; + + int socket_id; + + /** semaphore */ + struct k_sem sock_send_sem; + + /** socket callbacks */ + struct k_work recv_cb_work; + net_context_recv_cb_t recv_cb; + struct net_pkt *recv_pkt; + void *recv_user_data; +}; + +struct modem_iface_ctx { + struct net_if *iface; + u8_t mac_addr[6]; + + /* GPIO PORT devices */ + struct device *gpio_port_dev[MAX_MDM_CONTROL_PINS]; + + /* RX specific attributes */ + struct mdm_receiver_context mdm_ctx; + + /* socket data */ + struct modem_socket sockets[MDM_MAX_SOCKETS]; + int last_socket_id; + int last_error; + + /* semaphores */ + struct k_sem response_sem; + + /* 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]; + + /* modem state */ + int ev_creg; +}; + +struct cmd_handler { + const char *cmd; + u16_t cmd_len; + void (*func)(struct net_buf **buf, u16_t len); +}; + +static struct modem_iface_ctx ictx; + +static void modem_read_rx(struct net_buf **buf); + +/*** Verbose Debugging Functions ***/ +#if defined(ENABLE_VERBOSE_MODEM_RECV_HEXDUMP) +static inline void hexdump(const u8_t *packet, size_t length) +{ + char output[sizeof("xxxxyyyy xxxxyyyy")]; + int n = 0, k = 0; + u8_t byte; + + while (length--) { + if (n % 16 == 0) { + printk(" %08X ", n); + } + + byte = *packet++; + + printk("%02X ", byte); + + if (byte < 0x20 || byte > 0x7f) { + output[k++] = '.'; + } else { + output[k++] = byte; + } + + n++; + if (n % 8 == 0) { + if (n % 16 == 0) { + output[k] = '\0'; + printk(" [%s]\n", output); + k = 0; + } else { + printk(" "); + } + } + } + + if (n % 16) { + int i; + + output[k] = '\0'; + + for (i = 0; i < (16 - (n % 16)); i++) { + printk(" "); + } + + if ((n % 16) < 8) { + printk(" "); /* one extra delimiter after 8 chars */ + } + + printk(" [%s]\n", output); + } +} +#else +#define hexdump(...) +#endif + +static struct modem_socket *socket_get(void) +{ + int i; + struct modem_socket *sock = NULL; + + for (i = 0; i < MDM_MAX_SOCKETS; i++) { + if (!ictx.sockets[i].context) { + sock = &ictx.sockets[i]; + break; + } + } + + return sock; +} + +static struct modem_socket *socket_from_id(int socket_id) +{ + int i; + struct modem_socket *sock = NULL; + + if (socket_id < MDM_BASE_SOCKET_NUM) { + return NULL; + } + + for (i = 0; i < MDM_MAX_SOCKETS; i++) { + if (ictx.sockets[i].socket_id == socket_id) { + sock = &ictx.sockets[i]; + break; + } + } + + return sock; +} + +static void socket_put(struct modem_socket *sock) +{ + if (!sock) { + return; + } + + sock->context = NULL; + sock->socket_id = MDM_BASE_SOCKET_NUM - 1; + (void)memset(&sock->src, 0, sizeof(struct sockaddr)); + (void)memset(&sock->dst, 0, sizeof(struct sockaddr)); +} + +static char *modem_sprint_ip_addr(const struct sockaddr *addr) +{ + static char buf[NET_IPV6_ADDR_LEN]; + +#if defined(CONFIG_NET_IPV6) + if (addr->sa_family == AF_INET6) { + return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, + buf, sizeof(buf)); + } else +#endif +#if defined(CONFIG_NET_IPV4) + if (addr->sa_family == AF_INET) { + return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, + buf, sizeof(buf)); + } else +#endif + { + LOG_ERR("Unknown IP address family:%d", addr->sa_family); + return NULL; + } +} + +/* Send an AT command with a series of response handlers */ +static int send_at_cmd(struct modem_socket *sock, + const u8_t *data, int timeout) +{ + int ret; + + ictx.last_error = 0; + + LOG_DBG("OUT: [%s]", data); + mdm_receiver_send(&ictx.mdm_ctx, data, strlen(data)); + mdm_receiver_send(&ictx.mdm_ctx, "\r\n", 2); + + if (timeout == K_NO_WAIT) { + return 0; + } + + if (!sock) { + k_sem_reset(&ictx.response_sem); + ret = k_sem_take(&ictx.response_sem, timeout); + } else { + k_sem_reset(&sock->sock_send_sem); + ret = k_sem_take(&sock->sock_send_sem, timeout); + } + + if (ret == 0) { + ret = ictx.last_error; + } else if (ret == -EAGAIN) { + ret = -ETIMEDOUT; + } + + return ret; +} + +static int send_data(struct modem_socket *sock, + const struct sockaddr *dst_addr, int dst_port, + struct net_pkt *pkt) +{ + int ret, i; + struct net_buf *frag; + char buf[sizeof("AT+USO**=#,!###.###.###.###!,#####,####\r\n")]; + + if (!sock) { + return -EINVAL; + } + + ictx.last_error = 0; + + frag = pkt->frags; + + /* use SOCKWRITE with binary mode formatting */ + if (sock->ip_proto == IPPROTO_UDP) { + snprintk(buf, sizeof(buf), "AT+USOST=%d,\"%s\",%u,%u\r\n", + sock->socket_id, modem_sprint_ip_addr(dst_addr), + dst_port, net_buf_frags_len(frag)); + } else { + snprintk(buf, sizeof(buf), "AT+USOWR=%d,%u\r\n", + sock->socket_id, net_buf_frags_len(frag)); + } + mdm_receiver_send(&ictx.mdm_ctx, buf, strlen(buf)); + + /* Slight pause per spec so that @ prompt is received */ + k_sleep(MDM_PROMPT_CMD_DELAY); + + /* Loop through packet data and send */ + while (frag) { + if (frag->len > 0) { + /* + * HACK: Apparently enabling HEX transmit mode also + * affects the BINARY send method. We need to encode + * the "binary" data as HEX values here + * TODO: Something faster here. + */ + for (i = 0; i < frag->len; i++) { + snprintk(buf, sizeof(buf), "%02x", frag->data[i]); + mdm_receiver_send(&ictx.mdm_ctx, buf, 2); + } + } + frag = frag->frags; + } + + k_sem_reset(&sock->sock_send_sem); + ret = k_sem_take(&sock->sock_send_sem, MDM_CMD_SEND_TIMEOUT); + if (ret == 0) { + ret = ictx.last_error; + } else if (ret == -EAGAIN) { + ret = -ETIMEDOUT; + } + + return ret; +} + +/*** NET_BUF HELPERS ***/ + +static bool is_crlf(u8_t c) +{ + if (c == '\n' || c == '\r') { + return true; + } else { + return false; + } +} + +static void net_buf_skipcrlf(struct net_buf **buf) +{ + /* chop off any /n or /r */ + while (*buf && is_crlf(*(*buf)->data)) { + net_buf_pull_u8(*buf); + if (!(*buf)->len) { + *buf = net_buf_frag_del(NULL, *buf); + } + } +} + +static u16_t net_buf_findcrlf(struct net_buf *buf, struct net_buf **frag, + u16_t *offset) +{ + u16_t len = 0U, pos = 0U; + + while (buf && !is_crlf(*(buf->data + pos))) { + if (pos + 1 >= buf->len) { + len += buf->len; + buf = buf->frags; + pos = 0U; + } else { + pos++; + } + } + + if (buf && is_crlf(*(buf->data + pos))) { + len += pos; + *offset = pos; + *frag = buf; + return len; + } + + return 0; +} + +/*** UDP / TCP Helper Function ***/ + +/* Setup IP header data to be used by some network applications. + * While much is dummy data, some fields such as dst, port and family are + * important. + * Return the IP + protocol header length. + */ +static int pkt_setup_ip_data(struct net_pkt *pkt, struct modem_socket *sock) +{ + int hdr_len = 0; + u16_t src_port = 0U; + +#if defined(CONFIG_NET_IPV6) + if (net_pkt_family(pkt) == AF_INET6) { + if (net_ipv6_create( + pkt, + &((struct sockaddr_in6 *)&sock->dst)->sin6_addr, + &((struct sockaddr_in6 *)&sock->src)->sin6_addr)) { + return -1; + } + + src_port = net_sin6(&sock->dst)->sin6_port; + hdr_len = sizeof(struct net_ipv6_hdr); + } else +#endif +#if defined(CONFIG_NET_IPV4) + if (net_pkt_family(pkt) == AF_INET) { + if (net_ipv4_create( + pkt, + &((struct sockaddr_in *)&sock->dst)->sin_addr, + &((struct sockaddr_in *)&sock->src)->sin_addr)) { + return -1; + } + + src_port = net_sin(&sock->dst)->sin_port; + hdr_len = sizeof(struct net_ipv4_hdr); + } else +#endif + { + /* no error here as hdr_len is checked later for 0 value */ + } + +#if defined(CONFIG_NET_UDP) + if (sock->ip_proto == IPPROTO_UDP) { + if (net_udp_create(pkt, src_port, sock->dst_port)) { + return -1; + } + + hdr_len += NET_UDPH_LEN; + } else +#endif +#if defined(CONFIG_NET_TCP) + if (sock->ip_proto == IPPROTO_TCP) { + NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr); + struct net_tcp_hdr *tcp; + + tcp = (struct net_tcp_hdr *)net_pkt_get_data(pkt, &tcp_access); + if (!tcp) { + return -1; + } + + (void)memset(tcp, 0, NET_TCPH_LEN); + + /* Setup TCP header */ + tcp->src_port = src_port; + tcp->dst_port = sock->dst_port; + + if (net_pkt_set_data(pkt, &tcp_access)) { + return -1; + } + + hdr_len += NET_TCPH_LEN; + } else +#endif /* CONFIG_NET_TCP */ + { + /* no error here as hdr_len is checked later for 0 value */ + } + + return hdr_len; +} + +/*** MODEM RESPONSE HANDLERS ***/ + +/* Last Socket ID Handler */ +static void on_cmd_atcmdecho(struct net_buf **buf, u16_t len) +{ + char value[2]; + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + ictx.last_socket_id = atoi(value); +} + +/* Echo Handler for commands without related sockets */ +static void on_cmd_atcmdecho_nosock(struct net_buf **buf, u16_t len) +{ + /* clear last_socket_id */ + ictx.last_socket_id = MDM_BASE_SOCKET_NUM - 1; +} + +static void on_cmd_atcmdinfo_manufacturer(struct net_buf **buf, u16_t len) +{ + size_t out_len; + + out_len = net_buf_linearize(ictx.mdm_manufacturer, + sizeof(ictx.mdm_manufacturer) - 1, + *buf, 0, len); + ictx.mdm_manufacturer[out_len] = 0; + LOG_INF("Manufacturer: %s", ictx.mdm_manufacturer); +} + +static void on_cmd_atcmdinfo_model(struct net_buf **buf, u16_t len) +{ + size_t out_len; + + out_len = net_buf_linearize(ictx.mdm_model, + sizeof(ictx.mdm_model) - 1, + *buf, 0, len); + ictx.mdm_model[out_len] = 0; + LOG_INF("Model: %s", ictx.mdm_model); +} + +static void on_cmd_atcmdinfo_revision(struct net_buf **buf, u16_t len) +{ + size_t out_len; + + out_len = net_buf_linearize(ictx.mdm_revision, + sizeof(ictx.mdm_revision) - 1, + *buf, 0, len); + ictx.mdm_revision[out_len] = 0; + LOG_INF("Revision: %s", ictx.mdm_revision); +} + +static void on_cmd_atcmdecho_nosock_imei(struct net_buf **buf, u16_t len) +{ + struct net_buf *frag = NULL; + u16_t offset; + size_t out_len; + + /* skip CR/LF */ + net_buf_skipcrlf(buf); + if (!*buf) { + LOG_DBG("Unable to find IMEI (net_buf_skipcrlf)"); + return; + } + + frag = NULL; + len = net_buf_findcrlf(*buf, &frag, &offset); + if (!frag) { + LOG_DBG("Unable to find IMEI (net_buf_findcrlf)"); + return; + } + + out_len = net_buf_linearize(ictx.mdm_imei, sizeof(ictx.mdm_imei) - 1, + *buf, 0, len); + ictx.mdm_imei[out_len] = 0; + + LOG_INF("IMEI: %s", ictx.mdm_imei); +} + +/* Handler: +CESQ: [0],[1],[2],[3],[4],[5] */ +static void on_cmd_atcmdinfo_rssi(struct net_buf **buf, u16_t len) +{ + int i = 0, rssi, param_count = 0; + size_t value_size; + char value[12]; + + value_size = sizeof(value); + while (*buf && len > 0 && param_count < 6) { + i = 0; + (void)memset(value, 0, value_size); + + while (*buf && len > 0 && i < value_size) { + value[i] = net_buf_pull_u8(*buf); + len--; + if (!(*buf)->len) { + *buf = net_buf_frag_del(NULL, *buf); + } + + /* "," marks the end of each value */ + if (value[i] == ',') { + value[i] = '\0'; + break; + } + + i++; + } + + if (i == value_size) { + i = -1; + break; + } + + param_count++; + } + + if (param_count == 6 && i > 0) { + rssi = atoi(value); + if (rssi >= 0 && rssi <= 97) { + ictx.mdm_ctx.data_rssi = -140 + rssi; + } else { + ictx.mdm_ctx.data_rssi = -1000; + } + + LOG_INF("RSSI: %d", ictx.mdm_ctx.data_rssi); + return; + } + + LOG_WRN("Bad format found for RSSI"); + ictx.mdm_ctx.data_rssi = -1000; +} + +/* Handler: OK */ +static void on_cmd_sockok(struct net_buf **buf, u16_t len) +{ + struct modem_socket *sock = NULL; + + ictx.last_error = 0; + sock = socket_from_id(ictx.last_socket_id); + if (!sock) { + k_sem_give(&ictx.response_sem); + } else { + k_sem_give(&sock->sock_send_sem); + } +} + +/* Handler: ERROR */ +static void on_cmd_sockerror(struct net_buf **buf, u16_t len) +{ + struct modem_socket *sock = NULL; + + ictx.last_error = -EIO; + sock = socket_from_id(ictx.last_socket_id); + if (!sock) { + k_sem_give(&ictx.response_sem); + } else { + k_sem_give(&sock->sock_send_sem); + } +} + +/* Handler: +USOCR: */ +static void on_cmd_sockcreate(struct net_buf **buf, u16_t len) +{ + char value[2]; + struct modem_socket *sock = NULL; + + /* look up new socket by special id */ + sock = socket_from_id(MDM_MAX_SOCKETS + 1); + if (sock) { + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + value[1] = 0; + sock->socket_id = atoi(value); + } + + /* don't give back semaphore -- OK to follow */ +} + +/* Handler: +USO[WR|ST]: , */ +static void on_cmd_sockwrite(struct net_buf **buf, u16_t len) +{ + char value[2]; + + /* TODO: check length against original send length */ + + if (!*buf) { + return; + } + + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + value[1] = 0; + + ictx.last_socket_id = atoi(value); + if (ictx.last_socket_id < MDM_BASE_SOCKET_NUM) { + return; + } + + /* don't give back semaphore -- OK to follow */ +} + +static void sockreadrecv_cb_work(struct k_work *work) +{ + struct modem_socket *sock = NULL; + struct net_pkt *pkt; + + sock = CONTAINER_OF(work, struct modem_socket, recv_cb_work); + + /* return data */ + pkt = sock->recv_pkt; + sock->recv_pkt = NULL; + if (sock->recv_cb) { + sock->recv_cb(sock->context, pkt, NULL, NULL, + 0, sock->recv_user_data); + } else { + net_pkt_unref(pkt); + } +} + +/* Common code for +USOR[D|F] */ +static void on_cmd_sockread_common(int socket_id, struct net_buf **buf, + u16_t len) +{ + struct modem_socket *sock = NULL; + int i, actual_length, hdr_len = 0; + size_t value_size; + char value[10]; + u8_t c = 0U; + + /* comma marks the end of length */ + i = 0; + value_size = sizeof(value); + (void)memset(value, 0, value_size); + while (*buf && i < value_size) { + value[i] = net_buf_pull_u8(*buf); + len--; + if (!(*buf)->len) { + *buf = net_buf_frag_del(NULL, *buf); + } + + if (value[i] == ',') { + break; + } + + i++; + } + + /* make sure we still have buf data, the last pulled character was + * a comma and that the next char in the buffer is a quote. + */ + if (!*buf || value[i] != ',' || *(*buf)->data != '\"') { + LOG_ERR("Incorrect format! Ignoring data!"); + return; + } + + /* clear the comma */ + value[i] = '\0'; + actual_length = atoi(value); + + /* skip quote */ + len--; + net_buf_pull_u8(*buf); + if (!(*buf)->len) { + *buf = net_buf_frag_del(NULL, *buf); + } + + /* check that we have enough data */ + if (!*buf || len > (actual_length * 2) + 1) { + LOG_ERR("Incorrect format! Ignoring data!"); + return; + } + + sock = socket_from_id(socket_id); + if (!sock) { + LOG_ERR("Socket not found! (%d)", socket_id); + return; + } + + /* update last_socket_id */ + ictx.last_socket_id = socket_id; + + /* allocate an RX pkt */ + sock->recv_pkt = net_pkt_rx_alloc_with_buffer( + net_context_get_iface(sock->context), + actual_length, sock->family, sock->ip_proto, + BUF_ALLOC_TIMEOUT); + if (!sock->recv_pkt) { + LOG_ERR("Failed net_pkt_get_reserve_rx!"); + return; + } + + /* set pkt data */ + net_pkt_set_context(sock->recv_pkt, sock->context); + + /* add IP / protocol headers */ + hdr_len = pkt_setup_ip_data(sock->recv_pkt, sock); + + /* move hex encoded data from the buffer to the recv_pkt */ + for (i = 0; i < actual_length * 2; i++) { + char c2 = *(*buf)->data; + + if (isdigit(c2)) { + c += c2 - '0'; + } else if (isalpha(c2)) { + c += c2 - (isupper(c2) ? 'A' - 10 : 'a' - 10); + } else { + /* TODO: unexpected input! skip? */ + } + + if (i % 2) { + if (net_pkt_write_u8(sock->recv_pkt, c)) { + LOG_ERR("Unable to add data! Aborting!"); + net_pkt_unref(sock->recv_pkt); + sock->recv_pkt = NULL; + return; + } + + c = 0U; + } else { + c = c << 4; + } + + /* pull data from buf and advance to the next frag if needed */ + net_buf_pull_u8(*buf); + if (!(*buf)->len) { + *buf = net_buf_frag_del(NULL, *buf); + } + } + + net_pkt_cursor_init(sock->recv_pkt); + net_pkt_set_overwrite(sock->recv_pkt, true); + + if (hdr_len > 0) { + net_pkt_skip(sock->recv_pkt, hdr_len); + } + + /* Let's do the callback processing in a different work queue in + * case the app takes a long time. + */ + k_work_submit_to_queue(&modem_workq, &sock->recv_cb_work); +} + +/* + * Handler: +USORF: ,,,, + * "" +*/ +static void on_cmd_sockread_udp(struct net_buf **buf, u16_t len) +{ + int socket_id; + char value[2]; + + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + len--; + + /* skip first comma */ + net_buf_pull_u8(*buf); + len--; + + value[1] = 0; + socket_id = atoi(value); + if (socket_id < MDM_BASE_SOCKET_NUM - 1) { + return; + } + + /* TODO: handle remote_ip_addr */ + + /* skip remote_ip */ + while (*buf && len > 0 && net_buf_pull_u8(*buf) != ',') { + len--; + } + + len--; + /* skip remote_port */ + while (*buf && len > 0 && net_buf_pull_u8(*buf) != ',') { + len--; + } + + len--; + return on_cmd_sockread_common(socket_id, buf, len); +} + +/* Handler: +USORD: ,,"" */ +static void on_cmd_sockread_tcp(struct net_buf **buf, u16_t len) +{ + int socket_id; + char value[2]; + + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + len--; + + /* skip first comma */ + net_buf_pull_u8(*buf); + len--; + + value[1] = 0; + socket_id = atoi(value); + if (socket_id < MDM_BASE_SOCKET_NUM) { + return; + } + + return on_cmd_sockread_common(socket_id, buf, len); +} + +/* Handler: +UUSOCL: */ +static void on_cmd_socknotifyclose(struct net_buf **buf, u16_t len) +{ + char value[2]; + int socket_id; + + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + len--; + value[1] = 0; + + socket_id = atoi(value); + if (socket_id < MDM_BASE_SOCKET_NUM) { + return; + } + + /* TODO: handle URC socket close */ +} + +/* Handler: +UUSOR[D|F]: , */ +static void on_cmd_socknotifydata(struct net_buf **buf, u16_t len) +{ + int socket_id, left_bytes; + size_t out_len; + char value[sizeof("#,####\r")]; + char sendbuf[sizeof("AT+USOR*=#,#####\r")]; + struct modem_socket *sock = NULL; + + /* make sure only a single digit is picked up for socket_id */ + value[0] = net_buf_pull_u8(*buf); + len--; + value[1] = 0; + + socket_id = atoi(value); + if (socket_id < MDM_BASE_SOCKET_NUM) { + return; + } + + /* skip first comma */ + net_buf_pull_u8(*buf); + len--; + + /* Second parameter is length */ + out_len = net_buf_linearize(value, sizeof(value) - 1, *buf, 0, len); + value[out_len] = 0; + left_bytes = atoi(value); + + sock = socket_from_id(socket_id); + if (!sock) { + LOG_ERR("Unable to find socket_id:%d", socket_id); + return; + } + + if (left_bytes > 0) { + LOG_DBG("socket_id:%d left_bytes:%d", socket_id, left_bytes); + + snprintk(sendbuf, sizeof(sendbuf), "AT+USOR%s=%d,%d", + (sock->ip_proto == IPPROTO_UDP) ? "F" : "D", + sock->socket_id, left_bytes); + + /* We entered this trigger due to an unsolicited modem response. + * When we send the AT+USOR* command it won't generate an + * "OK" response directly. The modem will respond with + * "+USOR*: ..." and the data requested and then "OK" or + * "ERROR". Let's not wait here by passing in a timeout to + * send_at_cmd(). Instead, when the resulting response is + * received, we trigger on_cmd_sockread() to handle it. + */ + send_at_cmd(sock, sendbuf, K_NO_WAIT); + } +} + +static void on_cmd_socknotifycreg(struct net_buf **buf, u16_t len) +{ + char value[8]; + size_t out_len; + + out_len = net_buf_linearize(value, sizeof(value) - 1, *buf, 0, len); + value[out_len] = 0; + ictx.ev_creg = atoi(value); + LOG_DBG("CREG:%d", ictx.ev_creg); +} + +static int net_buf_ncmp(struct net_buf *buf, const u8_t *s2, size_t n) +{ + struct net_buf *frag = buf; + u16_t offset = 0U; + + while ((n > 0) && (*(frag->data + offset) == *s2) && (*s2 != '\0')) { + if (offset == frag->len) { + if (!frag->frags) { + break; + } + frag = frag->frags; + offset = 0U; + } else { + offset++; + } + + s2++; + n--; + } + + return (n == 0) ? 0 : (*(frag->data + offset) - *s2); +} + +static inline struct net_buf *read_rx_allocator(s32_t timeout, void *user_data) +{ + return net_buf_alloc((struct net_buf_pool *)user_data, timeout); +} + +static void modem_read_rx(struct net_buf **buf) +{ + u8_t uart_buffer[MDM_RECV_BUF_SIZE]; + size_t bytes_read = 0; + int ret; + u16_t rx_len; + + /* read all of the data from mdm_receiver */ + while (true) { + ret = mdm_receiver_recv(&ictx.mdm_ctx, + uart_buffer, + sizeof(uart_buffer), + &bytes_read); + if (ret < 0 || bytes_read == 0) { + /* mdm_receiver buffer is empty */ + break; + } + + hexdump(uart_buffer, bytes_read); + + /* make sure we have storage */ + if (!*buf) { + *buf = net_buf_alloc(&mdm_recv_pool, BUF_ALLOC_TIMEOUT); + if (!*buf) { + LOG_ERR("Can't allocate RX data! " + "Skipping data!"); + break; + } + } + + rx_len = net_buf_append_bytes(*buf, bytes_read, uart_buffer, + BUF_ALLOC_TIMEOUT, + read_rx_allocator, + &mdm_recv_pool); + if (rx_len < bytes_read) { + LOG_ERR("Data was lost! read %u of %u!", + rx_len, bytes_read); + } + } +} + +/* RX thread */ +static void modem_rx(void) +{ + struct net_buf *rx_buf = NULL; + struct net_buf *frag = NULL; + int i; + u16_t offset, len; + + static const struct cmd_handler handlers[] = { + /* NON-SOCKET COMMAND ECHOES to clear last_socket_id */ + CMD_HANDLER("ATE1", atcmdecho_nosock), + CMD_HANDLER("AT+CFUN=", atcmdecho_nosock), + CMD_HANDLER("AT+CREG=", atcmdecho_nosock), + CMD_HANDLER("AT+UDCONF=", atcmdecho_nosock), + CMD_HANDLER("ATI", atcmdecho_nosock), + CMD_HANDLER("AT+CGDCONT=", atcmdecho_nosock), + CMD_HANDLER("AT+COPS=", atcmdecho_nosock), + CMD_HANDLER("AT+CESQ", atcmdecho_nosock), + CMD_HANDLER("AT+USOCR=", atcmdecho_nosock), + CMD_HANDLER("AT+CGSN", atcmdecho_nosock_imei), + + /* SOCKET COMMAND ECHOES for last_socket_id processing */ + CMD_HANDLER("AT+USOCO=", atcmdecho), + CMD_HANDLER("AT+USOWR=", atcmdecho), + CMD_HANDLER("AT+USOST=", atcmdecho), + CMD_HANDLER("AT+USOCL=", atcmdecho), + + /* MODEM Information */ + CMD_HANDLER("Manufacturer: ", atcmdinfo_manufacturer), + CMD_HANDLER("Model: ", atcmdinfo_model), + CMD_HANDLER("Revision: ", atcmdinfo_revision), + CMD_HANDLER("+CESQ: ", atcmdinfo_rssi), + + /* SOLICITED SOCKET RESPONSES */ + CMD_HANDLER("OK", sockok), + CMD_HANDLER("ERROR", sockerror), + CMD_HANDLER("+USOCR: ", sockcreate), + CMD_HANDLER("+USOWR: ", sockwrite), + CMD_HANDLER("+USOST: ", sockwrite), + CMD_HANDLER("+USORD: ", sockread_tcp), + CMD_HANDLER("+USORF: ", sockread_udp), + + /* UNSOLICITED RESPONSE CODES */ + CMD_HANDLER("+UUSOCL: ", socknotifyclose), + CMD_HANDLER("+UUSORD: ", socknotifydata), + CMD_HANDLER("+UUSORF: ", socknotifydata), + CMD_HANDLER("+CREG: ", socknotifycreg), + }; + + while (true) { + /* wait for incoming data */ + k_sem_take(&ictx.mdm_ctx.rx_sem, K_FOREVER); + + modem_read_rx(&rx_buf); + + while (rx_buf) { + net_buf_skipcrlf(&rx_buf); + if (!rx_buf) { + break; + } + + frag = NULL; + len = net_buf_findcrlf(rx_buf, &frag, &offset); + if (!frag) { + break; + } + + /* look for matching data handlers */ + i = -1; + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + if (net_buf_ncmp(rx_buf, handlers[i].cmd, + handlers[i].cmd_len) == 0) { + /* found a matching handler */ + LOG_DBG("MATCH %s (len:%u)", + handlers[i].cmd, len); + + /* skip cmd_len */ + rx_buf = net_buf_skip(rx_buf, + handlers[i].cmd_len); + + /* locate next cr/lf */ + frag = NULL; + len = net_buf_findcrlf(rx_buf, + &frag, &offset); + if (!frag) { + break; + } + + /* call handler */ + if (handlers[i].func) { + handlers[i].func(&rx_buf, len); + } + + frag = NULL; + /* make sure buf still has data */ + if (!rx_buf) { + break; + } + + /* + * We've handled the current line + * and need to exit the "search for + * handler loop". Let's skip any + * "extra" data and look for the next + * CR/LF, leaving us ready for the + * next handler search. Ignore the + * length returned. + */ + (void)net_buf_findcrlf(rx_buf, + &frag, &offset); + break; + } + } + + if (frag && rx_buf) { + /* clear out processed line (buffers) */ + while (frag && rx_buf != frag) { + rx_buf = net_buf_frag_del(NULL, rx_buf); + } + + net_buf_pull(rx_buf, offset); + } + } + + /* give up time if we have a solid stream of data */ + k_yield(); + } +} + +static int modem_pin_init(void) +{ + LOG_INF("Setting Modem Pins"); + + gpio_pin_configure(ictx.gpio_port_dev[MDM_RESET], + pinconfig[MDM_RESET].pin, GPIO_DIR_OUT); + gpio_pin_configure(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, GPIO_DIR_OUT); + + LOG_DBG("MDM_RESET_PIN -> NOT_ASSERTED"); + gpio_pin_write(ictx.gpio_port_dev[MDM_RESET], + pinconfig[MDM_RESET].pin, MDM_RESET_NOT_ASSERTED); + + LOG_DBG("MDM_POWER_PIN -> DISABLE"); + gpio_pin_write(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, MDM_POWER_DISABLE); + /* make sure module is powered off */ + k_sleep(K_SECONDS(12)); + + LOG_DBG("MDM_POWER_PIN -> ENABLE"); + gpio_pin_write(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, MDM_POWER_ENABLE); + k_sleep(K_SECONDS(1)); + + LOG_DBG("MDM_POWER_PIN -> DISABLE"); + gpio_pin_write(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, MDM_POWER_DISABLE); + k_sleep(K_SECONDS(1)); + + LOG_DBG("MDM_POWER_PIN -> ENABLE"); + gpio_pin_write(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, MDM_POWER_ENABLE); + k_sleep(K_SECONDS(10)); + + gpio_pin_configure(ictx.gpio_port_dev[MDM_POWER], + pinconfig[MDM_POWER].pin, GPIO_DIR_IN); + + LOG_INF("... Done!"); + + return 0; +} + +static void modem_rssi_query_work(struct k_work *work) +{ + int ret; + + /* query modem RSSI */ + ret = send_at_cmd(NULL, "AT+CESQ", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CESQ ret:%d", ret); + } + + /* re-start RSSI query work */ + k_delayed_work_submit_to_queue(&modem_workq, + &ictx.rssi_query_work, + K_SECONDS(RSSI_TIMEOUT_SECS)); +} + +static void modem_reset(void) +{ + int ret = 0, retry_count = 0, counter = 0; + + /* bring down network interface */ + atomic_clear_bit(ictx.iface->if_dev->flags, NET_IF_UP); + +restart: + /* stop RSSI delay work */ + k_delayed_work_cancel(&ictx.rssi_query_work); + + modem_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 = send_at_cmd(NULL, "AT", MDM_CMD_TIMEOUT); + if (ret < 0 && ret != -ETIMEDOUT) { + break; + } + } + + if (ret < 0) { + LOG_ERR("MODEM WAIT LOOP ERROR: %d", ret); + goto error; + } + + /* echo on */ + ret = send_at_cmd(NULL, "ATE1", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("ATE1 ret:%d", ret); + goto error; + } + + /* stop functionality */ + ret = send_at_cmd(NULL, "AT+CFUN=0", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CFUN=0 ret:%d", ret); + goto error; + } + +#if defined(CONFIG_BOARD_PARTICLE_BORON) + /* use external SIM */ + ret = send_at_cmd(NULL, "AT+UGPIOC=23,0,0", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+UGPIOC=23,0,0 ret:%d", ret); + goto error; + } + + /* Let SIM settle */ + k_sleep(MDM_CMD_TIMEOUT); +#endif + + /* UNC messages for registration */ + ret = send_at_cmd(NULL, "AT+CREG=1", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CREG=1 ret:%d", ret); + goto error; + } + + /* HEX receive data mode */ + ret = send_at_cmd(NULL, "AT+UDCONF=1,1", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+UDCONF=1 ret:%d", ret); + } + + /* query modem info */ + LOG_INF("Querying modem information"); + ret = send_at_cmd(NULL, "ATI", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("ATI ret:%d", ret); + goto error; + } + + /* query modem IMEI */ + ret = send_at_cmd(NULL, "AT+CGSN", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CGSN ret:%d", ret); + goto error; + } + + /* setup PDP context definition */ + + ret = send_at_cmd(NULL, "AT+CGDCONT=1,\"IP\",\"" + CONFIG_MODEM_UBLOX_SARA_R4_APN "\"", + MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CGDCONT ret:%d", ret); + goto error; + } + + /* start functionality */ + ret = send_at_cmd(NULL, "AT+CFUN=1", MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+CFUN=1 ret:%d", ret); + goto error; + } + + if (strlen(CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO) > 0) { + /* use manual MCC/MNO entry */ + ret = send_at_cmd(NULL, "AT+COPS=1,2,\"" + CONFIG_MODEM_UBLOX_SARA_R4_MANUAL_MCCMNO "\"", + MDM_CMD_TIMEOUT); + } else { + /* register operator automatically */ + ret = send_at_cmd(NULL, "AT+COPS=0,0", MDM_REGISTRATION_TIMEOUT); + } + + if (ret < 0) { + LOG_ERR("AT+COPS ret:%d", ret); + goto error; + } + + LOG_INF("Waiting for network"); + + /* wait for +CREG: 1 notification (20 seconds max) */ + counter = 0; + while (counter++ < 20 && ictx.ev_creg != 1) { + 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 && + (ictx.mdm_ctx.data_rssi >= 0 || + ictx.mdm_ctx.data_rssi <= -1000)) { + /* stop RSSI delay work */ + k_delayed_work_cancel(&ictx.rssi_query_work); + modem_rssi_query_work(NULL); + k_sleep(MDM_WAIT_FOR_RSSI_DELAY); + } + + if (ictx.mdm_ctx.data_rssi >= 0 || ictx.mdm_ctx.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; + } + + LOG_INF("Network is ready."); + + /* Set iface up */ + net_if_up(ictx.iface); + +error: + return; +} + +static int modem_init(struct device *dev) +{ + int i, ret = 0; + + ARG_UNUSED(dev); + + /* check for valid pinconfig */ + __ASSERT(ARRAY_SIZE(pinconfig) == MAX_MDM_CONTROL_PINS, + "Incorrect modem pinconfig!"); + + (void)memset(&ictx, 0, sizeof(ictx)); + for (i = 0; i < MDM_MAX_SOCKETS; i++) { + k_work_init(&ictx.sockets[i].recv_cb_work, + sockreadrecv_cb_work); + k_sem_init(&ictx.sockets[i].sock_send_sem, 0, 1); + ictx.sockets[i].socket_id = MDM_BASE_SOCKET_NUM - 1; + } + k_sem_init(&ictx.response_sem, 0, 1); + + /* initialize the work queue */ + k_work_q_start(&modem_workq, + modem_workq_stack, + K_THREAD_STACK_SIZEOF(modem_workq_stack), + K_PRIO_COOP(7)); + + ictx.last_socket_id = MDM_BASE_SOCKET_NUM - 1; + + /* setup port devices and pin directions */ + for (i = 0; i < MAX_MDM_CONTROL_PINS; i++) { + ictx.gpio_port_dev[i] = + device_get_binding(pinconfig[i].dev_name); + if (!ictx.gpio_port_dev[i]) { + LOG_ERR("gpio port (%s) not found!", + pinconfig[i].dev_name); + return -ENODEV; + } + } + + /* Set modem data storage */ + ictx.mdm_ctx.data_manufacturer = ictx.mdm_manufacturer; + ictx.mdm_ctx.data_model = ictx.mdm_model; + ictx.mdm_ctx.data_revision = ictx.mdm_revision; + ictx.mdm_ctx.data_imei = ictx.mdm_imei; + + ret = mdm_receiver_register(&ictx.mdm_ctx, MDM_UART_DEV_NAME, + mdm_recv_buf, sizeof(mdm_recv_buf)); + if (ret < 0) { + LOG_ERR("Error registering modem receiver (%d)!", ret); + goto error; + } + + /* start RX thread */ + k_thread_create(&modem_rx_thread, modem_rx_stack, + K_THREAD_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(&ictx.rssi_query_work, modem_rssi_query_work); + + modem_reset(); + +error: + return ret; +} + +/*** OFFLOAD FUNCTIONS ***/ + +static int offload_get(sa_family_t family, + enum net_sock_type type, + enum net_ip_protocol ip_proto, + struct net_context **context) +{ + int ret; + char buf[sizeof("AT+USOCR=#,#####\r")]; + struct modem_socket *sock = NULL; + int local_port = 0; + + /* new socket */ + sock = socket_get(); + if (!sock) { + return -ENOMEM; + } + + (*context)->offload_context = sock; + sock->family = family; + sock->type = type; + sock->ip_proto = ip_proto; + sock->context = *context; + sock->socket_id = MDM_MAX_SOCKETS + 1; /* socket # needs assigning */ + + local_port = ntohs(net_sin((struct sockaddr *)&(*context)->local)->sin_port); + if (local_port > 0) { + snprintk(buf, sizeof(buf), "AT+USOCR=%d,%d", ip_proto, + local_port); + } else { + snprintk(buf, sizeof(buf), "AT+USOCR=%d", ip_proto); + } + + ret = send_at_cmd(NULL, buf, MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("%s ret:%d", buf, ret); + socket_put(sock); + } + + return ret; +} + +static int offload_bind(struct net_context *context, + const struct sockaddr *addr, + socklen_t addrlen) +{ + struct modem_socket *sock = NULL; + + if (!context) { + return -EINVAL; + } + + sock = (struct modem_socket *)context->offload_context; + if (!sock) { + LOG_ERR("Can't locate socket for net_ctx:%p!", context); + return -EINVAL; + } + + /* save bind address information */ + sock->src.sa_family = addr->sa_family; +#if defined(CONFIG_NET_IPV6) + if (addr->sa_family == AF_INET6) { + net_ipaddr_copy(&net_sin6(&sock->src)->sin6_addr, + &net_sin6(addr)->sin6_addr); + net_sin6(&sock->src)->sin6_port = net_sin6(addr)->sin6_port; + } else +#endif +#if defined(CONFIG_NET_IPV4) + if (addr->sa_family == AF_INET) { + net_ipaddr_copy(&net_sin(&sock->src)->sin_addr, + &net_sin(addr)->sin_addr); + net_sin(&sock->src)->sin_port = net_sin(addr)->sin_port; + } else +#endif + { + return -EPFNOSUPPORT; + } + + return 0; +} + +static int offload_listen(struct net_context *context, int backlog) +{ + /* NOT IMPLEMENTED */ + return -ENOTSUP; +} + +static int offload_connect(struct net_context *context, + const struct sockaddr *addr, + socklen_t addrlen, + net_context_connect_cb_t cb, + s32_t timeout, + void *user_data) +{ + int ret; + char buf[sizeof("AT+USOCO=#,!###.###.###.###!,#####,#\r")]; + struct modem_socket *sock; + + if (!context || !addr) { + return -EINVAL; + } + + sock = (struct modem_socket *)context->offload_context; + if (!sock) { + LOG_ERR("Can't locate socket for net_ctx:%p!", context); + return -EINVAL; + } + + if (sock->socket_id < MDM_BASE_SOCKET_NUM - 1) { + LOG_ERR("Invalid socket_id(%d) for net_ctx:%p!", + sock->socket_id, context); + return -EINVAL; + } + + sock->dst.sa_family = addr->sa_family; + +#if defined(CONFIG_NET_IPV6) + if (addr->sa_family == AF_INET6) { + net_ipaddr_copy(&net_sin6(&sock->dst)->sin6_addr, + &net_sin6(addr)->sin6_addr); + sock->dst_port = ntohs(net_sin6(addr)->sin6_port); + net_sin6(&sock->dst)->sin6_port = net_sin6(addr)->sin6_port; + } else +#endif +#if defined(CONFIG_NET_IPV4) + if (addr->sa_family == AF_INET) { + net_ipaddr_copy(&net_sin(&sock->dst)->sin_addr, + &net_sin(addr)->sin_addr); + sock->dst_port = ntohs(net_sin(addr)->sin_port); + net_sin(&sock->dst)->sin_port = net_sin6(addr)->sin6_port; + } else +#endif + { + return -EINVAL; + } + + if (sock->dst_port < 0) { + LOG_ERR("Invalid port: %d", sock->dst_port); + return -EINVAL; + } + + /* TODO: timeout is currently ignored */ + + /* skip socket connect if UDP */ + if (sock->ip_proto == IPPROTO_UDP) { + return 0; + } + + snprintk(buf, sizeof(buf), "AT+USOCO=%d,\"%s\",%d", sock->socket_id, + modem_sprint_ip_addr(addr), sock->dst_port); + ret = send_at_cmd(sock, buf, MDM_CMD_CONN_TIMEOUT); + if (ret < 0) { + LOG_ERR("%s ret:%d", buf, ret); + } + + if (cb) { + cb(context, ret, user_data); + } + + return ret; +} + +static int offload_accept(struct net_context *context, + net_tcp_accept_cb_t cb, + s32_t timeout, + void *user_data) +{ + /* NOT IMPLEMENTED */ + return -ENOTSUP; +} + +static int offload_sendto(struct net_pkt *pkt, + const struct sockaddr *dst_addr, + socklen_t addrlen, + net_context_send_cb_t cb, + s32_t timeout, + void *user_data) +{ + struct net_context *context = net_pkt_context(pkt); + struct modem_socket *sock; + int ret = 0, dst_port = -1; + + if (!context) { + return -EINVAL; + } + + sock = (struct modem_socket *)context->offload_context; + if (!sock) { + LOG_ERR("Can't locate socket for net_ctx:%p!", context); + return -EINVAL; + } + +#if defined(CONFIG_NET_IPV6) + if (dst_addr->sa_family == AF_INET6) { + dst_port = ntohs(net_sin6(dst_addr)->sin6_port); + } else +#endif +#if defined(CONFIG_NET_IPV4) + if (dst_addr->sa_family == AF_INET) { + dst_port = ntohs(net_sin(dst_addr)->sin_port); + } else +#endif + { + return -EINVAL; + } + + if (dst_port < 0) { + LOG_ERR("Invalid port: %d", dst_port); + return -EINVAL; + } + + ret = send_data(sock, dst_addr, dst_port, pkt); + if (ret < 0) { + LOG_ERR("send_data error: %d", ret); + } else { + net_pkt_unref(pkt); + } + + if (cb) { + cb(context, ret, user_data); + } + + return ret; +} + +static int offload_send(struct net_pkt *pkt, + net_context_send_cb_t cb, + s32_t timeout, + void *user_data) +{ + struct net_context *context = net_pkt_context(pkt); + socklen_t addrlen; + + addrlen = 0; +#if defined(CONFIG_NET_IPV6) + if (net_pkt_family(pkt) == AF_INET6) { + addrlen = sizeof(struct sockaddr_in6); + } else +#endif /* CONFIG_NET_IPV6 */ +#if defined(CONFIG_NET_IPV4) + if (net_pkt_family(pkt) == AF_INET) { + addrlen = sizeof(struct sockaddr_in); + } else +#endif /* CONFIG_NET_IPV4 */ + { + return -EPFNOSUPPORT; + } + + return offload_sendto(pkt, &context->remote, addrlen, cb, + timeout, user_data); +} + +static int offload_recv(struct net_context *context, + net_context_recv_cb_t cb, + s32_t timeout, + void *user_data) +{ + struct modem_socket *sock; + + if (!context) { + return -EINVAL; + } + + sock = (struct modem_socket *)context->offload_context; + if (!sock) { + LOG_ERR("Can't locate socket for net_ctx:%p!", context); + return -EINVAL; + } + + sock->recv_cb = cb; + sock->recv_user_data = user_data; + + return 0; +} + +static int offload_put(struct net_context *context) +{ + struct modem_socket *sock; + char buf[sizeof("AT+USOCL=#\r")]; + int ret; + + if (!context) { + return -EINVAL; + } + + sock = (struct modem_socket *)context->offload_context; + if (!sock) { + /* socket was already closed? Exit quietly here. */ + return 0; + } + + snprintk(buf, sizeof(buf), "AT+USOCL=%d", sock->socket_id); + + ret = send_at_cmd(sock, buf, MDM_CMD_TIMEOUT); + if (ret < 0) { + LOG_ERR("AT+USOCL=%d ret:%d", sock->socket_id, ret); + } + + /* clear last_socket_id */ + ictx.last_socket_id = MDM_BASE_SOCKET_NUM - 1; + + sock->context->connect_cb = NULL; + sock->context->recv_cb = NULL; + sock->context->send_cb = NULL; + socket_put(sock); + net_context_unref(context); + + return 0; +} + +static struct net_offload offload_funcs = { + .get = offload_get, + .bind = offload_bind, + .listen = offload_listen, /* TODO */ + .connect = offload_connect, + .accept = offload_accept, /* TODO */ + .send = offload_send, + .sendto = offload_sendto, + .recv = offload_recv, + .put = offload_put, +}; + +static inline u8_t *modem_get_mac(struct device *dev) +{ + struct modem_iface_ctx *ctx = dev->driver_data; + + ctx->mac_addr[0] = 0x00; + ctx->mac_addr[1] = 0x10; + + UNALIGNED_PUT(sys_cpu_to_be32(sys_rand32_get()), + (u32_t *)(ctx->mac_addr + 2)); + + return ctx->mac_addr; +} + +static void offload_iface_init(struct net_if *iface) +{ + struct device *dev = net_if_get_device(iface); + struct modem_iface_ctx *ctx = dev->driver_data; + + iface->if_dev->offload = &offload_funcs; + net_if_set_link_addr(iface, modem_get_mac(dev), + sizeof(ctx->mac_addr), + NET_LINK_ETHERNET); + ctx->iface = iface; +} + +static struct net_if_api api_funcs = { + .init = offload_iface_init, +}; + +NET_DEVICE_OFFLOAD_INIT(modem_sara_r4, "MODEM_SARA_R4", + modem_init, &ictx, + NULL, CONFIG_MODEM_UBLOX_SARA_R4_INIT_PRIORITY, &api_funcs, + MDM_MAX_DATA_LENGTH); diff --git a/drivers/modem/wncm14a2a.c b/drivers/modem/wncm14a2a.c index 70ceb116cf694..8cb38aa999d49 100644 --- a/drivers/modem/wncm14a2a.c +++ b/drivers/modem/wncm14a2a.c @@ -1426,7 +1426,7 @@ static int wncm14a2a_init(struct device *dev) ARG_UNUSED(dev); /* check for valid pinconfig */ - __ASSERT(sizeof(pinconfig) == MAX_MDM_CONTROL_PINS, + __ASSERT(ARRAY_SIZE(pinconfig) == MAX_MDM_CONTROL_PINS, "Incorrect modem pinconfig!"); (void)memset(&ictx, 0, sizeof(ictx)); @@ -1680,9 +1680,10 @@ static int offload_sendto(struct net_pkt *pkt, ret = send_data(sock, pkt); if (ret < 0) { LOG_ERR("send_data error: %d", ret); + } else { + net_pkt_unref(pkt); } - net_pkt_unref(pkt); if (cb) { cb(context, ret, user_data); } diff --git a/dts/bindings/gpio/arduino-header-r3.yaml b/dts/bindings/gpio/arduino-header-r3.yaml new file mode 100644 index 0000000000000..0779a7e06e86f --- /dev/null +++ b/dts/bindings/gpio/arduino-header-r3.yaml @@ -0,0 +1,25 @@ +# +# Copyright (c) 2019 Foundries.io +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: ARDUINO GPIO HEADER +version: 0.1 + +description: > + This is a representation of GPIO pin nodes exposed as headers on Arduino R3 + +properties: + compatible: + type: string + category: required + description: compatible strings + constraint: "arduino-header-r3" + generation: define + + gpio-map: + type: compound + category: required + +... diff --git a/dts/bindings/modem/ublox,sara-r4.yaml b/dts/bindings/modem/ublox,sara-r4.yaml new file mode 100644 index 0000000000000..7f4098de88aec --- /dev/null +++ b/dts/bindings/modem/ublox,sara-r4.yaml @@ -0,0 +1,37 @@ +# +# Copyright (c) 2019, Foundries.io +# +# SPDX-License-Identifier: Apache-2.0 +# +--- +title: u-blox SARA-R4 modem +version: 0.1 + +description: > + This is a representation of the u-blox SARA-R4 modem. + +inherits: + !include uart-device.yaml + +properties: + compatible: + type: string + category: required + constraint: "ublox,sara-r4" + generation: define + + label: + type: string + category: required + generation: define + + mdm-power-gpios: + type: compound + category: required + generation: define, use-prop-name + + mdm-reset-gpios: + type: compound + category: required + generation: define, use-prop-name +... diff --git a/dts/bindings/modem/wnc,m14a2a.yaml b/dts/bindings/modem/wnc,m14a2a.yaml index 959e30a091c71..6175cee8767a7 100644 --- a/dts/bindings/modem/wnc,m14a2a.yaml +++ b/dts/bindings/modem/wnc,m14a2a.yaml @@ -20,6 +20,11 @@ properties: constraint: "wnc,m14a2a" generation: define + label: + type: string + category: required + generation: define + mdm-boot-mode-sel-gpios: type: compound category: required diff --git a/samples/net/lwm2m_client/frdm_k64f.overlay b/samples/net/lwm2m_client/frdm_k64f.overlay deleted file mode 100644 index 57be016647789..0000000000000 --- a/samples/net/lwm2m_client/frdm_k64f.overlay +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2018 Intel Corporation - * - * SPDX-License-Identifier: Apache-2.0 - */ -&uart2 { - current-speed = <115200>; - hw-flow-control; - status = "ok"; - - wncm14a2a { - compatible = "wnc,m14a2a"; - label = "wncm14a2a"; - mdm-boot-mode-sel-gpios = <&gpioc 17 0>; - mdm-power-gpios = <&gpiob 9 0>; - mdm-keep-awake-gpios = <&gpioc 2 0>; - mdm-reset-gpios = <&gpioc 12 0>; - mdm-shld-trans-ena-gpios = <&gpioc 4 0>; - status = "ok"; - }; -}; diff --git a/samples/net/lwm2m_client/nrf52840_mdk.overlay b/samples/net/lwm2m_client/nrf52840_mdk.overlay deleted file mode 100644 index 8b3dbc84a4bcf..0000000000000 --- a/samples/net/lwm2m_client/nrf52840_mdk.overlay +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018 Linaro Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&uart1 { - current-speed = <115200>; - status = "ok"; - tx-pin = <16>; - rx-pin = <15>; - rts-pin = <14>; - cts-pin = <17>; - - wncm14a2a { - compatible = "wnc,m14a2a"; - label = "wncm14a2a"; - mdm-boot-mode-sel-gpios = <&gpio1 2 0>; - mdm-power-gpios = <&gpio1 3 0>; - mdm-keep-awake-gpios = <&gpio1 7 0>; - mdm-reset-gpios = <&gpio1 10 0>; - mdm-shld-trans-ena-gpios = <&gpio1 11 0>; - status = "ok"; - }; -}; diff --git a/samples/net/lwm2m_client/nrf52840_pca10056.overlay b/samples/net/lwm2m_client/nrf52840_pca10056.overlay deleted file mode 100644 index c1fff3d743cae..0000000000000 --- a/samples/net/lwm2m_client/nrf52840_pca10056.overlay +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2018 Linaro Limited - * - * SPDX-License-Identifier: Apache-2.0 - */ - -&uart1 { - current-speed = <115200>; - status = "ok"; - tx-pin = <46>; - rx-pin = <45>; - rts-pin = <44>; - cts-pin = <47>; - - wncm14a2a { - compatible = "wnc,m14a2a"; - label = "wncm14a2a"; - mdm-boot-mode-sel-gpios = <&gpio1 2 0>; - mdm-power-gpios = <&gpio1 3 0>; - mdm-keep-awake-gpios = <&gpio1 7 0>; - mdm-reset-gpios = <&gpio1 10 0>; - mdm-shld-trans-ena-gpios = <&gpio1 11 0>; - status = "ok"; - }; -}; diff --git a/samples/net/lwm2m_client/sample.yaml b/samples/net/lwm2m_client/sample.yaml index 36a4396c20c63..5b1ab714a9447 100644 --- a/samples/net/lwm2m_client/sample.yaml +++ b/samples/net/lwm2m_client/sample.yaml @@ -18,8 +18,8 @@ tests: extra_args: OVERLAY_CONFIG=overlay-bt.conf platform_whitelist: nrf52_pca10040 nrf52840_pca10056 disco_l475_iot1 tags: net lwm2m - sample.net.lwm2m_client.modem: + sample.net.lwm2m_client.wnc_m14a2a: harness: net - extra_args: OVERLAY_CONFIG=overlay-wncm14a2a.conf + extra_args: SHIELD=wnc_m14a2a platform_whitelist: frdm_k64f nrf52840_pca10056 tags: net lwm2m