/* * afskmodem-ptt.c * This module handles PTT of several modems on different hw-interfaces * * Copyright (C) 2014 Hannes Petermaier * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include #ifndef MACOS #include #endif #include /*#define _DEBUG*/ #ifdef _DEBUG # define DBG(...) printf(__VA_ARGS__) #else # define DBG(...) #endif #ifndef MACOS struct ttydev_t { char *name; /* name of tty */ int fd; /* responsible fd of tty */ }; struct pttcmn_t { struct ttydev_t ttydevs[16]; /* ttyDevs, since we use last * entry for "end-detection" * this entry always must beu * zero */ }; struct ptt_t { struct pttcmn_t *pcmn; /* pointer to common instance */ char devname[1024]; /* name of the device */ int bit; /* bit within device */ unsigned int laststate; /* last written state from modem */ unsigned int origportstate; /* Portstate on open, used to restore * device-state upon exit */ int ttyrelease; /* close tty after every switch-cycle */ int fd; /* handle of ptt-device */ int (*switchfct)(struct ptt_t *pInst, int value); int (*destroyfct)(struct ptt_t *pInst); }; struct pttcmn_t *gpcmn = NULL; static int tty_search_byname(struct pttcmn_t *pInst, char *name) { struct ttydev_t *ptty = pInst->ttydevs; while (ptty->fd != 0) { if (strcmp(ptty->name, name) == 0) { return ptty->fd; } ptty++; } return -1; } static int tty_register(struct pttcmn_t *pInst, char *name, int fd) { unsigned int i; struct ttydev_t *ptty = pInst->ttydevs; for (i=0; i<(sizeof(pInst->ttydevs)/sizeof(pInst->ttydevs[0])-1); i++) { if (ptty->fd == 0) { ptty->fd = fd; ptty->name = name; break; } ptty++; } if (i<(sizeof(pInst->ttydevs)/sizeof(pInst->ttydevs[0]))-1) return 0; return -1; } static int ptt_parport(struct ptt_t *pInst, int value) { int switchval = (pInst->bit > 0 ? 0 : 1) ^ value; int fd; int rc = -1; unsigned short port; fd = open(pInst->devname, O_WRONLY); if (fd < 0 || ioctl(fd, PPCLAIM) < 0) { printf("cannot open %s!\n", pInst->devname); } else { rc = ioctl(fd, PPRDATA, &port); if (switchval) port |= (0x01 << abs(pInst->bit)-1); else port &= ~(0x01 << abs(pInst->bit)-1); rc = ioctl(fd, PPWDATA, &port); } if (fd > 0) { ioctl(fd, PPRELEASE); close(fd); } return rc; } static int ptt_gpio(struct ptt_t *pInst, int value) { int switchval = (pInst->bit > 0 ? 0 : 1) ^ value; int fd, rc; char buf[64]; snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/value", abs(pInst->bit)-1); fd = open(buf, O_WRONLY); if (fd > 0) { if (switchval) rc = write(fd, "1", 1); else rc = write(fd, "0", 1); close(fd); return rc; } else { return -1; } } static int ptt_gpioDestroy(struct ptt_t *pInst) { int fd; char buf[64]; int rc; /* turn ptt off * * may not be the ultimative trick, because we afterwards reset the * device into original state. * Further kernel takes over control upon we've closed it. */ pInst->switchfct(pInst, 0); /* unexport the used gpio to give them to other users */ fd = open("/sys/class/gpio/unexport", O_WRONLY); if (fd > 0) { snprintf(buf, sizeof(buf), "%d", abs(pInst->bit)-1); rc = write(fd, buf, strlen(buf)); close(fd); return 0; } (void)rc; return -1; } static int ptt_gpio_create(int bit) { char buf[63] = { }; int fd; int cnt; int rc; fd = open("/sys/class/gpio/export", O_WRONLY); if (fd <= 0) return -1; snprintf(buf, sizeof(buf), "%d", abs(bit)-1); rc = write(fd, buf, strlen(buf)); close(fd); snprintf(buf, sizeof(buf), "/sys/class/gpio/gpio%d/direction", abs(bit)-1); cnt = 4; fd = 0; do { usleep(125 * 1000); fd = open(buf, O_WRONLY); } while (fd <= 0 && cnt--); if (fd <= 0) return -1; rc = write(fd, "out", 3); close(fd); (void)rc; return 0; } static int ptt_tty(struct ptt_t *pInst, int value) { int switchval = (pInst->bit > 0 ? 0 : 1) ^ value; int rc, flags, first = 0; int fd; if (pInst->fd <= 0) { fd = tty_search_byname(pInst->pcmn, pInst->devname); if (fd > 0) { pInst->fd = fd; DBG("%s: reuse allready open fd (%d) on %s\n", __func__, pInst->fd, pInst->devname); } else { DBG("%s: try to open %s ...\n", __func__, pInst->devname); fd = open(pInst->devname, O_RDONLY); if (fd > 0) { if (tty_register(pInst->pcmn, pInst->devname, fd) == 0 ) { pInst->fd = fd; first = 1; } else { close(fd); } } } } if (pInst->fd > 0) { if (first) { flags = TIOCM_RTS | TIOCM_DTR; switchval = 0; } else { flags = abs(pInst->bit)-1 == 0 ? TIOCM_RTS : TIOCM_DTR; } if (switchval) rc = ioctl(pInst->fd, TIOCMBIS, &flags); else rc = ioctl(pInst->fd, TIOCMBIC, &flags); if (pInst->ttyrelease || rc != 0) { pInst->destroyfct(pInst); pInst->fd = -1; } return rc; } else { return -1; } } static int ptt_ttyDestroy(struct ptt_t *pInst) { struct ttydev_t *ptty = pInst->pcmn->ttydevs; int flags = TIOCM_RTS | TIOCM_DTR; while (ptty->fd > 0) { ioctl(ptty->fd, TIOCMBIC, &flags); close(ptty->fd); ptty++; } return 0; } void *pttinit(char *devname, int bit) { struct ptt_t *pInst; int fd; char buf[64]; unsigned int tmp; int busycnt; if (gpcmn == NULL) { gpcmn = (struct pttcmn_t *)malloc(sizeof(struct pttcmn_t)); if (gpcmn != NULL) memset(gpcmn, 0, sizeof(struct pttcmn_t)); else { printf("%s: cannot allocate memory for common instance", __func__); return 0; } } if ( (pInst = (struct ptt_t *)malloc(sizeof(struct ptt_t))) != 0) { memset(pInst, 0, sizeof(struct ptt_t)); pInst->bit = bit; pInst->pcmn = gpcmn; strncpy(pInst->devname, devname, sizeof(pInst->devname)); if (strstr(devname, "tty") != 0) { if (pInst->bit > 2 || pInst->bit < -2) { printf("fail: %s has only to switchable bits!\n", pInst->devname); goto errorExit; } pInst->fd = -1; pInst->switchfct = &ptt_tty; pInst->destroyfct = &ptt_ttyDestroy; } else if (strcmp(devname, "gpio") == 0) { if (ptt_gpio_create(bit) != 0) goto errorExit; pInst->switchfct = &ptt_gpio; pInst->destroyfct = &ptt_gpioDestroy; } else if (strstr(devname, "parport") != 0) { if (pInst->bit > 8 || pInst->bit < -8) { printf("fail: parport has only 8 bits!\n"); goto errorExit; } pInst->switchfct = &ptt_parport; } else { goto errorExit; } return pInst; } return 0; errorExit: printf("PTT setup on device %s failed.\n", pInst->devname); if (fd > 0) close(fd); free(pInst); return 0; } void pttDestroy(void *arg) { struct ptt_t *pInst = (struct ptt_t *)arg; if (!pInst) return; if(pInst->destroyfct) { pInst->destroyfct(pInst); } free(pInst); } int ptt(void *arg, unsigned int val) { struct ptt_t *pInst = (struct ptt_t *)arg; if (!pInst) return -1; if(pInst->switchfct) { if (val != -1) { pInst->laststate = val; DBG("ptt (0x%08x) switch to %d\n", (unsigned int)pInst, val); return pInst->switchfct(pInst, val); } else { DBG("ptt (0x%08x) switch to %d (cyclic)\n", (unsigned int)pInst, pInst->laststate); return pInst->switchfct(pInst, pInst->laststate); } } return -1; } void pttSetclaim(void *arg, int val) { struct ptt_t *pInst = (struct ptt_t *)arg; if (!pInst) return; pInst->ttyrelease = val; } void pttHelp(char *str, unsigned int maxsize) { char *helptext = \ " -p pttport and bit to switch\n" \ " * /dev/ttyXX for serial\n" \ " * /dev/parport0 for parallel\n" \ " * gpio for kernel gpio-interface\n" \ " choose value for :\n" \ " * tty: 0=RTS, 1=DTR\n" \ " * parport: 0...7 / -0...-7 (inverted)\n" \ " -u close ptt-tty file between switch actions, " \ "may not work on USB tty"; strncpy(str, helptext, maxsize); } #else /* MACOS */ static int ptt_ttyDestroy(struct ptt_t *pInst) { return 0; } void *pttinit(char *devname, int bit) { return NULL; } void pttDestroy(void *arg) { return; } int ptt(void *arg, unsigned int val) { return 0; } void pttSetclaim(void *arg, int val) { return; } void pttHelp(char *str, unsigned int maxsize) { char *helptext = "PTT not supported yet on MacOS"; strncpy(str, helptext, maxsize); } #endif