forked from home-assistant/addons
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
silabs-multiprotocol: Update to Gecko SDK v4.2.0 (home-assistant#2806)
* Update add-on to use Gecko SDK v4.2.0 * Update firmware to v4.2.0 * Bump version
- Loading branch information
Showing
20 changed files
with
514 additions
and
127 deletions.
There are no files selected for viewing
278 changes: 278 additions & 0 deletions
278
silabs-multiprotocol/0001-Use-TCP-socket-instead-of-serial-port-SDK.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
Oops, something went wrong.