/* * pog422_pi_hs.c — Pi HS DE/IN for POG422_HS_PATH=pi (machine + OIS). * * Hot path uses GPIO v2 character-device ioctl (same uAPI as pog422_gpio_hs.py / * SuperBall GpioOutputLine). pinctrl system()/popen is fallback only — each * shell cost ~1–3 ms and was dominating SCR pre_bulk / de_hold / post_burst. */ #include "pog422_pi_hs.h" #include #include #include #include #include #include #include #include #include #include #define PI_HS_DE_MACHINE 11 #define PI_HS_IN_MACHINE 9 #define PI_HS_DE_OIS 10 #define PI_HS_IN_OIS 8 static int g_enabled = -1; static int g_logged; static int g_pins_ready; static int g_de_fd = -1; /* gpio_v2 line request fd (OUTPUT) */ static int g_in_fd = -1; /* gpio_v2 line request fd (INPUT) */ static int g_use_gpiochip; /* 1 = ioctl hot path; 0 = pinctrl fallback */ static int pinctrl_run(const char *arg1, const char *arg2, const char *arg3, const char *arg4); static int gpiochip_open_lines(void); static void gpiochip_close_lines(void); static int path_is_pi(const char *raw) { char buf[32]; size_t i; size_t n; if (!raw || !raw[0]) { return 0; } n = 0; for (i = 0; raw[i] && n + 1 < sizeof(buf); i++) { unsigned char c = (unsigned char)raw[i]; if (isspace(c)) { if (n) { break; } continue; } buf[n++] = (char)tolower(c); } buf[n] = '\0'; return (strcmp(buf, "pi") == 0 || strcmp(buf, "pi_hs") == 0 || strcmp(buf, "gpio") == 0 || strcmp(buf, "j19") == 0); } static int read_env_file_hs_path(char *out, size_t out_len) { FILE *f; char line[256]; if (!out || out_len == 0) { return -1; } out[0] = '\0'; f = fopen("/etc/ethergold/ethergold.env", "r"); if (!f) { return -1; } while (fgets(line, sizeof(line), f)) { char *p = line; char *eq; while (*p && isspace((unsigned char)*p)) { p++; } if (strncmp(p, "POG422_HS_PATH=", 15) != 0) { continue; } p += 15; eq = p; while (*eq && *eq != '\r' && *eq != '\n') { eq++; } *eq = '\0'; strncpy(out, p, out_len - 1u); out[out_len - 1u] = '\0'; fclose(f); return 0; } fclose(f); return -1; } int pog422_pi_hs_enabled(void) { const char *env; char file_val[64]; if (g_enabled >= 0) { return g_enabled; } env = getenv("POG422_HS_PATH"); if (!env || !env[0]) { env = getenv("hs_mode"); } if (env && env[0]) { g_enabled = path_is_pi(env) ? 1 : 0; } else if (read_env_file_hs_path(file_val, sizeof(file_val)) == 0) { g_enabled = path_is_pi(file_val) ? 1 : 0; } else { g_enabled = 0; } if (g_enabled && !g_logged) { fprintf(stderr, "pog422_pi_hs: enabled channel=%s DE=BCM%d IN=BCM%d\n", pog422_pi_hs_is_ois() ? "ois" : "machine", pog422_pi_hs_de_bcm(), pog422_pi_hs_in_bcm()); g_logged = 1; (void)pog422_pi_hs_ensure_pins(); } return g_enabled; } int pog422_pi_hs_is_ois(void) { const char *c = getenv("POG422_UARTD_CHANNEL"); return (c && strcmp(c, "ois") == 0) ? 1 : 0; } int pog422_pi_hs_de_bcm(void) { return pog422_pi_hs_is_ois() ? PI_HS_DE_OIS : PI_HS_DE_MACHINE; } int pog422_pi_hs_in_bcm(void) { return pog422_pi_hs_is_ois() ? PI_HS_IN_OIS : PI_HS_IN_MACHINE; } static int pinctrl_run(const char *arg1, const char *arg2, const char *arg3, const char *arg4) { char cmd[128]; int n; int rc; if (arg4) { n = snprintf(cmd, sizeof(cmd), "pinctrl %s %s %s %s", arg1, arg2, arg3, arg4); } else if (arg3) { n = snprintf(cmd, sizeof(cmd), "pinctrl %s %s %s", arg1, arg2, arg3); } else { n = snprintf(cmd, sizeof(cmd), "pinctrl %s %s", arg1, arg2); } if (n < 0 || (size_t)n >= sizeof(cmd)) { return -1; } rc = system(cmd); return (rc == 0) ? 0 : -1; } static int chip_label_match(const char *label) { static const char *const want[] = { "pinctrl-rp1", "pinctrl-bcm2711", "pinctrl-bcm2835", "pinctrl-bcm2712", NULL }; size_t i; if (!label || !label[0]) { return 0; } for (i = 0; want[i]; i++) { if (strcmp(label, want[i]) == 0) { return 1; } } return 0; } static int find_header_chip(char *path_out, size_t path_len, unsigned int *lines_out) { DIR *d; struct dirent *de; char best_path[64]; unsigned int best_lines = 0; int found_label = 0; if (!path_out || path_len == 0 || !lines_out) { return -1; } best_path[0] = '\0'; d = opendir("/dev"); if (!d) { return -1; } while ((de = readdir(d)) != NULL) { char path[64]; int fd; struct gpiochip_info info; if (strncmp(de->d_name, "gpiochip", 8) != 0) { continue; } snprintf(path, sizeof(path), "/dev/%s", de->d_name); fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) { continue; } memset(&info, 0, sizeof(info)); if (ioctl(fd, GPIO_GET_CHIPINFO_IOCTL, &info) != 0) { close(fd); continue; } close(fd); if (chip_label_match(info.label)) { strncpy(best_path, path, sizeof(best_path) - 1u); best_path[sizeof(best_path) - 1u] = '\0'; best_lines = info.lines; found_label = 1; break; } if (!found_label && info.lines >= 28u && info.lines > best_lines) { strncpy(best_path, path, sizeof(best_path) - 1u); best_path[sizeof(best_path) - 1u] = '\0'; best_lines = info.lines; } } closedir(d); if (!best_path[0] || best_lines == 0u) { return -1; } strncpy(path_out, best_path, path_len - 1u); path_out[path_len - 1u] = '\0'; *lines_out = best_lines; return 0; } static int request_output_line(const char *chip_path, unsigned int chip_lines, unsigned int line) { int chip_fd; struct gpio_v2_line_request req; int rc; if (line >= chip_lines) { return -1; } chip_fd = open(chip_path, O_RDONLY | O_CLOEXEC); if (chip_fd < 0) { return -1; } memset(&req, 0, sizeof(req)); req.offsets[0] = line; strncpy(req.consumer, "pog422-uartd-de", sizeof(req.consumer) - 1u); req.config.flags = GPIO_V2_LINE_FLAG_OUTPUT; req.config.num_attrs = 1; req.config.attrs[0].attr.id = GPIO_V2_LINE_ATTR_ID_OUTPUT_VALUES; req.config.attrs[0].attr.values = 0; /* idle low */ req.config.attrs[0].mask = 1ull; req.num_lines = 1; req.event_buffer_size = 0; rc = ioctl(chip_fd, GPIO_V2_GET_LINE_IOCTL, &req); close(chip_fd); if (rc != 0) { return -1; } if (req.fd < 0) { return -1; } return req.fd; } static int request_input_line(const char *chip_path, unsigned int chip_lines, unsigned int line) { int chip_fd; struct gpio_v2_line_request req; int rc; if (line >= chip_lines) { return -1; } chip_fd = open(chip_path, O_RDONLY | O_CLOEXEC); if (chip_fd < 0) { return -1; } memset(&req, 0, sizeof(req)); req.offsets[0] = line; strncpy(req.consumer, "pog422-uartd-hsin", sizeof(req.consumer) - 1u); req.config.flags = GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_BIAS_PULL_UP; req.config.num_attrs = 0; req.num_lines = 1; req.event_buffer_size = 0; rc = ioctl(chip_fd, GPIO_V2_GET_LINE_IOCTL, &req); close(chip_fd); if (rc != 0) { return -1; } if (req.fd < 0) { return -1; } return req.fd; } static void gpiochip_close_lines(void) { if (g_de_fd >= 0) { struct gpio_v2_line_values vals; memset(&vals, 0, sizeof(vals)); vals.mask = 1ull; vals.bits = 0; /* idle low */ (void)ioctl(g_de_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &vals); close(g_de_fd); g_de_fd = -1; } if (g_in_fd >= 0) { close(g_in_fd); g_in_fd = -1; } g_use_gpiochip = 0; } static int gpiochip_open_lines(void) { char chip[64]; unsigned int lines = 0; int de_fd; int in_fd; unsigned int de_bcm = (unsigned int)pog422_pi_hs_de_bcm(); unsigned int in_bcm = (unsigned int)pog422_pi_hs_in_bcm(); gpiochip_close_lines(); if (find_header_chip(chip, sizeof(chip), &lines) != 0) { return -1; } de_fd = request_output_line(chip, lines, de_bcm); if (de_fd < 0) { fprintf(stderr, "pog422_pi_hs: gpiochip DE BCM%u open failed errno=%d (%s) — pinctrl fallback\n", de_bcm, errno, strerror(errno)); return -1; } in_fd = request_input_line(chip, lines, in_bcm); if (in_fd < 0) { fprintf(stderr, "pog422_pi_hs: gpiochip IN BCM%u open failed errno=%d (%s) — pinctrl fallback\n", in_bcm, errno, strerror(errno)); close(de_fd); return -1; } g_de_fd = de_fd; g_in_fd = in_fd; g_use_gpiochip = 1; fprintf(stderr, "pog422_pi_hs: gpiochip hot path DE=BCM%u IN=BCM%u chip=%s\n", de_bcm, in_bcm, chip); return 0; } int pog422_pi_hs_ensure_pins(void) { char de[8]; char in[8]; int n; char other_de[8]; char other_in[8]; int other_de_n; int other_in_n; if (g_pins_ready) { return 0; } n = snprintf(de, sizeof(de), "%d", pog422_pi_hs_de_bcm()); if (n < 0 || (size_t)n >= sizeof(de)) { return -1; } n = snprintf(in, sizeof(in), "%d", pog422_pi_hs_in_bcm()); if (n < 0 || (size_t)n >= sizeof(in)) { return -1; } other_de_n = pog422_pi_hs_is_ois() ? PI_HS_DE_MACHINE : PI_HS_DE_OIS; other_in_n = pog422_pi_hs_is_ois() ? PI_HS_IN_MACHINE : PI_HS_IN_OIS; snprintf(other_de, sizeof(other_de), "%d", other_de_n); snprintf(other_in, sizeof(other_in), "%d", other_in_n); /* Prefer gpiochip for inject hot path. */ if (gpiochip_open_lines() == 0) { /* Park the unused channel OUT via pinctrl so TXS never floats. */ (void)pinctrl_run("set", other_de, "op", "dl"); (void)pinctrl_run("set", other_in, "ip", "pu"); g_pins_ready = 1; return 0; } /* Fallback: pinctrl configure (slow system() — only at init / if chip busy). */ if (pinctrl_run("set", de, "op", "dl") != 0) { fprintf(stderr, "pog422_pi_hs: FATAL cannot set BCM%s op dl\n", de); return -1; } if (pinctrl_run("set", in, "ip", "pu") != 0) { fprintf(stderr, "pog422_pi_hs: FATAL cannot set BCM%s ip pu\n", in); return -1; } (void)pinctrl_run("set", other_de, "op", "dl"); (void)pinctrl_run("set", other_in, "ip", "pu"); g_use_gpiochip = 0; g_pins_ready = 1; fprintf(stderr, "pog422_pi_hs: pins ready (pinctrl fallback) DE=BCM%s IN=BCM%s\n", de, in); return 0; } int pog422_pi_hs_de_set(int high) { if (!g_pins_ready && pog422_pi_hs_ensure_pins() != 0) { return -1; } if (g_use_gpiochip && g_de_fd >= 0) { struct gpio_v2_line_values vals; memset(&vals, 0, sizeof(vals)); vals.mask = 1ull; vals.bits = high ? 1ull : 0ull; if (ioctl(g_de_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &vals) != 0) { return -1; } return 0; } { char bcm[8]; int n = snprintf(bcm, sizeof(bcm), "%d", pog422_pi_hs_de_bcm()); if (n < 0 || (size_t)n >= sizeof(bcm)) { return -1; } return pinctrl_run("set", bcm, "op", high ? "dh" : "dl"); } } int pog422_pi_hs_in_active(uint8_t *active) { if (!active) { return -1; } *active = 0; if (!g_pins_ready && pog422_pi_hs_ensure_pins() != 0) { return -1; } if (g_use_gpiochip && g_in_fd >= 0) { struct gpio_v2_line_values vals; int hi; memset(&vals, 0, sizeof(vals)); vals.mask = 1ull; if (ioctl(g_in_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &vals) != 0) { return -1; } hi = (vals.bits & 1ull) ? 1 : 0; /* active-low: IN low = bus driven */ *active = hi ? 0u : 1u; return 0; } { char bcm[8]; char cmd[64]; char line[256]; FILE *fp; int n; int hi = -1; n = snprintf(bcm, sizeof(bcm), "%d", pog422_pi_hs_in_bcm()); if (n < 0 || (size_t)n >= sizeof(bcm)) { return -1; } n = snprintf(cmd, sizeof(cmd), "pinctrl get %s", bcm); if (n < 0 || (size_t)n >= sizeof(cmd)) { return -1; } fp = popen(cmd, "r"); if (!fp) { return -1; } if (fgets(line, sizeof(line), fp)) { if (strstr(line, "| hi") || strstr(line, "|hi")) { hi = 1; } else if (strstr(line, "| lo") || strstr(line, "|lo")) { hi = 0; } } pclose(fp); if (hi < 0) { return -1; } *active = hi ? 0u : 1u; return 0; } }