mirror of https://github.com/wb2osz/direwolf.git
Merge branch 'txinh' of git://github.com/ab0tj/direwolf into ab0tj-txinh
Conflicts: hdlc_rec.c modified: atest.c modified: audio.h modified: config.c modified: hdlc_rec.c modified: ptt.c modified: ptt.h
This commit is contained in:
commit
b68039d2c6
4
atest.c
4
atest.c
|
@ -705,6 +705,10 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int get_input (int it, int chan)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
static void usage (void) {
|
static void usage (void) {
|
||||||
|
|
||||||
|
|
10
audio.h
10
audio.h
|
@ -191,6 +191,16 @@ struct audio_s {
|
||||||
|
|
||||||
} octrl[NUM_OCTYPES];
|
} octrl[NUM_OCTYPES];
|
||||||
|
|
||||||
|
#define ICTYPE_TXINH 0
|
||||||
|
|
||||||
|
#define NUM_ICTYPES 1
|
||||||
|
|
||||||
|
struct {
|
||||||
|
ptt_method_t method; /* none, serial port, GPIO, LPT. */
|
||||||
|
int gpio; /* GPIO number */
|
||||||
|
int invert; /* 1 = active low */
|
||||||
|
} ictrl[NUM_ICTYPES];
|
||||||
|
|
||||||
/* Transmit timing. */
|
/* Transmit timing. */
|
||||||
|
|
||||||
int dwait; /* First wait extra time for receiver squelch. */
|
int dwait; /* First wait extra time for receiver squelch. */
|
||||||
|
|
47
config.c
47
config.c
|
@ -1610,6 +1610,53 @@ void config_init (char *fname, struct audio_s *p_audio_config,
|
||||||
|
|
||||||
} /* end of PTT */
|
} /* end of PTT */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* INPUTS
|
||||||
|
*
|
||||||
|
* TXINH - TX holdoff input
|
||||||
|
*
|
||||||
|
* xxx GPIO [-]gpio-num (only type supported yet)
|
||||||
|
*/
|
||||||
|
|
||||||
|
else if (strcasecmp(t, "TXINH") == 0) {
|
||||||
|
int it;
|
||||||
|
char itname[8];
|
||||||
|
|
||||||
|
it = ICTYPE_TXINH;
|
||||||
|
strlcpy (itname, "TXINH", sizeof(itname));
|
||||||
|
|
||||||
|
t = split(NULL,0);
|
||||||
|
if (t == NULL) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Config file line %d: Missing input type name for %s command.\n", line, itname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(t, "GPIO") == 0) {
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Config file line %d: %s with GPIO is only available on Linux.\n", line, itname);
|
||||||
|
#else
|
||||||
|
t = split(NULL,0);
|
||||||
|
if (t == NULL) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Config file line %d: Missing GPIO number for %s.\n", line, itname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*t == '-') {
|
||||||
|
p_audio_config->achan[channel].ictrl[it].gpio = atoi(t+1);
|
||||||
|
p_audio_config->achan[channel].ictrl[it].invert = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p_audio_config->achan[channel].ictrl[it].gpio = atoi(t);
|
||||||
|
p_audio_config->achan[channel].ictrl[it].invert = 0;
|
||||||
|
}
|
||||||
|
p_audio_config->achan[channel].ictrl[it].method = PTT_METHOD_GPIO;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DWAIT - Extra delay for receiver squelch.
|
* DWAIT - Extra delay for receiver squelch.
|
||||||
|
|
|
@ -626,10 +626,13 @@ void dcd_change (int chan, int subchan, int slice, int state)
|
||||||
* at the same time. The channel is busy if ANY of them
|
* at the same time. The channel is busy if ANY of them
|
||||||
* thinks the channel is busy.
|
* thinks the channel is busy.
|
||||||
*
|
*
|
||||||
|
* Version 1.3: New option for input signal to inhibit transmit.
|
||||||
|
*
|
||||||
*--------------------------------------------------------------------*/
|
*--------------------------------------------------------------------*/
|
||||||
|
|
||||||
int hdlc_rec_data_detect_any (int chan)
|
int hdlc_rec_data_detect_any (int chan)
|
||||||
{
|
{
|
||||||
|
|
||||||
int sc;
|
int sc;
|
||||||
assert (chan >= 0 && chan < MAX_CHANS);
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
|
||||||
|
@ -637,6 +640,9 @@ int hdlc_rec_data_detect_any (int chan)
|
||||||
if (composite_dcd[chan][sc] != 0)
|
if (composite_dcd[chan][sc] != 0)
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (get_input(ICTYPE_TXINH, chan) == 1) return (1);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
||||||
} /* end hdlc_rec_data_detect_any */
|
} /* end hdlc_rec_data_detect_any */
|
||||||
|
|
305
ptt.c
305
ptt.c
|
@ -147,6 +147,135 @@ void ptt_set_debug(int debug)
|
||||||
ptt_debug_level = debug;
|
ptt_debug_level = debug;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: export_gpio
|
||||||
|
*
|
||||||
|
* Purpose: Tell the GPIO subsystem to export a GPIO line for
|
||||||
|
* us to use, and set the initial state of the GPIO.
|
||||||
|
*
|
||||||
|
* Inputs: gpio - GPIO line to export
|
||||||
|
* invert: - Is the GPIO active low?
|
||||||
|
* direction: - 0 for input, 1 for output
|
||||||
|
*
|
||||||
|
* Outputs: None.
|
||||||
|
*
|
||||||
|
*------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
void export_gpio(int gpio, int invert, int direction)
|
||||||
|
{
|
||||||
|
HANDLE fd;
|
||||||
|
char stemp[80];
|
||||||
|
struct stat finfo;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
fd = open("/sys/class/gpio/export", O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
||||||
|
dw_printf ("Log in as root and type this command:\n");
|
||||||
|
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
snprintf (stemp, sizeof(stemp), "%d", gpio);
|
||||||
|
if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
|
||||||
|
int e = errno;
|
||||||
|
/* Ignore EBUSY error which seems to mean */
|
||||||
|
/* the device node already exists. */
|
||||||
|
if (e != EBUSY) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error writing \"%s\" to /sys/class/gpio/export, errno=%d\n", stemp, e);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Idea for future:
|
||||||
|
|
||||||
|
On the RPi, the device path for GPIO number XX is /sys/class/gpio/gpioXX.
|
||||||
|
There was a report that it is different for the Cubieboard. For instance
|
||||||
|
GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
|
||||||
|
|
||||||
|
For another similar single board computer, we find the same thing:
|
||||||
|
https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
|
||||||
|
|
||||||
|
How should we deal with this? Some possibilities:
|
||||||
|
|
||||||
|
(1) The user might explicitly mention the name in direwolf.conf.
|
||||||
|
(2) We might be able to find the names in some system device config file.
|
||||||
|
(3) Get a directory listing of /sys/class/gpio then search for a
|
||||||
|
matching name. Suppose we wanted GPIO 61. First look for an exact
|
||||||
|
match to "gpio61". If that is not found, look for something
|
||||||
|
matching the pattern "gpio61_*".
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We will have the same permission problem if not root.
|
||||||
|
* We only care about "direction" and "value".
|
||||||
|
*/
|
||||||
|
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", gpio);
|
||||||
|
err = system (stemp);
|
||||||
|
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", gpio);
|
||||||
|
err = system (stemp);
|
||||||
|
|
||||||
|
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", gpio);
|
||||||
|
|
||||||
|
if (stat(stemp, &finfo) < 0) {
|
||||||
|
int e = errno;
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Failed to get status for %s \n", stemp);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (geteuid() != 0) {
|
||||||
|
if ( ! (finfo.st_mode & S_IWOTH)) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
||||||
|
dw_printf ("Log in as root and type these commands:\n");
|
||||||
|
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/direction", gpio);
|
||||||
|
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/value", gpio);
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set output direction and initial state
|
||||||
|
*/
|
||||||
|
|
||||||
|
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/direction", gpio);
|
||||||
|
fd = open(stemp, O_WRONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
int e = errno;
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error opening %s\n", stemp);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
char gpio_val[8];
|
||||||
|
if (direction) {
|
||||||
|
if (invert) {
|
||||||
|
strlcpy (gpio_val, "high", sizeof(gpio_val));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strlcpy (gpio_val, "low", sizeof(gpio_val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
strlcpy (gpio_val, "in", sizeof(gpio_val));
|
||||||
|
}
|
||||||
|
if (write (fd, gpio_val, strlen(gpio_val)) != strlen(gpio_val)) {
|
||||||
|
int e = errno;
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error writing initial state to %s\n", stemp);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
exit (1);
|
||||||
|
}
|
||||||
|
close (fd);
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
@ -368,6 +497,11 @@ void ptt_init (struct audio_s *audio_config_p)
|
||||||
using_gpio = 1;
|
using_gpio = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (ot = 0; ot < NUM_ICTYPES; ot++) {
|
||||||
|
if (audio_config_p->achan[ch].ictrl[ot].method == PTT_METHOD_GPIO) {
|
||||||
|
using_gpio = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,111 +568,12 @@ void ptt_init (struct audio_s *audio_config_p)
|
||||||
int ot;
|
int ot;
|
||||||
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
||||||
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
||||||
char stemp[80];
|
export_gpio(audio_config_p->achan[ch].octrl[ot].ptt_gpio, audio_config_p->achan[ch].octrl[ot].ptt_invert, 1);
|
||||||
struct stat finfo;
|
}
|
||||||
int err;
|
}
|
||||||
|
for (ot = 0; ot < NUM_ICTYPES; ot++) {
|
||||||
fd = open("/sys/class/gpio/export", O_WRONLY);
|
if (audio_config_p->achan[ch].ictrl[ot].method == PTT_METHOD_GPIO) {
|
||||||
if (fd < 0) {
|
export_gpio(audio_config_p->achan[ch].ictrl[ot].gpio, audio_config_p->achan[ch].ictrl[ot].invert, 0);
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
|
||||||
dw_printf ("Log in as root and type this command:\n");
|
|
||||||
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
snprintf (stemp, sizeof(stemp), "%d", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
|
|
||||||
int e = errno;
|
|
||||||
/* Ignore EBUSY error which seems to mean */
|
|
||||||
/* the device node already exists. */
|
|
||||||
if (e != EBUSY) {
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Error writing \"%s\" to /sys/class/gpio/export, errno=%d\n", stemp, e);
|
|
||||||
dw_printf ("%s\n", strerror(e));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close (fd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Idea for future:
|
|
||||||
|
|
||||||
On the RPi, the device path for GPIO number XX is /sys/class/gpio/gpioXX.
|
|
||||||
There was a report that it is different for the Cubieboard. For instance
|
|
||||||
GPIO 61 has gpio61_pi13 in the path. This indicates connector "i" pin 13.
|
|
||||||
|
|
||||||
For another similar single board computer, we find the same thing:
|
|
||||||
https://www.olimex.com/wiki/A20-OLinuXino-LIME#GPIO_under_Linux
|
|
||||||
|
|
||||||
How should we deal with this? Some possibilities:
|
|
||||||
|
|
||||||
(1) The user might explicitly mention the name in direwolf.conf.
|
|
||||||
(2) We might be able to find the names in some system device config file.
|
|
||||||
(3) Get a directory listing of /sys/class/gpio then search for a
|
|
||||||
matching name. Suppose we wanted GPIO 61. First look for an exact
|
|
||||||
match to "gpio61". If that is not found, look for something
|
|
||||||
matching the pattern "gpio61_*".
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We will have the same permission problem if not root.
|
|
||||||
* We only care about "direction" and "value".
|
|
||||||
*/
|
|
||||||
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
err = system (stemp);
|
|
||||||
snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
err = system (stemp);
|
|
||||||
|
|
||||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
|
|
||||||
if (stat(stemp, &finfo) < 0) {
|
|
||||||
int e = errno;
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Failed to get status for %s \n", stemp);
|
|
||||||
dw_printf ("%s\n", strerror(e));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (geteuid() != 0) {
|
|
||||||
if ( ! (finfo.st_mode & S_IWOTH)) {
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
|
||||||
dw_printf ("Log in as root and type these commands:\n");
|
|
||||||
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set output direction with initial state off.
|
|
||||||
*/
|
|
||||||
|
|
||||||
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
||||||
fd = open(stemp, O_WRONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
int e = errno;
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Error opening %s\n", stemp);
|
|
||||||
dw_printf ("%s\n", strerror(e));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
|
|
||||||
char hilo[8];
|
|
||||||
if (audio_config_p->achan[ch].octrl[ot].ptt_invert) {
|
|
||||||
strlcpy (hilo, "high", sizeof(hilo));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
strlcpy (hilo, "low", sizeof(hilo));
|
|
||||||
}
|
|
||||||
if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) {
|
|
||||||
int e = errno;
|
|
||||||
text_color_set(DW_COLOR_ERROR);
|
|
||||||
dw_printf ("Error writing initial state to %s\n", stemp);
|
|
||||||
dw_printf ("%s\n", strerror(e));
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
close (fd);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -823,7 +858,67 @@ void ptt_set (int ot, int chan, int ptt_signal)
|
||||||
|
|
||||||
} /* end ptt_set */
|
} /* end ptt_set */
|
||||||
|
|
||||||
|
/*-------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* Name: get_input
|
||||||
|
*
|
||||||
|
* Purpose: Read the value of an input line
|
||||||
|
*
|
||||||
|
* Inputs: it - Input type (ICTYPE_TCINH supported so far)
|
||||||
|
* chan - Audio channel number
|
||||||
|
*
|
||||||
|
* Outputs: 0 = inactive, 1 = active, -1 = error
|
||||||
|
*
|
||||||
|
* ------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
int get_input (int it, int chan)
|
||||||
|
{
|
||||||
|
assert (it >= 0 && it < NUM_ICTYPES);
|
||||||
|
assert (chan >= 0 && chan < MAX_CHANS);
|
||||||
|
|
||||||
|
if ( ! save_audio_config_p->achan[chan].valid) {
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Internal error, get_input ( %d, %d ), did not expect invalid channel.\n", it, chan);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __WIN32__
|
||||||
|
#else
|
||||||
|
if (save_audio_config_p->achan[chan].ictrl[it].method == PTT_METHOD_GPIO) {
|
||||||
|
int fd;
|
||||||
|
char stemp[80];
|
||||||
|
|
||||||
|
snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].ictrl[it].gpio);
|
||||||
|
|
||||||
|
fd = open(stemp, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
int e = errno;
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error opening %s to check input.\n", stemp);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char vtemp[2];
|
||||||
|
if (read (fd, vtemp, 1) != 1) {
|
||||||
|
int e = errno;
|
||||||
|
text_color_set(DW_COLOR_ERROR);
|
||||||
|
dw_printf ("Error getting GPIO %d value\n", save_audio_config_p->achan[chan].ictrl[it].gpio);
|
||||||
|
dw_printf ("%s\n", strerror(e));
|
||||||
|
}
|
||||||
|
close (fd);
|
||||||
|
|
||||||
|
if (atoi(vtemp) != save_audio_config_p->achan[chan].ictrl[it].invert) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return -1; /* Method was none, or something went wrong */
|
||||||
|
}
|
||||||
|
|
||||||
/*-------------------------------------------------------------------
|
/*-------------------------------------------------------------------
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue