X-Git-Url: http://sigrok.org/gitweb/?a=blobdiff_plain;f=hardware%2Fcommon%2Fserial.c;h=4a92a6684eba4edac865ab4c6ccd6658e1989877;hb=6f22a8ef2ccf7091324b41b553632695507215a7;hp=853c26afb06c957e45fd0a3c2c715a75a49cb273;hpb=2119ab0364b6a161091a89a7018be14d49bdc7b3;p=libsigrok.git diff --git a/hardware/common/serial.c b/hardware/common/serial.c index 853c26af..4a92a668 100644 --- a/hardware/common/serial.c +++ b/hardware/common/serial.c @@ -1,7 +1,7 @@ /* * This file is part of the sigrok project. * - * Copyright (C) 2010 Bert Vermeulen + * 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 @@ -23,21 +23,22 @@ #include #include #ifdef _WIN32 -#include +#include #else #include #include #endif #include #include -#include +#include "libsigrok.h" +#include "libsigrok-internal.h" // FIXME: Must be moved, or rather passed as function argument. #ifdef _WIN32 -HANDLE hdl; +static HANDLE hdl; #endif -char *serial_port_glob[] = { +static const char *serial_port_glob[] = { /* Linux */ "/dev/ttyS*", "/dev/ttyUSB*", @@ -49,14 +50,14 @@ char *serial_port_glob[] = { NULL, }; -GSList *list_serial_ports(void) +SR_PRIV GSList *list_serial_ports(void) { GSList *ports; #ifdef _WIN32 /* TODO */ ports = NULL; - ports = g_slist_append(ports, strdup("COM1")); + ports = g_slist_append(ports, g_strdup("COM1")); #else glob_t g; unsigned int i, j; @@ -66,7 +67,7 @@ GSList *list_serial_ports(void) if (glob(serial_port_glob[i], 0, NULL, &g)) continue; for (j = 0; j < g.gl_pathc; j++) - ports = g_slist_append(ports, strdup(g.gl_pathv[j])); + ports = g_slist_append(ports, g_strdup(g.gl_pathv[j])); globfree(&g); } #endif @@ -74,7 +75,7 @@ GSList *list_serial_ports(void) return ports; } -int serial_open(const char *pathname, int flags) +SR_PRIV int serial_open(const char *pathname, int flags) { #ifdef _WIN32 /* FIXME: Don't hardcode COM1. */ @@ -93,7 +94,7 @@ int serial_open(const char *pathname, int flags) * Close the serial port. * Returns 0 upon success, -1 upon failure. */ -int serial_close(int fd) +SR_PRIV int serial_close(int fd) { #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ @@ -108,7 +109,7 @@ int serial_close(int fd) * Flush serial port buffers (if any). * Returns 0 upon success, -1 upon failure. */ -int serial_flush(int fd) +SR_PRIV int serial_flush(int fd) { #ifdef _WIN32 /* Returns non-zero upon success, 0 upon failure. */ @@ -123,7 +124,7 @@ int serial_flush(int fd) * Write a number of bytes to the specified serial port. * Returns the number of bytes written, or -1 upon failure. */ -int serial_write(int fd, const void *buf, size_t count) +SR_PRIV int serial_write(int fd, const void *buf, size_t count) { #ifdef _WIN32 DWORD tmp = 0; @@ -141,7 +142,7 @@ int serial_write(int fd, const void *buf, size_t count) * Read a number of bytes from the specified serial port. * Returns the number of bytes read, or -1 upon failure. */ -int serial_read(int fd, void *buf, size_t count) +SR_PRIV int serial_read(int fd, void *buf, size_t count) { #ifdef _WIN32 DWORD tmp = 0; @@ -155,21 +156,26 @@ int serial_read(int fd, void *buf, size_t count) #endif } -void *serial_backup_params(int fd) +SR_PRIV void *serial_backup_params(int fd) { #ifdef _WIN32 /* TODO */ #else struct termios *term; - term = malloc(sizeof(struct termios)); + /* 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 } -void serial_restore_params(int fd, void *backup) +SR_PRIV void serial_restore_params(int fd, void *backup) { #ifdef _WIN32 /* TODO */ @@ -184,19 +190,18 @@ void serial_restore_params(int fd, void *backup) * flowcontrol: 1 = rts/cts, 2 = xon/xoff * parity: 0 = none, 1 = even, 2 = odd */ -int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, - int flowcontrol) +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 SIGROK_ERR; + return SR_ERR; } - /* TODO: Rename 'speed' to 'baudrate'. */ - switch(speed) { + switch (baudrate) { /* TODO: Support for higher baud rates. */ case 115200: dcb.BaudRate = CBR_115200; @@ -213,6 +218,12 @@ int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, case 9600: dcb.BaudRate = CBR_9600; break; + case 4800: + dcb.BaudRate = CBR_4800; + break; + case 2400: + dcb.BaudRate = CBR_2400; + break; default: /* TODO: Error handling. */ break; @@ -223,13 +234,22 @@ int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, if (!SetCommState(hdl, &dcb)) { /* TODO: Error handling. */ - return SIGROK_ERR; + return SR_ERR; } #else struct termios term; speed_t baud; - switch (speed) { + if (tcgetattr(fd, &term) < 0) + return SR_ERR; + + switch (baudrate) { + case 2400: + baud = B2400; + break; + case 4800: + baud = B4800; + break; case 9600: baud = B9600; break; @@ -242,17 +262,18 @@ int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, case 115200: baud = B115200; break; +#ifndef __APPLE__ case 460800: baud = B460800; break; +#endif default: - return SIGROK_ERR; + return SR_ERR; } - - if (tcgetattr(fd, &term) < 0) - return SIGROK_ERR; + if (cfsetospeed(&term, baud) < 0) + return SR_ERR; if (cfsetispeed(&term, baud) < 0) - return SIGROK_ERR; + return SR_ERR; term.c_cflag &= ~CSIZE; switch (bits) { @@ -263,7 +284,7 @@ int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, term.c_cflag |= CS7; break; default: - return SIGROK_ERR; + return SR_ERR; } term.c_cflag &= ~CSTOPB; @@ -273,39 +294,126 @@ int serial_set_params(int fd, int speed, int bits, int parity, int stopbits, case 2: term.c_cflag |= CSTOPB; default: - return SIGROK_ERR; + return SR_ERR; } - term.c_cflag &= ~(IXON | IXOFF | CRTSCTS); + term.c_iflag &= ~(IXON | IXOFF); + term.c_cflag &= ~CRTSCTS; switch (flowcontrol) { - case 2: - term.c_cflag |= IXON | IXOFF; + case 0: + /* No flow control. */ break; case 1: term.c_cflag |= CRTSCTS; + case 2: + term.c_iflag |= IXON | IXOFF; + break; default: - return SIGROK_ERR; + return SR_ERR; } term.c_iflag &= ~IGNPAR; term.c_cflag &= ~(PARODD | PARENB); switch (parity) { - case 0: + case SERIAL_PARITY_NONE: term.c_iflag |= IGNPAR; break; - case 1: + case SERIAL_PARITY_EVEN: term.c_cflag |= PARENB; break; - case 2: + case SERIAL_PARITY_ODD: term.c_cflag |= PARENB | PARODD; break; default: - return SIGROK_ERR; + return SR_ERR; } + /* Some default parameters */ + term.c_iflag &= ~(ICRNL); + term.c_lflag &= ~(ICANON | ECHO); + if (tcsetattr(fd, TCSADRAIN, &term) < 0) - return SIGROK_ERR; + return SR_ERR; #endif - return SIGROK_OK; + 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; +} + +SR_PRIV int serial_readline(int fd, char **buf, int *buflen, + uint64_t timeout_ms) +{ + uint64_t start; + int maxlen, len; + + timeout_ms *= 1000; + start = g_get_monotonic_time(); + + maxlen = *buflen; + *buflen = len = 0; + while(1) { + len = maxlen - *buflen - 1; + if (len < 1) + break; + len = serial_read(fd, *buf + *buflen, 1); + if (len > 0) { + *buflen += len; + *(*buf + *buflen) = '\0'; + if (*buflen > 0 && *(*buf + *buflen - 1) == '\r') { + /* Strip LF and terminate. */ + *(*buf + --*buflen) = '\0'; + break; + } + } + if (g_get_monotonic_time() - start > timeout_ms) + /* Timeout */ + break; + g_usleep(2000); + } + sr_dbg("Received %d: '%s'.", *buflen, *buf); + + return SR_OK; }