/*====================================================================*
*
* 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.
*
*--------------------------------------------------------------------*/
/*====================================================================*
*
* int6kdetect.c
*
*
* Contributor(s):
* Nathaniel Houghton <nhoughto@qca.qualcomm.com>
*
*--------------------------------------------------------------------*/
/*====================================================================*
* system header files;
*--------------------------------------------------------------------*/
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef WIN32
#include <windows.h>
#else
#include <termios.h>
#endif
/*====================================================================*
* custom header files;
*--------------------------------------------------------------------*/
#include "../tools/error.h"
#include "../tools/files.h"
#include "../tools/flags.h"
#include "../tools/putoptv.h"
#include "../tools/getoptv.h"
#include "../serial/serial.h"
/*====================================================================*
* custom source files;
*--------------------------------------------------------------------*/
#ifndef MAKEFILE
#include "../tools/getoptv.c"
#include "../tools/putoptv.c"
#include "../tools/version.c"
#include "../tools/error.c"
#endif
#ifndef MAKEFILE
#include "../serial/baudrate.c"
#endif
/*====================================================================*
* program constants;
*--------------------------------------------------------------------*/
#define SERIAL_PORT "/dev/ttyS0"
#define INT6KDETECT_QUIET (1 << 0)
#define INT6KDETECT_CMD_MODE (1 << 1)
struct serial
{
#ifdef WIN32
HANDLE h;
#else
int fd;
#endif
};
#define UART_NOPARITY 0
#define UART_EVENPARITY 1
#define UART_ODDPARITY 2
struct serial_mode
{
int baud_rate;
int parity;
int data_bits;
int stop_bits;
};
ssize_t read_serial (struct serial *s, void *buf, size_t nbytes)
{
#ifdef WIN32
DWORD read;
BOOL r;
r = ReadFile (s->h, buf, (DWORD) nbytes, &read, NULL);
if (r) return read;
else return -1;
#else
return (read (s->fd, buf, nbytes));
#endif
}
ssize_t write_serial (struct serial *s, void *buf, size_t nbytes)
{
#ifdef WIN32
DWORD written;
BOOL r;
r = WriteFile (s->h, buf, (DWORD) nbytes, &written, NULL);
if (r) return written;
else return -1;
#else
return (write (s->fd, buf, nbytes));
#endif
}
int open_serial (char const *file, struct serial *s)
{
#ifdef WIN32
s->h = CreateFile (file, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (s->h == INVALID_HANDLE_VALUE)
{
return (-1);
}
#else
if ((s->fd = open (file, O_RDWR)) == -1)
{
return (-1);
}
#endif
return (0);
}
int close_serial (struct serial *s)
{
#ifdef WIN32
if (CloseHandle (s->h)) return 0;
else return -1;
#else
return (close (s->fd));
#endif
}
int set_serial (struct serial *s, struct serial_mode *serial_mode)
{
#ifdef WIN32
COMMTIMEOUTS timeouts;
DCB dcbSerial;
memset (&dcbSerial, 0, sizeof (dcbSerial));
dcbSerial.DCBlength = sizeof (dcbSerial);
if (!GetCommState (s->h, &dcbSerial))
{
return (-1);
}
dcbSerial.BaudRate = serial_mode->baud_rate;
dcbSerial.ByteSize = serial_mode->data_bits;
switch (serial_mode->stop_bits)
{
case 1:
dcbSerial.StopBits = ONESTOPBIT;
break;
case 2:
dcbSerial.StopBits = TWOSTOPBITS;
break;
default:
error (1, 0, "invalid stop bit setting");
}
switch (serial_mode->parity)
{
case UART_ODDPARITY:
dcbSerial.Parity = ODDPARITY;
dcbSerial.fParity = TRUE;
break;
case UART_EVENPARITY:
dcbSerial.Parity = EVENPARITY;
dcbSerial.fParity = TRUE;
break;
case UART_NOPARITY:
dcbSerial.Parity = NOPARITY;
dcbSerial.fParity = FALSE;
break;
default:
error (1, 0, "invalid parity serial_mode");
}
if (!SetCommState (s->h, &dcbSerial))
{
error (0, 0, "could not set serial port settings");
return (-1);
}
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutConstant = 10;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 10;
timeouts.WriteTotalTimeoutMultiplier = 10;
if (!SetCommTimeouts (s->h, &timeouts))
{
return (-1);
}
#else
struct termios termios;
speed_t speed;
tcgetattr (s->fd, &termios);
cfmakeraw (&termios);
termios.c_cflag &= ~CSIZE;
switch (serial_mode->data_bits)
{
case 8:
termios.c_cflag |= CS8;
break;
case 7:
termios.c_cflag |= CS7;
break;
case 6:
termios.c_cflag |= CS6;
break;
case 5:
termios.c_cflag |= CS5;
break;
default:
error (1, 0, "invalid serial byte size");
}
switch (serial_mode->stop_bits)
{
case 2:
termios.c_cflag |= CSTOPB;
break;
case 1:
termios.c_cflag &= ~CSTOPB;
break;
default:
error (1, 0, "invalid number of stop bits");
}
switch (serial_mode->parity)
{
case UART_ODDPARITY:
termios.c_cflag |= PARENB;
termios.c_cflag |= PARODD;
break;
case UART_EVENPARITY:
termios.c_cflag |= PARENB;
termios.c_cflag &= ~PARODD;
break;
case UART_NOPARITY:
termios.c_cflag &= ~PARENB;
break;
default:
error (1, 0, "invalid parity serial_mode");
}
if (baudrate (serial_mode->baud_rate, &speed) == -1)
{
error (0, 0, "warning: unsupported baud rate: %d", serial_mode->baud_rate);
return (-1);
}
if (cfsetspeed (&termios, speed) == -1) error (1, 0, "could not set serial baud rate");
termios.c_cc [VTIME] = 1;
termios.c_cc [VMIN] = 0;
if (tcsetattr (s->fd, TCSANOW, &termios) == -1) error (1, 0, "could not set serial attributes");
#endif
return (0);
}
int at_cmd (struct serial *s)
{
char buf [32];
ssize_t r;
if (write_serial (s, "AT\r", 3) == -1) error (1, 0, "could not write");
memset (buf, 0, sizeof (buf));
r = read_serial (s, buf, sizeof (buf) - 1);
if (r < 0) return -1;
else if (r == 0) return -1;
if (!strcmp (buf, "OK\r")) return 0;
return (-1);
}
void wakeup (struct serial *s)
{
sleep (1);
if (write_serial (s, "+++", 3) == -1) error (1, 0, "could not write");
sleep (1);
}
void dump_serial_mode (struct serial_mode *serial_mode)
{
printf ("baud_rate = %d\n", serial_mode->baud_rate);
printf ("stop_bits = %d\n", serial_mode->stop_bits);
printf ("data_bits = %d\n", serial_mode->data_bits);
printf ("parity = %d\n", serial_mode->parity);
}
int try_serial_mode (struct serial *s, struct serial_mode *serial_mode, flag_t flags)
{
if (set_serial (s, serial_mode) == -1)
{
error (0, 0, "could not set serial_mode");
return (-1);
}
if (!_anyset (flags, INT6KDETECT_CMD_MODE)) wakeup (s);
at_cmd (s);
return (at_cmd (s));
}
int detect (struct serial *s, struct serial_mode *serial_mode, flag_t flags)
{
static int rate [] =
{
115200,
9600,
460800,
230400,
57600,
38400,
19200,
4800,
2400,
600,
300,
50
};
static int parity [] =
{
UART_NOPARITY,
UART_EVENPARITY,
UART_ODDPARITY
};
size_t i;
size_t j;
unsigned current;
unsigned total;
total = 2 * 2 * 3 * (sizeof (rate) / sizeof (int));
current = 0;
for (serial_mode->stop_bits = 1; serial_mode->stop_bits <= 2; ++serial_mode->stop_bits)
{
for (serial_mode->data_bits = 8; serial_mode->data_bits >= 7; --serial_mode->data_bits)
{
for (i = 0; i < sizeof (parity) / sizeof (int); ++i)
{
serial_mode->parity = parity [i];
for (j = 0; j < sizeof (rate) / sizeof (int); ++j)
{
serial_mode->baud_rate = rate [j];
++current;
if (!_anyset (flags, INT6KDETECT_QUIET))
{
printf ("\rTesting mode: %03d/%03d (%.01f%%)...", current, total, current * 100.0 / total);
fflush (stdout);
}
if (!try_serial_mode (s, serial_mode, flags))
{
if (!_anyset (flags, INT6KDETECT_QUIET)) printf ("\n");
return (0);
}
}
}
}
}
if (!_anyset (flags, INT6KDETECT_QUIET)) printf ("\n");
return (-1);
}
int main (int argc, char const * argv [])
{
static char const * optv [] =
{
"cl:qv",
"",
"Atheros UART Device Detector",
"c\tassume device is in command mode",
"l f\tserial port is (f) [" SERIAL_PORT "]",
"q\tquiet mode",
"v\tverbose mode",
(char const *) (0)
};
signed c;
char const *line = SERIAL_PORT;
struct serial serial;
struct serial_mode serial_mode;
flag_t flags = 0;
optind = 1;
while ((c = getoptv (argc, argv, optv)) != -1)
{
switch ((char) (c))
{
case 'c':
_setbits (flags, INT6KDETECT_CMD_MODE);
break;
case 'l':
line = optarg;
break;
case 'q':
_setbits (flags, INT6KDETECT_QUIET);
break;
default:
break;
}
}
argc -= optind;
argv += optind;
if (open_serial (line, &serial) == -1) error (1, errno, "could not open %s", line);
if (detect (&serial, &serial_mode, flags) == -1) error (1, 0, "could not detect device");
printf ("Detected the following serial mode:\n");
dump_serial_mode (&serial_mode);
close_serial (&serial);
return (0);
}