You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
QnetGateway/DVAPDongle.cpp

852 lines
16 KiB

/*
* Copyright 2017-2018,2021 by Thomas Early, N7TAE
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <termios.h>
#include <time.h>
#include <sys/file.h>
#include <sys/select.h>
#include "DVAPDongle.h"
CDVAPDongle::CDVAPDongle() : MAX_REPL_CNT(20u)
{
}
CDVAPDongle::~CDVAPDongle()
{
}
bool CDVAPDongle::open_device(const char *device)
{
if (0 == access(device, R_OK | W_OK))
{
if (open_serial(device))
{
if (0 == flock(serfd, LOCK_EX | LOCK_NB))
{
printf("Device %s is now locked for exclusive use\n", device);
return true;
}
else
{
close(serfd);
serfd = -1;
fprintf(stderr, "Device %s is alread locked/used\n", device);
}
}
}
return false;
}
bool CDVAPDongle::Initialize(const char *devpath, const char *serialno, const int frequency, const int offset, int const power, const int squelch)
{
bool ok = false;
if (strlen(devpath))
{
// device path is specified in cfg file, try to open it
ok = open_device(devpath);
if (! ok)
{
fprintf(stderr, "Device '%s' could not be opened\n", devpath);
}
}
else
{
// serial number is specified in cfg file, try to find it and open it
char device[16];
for (int i = 0; i < 32; i++)
{
sprintf(device, "/dev/ttyUSB%d", i);
printf("Trying device %s...\n", device);
if (open_device(device))
{
if (get_ser(device, serialno))
{
ok = true;
}
else
{
close(serfd);
serfd = -1;
}
}
if (ok)
break;
}
}
ok = ok && get_name();
ok = ok && get_fw();
ok = ok && set_modu();
ok = ok && set_mode();
ok = ok && set_sql(squelch);
ok = ok && set_pwr(power);
ok = ok && set_off(offset);
ok = ok && set_freq(frequency);
ok = ok && start_dvap();
if (!ok)
{
if (serfd != -1)
{
Stop();
close(serfd);
serfd = -1;
}
}
return ok;
}
REPLY_TYPE CDVAPDongle::GetReply(SDVAP_REGISTER &dr)
{
dr.header = dr.param.control = 0;
unsigned int off = 2;
int rc = read_from_dvp(&dr.header, 2); // read the header
if (rc == 0)
return RT_TIMEOUT;
if (rc != 2)
return RT_ERR;
switch (dr.header)
{
case 0x5u:
case 0x6u:
case 0x7u:
case 0x8u:
case 0xcu:
case 0xdu:
case 0x10u:
case 0x2005u:
case 0x2007u:
case 0x602fu:
case 0xa02fu:
case 0xc012u:
break; // these are all expected headers
default:
printf("unknown header=0x%d\n", (unsigned)dr.header);
if (syncit())
return RT_ERR;
return RT_TIMEOUT;
}
// read the rest of the register
uint16_t len = dr.header & 0x1fff;
while (off < len)
{
uint8_t *ptr = (uint8_t *)&dr;
rc = read_from_dvp(ptr + off, len - off);
if (rc < 0)
return RT_TIMEOUT;
if (rc > 0)
off += rc;
}
// okay, now we'll parse the register and return its type
switch (dr.header)
{
case 0x5u:
switch (dr.param.control)
{
case 0x18u:
if (dr.param.byte)
return RT_START;
else
return RT_STOP;
case 0x28u:
if (0x1u==dr.param.ustr[0])
return RT_MODU;
break;
case 0x80u:
return RT_SQL;
case 0x2au:
if (0x0u==dr.param.ustr[0])
return RT_MODE;
break;
}
break;
case 0x6u:
switch (dr.param.control)
{
case 0x138u:
return RT_PWR;
case 0x400u:
return RT_OFF;
}
break;
case 0x7u:
if (0x4u==dr.param.control && 0x1u==dr.param.ustr[0])
return RT_FW;
break;
case 0x8u:
if (0x220u==dr.param.control)
return RT_FREQ;
break;
case 0xcu:
if (0x230u==dr.param.control)
return RT_FREQ_LIMIT;
break;
case 0xdu:
if (0x2u==dr.param.control)
return RT_SER;
break;
case 0x10u:
if (0x1u==dr.param.control)
return RT_NAME;
break;
case 0x2005u:
if (0x118u==dr.param.control)
return RT_PTT;
break;
case 0x2007u:
if (0x90u==dr.param.control)
return RT_STS;
break;
case 0x602fu:
return RT_HDR_ACK;
case 0xa02fu:
return RT_HDR;
case 0xc012u:
return RT_DAT;
}
printf("Unrecognized data from dvap: header=%#x control=%#x\n", (unsigned)dr.header, (unsigned)dr.param.control);
if (syncit())
return RT_ERR;
return RT_TIMEOUT;
}
bool CDVAPDongle::syncit()
{
unsigned char data[7];
struct timeval tv;
fd_set fds;
short cnt = 0;
printf("Starting syncing dvap\n");
memset(data, 0x00, 7);
dvapreg.header = 0x2007u;
dvapreg.param.control = 0x90u;
while (memcmp(data, &dvapreg, 4) != 0)
{
FD_ZERO(&fds);
FD_SET(serfd, &fds);
tv.tv_sec = 0;
tv.tv_usec = 1000;
int n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n <= 0)
{
cnt ++;
if (cnt > 100)
{
printf("syncit() uncessful...stopping\n");
return true;
}
}
else
{
unsigned char c;
n = read_from_dvp(&c, 1);
if (n > 0)
{
data[0] = data[1];
data[1] = data[2];
data[2] = data[3];
data[3] = data[4];
data[4] = data[5];
data[5] = data[6];
data[6] = c;
cnt = 0;
}
}
}
printf("Stopping syncing dvap\n");
return false;
}
bool CDVAPDongle::get_ser(const char *dvp, const char *dvap_serial_number)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x2u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4)
{
printf("Failed to send request to get dvap serial#\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap serial#\n");
return false;
}
}
while (reply != RT_SER);
if (0 == strcmp(dvapreg.param.sstr, dvap_serial_number))
{
printf("Using %s: %s, because serial number matches your configuration file\n", dvp, dvap_serial_number);
return true;
}
printf("Device %s has serial %s, but does not match your configuration value %s\n", dvp, dvapreg.param.sstr, dvap_serial_number);
return false;
}
bool CDVAPDongle::get_name()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x1u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4)
{
printf("Failed to send request to get dvap name\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap name\n");
return false;
}
}
while (reply != RT_NAME);
if (0x10u!=dvapreg.header || 0x1u!=dvapreg.param.control || strncmp(dvapreg.param.sstr, "DVAP Dongle", 11))
{
printf("Failed to receive dvap name, got %s\n", dvapreg.param.sstr);
return false;
}
printf("Device name: %.*s\n", 11, dvapreg.param.sstr);
return true;
}
bool CDVAPDongle::get_fw()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x2005u;
dvapreg.param.control = 0x4u;
dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5)
{
printf("Failed to send request to get dvap fw\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to receive dvap fw\n");
return false;
}
}
while (reply != RT_FW);
unsigned int ver = dvapreg.param.ustr[1] + 256 * dvapreg.param.ustr[2];
printf("dvap fw ver: %u.%u\n", ver / 100, ver % 100);
return true;
}
bool CDVAPDongle::set_modu()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x28u;
dvapreg.param.ustr[0] = 0x1u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5)
{
printf("Failed to send request to set dvap modulation\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap modulation\n");
return false;
}
}
while (reply != RT_MODU);
return true;
}
bool CDVAPDongle::set_mode()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x2au;
dvapreg.param.ustr[0] = 0x0u;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5)
{
printf("Failed to send request to set dvap mode\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap mode\n");
return false;
}
}
while (reply != RT_MODE);
return true;
}
bool CDVAPDongle::set_sql(int squelch)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x80u;
if (squelch < -128)
{
printf("Squelch setting of %d too small, resetting...\n", squelch);
squelch = -128;
}
else if (squelch > -45)
{
printf("Squelch setting of %d too large, resetting...\n", squelch);
squelch = -45;
}
dvapreg.param.byte = (int8_t)squelch;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5)
{
printf("Failed to send request to set dvap sql\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap sql\n");
return false;
}
}
while (reply != RT_SQL);
printf("DVAP squelch is %d dB\n", (int)dvapreg.param.byte);
return true;
}
bool CDVAPDongle::set_pwr(int power)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x6u;
dvapreg.param.control = 0x138u;
if (power < -12)
{
printf("Power setting of %d is too low, resetting...\n", power);
power = -12;
}
else if (power > 10)
{
printf("Power setting of %d is too high, resetting...\n", power);
power = 10;
}
dvapreg.param.word = (int16_t)power;
int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6)
{
printf("Failed to send request to set dvap pwr\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap pwr\n");
return false;
}
}
while (reply != RT_PWR);
printf("DVAP power is %d dB\n", (int)dvapreg.param.word);
return true;
}
bool CDVAPDongle::set_off(int offset)
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x6u;
dvapreg.param.control = 0x400u;
if (offset < -2000)
{
printf("Offset of %d is too low, resetting...\n", offset);
offset = -2000;
}
else if (offset > 2000)
{
printf("Offset of %d is too high, resetting...\n", offset);
offset = 2000;
}
dvapreg.param.word = (int16_t)offset;
int rc = write_to_dvp(&dvapreg, 6);
if (rc != 6)
{
printf("Failed to send request to set dvap offset\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap offset\n");
return false;
}
}
while (reply != RT_OFF);
printf("DVAP offset is %d Hz\n", (int)dvapreg.param.word);
return true;
}
bool CDVAPDongle::set_freq(int frequency)
{
unsigned cnt = 0;
REPLY_TYPE reply;
// first get the frequency limits
dvapreg.header = 0x2004u;
dvapreg.param.control = 0x230u;
int rc = write_to_dvp(&dvapreg, 4);
if (rc != 4)
{
printf("Failed to send request for frequency limits\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests for dvap frequency limits\n");
return false;
}
}
while (reply != RT_FREQ_LIMIT);
printf("DVAP Frequency limits are from %d to %d Hz\n", dvapreg.param.twod[0], dvapreg.param.twod[1]);
// okay, now we know the frequency limits, get on with the show...
if (frequency < dvapreg.param.twod[0])
{
printf("Frequency of %d is too small, resetting...\n", frequency);
frequency = dvapreg.param.twod[0];
}
else if (frequency > dvapreg.param.twod[1])
{
printf("Frequency of %d is too large, resetting...\n", frequency);
frequency = dvapreg.param.twod[1];
}
cnt = 0;
dvapreg.header = 0x8u;
dvapreg.param.control = 0x220u;
dvapreg.param.dword = frequency;
rc = write_to_dvp(&dvapreg, 8);
if (rc != 8)
{
printf("Failed to send request to set dvap frequency\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to set dvap frequency\n");
return false;
}
}
while (reply != RT_FREQ);
printf("DVAP frequency is %d Hz\n", dvapreg.param.dword);
return true;
}
bool CDVAPDongle::start_dvap()
{
unsigned cnt = 0;
REPLY_TYPE reply;
dvapreg.header = 0x5u;
dvapreg.param.control = 0x18u;
dvapreg.param.byte = 0x1;
int rc = write_to_dvp(&dvapreg, 5);
if (rc != 5)
{
printf("Failed to send request to start the dvap dongle\n");
return false;
}
do
{
usleep(5000);
reply = GetReply(dvapreg);
cnt ++;
if (cnt >= MAX_REPL_CNT)
{
printf("Reached max number of requests to start the dvap dongle\n");
return false;
}
}
while (reply != RT_START);
return true;
}
void CDVAPDongle::SendRegister(SDVAP_REGISTER &dr)
{
unsigned int len = dr.header & 0x1fff;
write_to_dvp(&dr, len);
return;
}
int CDVAPDongle::write_to_dvp(const void *buffer, const unsigned int len)
{
unsigned int ptr = 0;
if (len == 0)
return 0;
uint8_t *buf = (uint8_t *)buffer;
while (ptr < len)
{
ssize_t n = write(serfd, buf + ptr, len - ptr);
if (n < 0)
{
printf("Error %d writing to dvap, message=%s\n", errno, strerror(errno));
return -1;
}
if (n > 0)
ptr += n;
}
return len;
}
int CDVAPDongle::read_from_dvp(void *buffer, unsigned int len)
{
unsigned int off = 0;
fd_set fds;
int n;
struct timeval tv;
ssize_t temp_len;
uint8_t *buf = (uint8_t *)buffer;
if (len == 0)
return 0;
while (off < len)
{
FD_ZERO(&fds);
FD_SET(serfd, &fds);
if (off == 0)
{
tv.tv_sec = 0;
tv.tv_usec = 0;
n = select(serfd + 1, &fds, NULL, NULL, &tv);
if (n == 0)
return 0; // nothing to read from the dvap
}
else
n = select(serfd + 1, &fds, NULL, NULL, NULL);
if (n < 0)
{
printf("select error=%d on dvap\n", errno);
return -1;
}
if (n > 0)
{
temp_len = read(serfd, buf + off, len - off);
if (temp_len > 0)
off += temp_len;
}
}
return len;
}
bool CDVAPDongle::open_serial(const char *device)
{
static termios t;
serfd = open(device, O_RDWR | O_NOCTTY | O_NDELAY, 0);
if (serfd < 0)
{
printf("Failed to open device [%s], error=%d, message=%s\n", device, errno, strerror(errno));
return false;
}
if (isatty(serfd) == 0)
{
printf("Device %s is not a tty device\n", device);
close(serfd);
serfd = -1;
return false;
}
if (tcgetattr(serfd, &t) < 0)
{
printf("tcgetattr failed for %s, error=%d, message-%s\n", device, errno, strerror(errno));
close(serfd);
serfd = -1;
return false;
}
t.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG);
t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY);
t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS);
t.c_cflag |= CS8;
t.c_oflag &= ~(OPOST);
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = 10;
cfsetospeed(&t, B230400);
cfsetispeed(&t, B230400);
if (tcsetattr(serfd, TCSANOW, &t) < 0)
{
printf("tcsetattr failed for %s, error=%dm message=%s\n", device, errno, strerror(errno));
close(serfd);
serfd = -1;
return false;
}
return true;
}
void CDVAPDongle::Stop()
{
SDVAP_REGISTER dvap;
dvap.header = 0x5u;
dvap.param.control = 0x18u;
dvap.param.byte = 0;
write_to_dvp(&dvap, 5);
return;
}
int CDVAPDongle::KeepAlive()
{
SDVAP_REGISTER dvap;
dvap.header = 0x6003u;
dvap.nul = 0x0u;
return write_to_dvp(&dvap, 3);
}

Powered by TurnKey Linux.