Skip to content

Commit

Permalink
silabs-multiprotocol: Update to Gecko SDK v4.2.0 (home-assistant#2806)
Browse files Browse the repository at this point in the history
* Update add-on to use Gecko SDK v4.2.0

* Update firmware to v4.2.0

* Bump version
  • Loading branch information
agners authored Dec 16, 2022
1 parent a5fdf97 commit 2036215
Show file tree
Hide file tree
Showing 20 changed files with 514 additions and 127 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
From a6c952a9c31a535d7eae22e1803ad2f2ab7de915 Mon Sep 17 00:00:00 2001
Message-Id: <a6c952a9c31a535d7eae22e1803ad2f2ab7de915.1671118726.git.stefan@agner.ch>
From: Stefan Agner <[email protected]>
Date: Thu, 15 Dec 2022 16:38:34 +0100
Subject: [PATCH] Use TCP socket instead of serial port (SDK)

Instead of opening a serial port, open a TCP socket on port 9999. Pass
the listening socket to the new instance in case a controller reset is
being requested. This makes sure the TCP connection stays up even when
zigbeed restarts.
---
protocol/zigbee/app/zigbeed/serial_adapter.c | 169 +++++++++++--------
1 file changed, 99 insertions(+), 70 deletions(-)

diff --git a/protocol/zigbee/app/zigbeed/serial_adapter.c b/protocol/zigbee/app/zigbeed/serial_adapter.c
index f48630509..2807ab91c 100644
--- a/protocol/zigbee/app/zigbeed/serial_adapter.c
+++ b/protocol/zigbee/app/zigbeed/serial_adapter.c
@@ -17,13 +17,16 @@
*
******************************************************************************/

-#include <stdint.h>
-#include <stdbool.h>
#include <errno.h>
#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <stdbool.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <termios.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
#include <unistd.h>

#include "ember-types.h"
@@ -37,19 +40,17 @@
#define MAX_OUT_BLOCK_LEN 512 // maximum bytes to output at one time
#define MAX_IN_BLOCK_LEN 512 // maximum bytes to input at one time

-#define DEFAULT_SERIAL_PORT "/tmp/ttyZigbeed"
#define DEFAULT_OUT_BLOCK_LEN 1
#define DEFAULT_IN_BLOCK_LEN 256

-char serialPort[SERIAL_PORT_NAME_MAX_LEN] = DEFAULT_SERIAL_PORT;

-#ifdef ZIGBEE_PRO_COMPLIANCE_ON_HOST
-#include "sl_cli_threaded_host.h"
-extern bool sli_cli_is_input_handled(void);
-extern int sli_cli_get_pipe_read_fd(void);
-#endif // ZIGBEE_PRO_COMPLIANCE_ON_HOST
+struct in6_addr ezspListenAddress = IN6ADDR_ANY_INIT;
+uint16_t ezspPort = 9999;
+
+extern jmp_buf gResetJump;
+
+static int socketFd = NULL_FILE_DESCRIPTOR;

-static int serialFd = NULL_FILE_DESCRIPTOR; // file descriptor for serial port
static uint8_t outBuffer[MAX_OUT_BLOCK_LEN]; // array to buffer output
static uint8_t *outBufRd; // outBuffer read pointer
static uint8_t *outBufWr; // outBuffer write pointer
@@ -59,13 +60,59 @@ static uint8_t *inBufRd; // inBuffer read pointer
static uint8_t *inBufWr; // inBuffer write pointer
static uint16_t inBlockLen; // bytes to read ahead

-static void serialClose(void)
+static EmberStatus initSocket(void)
{
- if (serialFd != NULL_FILE_DESCRIPTOR) {
- tcflush(serialFd, TCIOFLUSH);
- close(serialFd);
- serialFd = NULL_FILE_DESCRIPTOR;
+ struct sockaddr_in6 address;
+ int addrlen = sizeof(address);
+ int opt = 1;
+ static int serverFd = -1;
+
+ if ((serverFd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
+ fprintf(stderr, "Failed to create server socket: %s\r\n", strerror(errno));
+ return EMBER_ERR_FATAL;
+ }
+
+ // Forcefully attaching socket to the port 8080
+ if (setsockopt(serverFd, SOL_SOCKET,
+ SO_REUSEADDR, &opt,
+ sizeof(opt))) {
+ fprintf(stderr, "Failed to set server socket options: %s\r\n", strerror(errno));
+ return EMBER_ERR_FATAL;
}
+
+ address.sin6_family = AF_INET6;
+ address.sin6_addr = ezspListenAddress;
+ address.sin6_port = htons(ezspPort);
+
+ if (bind(serverFd, (struct sockaddr*)&address, sizeof(address)) < 0) {
+ fprintf(stderr, "Failed to bind server socket to port %d: %s\r\n", ezspPort, strerror(errno));
+ return EMBER_ERR_FATAL;
+ }
+
+ fprintf(stderr, "Listening on port %d for connection...\r\n", ezspPort);
+ if (listen(serverFd, 3) < 0) {
+ fprintf(stderr, "Failed to listen on server socket: %s\r\n", strerror(errno));
+ return EMBER_ERR_FATAL;
+ }
+
+ fprintf(stderr, "Accepting connection.\r\n");
+ if ((socketFd = accept(serverFd, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0) {
+ fprintf(stderr, "Failed to accept on server socket: %s\r\n", strerror(errno));
+ return EMBER_SERIAL_RX_EMPTY;
+ }
+ fprintf(stderr, "Accepted connection %d.\r\n", socketFd);
+
+ close(serverFd);
+
+ fcntl(socketFd, F_SETFL, O_NONBLOCK);
+
+ if (setsockopt(socketFd, IPPROTO_TCP,
+ TCP_NODELAY, &opt,
+ sizeof(opt))) {
+ fprintf(stderr, "Failed to set server socket options: %s\r\n", strerror(errno));
+ }
+
+ return EMBER_SUCCESS;
}

static void writeFlush(void)
@@ -73,11 +120,10 @@ static void writeFlush(void)
int16_t count;

if (outBufWr - outBufRd) {
- count = write(serialFd, outBufRd, outBufWr - outBufRd);
+ count = write(socketFd, outBufRd, outBufWr - outBufRd);
if (count > 0) {
outBufRd += count;
}
- fsync(serialFd);
if (outBufRd == outBufWr) {
outBufRd = outBufWr = outBuffer;
}
@@ -86,13 +132,23 @@ static void writeFlush(void)

static EmberStatus readAvailable(uint16_t *count)
{
- int16_t bytesRead;
+ int bytesRead;
+
if (inBufRd == inBufWr) {
inBufRd = inBufWr = inBuffer;
- bytesRead = read(serialFd, inBuffer, inBlockLen);
+ bytesRead = read(socketFd, inBuffer, inBlockLen);
if (bytesRead > 0) {
inBufWr += bytesRead;
}
+ if (bytesRead == 0) {
+ fprintf(stderr, "Socket connection has been closed, restarting...\r\n");
+ close(socketFd);
+ socketFd = -1;
+
+ // ZHA cannot handle short interruptions of the port, so reopen the
+ // port asap
+ initSocket();
+ }
}
*count = inBufWr - inBufRd;
if (inBufRd == inBufWr) {
@@ -113,7 +169,8 @@ EmberStatus emberSerialInit(uint8_t port,
#ifdef IO_LOG
logFile = fopen(IO_LOG, "w");
#endif
- struct termios tios;
+ const char *env;
+
outBufRd = outBuffer;
outBufWr = outBuffer;
outBlockLen = DEFAULT_OUT_BLOCK_LEN;
@@ -127,38 +184,28 @@ EmberStatus emberSerialInit(uint8_t port,
inBlockLen = MAX_IN_BLOCK_LEN;
}

- // Make sure any previous file descriptor is nicely closed.
- // This should only be necessary during a failure recovery when the
- // host encountered an error.
- serialClose();
-
- serialFd = open(serialPort,
- O_RDWR | O_NOCTTY | O_NONBLOCK);
-
- if (serialFd == NULL_FILE_DESCRIPTOR) {
- fprintf(stderr, "Failed to open %s: %s\r\n", serialPort, strerror(errno));
- fprintf(stderr, "Use socat to create PTYs for zigbeed and the host app. Eg:\r\n");
- fprintf(stderr, "socat -x -v pty,link=/dev/ttyZigbeeNCP pty,link=/tmp/ttyZigbeeNCP\r\n");
- serialClose();
- return EMBER_ERR_FATAL;
+ // Get file descriptor from previous instance, if valid.
+ env = getenv("ZIGBEED_SOCKET_FD");
+ if (env && (socketFd = strtol(env, NULL, 10)) > 0) {
+ fprintf(stderr, "Reusing socket from previous instance.\r\n");
+ return EMBER_SUCCESS;
}

- tcflush(serialFd, TCIOFLUSH); // flush all input and output data
- fcntl(serialFd, F_SETFL, O_NDELAY);
- tcgetattr(serialFd, &tios); // get current serial port options
-
- tios.c_iflag &= ~(BRKINT | INLCR | IGNCR | ICRNL | INPCK
- | ISTRIP | IMAXBEL | IXON | IXOFF | IXANY);
-
- tios.c_lflag &= ~(ICANON | ECHO | IEXTEN | ISIG); // raw input
-
- tios.c_oflag &= ~OPOST; // raw output
-
- (void) memset(tios.c_cc, _POSIX_VDISABLE, NCCS); // disable all control chars
+ return initSocket();
+}

- tcsetattr(serialFd, TCSAFLUSH, &tios); // set EZSP serial port options
+void serialShutdown(void)
+{
+ char buf[16];

- return EMBER_SUCCESS;
+ // If we aave a valid socket, leave it open and pass fd number to
+ // the new instance.
+ if (socketFd > 0) {
+ snprintf(buf, sizeof(buf), "%d", socketFd);
+ setenv("ZIGBEED_SOCKET_FD", buf, true);
+ } else {
+ unsetenv("ZIGBEED_SOCKET_FD");
+ }
}

EmberStatus emberSerialWriteByte(uint8_t port, uint8_t dataByte)
@@ -211,36 +258,18 @@ void sli_serial_adapter_tick_callback(void)
FD_ZERO(&mainloop.mWriteFdSet);
FD_ZERO(&mainloop.mErrorFdSet);

-#ifdef ZIGBEE_PRO_COMPLIANCE_ON_HOST
- // pro-compliance on host has CLI interface
- // need to set pipeReadFd into the mainloop file descriptor.
- int pipeReadFd = sli_cli_get_pipe_read_fd();
- FD_SET(pipeReadFd, &mainloop.mReadFdSet);
- FD_SET(pipeReadFd, &mainloop.mErrorFdSet);
- serialFd = (serialFd > pipeReadFd ? serialFd : pipeReadFd);
-#endif // ZIGBEE_PRO_COMPLIANCE_ON_HOST
-
// Update mainloop initial FD and its timeout value
- mainloop.mMaxFd = serialFd;
+ mainloop.mMaxFd = socketFd;
mainloop.mTimeout.tv_sec = timeoutMs / 1000;
mainloop.mTimeout.tv_usec = (timeoutMs - mainloop.mTimeout.tv_sec * 1000) * 1000;

// Update mainloop FDs to monitor
- FD_SET(serialFd, &mainloop.mReadFdSet);
- FD_SET(serialFd, &mainloop.mErrorFdSet);
+ FD_SET(socketFd, &mainloop.mReadFdSet);
+ FD_SET(socketFd, &mainloop.mErrorFdSet);
otSysMainloopUpdate(NULL, &mainloop);

if (otSysMainloopPoll(&mainloop) >= 0) {
otSysMainloopProcess(NULL, &mainloop);
-#ifdef ZIGBEE_PRO_COMPLIANCE_ON_HOST
- // If the command is handled by the CLI component, read the data
- // to empty the pipe so that it is ready for the next command.
- if (sli_cli_is_input_handled()) {
- char buff[SL_CLI_THREADED_HOST_PIPE_DATA_LENGTH];
- assert(SL_CLI_THREADED_HOST_PIPE_DATA_LENGTH
- == read(sli_cli_get_pipe_read_fd(), buff, SL_CLI_THREADED_HOST_PIPE_DATA_LENGTH));
- }
-#endif // ZIGBEE_PRO_COMPLIANCE_ON_HOST
} else if (errno != EINTR) {
//printf("%d\n", errno);
assert(false);
--
2.39.0

Loading

0 comments on commit 2036215

Please sign in to comment.