diff options
| author | info@mode42.com <info@mode42.com> | 2026-08-08 03:54:55 +0000 |
|---|---|---|
| committer | info@mode42.com <info@mode42.com> | 2026-08-08 03:54:55 +0000 |
| commit | 20cb29c2f8c5c87bc590896854a20b1473ceb358 (patch) | |
| tree | 2857f41513a56ad41af97b57362639298aa7f033 /plugins/baycom/baycom_kiss.c | |
#2
Diffstat (limited to 'plugins/baycom/baycom_kiss.c')
| -rw-r--r-- | plugins/baycom/baycom_kiss.c | 262 |
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); +} |
