Browse Source

Add serial I/O helpers

git-svn-id: https://bucket.mit.edu/svn/nilm/zoom@7604 ddd99763-3ecb-0310-9145-efcb8ce7c51f
tags/zoom-1.0
jim 13 years ago
parent
commit
0399f1cb3e
5 changed files with 258 additions and 100 deletions
  1. +12
    -0
      pc/Makefile
  2. +11
    -54
      pc/calibrate.c
  3. +2
    -46
      pc/read.c
  4. +199
    -0
      pc/serial-util.c
  5. +34
    -0
      pc/serial-util.h

+ 12
- 0
pc/Makefile View File

@@ -1 +1,13 @@
CFLAGS=-Wall -Werror

all: read calibrate

serial-util.o: serial-util.h

read.o: serial-util.h

calibrate.o: serial-util.h

read: read.o serial-util.o

calibrate: calibrate.o serial-util.o

+ 11
- 54
pc/calibrate.c View File

@@ -1,18 +1,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <termio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <err.h>
#include <linux/serial.h>
#include <sys/signal.h>
#include "serial-util.h"

int zoom, gpib;

@@ -26,62 +24,11 @@ void handle_sig(int sig)
quit = 1;
}

int serial_open(const char *device, int rate)
{
struct termios options;
struct serial_struct serinfo;
int fd;
int speed = 0;

/* Open and configure serial port */
if ((fd = open(device,O_RDWR|O_NOCTTY)) == -1)
err(1, "%s", device);

switch (rate) {
case 9600: speed = B9600; break;
case 115200: speed = B115200; break;
}

if (speed == 0) {
serinfo.reserved_char[0] = 0;
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
err(1, "%s: TIOCGSERIAL", device);
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = (serinfo.baud_base + (rate / 2)) / rate;
if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
err(1, "%s: TIOCSSERIAL", device);
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
err(1, "%s: TIOCGSERIAL 2", device);
if (serinfo.custom_divisor * rate != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)serinfo.baud_base / serinfo.custom_divisor);
}
}

fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options);
cfsetispeed(&options, speed ?: B38400);
cfsetospeed(&options, speed ?: B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &options) != 0)
err(1, "TCSANOW");

return fd;
}

int main(int argc, char *argv[])
{
char *zoomdev=strdup("/dev/ttyUSB1");
char *gpibdev=strdup("/dev/ttyUSB0");
int getopt_index;
int i;
char buf[1024];

static struct option long_opts[] = {
{ "zoom-device", required_argument, NULL, 'z' },
@@ -92,6 +39,16 @@ int main(int argc, char *argv[])
int help=0;
char c;

{
char buf[1024];
drain(0);
fdprintf(1,"type a string in 5 sec: ");
fdgets(buf, 1024, 0, 5000);
chomp(buf);
fdprintf(1,"you typed: '%s' len %d\n", buf, strlen(buf));
abort();
}

while ((c = getopt_long(argc, argv, "z:g:h?",
long_opts, &getopt_index)) != -1) {
switch(c)


+ 2
- 46
pc/read.c View File

@@ -1,17 +1,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <termio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <stdint.h>
#include <string.h>
#include <syslog.h>
#include <fcntl.h>
#include <err.h>
#include <linux/serial.h>
#include "serial-util.h"

int hex = 0;
int dec = 0;
@@ -21,54 +19,12 @@ int total = 0;

int process(const uint8_t *buf, int len);

int serial_open(const char *device, int rate)
{
struct termios options;
struct serial_struct serinfo;
int fd;

/* Open and configure serial port */
if ((fd = open(device,O_RDWR|O_NOCTTY)) == -1)
err(1, "%s", device);

serinfo.reserved_char[0] = 0;
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
err(1, "%s: TIOCGSERIAL", device);
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = (serinfo.baud_base + (rate / 2)) / rate;
if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
err(1, "%s: TIOCSSERIAL", device);
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
err(1, "%s: TIOCGSERIAL 2", device);
if (serinfo.custom_divisor * rate != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)serinfo.baud_base / serinfo.custom_divisor);
}

fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options);
cfsetispeed(&options, B38400);
cfsetospeed(&options, B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &options) != 0)
err(1, "TCSANOW");

return fd;
}

int main(int argc, char *argv[])
{
char *device=strdup("/dev/ttyUSB0");
int rate=500000;
int fd;
int getopt_index;
int i;
char buf[1024];
int len;

@@ -146,7 +102,7 @@ int main(int argc, char *argv[])
if (n <= 0)
err(1, "read");
len += n;
processed = process(buf, len);
processed = process((uint8_t *) buf, len);
memmove(buf, buf + processed, len - processed);
len -= processed;
}


+ 199
- 0
pc/serial-util.c View File

@@ -0,0 +1,199 @@
/*
* Serial I/O helper routines
*
* Jim Paris <jim@jtan.com>
* $Id$
*/

#include <stdio.h>
#include <stdlib.h>
#include <termio.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <err.h>
#include <linux/serial.h>
#include <poll.h>
#include <stdarg.h>
#include "serial-util.h"

static int rate_to_constant(int baudrate) {
#define B(x) case x: return B##x
switch(baudrate) {
B(50); B(75); B(110); B(134); B(150);
B(200); B(300); B(600); B(1200); B(1800);
B(2400); B(4800); B(9600); B(19200); B(38400);
B(57600); B(115200); B(230400); B(460800); B(500000);
B(576000); B(921600); B(1000000);B(1152000);B(1500000);
default: return 0;
}
#undef B
}

/* Open serial port in raw mode, with custom baudrate if necessary */
int serial_open(const char *device, int rate)
{
struct termios options;
struct serial_struct serinfo;
int fd;
int speed = 0;

/* Open and configure serial port */
if ((fd = open(device,O_RDWR|O_NOCTTY)) == -1)
return -1;

speed = rate_to_constant(rate);

if (speed == 0) {
serinfo.reserved_char[0] = 0;
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
return -1;
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = (serinfo.baud_base + (rate / 2)) / rate;
if (serinfo.custom_divisor < 1)
serinfo.custom_divisor = 1;
if (ioctl(fd, TIOCSSERIAL, &serinfo) < 0)
return -1;
if (ioctl(fd, TIOCGSERIAL, &serinfo) < 0)
return -1;
if (serinfo.custom_divisor * rate != serinfo.baud_base) {
warnx("actual baudrate is %d / %d = %f",
serinfo.baud_base, serinfo.custom_divisor,
(float)serinfo.baud_base / serinfo.custom_divisor);
}
}

fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &options);
cfsetispeed(&options, speed ?: B38400);
cfsetospeed(&options, speed ?: B38400);
cfmakeraw(&options);
options.c_cflag |= (CLOCAL | CREAD);
options.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &options) != 0)
return -1;

return fd;
}

/* Like read(), but restarts after EINTR, and reads until count bytes
are received or a timeout occurs. */
int saferead_timeout(int fd, void *buf, size_t count, int timeout_ms)
{
struct pollfd pfd;
int r;
size_t nread = 0;

while (count > 0) {
pfd.fd = fd;
pfd.events = POLLIN;
r = poll(&pfd, 1, timeout_ms);
if (r < 0 && errno == EINTR) /* retry */
continue;
else if (r == 0) /* timeout */
return nread;
else if (r == 1 && (pfd.revents & POLLIN)) { /* readable */
r = read(fd, buf, count);
if (r < 0 && errno == EINTR) /* retry */
continue;
if (r < 0) /* error */
return r;
if (r == 0) /* EOF */
return nread;
buf = (char *) buf + r;
count -= r;
nread += r;
} else {
/* error */
return -1;
}
}
return nread;
}

/* Like write(), but restarts after EINTR */
ssize_t safewrite(int fd, const void *buf, size_t count)
{
size_t nwritten = 0;
while (count > 0) {
ssize_t r = write(fd, buf, count);
if (r < 0 && errno == EINTR)
continue;
if (r < 0)
return r;
if (r == 0)
return nwritten;
buf = (const char *)buf + r;
count -= r;
nwritten += r;
}
return nwritten;
}

/* Read bytes until no more are available for 0.1 sec */
int drain(int fd)
{
char buf[1024];
int ret;
while (1) {
ret = saferead_timeout(fd, buf, sizeof(buf), 100);
if (ret <= 0)
return ret;
}
}

/* Like fprintf, but to a fd, using safewrite. */
static int vfdprintf(int fd, const char *fmt, va_list args)
{
static char buf[1024];
vsnprintf(buf, sizeof(buf), fmt, args);
return safewrite(fd, buf, strlen(buf));
}
int fdprintf(int fd, const char *fmt, ...)
{
int ret;
va_list args;
va_start(args, fmt);
ret = vfdprintf(fd, fmt, args);
va_end(args);
return ret;
}

/* Like fgets, but from a fd, using saferead_timeout. */
char *fdgets(char *s, int size, int fd, int timeout_ms)
{
int ret;
int nread = 0;

/* Not very efficient, needs to read one char at a time to avoid buffering */
while (nread < (size - 1)) {
ret = saferead_timeout(fd, &s[nread], 1, timeout_ms);
if (ret <= 0) {
s[nread] = '\0';
return NULL;
}
if (ret == 1) {
nread++;

/* found terminator? */
if (s[nread-1] == '\n')
break;
}
}
s[nread] = '\0';
return s;
}

/* Like perl chomp. */
void chomp(char *s)
{
int len = strlen(s);
/* do it twice to remove \r\n as well */
if (len > 1 && (s[len - 1] == '\r' || s[len - 1] == '\n')) s[--len] = '\0';
if (len > 1 && (s[len - 1] == '\r' || s[len - 1] == '\n')) s[--len] = '\0';
}

+ 34
- 0
pc/serial-util.h View File

@@ -0,0 +1,34 @@
/*
* Serial I/O helper routines
*
* Jim Paris <jim@jtan.com>
* $Id$
*/
#ifndef UTIL_H
#define UTIL_H

#include <unistd.h>

/* Open serial port in raw mode, with custom baudrate if necessary */
int serial_open(const char *device, int rate);

/* Like read(), but restarts after EINTR, and reads until count bytes
are received or a timeout occurs. */
int saferead_timeout(int fd, void *buf, size_t count, int timeout_ms);

/* Like write(), but restarts after EINTR */
ssize_t safewrite(int fd, const void *buf, size_t count);

/* Read bytes until no more are available for 0.1 sec */
int drain(int fd);

/* Like fprintf, but to a fd, using safewrite */
int fdprintf(int fd, const char *fmt, ...);

/* Like fgets, but from a fd, using saferead_timeout. */
char *fdgets(char *s, int size, int fd, int timeout_ms);

/* Like perl chomp. */
void chomp(char *s);

#endif

Loading…
Cancel
Save