spdxl/src/afskmodemptt.c

415 wiersze
8.8 KiB
C

/*
* afskmodem-ptt.c
* This module handles PTT of several modems on different hw-interfaces
*
* Copyright (C) 2014 Hannes Petermaier <oe5hpm@oe5xbl.#oe5.aut.eu, oe5hpm@oevsv.at>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#ifndef MACOS
#include <linux/ppdev.h>
#endif
#include <unistd.h>
/*#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];
/* 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);
write(fd, buf, strlen(buf));
close(fd);
return 0;
}
return -1;
}
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) {
fd = open("/sys/class/gpio/export", O_WRONLY);
if (fd > 0) {
snprintf(buf, sizeof(buf),
"%d", abs(pInst->bit)-1);
write(fd, buf, strlen(buf));
close(fd);
} else {
goto errorExit;
}
snprintf(buf, sizeof(buf),
"/sys/class/gpio/gpio%d/direction",
abs(pInst->bit)-1);
fd = open(buf, O_WRONLY);
if (fd > 0) {
write(fd, "out", 3);
close(fd);
} else {
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 <devname> <pttbit> 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 <pttbit>:\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