summaryrefslogtreecommitdiff
path: root/stacks/max25-bcpr/src/bcpr_lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'stacks/max25-bcpr/src/bcpr_lock.c')
-rw-r--r--stacks/max25-bcpr/src/bcpr_lock.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/stacks/max25-bcpr/src/bcpr_lock.c b/stacks/max25-bcpr/src/bcpr_lock.c
new file mode 100644
index 0000000..b179323
--- /dev/null
+++ b/stacks/max25-bcpr/src/bcpr_lock.c
@@ -0,0 +1,202 @@
+/*
+ * Exclusive lock on owned COM ports only (flock + optional setserial uart none).
+ * Pass real IRQ — refuse mismatch. See vault IRQ/lock contract.
+ */
+#define _GNU_SOURCE
+#include "bcpr/bcpr_lock.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static void tty_basename(const char *serial, char *out, size_t out_sz)
+{
+ const char *p = strrchr(serial, '/');
+ p = p ? p + 1 : serial;
+ snprintf(out, out_sz, "%s", p);
+}
+
+int bcpr_lock_verify_irq(const char *serial, unsigned expect_irq,
+ unsigned *got_irq, unsigned *got_io)
+{
+ char name[64];
+ char path[128];
+ char buf[64];
+ FILE *f;
+ unsigned irq = 0;
+ unsigned io = 0;
+
+ if (!serial || !serial[0]) {
+ return -1;
+ }
+ tty_basename(serial, name, sizeof(name));
+ snprintf(path, sizeof(path), "/sys/class/tty/%s/irq", name);
+ f = fopen(path, "r");
+ if (f) {
+ if (fgets(buf, sizeof(buf), f)) {
+ irq = (unsigned)strtoul(buf, NULL, 0);
+ }
+ fclose(f);
+ } else {
+ /* Fallback: setserial -g (best-effort). */
+ char cmd[256];
+ FILE *p;
+ snprintf(cmd, sizeof(cmd), "setserial -g %s 2>/dev/null", serial);
+ p = popen(cmd, "r");
+ if (!p) {
+ return -1;
+ }
+ if (fgets(buf, sizeof(buf), p)) {
+ char *ip = strstr(buf, "IRQ: ");
+ char *pp = strstr(buf, "Port: ");
+ if (ip) {
+ irq = (unsigned)strtoul(ip + 5, NULL, 0);
+ }
+ if (pp) {
+ io = (unsigned)strtoul(pp + 6, NULL, 0);
+ }
+ }
+ pclose(p);
+ }
+
+ snprintf(path, sizeof(path), "/sys/class/tty/%s/iomem_base", name);
+ f = fopen(path, "r");
+ if (f) {
+ if (fgets(buf, sizeof(buf), f)) {
+ io = (unsigned)strtoul(buf, NULL, 0);
+ }
+ fclose(f);
+ }
+
+ if (got_irq) {
+ *got_irq = irq;
+ }
+ if (got_io) {
+ *got_io = io;
+ }
+ if (expect_irq == 0) {
+ return -1; /* never invent; refuse zero when required */
+ }
+ if (irq == 0 || irq != expect_irq) {
+ return -1;
+ }
+ return 0;
+}
+
+static int make_lock_path(const bcpr_dev_config_t *dev, char *out, size_t sz)
+{
+ char name[64];
+ const char *dir = "/var/run/max25-bcpr";
+ tty_basename(dev->serial, name, sizeof(name));
+ if (name[0] == '\0') {
+ return -1;
+ }
+ snprintf(out, sz, "%s/lock-%s", dir, name);
+ return 0;
+}
+
+int bcpr_lock_acquire(bcpr_port_lock_t *lk, const bcpr_dev_config_t *dev,
+ int dry_run)
+{
+ char lockpath[192];
+ unsigned got_irq = 0, got_io = 0;
+
+ if (!lk || !dev) {
+ return -1;
+ }
+ memset(lk, 0, sizeof(*lk));
+ snprintf(lk->serial, sizeof(lk->serial), "%s", dev->serial);
+ lk->iobase = dev->iobase;
+ lk->irq = dev->irq;
+ lk->lock_fd = -1;
+ lk->dry_run = dry_run ? 1 : 0;
+
+ if (dry_run) {
+ return 0;
+ }
+ if (!dev->serial[0] || dev->irq == 0 || dev->iobase == 0) {
+ return -1;
+ }
+ if (bcpr_lock_verify_irq(dev->serial, dev->irq, &got_irq, &got_io) != 0) {
+#if defined(__FreeBSD__)
+ /*
+ * No Linux sysfs/setserial — operator INI is SSoT for iobase/irq
+ * (see vault ExSys AM1 map). Skip hard fail when probe unavailable.
+ */
+ got_irq = dev->irq;
+ got_io = dev->iobase;
+#else
+ fprintf(stderr,
+ "bcpr: IRQ mismatch or unreadable for %s (expect %u got %u)\n",
+ dev->serial, dev->irq, got_irq);
+ return -1;
+#endif
+ }
+ if (got_io && got_io != dev->iobase) {
+ fprintf(stderr,
+ "bcpr: iobase mismatch for %s (expect 0x%x got 0x%x)\n",
+ dev->serial, dev->iobase, got_io);
+ return -1;
+ }
+
+ if (make_lock_path(dev, lockpath, sizeof(lockpath)) != 0) {
+ return -1;
+ }
+ (void)mkdir("/var/run/max25-bcpr", 0755);
+ lk->lock_fd = open(lockpath, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
+ if (lk->lock_fd < 0) {
+ perror("bcpr lock open");
+ return -1;
+ }
+ if (flock(lk->lock_fd, LOCK_EX | LOCK_NB) != 0) {
+ fprintf(stderr, "bcpr: port %s already locked\n", dev->serial);
+ close(lk->lock_fd);
+ lk->lock_fd = -1;
+ return -1;
+ }
+
+ /* Release 8250 claim on this COM only so userspace can bit-bang. */
+ {
+ char cmd[256];
+ snprintf(cmd, sizeof(cmd), "setserial %s uart none 2>/dev/null",
+ dev->serial);
+ if (system(cmd) == 0) {
+ lk->uart_released = 1;
+ }
+ }
+ return 0;
+}
+
+void bcpr_lock_release(bcpr_port_lock_t *lk)
+{
+ if (!lk) {
+ return;
+ }
+ if (lk->dry_run) {
+ memset(lk, 0, sizeof(*lk));
+ lk->lock_fd = -1;
+ return;
+ }
+ if (lk->uart_released && lk->serial[0]) {
+ char cmd[256];
+ /* Restore 16550A claim — best effort. */
+ snprintf(cmd, sizeof(cmd),
+ "setserial %s uart 16550A port 0x%x irq %u 2>/dev/null",
+ lk->serial, lk->iobase, lk->irq);
+ (void)system(cmd);
+ lk->uart_released = 0;
+ }
+ if (lk->lock_fd >= 0) {
+ flock(lk->lock_fd, LOCK_UN);
+ close(lk->lock_fd);
+ lk->lock_fd = -1;
+ }
+ memset(lk, 0, sizeof(*lk));
+ lk->lock_fd = -1;
+}
git clone -b <branch> https://cgit.mode42.com/<repo>.git
git clone -b <branch> git://cgit.mode42.com/<repo>.git

info@mode42.com