diff options
| author | info@mode42.com <info@mode42.com> | 2026-08-07 18:25:13 +0000 |
|---|---|---|
| committer | info@mode42.com <info@mode42.com> | 2026-08-07 18:25:13 +0000 |
| commit | 04d965d67a7264a1c7c211494aebda1953df7603 (patch) | |
| tree | 0ebd700a6e219f84a26a656f4bee8778bc75da7b /stacks/max25-bcpr/src/max25-bcprd.c | |
Initial push
Diffstat (limited to 'stacks/max25-bcpr/src/max25-bcprd.c')
| -rw-r--r-- | stacks/max25-bcpr/src/max25-bcprd.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/stacks/max25-bcpr/src/max25-bcprd.c b/stacks/max25-bcpr/src/max25-bcprd.c new file mode 100644 index 0000000..084c5df --- /dev/null +++ b/stacks/max25-bcpr/src/max25-bcprd.c @@ -0,0 +1,205 @@ +/* + * max25-bcprd — BayCom/based SER12 userspace daemon (unprivileged live path). + * Live SER12: max25-bcprd-init (setuid). Dry-run / offline: this binary. + */ +#define _GNU_SOURCE +#include "bcpr/bcpr_config.h" +#include "bcpr/bcpr_daemon.h" +#include "bcpr/bcpr_kiss.h" +#include "bcpr/bcpr_runas.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> + +static void usage(const char *argv0) +{ + fprintf(stderr, + "Usage: %s -c <bcpr.ini> [--dry-run] [--once] [--seconds N]\n" + " [--cal high|low|alt] [--txd-bias pulse|steady]\n" + " [--ptt-wd|--no-ptt-wd] [--ptt-wd-key-ms N]\n" + " [--ptt-wd-pause-ms N] [--version]\n" + " Host face: max25e0:bc0 / bc1 (KISS PTY via kiss_link)\n" + " Live SER12: max25-bcprd-init (setuid) — this binary refuses root.\n" + " --cal: continuous SER12 tone+PTT (use init for live hardware)\n", + argv0); +} + +int main(int argc, char **argv) +{ + const char *cfg_path = NULL; + int dry = 0; + int seconds = 0; + int cal_mode = BCPR_CAL_OFF; + int cli_txd_bias = -1; + int cli_ptt_wd = -1; + int cli_ptt_wd_key_ms = -1; + int cli_ptt_wd_pause_ms = -1; + int i; + bcpr_config_t cfg; + bcpr_kiss_pty_t ptys[BCPR_MAX_DEVICES]; + int npty = 0; + bcpr_daemon_opts_t opts; + char ver[32] = "0.1.0"; + + for (i = 1; i < argc; i++) { + if ((strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--config") == 0) && + i + 1 < argc) { + cfg_path = argv[++i]; + } else if (strcmp(argv[i], "--dry-run") == 0) { + dry = 1; + } else if (strcmp(argv[i], "--once") == 0) { + if (seconds <= 0) { + seconds = 1; + } + } else if (strcmp(argv[i], "--seconds") == 0 && i + 1 < argc) { + seconds = atoi(argv[++i]); + } else if (strcmp(argv[i], "--cal") == 0 && i + 1 < argc) { + const char *m = argv[++i]; + if (strcmp(m, "high") == 0 || strcmp(m, "1") == 0) { + cal_mode = BCPR_CAL_HIGH; + } else if (strcmp(m, "low") == 0 || strcmp(m, "2") == 0) { + cal_mode = BCPR_CAL_LOW; + } else if (strcmp(m, "alt") == 0 || strcmp(m, "3") == 0) { + cal_mode = BCPR_CAL_ALT; + } else { + fprintf(stderr, "max25-bcprd: --cal needs high|low|alt\n"); + return 2; + } + } else if (strcmp(argv[i], "--txd-bias") == 0 && i + 1 < argc) { + const char *m = argv[++i]; + if (strcasecmp(m, "pulse") == 0 || strcasecmp(m, "sailer") == 0) { + cli_txd_bias = BCPR_TXD_PULSE; + } else if (strcasecmp(m, "steady") == 0 || + strcasecmp(m, "tfpcx") == 0 || + strcasecmp(m, "break") == 0) { + cli_txd_bias = BCPR_TXD_STEADY; + } else { + fprintf(stderr, "max25-bcprd: --txd-bias needs pulse|steady\n"); + return 2; + } + } else if (strcmp(argv[i], "--ptt-wd") == 0) { + cli_ptt_wd = 1; + } else if (strcmp(argv[i], "--no-ptt-wd") == 0) { + cli_ptt_wd = 0; + } else if (strcmp(argv[i], "--ptt-wd-key-ms") == 0 && i + 1 < argc) { + cli_ptt_wd_key_ms = atoi(argv[++i]); + } else if (strcmp(argv[i], "--ptt-wd-pause-ms") == 0 && + i + 1 < argc) { + cli_ptt_wd_pause_ms = atoi(argv[++i]); + } else if (strcmp(argv[i], "--version") == 0 || + strcmp(argv[i], "-V") == 0) { + FILE *vf = fopen("/usr/local/share/max25/max25-bcpr/VERSION", "r"); + if (!vf) { + vf = fopen("stacks/max25-bcpr/VERSION", "r"); + } + if (vf) { + if (fgets(ver, sizeof(ver), vf)) { + ver[strcspn(ver, "\r\n")] = '\0'; + } + fclose(vf); + } + printf("bcprd %s\n", ver); + return 0; + } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + usage(argv[0]); + return 0; + } else { + usage(argv[0]); + return 2; + } + } + if (!cfg_path) { + usage(argv[0]); + return 2; + } + if (cal_mode != BCPR_CAL_OFF && seconds <= 0) { + seconds = 5; + } + + if (bcpr_config_load(&cfg, cfg_path) != 0) { + fprintf(stderr, "max25-bcprd: cannot load %s\n", cfg_path); + return 1; + } + if (dry) { + cfg.dry_run = 1; + } + + if (bcpr_runas_refuse_root(cfg.dry_run) != 0) { + return 1; + } + if (!cfg.dry_run && cal_mode == BCPR_CAL_OFF) { + fprintf(stderr, + "max25-bcprd: live SER12 requires max25-bcprd-init — " + "not this binary alone\n"); + return 1; + } + + { + char self[512]; + ssize_t n = readlink("/proc/self/exe", self, sizeof(self) - 1); + FILE *vf = fopen("/usr/local/share/bcpr/VERSION", "r"); + if (!vf) { + vf = fopen("stacks/max25-bcpr/VERSION", "r"); + } + ver[0] = '\0'; + if (vf) { + if (fgets(ver, sizeof(ver), vf)) { + ver[strcspn(ver, "\r\n")] = '\0'; + } + fclose(vf); + } + if (n > 0) { + self[n] = '\0'; + fprintf(stderr, "max25-bcprd: path=%s version=%s\n", self, + ver[0] ? ver : "0.1.0"); + } else { + fprintf(stderr, "max25-bcprd: version=%s\n", ver[0] ? ver : "0.1.0"); + } + } + + if (cfg.dry_run) { + fprintf(stderr, "max25-bcprd: dry-run (no KISS PTY, no UART I/O)\n"); + } else if (cal_mode == BCPR_CAL_OFF) { + for (i = 0; i < BCPR_MAX_DEVICES; i++) { + if (!cfg.dev[i].enabled) { + continue; + } + if (bcpr_kiss_pty_open(&ptys[npty], i, cfg.dev[i].kiss_link, + cfg.state_dir) != 0) { + while (npty > 0) { + npty--; + bcpr_kiss_pty_close(&ptys[npty]); + } + return 1; + } + npty++; + } + } + if (cal_mode != BCPR_CAL_OFF) { + static const char *cal_names[] = {"off", "high", "low", "alt"}; + fprintf(stderr, + "max25-bcprd: CAL mode=%s seconds=%d (no KISS; SER12 tone+PTT)%s\n", + cal_names[cal_mode], seconds, + cfg.dry_run ? " [dry-run]" : ""); + if (!cfg.dry_run) { + fprintf(stderr, + "max25-bcprd: live cal needs max25-bcprd-init for hardware access\n"); + return 1; + } + } + + memset(&opts, 0, sizeof(opts)); + opts.dry_run = cfg.dry_run; + opts.seconds = seconds; + opts.cal_mode = cal_mode; + opts.cli_txd_bias = cli_txd_bias; + opts.cli_ptt_wd = cli_ptt_wd; + opts.cli_ptt_wd_key_ms = cli_ptt_wd_key_ms; + opts.cli_ptt_wd_pause_ms = cli_ptt_wd_pause_ms; + opts.engine_preopened = 0; + + return bcpr_daemon_run(&cfg, ptys, npty, NULL, &opts); +} |
