409 lines
9.0 KiB
C
409 lines
9.0 KiB
C
/*
|
|
* serial.c -- Serial port routines.
|
|
*
|
|
* Copyright (C) 2008 Hugo Villeneuve <hugo@hugovil.com>
|
|
*
|
|
* 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 "config.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h> /* Standard input/output definitions */
|
|
#include <string.h> /* String function definitions */
|
|
#include <unistd.h> /* UNIX standard function definitions */
|
|
#include <fcntl.h> /* File control definitions */
|
|
#include <errno.h> /* Error number definitions */
|
|
#include <termios.h> /* POSIX terminal control definitions */
|
|
#include <ctype.h> /* For isdigit() and friends... */
|
|
#include <sys/time.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include "common.h"
|
|
#include "serial.h"
|
|
|
|
#define _POSIX_SOURCE 1 /* POSIX compliant source */
|
|
|
|
#define CHAR_BUF_MAX 256
|
|
#define MAX_LINE_CHAR 768
|
|
|
|
const char *baud_rate_ascii[] = {
|
|
"2400",
|
|
"4800",
|
|
"9600",
|
|
"19200",
|
|
"38400",
|
|
"57600",
|
|
"115200"
|
|
};
|
|
|
|
#define BAUD_RATE_VALUES_COUNT \
|
|
(sizeof(baud_rate_ascii)/sizeof(baud_rate_ascii[0]))
|
|
|
|
const speed_t baud_rate_termios[] = {
|
|
B2400,
|
|
B4800,
|
|
B9600,
|
|
B19200,
|
|
B38400,
|
|
B57600,
|
|
B115200
|
|
};
|
|
|
|
struct serial_port_t {
|
|
int fd; /* Serial port file descriptor.*/
|
|
struct termios oldtio; /* To save serial port original settings. */
|
|
struct termios newtio;
|
|
};
|
|
|
|
static struct serial_port_t serial_port;
|
|
|
|
/*
|
|
* Validates baud rate entered on the command line as an ASCII string, and
|
|
* returns an index corresponding to this speed inside an array (see above).
|
|
*/
|
|
int
|
|
validate_baudrate(char *baud_rate_string)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < BAUD_RATE_VALUES_COUNT; i++) {
|
|
if (STREQ(baud_rate_ascii[i], baud_rate_string))
|
|
return i; /* Found it! */
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
serial_port_configure(int baud_rate_index)
|
|
{
|
|
int status;
|
|
speed_t speed = baud_rate_termios[baud_rate_index];
|
|
|
|
/* Save current port settings. */
|
|
tcgetattr(serial_port.fd, &serial_port.oldtio);
|
|
|
|
/* Clear struct for new port settings. */
|
|
bzero(&serial_port.newtio, sizeof(serial_port.newtio));
|
|
|
|
/*
|
|
* speed : Set bps rate.
|
|
* CRTSCTS : output hardware flow control (only used if the cable has
|
|
* all necessary lines. See sect. 7 of Serial-HOWTO)
|
|
* CS8 : 8n1 (8bit,no parity,1 stopbit)
|
|
* CLOCAL : local connection, no modem contol
|
|
* CREAD : enable receiving characters
|
|
*/
|
|
serial_port.newtio.c_cflag = speed | CS8 | CLOCAL | CREAD;
|
|
|
|
/*
|
|
* IGNPAR : ignore bytes with parity errors
|
|
* ICRNL : map CR to NL (otherwise a CR input on the other computer
|
|
* will not terminate input)
|
|
* otherwise make device raw (no other input processing)
|
|
*/
|
|
serial_port.newtio.c_iflag = IGNPAR;
|
|
|
|
/* Raw output. */
|
|
serial_port.newtio.c_oflag = 0;
|
|
|
|
/* Set input mode (non-canonical, no echo,...) */
|
|
serial_port.newtio.c_lflag = 0;
|
|
|
|
/* Inter-character timer unused */
|
|
serial_port.newtio.c_cc[VTIME] = 0;
|
|
|
|
/* Blocking read until 1 chars received */
|
|
serial_port.newtio.c_cc[VMIN] = 1;
|
|
|
|
/* Now clean the modem line and activate the settings for the port. */
|
|
tcflush(serial_port.fd, TCIFLUSH);
|
|
status = tcsetattr(serial_port.fd, TCSANOW, &serial_port.newtio);
|
|
if (status < 0) {
|
|
log_fail("tcsetattr() failed");
|
|
return status;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
serial_port_open(const char *port, const int baud_rate_index)
|
|
{
|
|
int status;
|
|
|
|
log_debug2("baud_rate_index = %d", baud_rate_index);
|
|
|
|
/*
|
|
* The O_NOCTTY flag tells UNIX that this program doesn't want to be the
|
|
* "controlling terminal" for that port. If you don't specify this then
|
|
* any input (such as keyboard abort signals and so forth) will affect
|
|
* your process.
|
|
*
|
|
* The O_NDELAY flag tells UNIX that this program doesn't care what
|
|
* state the DCD signal line is in - whether the other end of the port
|
|
* is up and running. If you do not specify this flag, your process will
|
|
* be put to sleep until the DCD signal line is the space voltage.
|
|
*
|
|
* Timeouts are ignored in canonical input mode or when the O_NDELAY
|
|
* option is set on the file via open or fcntl. */
|
|
serial_port.fd = open(port, O_RDWR | O_NOCTTY);
|
|
if (serial_port.fd < 0) {
|
|
/* Could not open the port. */
|
|
log_fail("Error opening serial port %s", port);
|
|
perror(PACKAGE_NAME);
|
|
status = EXIT_FAILURE;
|
|
} else
|
|
status = serial_port_configure(baud_rate_index);
|
|
|
|
/* Flush unread data. */
|
|
status = tcflush(serial_port.fd, TCIFLUSH);
|
|
if (status != 0)
|
|
log_fail("Failure calling ioctl with TCFLSH argument");
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
serial_port_close(void)
|
|
{
|
|
if (serial_port.fd != 0) {
|
|
log_debug2("Closing Serial Port");
|
|
|
|
/* Restoring original port settings. */
|
|
tcsetattr(serial_port.fd, TCSANOW, &serial_port.oldtio);
|
|
|
|
close(serial_port.fd);
|
|
serial_port.fd = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
set_dtr(int dtr)
|
|
{
|
|
int status;
|
|
|
|
ioctl(serial_port.fd, TIOCMGET, &status);
|
|
if (dtr) status &= ~TIOCM_DTR;
|
|
else status |= TIOCM_DTR;
|
|
ioctl(serial_port.fd, TIOCMSET, &status);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int
|
|
set_rts(int rts)
|
|
{
|
|
int status;
|
|
|
|
ioctl(serial_port.fd, TIOCMGET, &status);
|
|
if (rts) status &= ~TIOCM_RTS;
|
|
else status |= TIOCM_RTS;
|
|
ioctl(serial_port.fd, TIOCMSET, &status);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int
|
|
send_message(const char *msg)
|
|
{
|
|
int status;
|
|
ssize_t len;
|
|
int size = strlen(msg);
|
|
int k;
|
|
char buf[CHAR_BUF_MAX];
|
|
|
|
log_debug2("Sending size = %d", size);
|
|
|
|
strncpy(buf, msg, sizeof(buf));
|
|
|
|
for (k = 0; k < size; k++) {
|
|
/* Replace CR/LF by \0 */
|
|
if (buf[k] == '\n')
|
|
buf[k] = 0x00;
|
|
|
|
log_debug3(" [%02d] 0x%02X", k, (u_int8_t) buf[k]);
|
|
}
|
|
|
|
len = write(serial_port.fd, buf, size);
|
|
if (len < 0) {
|
|
perror(PACKAGE_NAME);
|
|
log_fail(" Error writing message to serial port.");
|
|
status = EXIT_FAILURE;
|
|
} else
|
|
status = EXIT_SUCCESS;
|
|
|
|
log_debug2(" Written %d characters", len);
|
|
|
|
return status;
|
|
}
|
|
|
|
int
|
|
send_number(u_int32_t number, int digits)
|
|
{
|
|
char buf[CHAR_BUF_MAX];
|
|
int index = 0;
|
|
|
|
sprintf(buf, "%08X", number);
|
|
index = 8 - digits;
|
|
|
|
return send_message(&buf[index]);
|
|
}
|
|
|
|
int
|
|
send_buffer(u_int8_t *data, ssize_t size_in_bytes)
|
|
{
|
|
int k;
|
|
u_int32_t *data32 = (u_int32_t *) data;
|
|
|
|
if ((size_in_bytes % 4) != 0) {
|
|
log_fail("Data must be 32-bits aligned");
|
|
return 1;
|
|
}
|
|
|
|
for (k = 0; k < (size_in_bytes / 4); k++)
|
|
send_number(data32[k], 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
send_buffer_bin(u_int8_t *data, ssize_t size_in_bytes)
|
|
{
|
|
for (; size_in_bytes > 0; size_in_bytes--, data++)
|
|
write(serial_port.fd, data, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
send_ascii_data(u_int8_t *data, ssize_t size)
|
|
{
|
|
int status;
|
|
ssize_t len;
|
|
|
|
log_debug2("Sending size = %d", size);
|
|
|
|
len = write(serial_port.fd, data, size);
|
|
if (len < 0) {
|
|
perror(PACKAGE_NAME);
|
|
log_fail(" Error writing ADCII data to serial port.");
|
|
status = EXIT_FAILURE;
|
|
} else
|
|
status = EXIT_SUCCESS;
|
|
|
|
log_debug2(" Written %d characters", len);
|
|
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Compare received characters with str1, str2 and str3.
|
|
* The function returns when either str1, str2 or str3 is found,
|
|
* or after a timeout.
|
|
*
|
|
* Return value:
|
|
* -1 = Error
|
|
* 0 = no strings found
|
|
* 1 = str1 found
|
|
* 2 = str2 found
|
|
* 3 = str3 found
|
|
*/
|
|
int
|
|
wait_for_message(const char *str1, const char *str2, const char *str3)
|
|
{
|
|
int str_found = 0;
|
|
char input[CHAR_BUF_MAX];
|
|
char *message;
|
|
const char *received_string = NULL;
|
|
int i;
|
|
ssize_t len;
|
|
|
|
i = 0;
|
|
input[0] = '\0';
|
|
|
|
if (!str1) {
|
|
log_fail("str1 search string not specified");
|
|
return -1;
|
|
}
|
|
|
|
len = sprintf(input, "[%s]", str1);
|
|
if (str2)
|
|
len += sprintf(&input[len], " [%s]", str2);
|
|
if (str3)
|
|
len += sprintf(&input[len], " [%s]", str3);
|
|
|
|
log_info("Expecting messages: %s", input);
|
|
|
|
while (!str_found) {
|
|
do {
|
|
/* read() returns after 1 char have been input */
|
|
len = read(serial_port.fd, &input[i], 1);
|
|
if (len != 0) {
|
|
log_debug3("[%d] 0x%02X", i,
|
|
(u_int8_t) input[i]);
|
|
i++;
|
|
} else
|
|
log_debug2("LEN == 0");
|
|
} while ((input[i - 1] != 0) &&
|
|
(i < (CHAR_BUF_MAX - 1)) &&
|
|
(input[i - 1] != 0x0A) &&
|
|
(input[i - 1] != 0x0D));
|
|
|
|
/* Remove trailing CR or LF */
|
|
if ((input[i-1] == 0x0A) || (input[i-1] == 0x0D))
|
|
input[i-1] = '\0';
|
|
|
|
i = 0;
|
|
/* Remove space before string */
|
|
while (input[i] == ' ')
|
|
i++;
|
|
|
|
message = &input[i];
|
|
|
|
if (strlen(message) == 0)
|
|
continue;
|
|
|
|
log_debug1(" Reading: [%s]", message);
|
|
|
|
if (strstr(message, str1)) {
|
|
str_found = 1;
|
|
received_string = str1;
|
|
}
|
|
|
|
if (str2 != NULL) {
|
|
if (strstr(message, str2)) {
|
|
str_found = 2;
|
|
received_string = str2;
|
|
}
|
|
}
|
|
|
|
if (str3 != NULL) {
|
|
if (strstr(message, str3)) {
|
|
str_found = 3;
|
|
received_string = str3;
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
}
|
|
|
|
if (received_string)
|
|
log_info(" [%s] received", received_string);
|
|
|
|
return str_found;
|
|
}
|