summaryrefslogtreecommitdiff
path: root/plugins/baycom/baycom_kiss.c
diff options
context:
space:
mode:
authorinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
committerinfo@mode42.com <info@mode42.com>2026-08-08 03:54:55 +0000
commit20cb29c2f8c5c87bc590896854a20b1473ceb358 (patch)
tree2857f41513a56ad41af97b57362639298aa7f033 /plugins/baycom/baycom_kiss.c
#2
Diffstat (limited to 'plugins/baycom/baycom_kiss.c')
-rw-r--r--plugins/baycom/baycom_kiss.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/plugins/baycom/baycom_kiss.c b/plugins/baycom/baycom_kiss.c
new file mode 100644
index 0000000..c66b220
--- /dev/null
+++ b/plugins/baycom/baycom_kiss.c
@@ -0,0 +1,262 @@
+#if defined(__linux__) || defined(__GLIBC__)
+#define _DEFAULT_SOURCE 1
+#endif
+
+#include "baycom_kiss.h"
+
+#include "hybbx/kiss.h"
+#include "hybbx/rf_tx_pace.h"
+#include "serial_port.h"
+#include "hybbx/log.h"
+#include "hybbx/limits.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static hybbx_result_t kiss_wait_device(const char *path)
+{
+ unsigned waited_ms = 0;
+ const unsigned step_ms = 500u;
+ const unsigned max_wait_ms = 30000u;
+
+ if (path == NULL || path[0] == '\0') {
+ return HYBBX_ERR_INVALID;
+ }
+
+ if (access(path, F_OK) == 0) {
+ return HYBBX_OK;
+ }
+
+ while (access(path, F_OK) != 0) {
+ if (waited_ms >= max_wait_ms) {
+ hybbx_log_warn("[baycom] kiss device not ready: %s", path);
+ return HYBBX_ERR_IO;
+ }
+ if (waited_ms == 0u) {
+ hybbx_log_info("[baycom] waiting for kiss device %s (up to %u s)",
+ path, max_wait_ms / 1000u);
+ }
+ usleep((useconds_t)step_ms * 1000u);
+ waited_ms += step_ms;
+ }
+
+ return HYBBX_OK;
+}
+
+struct baycom_kiss_modem {
+ hybbx_baycom_config_t cfg;
+ hybbx_serial_port_t *serial;
+ hybbx_kiss_decoder_t kiss_dec;
+ baycom_modem_frame_cb frame_cb;
+ void *frame_userdata;
+};
+
+static char *baycom_strdup(const char *s)
+{
+ size_t len;
+ char *copy;
+
+ if (s == NULL) {
+ return NULL;
+ }
+
+ len = strlen(s) + 1;
+ copy = malloc(len);
+ if (copy != NULL) {
+ memcpy(copy, s, len);
+ }
+ return copy;
+}
+
+static void on_kiss_frame(uint8_t port, const uint8_t *frame, size_t len,
+ void *userdata)
+{
+ baycom_kiss_modem_t *km = (baycom_kiss_modem_t *)userdata;
+
+ (void)port;
+
+ if (km == NULL || km->frame_cb == NULL || len == 0) {
+ return;
+ }
+
+ km->frame_cb(frame, len, km->frame_userdata);
+}
+
+static hybbx_result_t kiss_send_param(baycom_kiss_modem_t *km,
+ hybbx_kiss_command_t cmd, uint8_t value)
+{
+ uint8_t out[16];
+ size_t out_len;
+
+ out_len = hybbx_kiss_encode(0, cmd, &value, 1, out, sizeof(out));
+ if (out_len == 0) {
+ return HYBBX_ERR_IO;
+ }
+
+ return hybbx_serial_write(km->serial, out, out_len);
+}
+
+static hybbx_result_t kiss_apply_channel_params(baycom_kiss_modem_t *km)
+{
+ hybbx_result_t rc;
+
+ rc = kiss_send_param(km, HYBBX_KISS_CMD_TXDELAY, (uint8_t)km->cfg.txdelay);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ rc = kiss_send_param(km, HYBBX_KISS_CMD_PERSIST, (uint8_t)km->cfg.persist);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ rc = kiss_send_param(km, HYBBX_KISS_CMD_SLOTTIME, (uint8_t)km->cfg.slot);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ rc = kiss_send_param(km, HYBBX_KISS_CMD_TXTAIL, (uint8_t)km->cfg.txtail);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+ return kiss_send_param(km, HYBBX_KISS_CMD_FULLDUPLEX,
+ km->cfg.full_duplex ? 1u : 0u);
+}
+
+hybbx_result_t baycom_kiss_open(baycom_kiss_modem_t **out,
+ const hybbx_baycom_config_t *cfg,
+ baycom_modem_frame_cb cb, void *userdata)
+{
+ baycom_kiss_modem_t *km;
+ hybbx_serial_params_t sparams;
+ hybbx_result_t rc;
+
+ if (out == NULL || cfg == NULL || cfg->device == NULL) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ *out = NULL;
+ km = calloc(1, sizeof(*km));
+ if (km == NULL) {
+ return HYBBX_ERR_NOMEM;
+ }
+
+ /* Own string copies only — never shallow-copy pointers from caller config
+ * (hybbx_baycom_config_free on close would double-free inst->config). */
+ memset(&km->cfg, 0, sizeof(km->cfg));
+ km->cfg.backend = cfg->backend;
+ km->cfg.mode = cfg->mode;
+ km->cfg.iobase = cfg->iobase;
+ km->cfg.irq = cfg->irq;
+ km->cfg.radio_baud = cfg->radio_baud;
+ km->cfg.kernel_autoload = cfg->kernel_autoload;
+ km->cfg.txdelay = cfg->txdelay;
+ km->cfg.txtail = cfg->txtail;
+ km->cfg.slot = cfg->slot;
+ km->cfg.persist = cfg->persist;
+ km->cfg.full_duplex = cfg->full_duplex;
+ km->cfg.serial_baud = cfg->serial_baud;
+ km->cfg.circuit_port = cfg->circuit_port;
+ km->cfg.device = baycom_strdup(cfg->device);
+ km->cfg.mycall = baycom_strdup(cfg->mycall);
+ km->cfg.circuit_host = baycom_strdup(cfg->circuit_host);
+ km->cfg.link_id = baycom_strdup(cfg->link_id);
+ km->cfg.link_password = baycom_strdup(cfg->link_password);
+ km->cfg.link_role = baycom_strdup(cfg->link_role);
+ km->cfg.frequency_mhz = baycom_strdup(cfg->frequency_mhz);
+ km->frame_cb = cb;
+ km->frame_userdata = userdata;
+ hybbx_kiss_decoder_init(&km->kiss_dec);
+
+ hybbx_serial_params_default(&sparams, cfg->serial_baud);
+ sparams.data_bits = 8;
+ sparams.parity = HYBBX_SERIAL_PARITY_NONE;
+ sparams.stop_bits = 1;
+ sparams.assert_modem_lines = 0;
+
+ rc = kiss_wait_device(km->cfg.device);
+ if (rc != HYBBX_OK) {
+ baycom_kiss_close(km);
+ return rc;
+ }
+
+ if (access(km->cfg.device, R_OK | W_OK) != 0) {
+ hybbx_log_warn("[baycom] kiss device not readable/writable: %s (%s; user in dialout?)",
+ km->cfg.device, strerror(errno));
+ baycom_kiss_close(km);
+ return HYBBX_ERR_IO;
+ }
+
+ rc = hybbx_serial_open(&km->serial, km->cfg.device, &sparams);
+ if (rc != HYBBX_OK) {
+ hybbx_log_warn("[baycom] kiss serial open failed: %s (see [serial] log)",
+ km->cfg.device);
+ baycom_kiss_close(km);
+ return rc;
+ }
+
+ rc = kiss_apply_channel_params(km);
+ if (rc != HYBBX_OK) {
+ hybbx_log_warn("[baycom] kiss channel params failed");
+ }
+
+ *out = km;
+ return HYBBX_OK;
+}
+
+void baycom_kiss_close(baycom_kiss_modem_t *km)
+{
+ if (km == NULL) {
+ return;
+ }
+
+ if (km->serial != NULL) {
+ hybbx_serial_close(km->serial);
+ }
+
+ hybbx_baycom_config_free(&km->cfg);
+ free(km);
+}
+
+hybbx_result_t baycom_kiss_poll(baycom_kiss_modem_t *km)
+{
+ uint8_t buf[256];
+ size_t read_len = 0;
+ hybbx_result_t rc;
+
+ if (km == NULL || km->serial == NULL) {
+ return HYBBX_ERR_IO;
+ }
+
+ rc = hybbx_serial_read(km->serial, buf, sizeof(buf), &read_len);
+ if (rc != HYBBX_OK) {
+ return rc;
+ }
+
+ if (read_len > 0) {
+ hybbx_kiss_decoder_feed(&km->kiss_dec, buf, read_len, on_kiss_frame, km);
+ }
+
+ return HYBBX_OK;
+}
+
+hybbx_result_t baycom_kiss_send_frame(baycom_kiss_modem_t *km,
+ const uint8_t *frame, size_t len)
+{
+ uint8_t out[HYBBX_KISS_MAX_FRAME + 8];
+ size_t out_len;
+
+ if (km == NULL || km->serial == NULL || frame == NULL || len == 0) {
+ return HYBBX_ERR_INVALID;
+ }
+
+ hybbx_rf_tx_burst_guard();
+
+ out_len = hybbx_kiss_encode(0, HYBBX_KISS_CMD_DATA, frame, len,
+ out, sizeof(out));
+ if (out_len == 0) {
+ return HYBBX_ERR_IO;
+ }
+
+ return hybbx_serial_write(km->serial, out, out_len);
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com