/*====================================================================*
*
* Copyright (c) 2013 Qualcomm Atheros, Inc.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted (subject to the limitations
* in the disclaimer below) provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* * Neither the name of Qualcomm Atheros nor the names of
* its contributors may be used to endorse or promote products
* derived from this software without specific prior written
* permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
* GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
* COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*--------------------------------------------------------------------*/
/*====================================================================*
*
* ptsctl.c - PTS Module Controller;
*
* Contributor(s):
* Nathaniel Houghton <nhoughto@qca.qualcomm.com>
* Charles Maier <cmaier@qca.qualcomm.com>
* Mathieu Olivari <mathieu@qca.qualcomm.com>
*
*--------------------------------------------------------------------*/
/*====================================================================*
* system header files;
*--------------------------------------------------------------------*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#if defined (__linux__)
# include <termios.h>
#elif defined (__APPLE__)
# include <termios.h>
#elif defined (__OpenBSD__)
# include <termios.h>
#elif defined (WIN32)
# include <windows.h>
#else
#error "Unknown Environment"
#endif
/*====================================================================*
* custom header files;
*--------------------------------------------------------------------*/
#include "../tools/getoptv.h"
#include "../tools/putoptv.h"
#include "../tools/version.h"
#include "../tools/number.h"
#include "../tools/symbol.h"
#include "../tools/timer.h"
#include "../tools/files.h"
#include "../tools/flags.h"
#include "../tools/timer.h"
#include "../tools/error.h"
/*====================================================================*
* custom source files;
*--------------------------------------------------------------------*/
#ifndef MAKEFILE
#include "../tools/getoptv.c"
#include "../tools/putoptv.c"
#include "../tools/version.c"
#include "../tools/uintspec.c"
#include "../tools/synonym.c"
#include "../tools/todigit.c"
#include "../tools/error.c"
#endif
/*====================================================================*
* program constants;
*--------------------------------------------------------------------*/
#define PTSCTL_DEBUG 0
#define PTSCTL_UNITS "CBA"
#define PTSCTL_LEDS 5
#define PTSCTL_BITS 7
#define PTSCTL_WAIT 50
#define PTSCTL_ECHO 0
#define PTSCTL_MODE 1
#define PTSCTL_LINE_ATTN 127
#define PTSCTL_GRND_ATTN 127
#define PTSCTL_BUFFER_SIZE 10
#define PTSCTL_STRING_SIZE 15
#ifdef WIN32
# define PTSCTL_PORT "com1:"
#else
# define PTSCTL_PORT "/dev/ttyS0"
#endif
#define PTSCTL_SILENCE (1 << 0)
#define PTSCTL_VERBOSE (1 << 1)
#define PTSCTL_CHANGE (1 << 2)
#define PTSCTL_DISPLAY (1 << 3)
#define PTSCTL_ITERATE (1 << 4)
/*====================================================================*
* program variables;
*--------------------------------------------------------------------*/
static const struct _term_ modes [] =
{
{
"off",
"0"
},
{
"on",
"1"
}
};
static char buffer [PTSCTL_BUFFER_SIZE];
static char string [PTSCTL_STRING_SIZE];
static signed length = 0;
static signed offset = 0;
/*====================================================================*
*
* void cycle (char * string, unsigned offset, unsigned length);
*
* rotate a number of consecutive characters starting at a given
* offset within a string; this is used to shift the character,
* that represents the power on/off bit, out of the way during
* data conversions from binary to ASCII and ASCII to binary;
*
*--------------------------------------------------------------------*/
static void cycle (char * string, unsigned offset, unsigned length)
{
signed c = string [offset];
memcpy (&string [offset], &string [offset + 1], length);
string [offset + length] = c;
return;
}
/*====================================================================*
*
* void function1 (struct _file_ * port, char const * units, unsigned wait, unsigned echo);
*
* send echo command to Weeder Solid State Relay modules in an order
* specified by units;
*
*--------------------------------------------------------------------*/
static void function1 (struct _file_ * port, char const * units, unsigned wait, unsigned echo)
{
extern char buffer [PTSCTL_BUFFER_SIZE];
extern signed length;
while (*units)
{
length = 0;
buffer [length++] = *units++;
buffer [length++] = 'X';
buffer [length++] = '0' + (echo & 1);
buffer [length++] = '\r';
if (write (port->file, buffer, length) != length)
{
error (1, errno, FILE_CANTSAVE, port->name);
}
SLEEP (wait);
}
return;
}
/*====================================================================*
*
* void function2 (struct _file_ * port, char const * units, unsigned wait, unsigned data);
*
* send write command to Weeder Solid State Relay modules in an
* order specified by units;
*
*--------------------------------------------------------------------*/
static void function2 (struct _file_ * port, char const * units, unsigned wait, unsigned data)
{
extern char buffer [PTSCTL_BUFFER_SIZE];
extern char string [PTSCTL_STRING_SIZE];
extern signed length;
extern signed offset;
memset (string, 0, sizeof (string));
memset (buffer, 0, sizeof (buffer));
for (offset = 0; offset < (signed)(sizeof (string)); offset++)
{
string [offset] = '0' + (data & 1);
data >>= 1;
}
cycle (string, 0, 5);
for (offset = 0; *units; offset += PTSCTL_LEDS)
{
length = 0;
buffer [length++] = *units++;
buffer [length++] = 'W';
memcpy (&buffer [length], &string [offset], PTSCTL_LEDS);
length += PTSCTL_LEDS;
buffer [length++] = '\r';
if (write (port->file, buffer, length) != length)
{
error (1, errno, FILE_CANTSAVE, port->name);
}
SLEEP (wait);
}
return;
}
/*====================================================================*
*
* void function3 (struct _file_ * port, char const * units, unsigned wait);
*
* read weeder solid state modules and display settings on the
* console as attenuation;
*
*--------------------------------------------------------------------*/
static void function3 (struct _file_ * port, char const * units, unsigned wait)
{
extern char buffer [PTSCTL_BUFFER_SIZE];
extern char string [PTSCTL_STRING_SIZE];
extern signed length;
extern signed offset;
signed value1 = 0;
signed value2 = 0;
memset (string, 0, sizeof (string));
for (offset = 0; *units; offset += PTSCTL_LEDS)
{
length = 0;
buffer [length++] = *units++;
buffer [length++] = 'R';
buffer [length++] = '\r';
if (write (port->file, buffer, length) != length)
{
error (1, errno, FILE_CANTSAVE, port->name);
}
SLEEP (wait);
memset (buffer, 0, sizeof (buffer));
if (read (port->file, buffer, PTSCTL_LEDS + 2) == -1)
{
error (1, errno, FILE_CANTREAD, port->name);
}
memcpy (&string [offset], &buffer [1], PTSCTL_LEDS);
SLEEP (wait);
}
cycle (string, PTSCTL_LEDS, 2);
while (--offset > PTSCTL_BITS)
{
value1 <<= 1;
value1 |= string [offset] - '0';
}
while (offset-- > 0)
{
value2 <<= 1;
value2 |= string [offset] - '0';
}
if ((value1 >= 0) && (value2 >= 0))
{
printf ("%d %d\n", value1, value2);
}
return;
}
/*====================================================================*
*
* void function4 (struct _file_ * port, char const * units, unsigned wait);
*
* sequence through all attenuator settings at one second intervals;
* this function can be used to debug program additions and changes;
*
*--------------------------------------------------------------------*/
static void function4 (struct _file_ * port, char const * units, unsigned wait)
{
signed value;
for (value = 0; value < 128; value++)
{
function2 (port, units, wait, (value << 8) | (value << 1) | 1);
function3 (port, units, wait);
SLEEP (wait);
}
return;
}
/*====================================================================*
*
* int main (int argc, char const * argv []);
*
*--------------------------------------------------------------------*/
int main (int argc, char const * argv [])
{
static char const * optv [] =
{
"f:g:n:p:iqrvw:z",
"",
"PTS Module Controller",
"f f\tport is (f) [" PTSCTL_PORT "]",
"g n\tline ground attenuation is (n) [" LITERAL (PTSCTL_GRND_ATTN) "]",
"n n\tline neutral attenuation is (n) [" LITERAL (PTSCTL_LINE_ATTN) "]",
"p n\tpower is (n) [" LITERAL (PTSCTL_MODE) "]",
"q\tquiet mode",
"r\tread and display attenuator settings",
"v\tverbose mode",
"w n\twait (n) millseconds [" LITERAL (PTSCTL_WAIT) "]",
(char const *) (0)
};
struct _file_ port =
{
-1,
PTSCTL_PORT
};
#if defined (WIN32)
HANDLE hSerial;
DCB dcbSerial =
{
0
};
#else
struct termios termios;
#endif
char const * units = PTSCTL_UNITS;
unsigned wait = PTSCTL_WAIT;
unsigned mode = PTSCTL_MODE;
unsigned echo = PTSCTL_ECHO;
unsigned line = PTSCTL_LINE_ATTN;
unsigned grnd = PTSCTL_GRND_ATTN;
unsigned data = 0;
flag_t flags = (flag_t)(0);
signed c;
optind = 1;
if (getenv ("PTSCTL"))
{
port.name = strdup (getenv ("PTSCTL"));
}
while ((c = getoptv (argc, argv, optv)) != -1)
{
switch (c)
{
case 'f':
port.name = optarg;
break;
case 'g':
_setbits (flags, PTSCTL_CHANGE);
grnd = (unsigned)(uintspec (optarg, 0, 0x7F));
break;
case 'n':
_setbits (flags, PTSCTL_CHANGE);
line = (unsigned)(uintspec (optarg, 0, 0x7F));
break;
case 'p':
_setbits (flags, PTSCTL_CHANGE);
mode = (unsigned)(uintspec (synonym (optarg, modes, SIZEOF (modes)), 0, 1));
break;
case 'w':
wait = (unsigned)(uintspec (optarg, 5, 100));
break;
case 'q':
_setbits (flags, PTSCTL_SILENCE);
break;
case 'r':
_setbits (flags, PTSCTL_DISPLAY);
break;
case 'v':
_setbits (flags, PTSCTL_VERBOSE);
break;
case 'z':
_setbits (flags, PTSCTL_ITERATE);
break;
default:
break;
}
}
argc -= optind;
argv += optind;
if (argc)
{
error (1, ENOTSUP, ERROR_TOOMANY);
}
#if defined (WIN32)
hSerial = CreateFile (port.name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hSerial == INVALID_HANDLE_VALUE)
{
error (1, errno, FILE_CANTOPEN, port.name);
}
dcbSerial.DCBlength = sizeof (dcbSerial);
if (!GetCommState (hSerial, &dcbSerial))
{
error (1, 0, FILE_CANTREAD " state", port.name);
}
dcbSerial.BaudRate = CBR_9600;
dcbSerial.ByteSize = 8;
dcbSerial.StopBits = ONESTOPBIT;
dcbSerial.Parity = NOPARITY;
if (!SetCommState (hSerial, &dcbSerial))
{
error (1, 0, FILE_CANTSAVE " state", port.name);
}
CloseHandle (hSerial);
if ((port.file = open (port.name, O_BINARY | O_RDWR)) == -1)
{
error (1, errno, FILE_CANTOPEN, port.name);
}
#else
if ((port.file = open (port.name, O_RDWR|O_NOCTTY|O_NDELAY)) == -1)
{
error (1, 0, FILE_CANTOPEN, port.name);
}
tcgetattr (port.file, &termios);
termios.c_cflag = CS8;
cfsetospeed (&termios, B9600);
tcsetattr (port.file, TCSANOW, &termios);
#endif
function1 (&port, units, wait, echo);
if (_anyset (flags, PTSCTL_CHANGE))
{
data = line << 8 | grnd << 1 | mode;
function2 (&port, units, wait, data);
}
if (_anyset (flags, PTSCTL_DISPLAY))
{
function3 (&port, units, wait);
}
if (_anyset (flags, PTSCTL_ITERATE))
{
function4 (&port, units, wait);
}
close (port.file);
return (0);
}