/* * serial.c -- Serial port routines. * * Copyright (C) 2008 Hugo Villeneuve * * 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 #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include /* POSIX terminal control definitions */ #include /* For isdigit() and friends... */ #include #include #include #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; }