/* * This file is part of the sigrok project. * * Copyright (C) 2010-2012 Bert Vermeulen * * 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 3 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, see . */ #include #include #include #include #include #ifdef _WIN32 #include #else #include #include #endif #include #include #include "libsigrok.h" #include "libsigrok-internal.h" // FIXME: Must be moved, or rather passed as function argument. #ifdef _WIN32 static HANDLE hdl; #endif static const char *serial_port_glob[] = { /* Linux */ "/dev/ttyS*", "/dev/ttyUSB*", "/dev/ttyACM*", /* MacOS X */ "/dev/ttys*", "/dev/tty.USB-*", "/dev/tty.Modem-*", NULL, }; SR_PRIV GSList *list_serial_ports(void) { GSList *ports; #ifdef _WIN32 /* TODO */ ports = NULL; ports = g_slist_append(ports, g_strdup("COM1")); #else glob_t g; unsigned int i, j; ports = NULL; for (i = 0; serial_port_glob[i]; i++) { if (glob(serial_port_glob[i], 0, NULL, &g)) continue; for (j = 0; j < g.gl_pathc; j++) ports = g_slist_append(ports, g_strdup(g.gl_pathv[j])); globfree(&g); } #endif return ports; } SR_PRIV int serial_open(const char *pathname, int flags) { #ifdef _WIN32 /* FIXME: Don't hardcode COM1. */ hdl = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (hdl == INVALID_HANDLE_VALUE) { /* TODO: Error handling. */ } return 0; #else return open(pathname, flags); #endif } /* * Close the serial port. * Returns 0 upon success, -1 upon failure. */ SR_PRIV int serial_close(int fd) { #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ return (CloseHandle(hdl) == 0) ? -1 : 0; #else /* Returns 0 upon success, -1 upon failure. */ return close(fd); #endif } /* * Flush serial port buffers (if any). * Returns 0 upon success, -1 upon failure. */ SR_PRIV int serial_flush(int fd) { #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ return (PurgeComm(hdl, PURGE_RXCLEAR | PURGE_TXCLEAR) == 0) ? -1 : 0; #else /* Returns 0 upon success, -1 upon failure. */ return tcflush(fd, TCIOFLUSH); #endif } /* * Write a number of bytes to the specified serial port. * Returns the number of bytes written, or -1 upon failure. */ SR_PRIV int serial_write(int fd, const void *buf, size_t count) { #ifdef _WIN32 DWORD tmp = 0; /* FIXME */ /* Returns non-zero upon success, 0 upon failure. */ WriteFile(hdl, buf, count, &tmp, NULL); #else /* Returns the number of bytes written, or -1 upon failure. */ return write(fd, buf, count); #endif } /* * Read a number of bytes from the specified serial port. * Returns the number of bytes read, or -1 upon failure. */ SR_PRIV int serial_read(int fd, void *buf, size_t count) { #ifdef _WIN32 DWORD tmp = 0; /* FIXME */ /* Returns non-zero upon success, 0 upon failure. */ return ReadFile(hdl, buf, count, &tmp, NULL); #else /* Returns the number of bytes read, or -1 upon failure. */ return read(fd, buf, count); #endif } SR_PRIV void *serial_backup_params(int fd) { #ifdef _WIN32 /* TODO */ #else struct termios *term; /* TODO: 'term' is never g_free()'d? */ if (!(term = g_try_malloc(sizeof(struct termios)))) { sr_err("serial: %s: term malloc failed", __func__); return NULL; } tcgetattr(fd, term); return term; #endif } SR_PRIV void serial_restore_params(int fd, void *backup) { #ifdef _WIN32 /* TODO */ #else tcsetattr(fd, TCSADRAIN, (struct termios *)backup); #endif } /* * Set serial parameters. * * flowcontrol: 1 = rts/cts, 2 = xon/xoff * parity: 0 = none, 1 = even, 2 = odd */ SR_PRIV int serial_set_params(int fd, int baudrate, int bits, int parity, int stopbits, int flowcontrol) { #ifdef _WIN32 DCB dcb; if (!GetCommState(hdl, &dcb)) { /* TODO: Error handling. */ return SR_ERR; } switch (baudrate) { /* TODO: Support for higher baud rates. */ case 115200: dcb.BaudRate = CBR_115200; break; case 57600: dcb.BaudRate = CBR_57600; break; case 38400: dcb.BaudRate = CBR_38400; break; case 19200: dcb.BaudRate = CBR_19200; break; case 9600: dcb.BaudRate = CBR_9600; break; case 4800: dcb.BaudRate = CBR_4800; break; default: /* TODO: Error handling. */ break; } dcb.ByteSize = bits; dcb.Parity = NOPARITY; /* TODO: Don't hardcode. */ dcb.StopBits = ONESTOPBIT; /* TODO: Don't hardcode. */ if (!SetCommState(hdl, &dcb)) { /* TODO: Error handling. */ return SR_ERR; } #else struct termios term; speed_t baud; if (tcgetattr(fd, &term) < 0) return SR_ERR; switch (baudrate) { case 4800: baud = B4800; break; case 9600: baud = B9600; break; case 38400: baud = B38400; break; case 57600: baud = B57600; break; case 115200: baud = B115200; break; #ifndef __APPLE__ case 460800: baud = B460800; break; #endif default: return SR_ERR; } if (cfsetospeed(&term, baud) < 0) return SR_ERR; if (cfsetispeed(&term, baud) < 0) return SR_ERR; term.c_cflag &= ~CSIZE; switch (bits) { case 8: term.c_cflag |= CS8; break; case 7: term.c_cflag |= CS7; break; default: return SR_ERR; } term.c_cflag &= ~CSTOPB; switch (stopbits) { case 1: break; case 2: term.c_cflag |= CSTOPB; default: return SR_ERR; } term.c_iflag &= ~(IXON | IXOFF); term.c_cflag &= ~CRTSCTS; switch (flowcontrol) { case 0: /* No flow control. */ break; case 1: term.c_cflag |= CRTSCTS; case 2: term.c_iflag |= IXON | IXOFF; break; default: return SR_ERR; } term.c_iflag &= ~IGNPAR; term.c_cflag &= ~(PARODD | PARENB); switch (parity) { case SERIAL_PARITY_NONE: term.c_iflag |= IGNPAR; break; case SERIAL_PARITY_EVEN: term.c_cflag |= PARENB; break; case SERIAL_PARITY_ODD: term.c_cflag |= PARENB | PARODD; break; default: return SR_ERR; } /* Some default parameters */ term.c_iflag &= ~(ICRNL); term.c_lflag &= ~(ICANON | ECHO); if (tcsetattr(fd, TCSADRAIN, &term) < 0) return SR_ERR; #endif return SR_OK; } #define SERIAL_COMM_SPEC "^(\\d+)/([78])([neo])([12])$" SR_PRIV int serial_set_paramstr(int fd, const char *paramstr) { GRegex *reg; GMatchInfo *match; int speed, databits, parity, stopbits; char *mstr; speed = databits = parity = stopbits = 0; reg = g_regex_new(SERIAL_COMM_SPEC, 0, 0, NULL); if (g_regex_match(reg, paramstr, 0, &match)) { if ((mstr = g_match_info_fetch(match, 1))) speed = strtoul(mstr, NULL, 10); g_free(mstr); if ((mstr = g_match_info_fetch(match, 2))) databits = strtoul(mstr, NULL, 10); g_free(mstr); if ((mstr = g_match_info_fetch(match, 3))) { switch (mstr[0]) { case 'n': parity = SERIAL_PARITY_NONE; break; case 'e': parity = SERIAL_PARITY_EVEN; break; case 'o': parity = SERIAL_PARITY_ODD; break; } } g_free(mstr); if ((mstr = g_match_info_fetch(match, 4))) stopbits = strtoul(mstr, NULL, 10); g_free(mstr); } g_match_info_unref(match); g_regex_unref(reg); if (speed) return serial_set_params(fd, speed, databits, parity, stopbits, 0); else return SR_ERR_ARG; }