rs

FTDI serial communication program
git clone git://git.2f30.org/rs.git
Log | Files | Refs | README | LICENSE

commit 08ff7dacd98f90282d17676773d07167056a8292
Author: sin <sin@2f30.org>
Date:   Fri, 15 Nov 2013 12:11:26 +0000

Initial commit

Diffstat:
LICENSE | 21+++++++++++++++++++++
Makefile | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
arg.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
config.mk | 11+++++++++++
rs.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
util.h | 16++++++++++++++++
util/agetcwd.c | 18++++++++++++++++++
util/apathmax.c | 25+++++++++++++++++++++++++
util/dev.c | 46++++++++++++++++++++++++++++++++++++++++++++++
util/eprintf.c | 46++++++++++++++++++++++++++++++++++++++++++++++
util/estrtol.c | 27+++++++++++++++++++++++++++
util/mkpath.c | 29+++++++++++++++++++++++++++++
util/recurse.c | 40++++++++++++++++++++++++++++++++++++++++
util/strlcpy.c | 15+++++++++++++++
14 files changed, 605 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -0,0 +1,21 @@ +MIT/X Consortium License + +© 2013 sin <sin@2f30.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile @@ -0,0 +1,61 @@ +include config.mk + +.POSIX: +.SUFFIXES: .c .o + +LIB = \ + util/agetcwd.o \ + util/apathmax.o \ + util/dev.o \ + util/eprintf.o \ + util/estrtol.o \ + util/mkpath.o \ + util/recurse.o \ + util/strlcpy.o + +SRC = rs.c + +OBJ = $(SRC:.c=.o) $(LIB) +BIN = $(SRC:.c=) +MAN = $(SRC:.c=.1) + +all: options binlib + +options: + @echo rs build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +binlib: util.a + $(MAKE) bin + +bin: $(BIN) + +$(OBJ): util.h config.mk + +.o: + @echo LD $@ + @$(LD) -o $@ $< util.a $(LDFLAGS) + +.c.o: + @echo CC $< + @$(CC) -c -o $@ $< $(CFLAGS) + +util.a: $(LIB) + @echo AR $@ + @$(AR) -r -c $@ $(LIB) + @ranlib $@ + +install: all + @echo installing executable to $(DESTDIR)$(PREFIX)/bin + @cp -f $(BIN) $(DESTDIR)$(PREFIX)/bin + @cd $(DESTDIR)$(PREFIX)/bin && chmod 755 $(BIN) + +uninstall: + @echo removing executable from $(DESTDIR)$(PREFIX)/bin + @cd $(DESTDIR)$(PREFIX)/bin && rm -f $(BIN) + +clean: + @echo cleaning + @rm -f $(BIN) $(OBJ) $(LIB) util.a diff --git a/arg.h b/arg.h @@ -0,0 +1,63 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef __ARG_H__ +#define __ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][1]\ + && argv[0][0] == '-';\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +/* Handles obsolete -NUM syntax */ +#define ARGNUM case '0':\ + case '1':\ + case '2':\ + case '3':\ + case '4':\ + case '5':\ + case '6':\ + case '7':\ + case '8':\ + case '9' + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define ARGNUMF(base) (brk_ = 1, estrtol(argv[0], (base))) + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/config.mk b/config.mk @@ -0,0 +1,11 @@ +# rs version +VERSION = 0.1 + +# paths +PREFIX = /usr/local + +CC = cc +LD = $(CC) +CPPFLAGS = -D_BSD_SOURCE -D_GNU_SOURCE +CFLAGS = -g -std=c99 -Wall $(CPPFLAGS) +LDFLAGS = -g -lftdi diff --git a/rs.c b/rs.c @@ -0,0 +1,187 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <signal.h> +#include <ftdi.h> +#include <libusb-1.0/libusb.h> +#include "util.h" + +static struct ftdi_context ftdictx; +static struct ftdi_device_list *devlist; +static struct termios tio, saved_tio; + +static int bflag = 0; +static int lflag = 0; +static int dflag = 0; +static int vflag = 0; +static int pflag = 0; + +static sig_atomic_t done = 0; + +static void +interrupt(int sig) +{ + done = 1; +} + +static int +ttyinit(void) +{ + tcgetattr(STDIN_FILENO, &tio); + memcpy(&saved_tio, &tio, sizeof(struct termios)); + cfmakeraw(&tio); + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = 0; + tio.c_lflag |= ISIG; + tio.c_cc[VQUIT] = CTRL('\\'); + tio.c_cc[VINTR] = 0; + tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio); + return 0; +} + +static void +ttyrestore(void) +{ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio); + putchar('\n'); +} + +static int +loop(void) +{ + unsigned char buf[BUFSIZ]; + ssize_t n = 0; + + while (!done) { + n = read(STDIN_FILENO, buf, sizeof(buf)); + if (n > 0) + ftdi_write_data(&ftdictx, buf, n); + n = ftdi_read_data(&ftdictx, buf, sizeof(buf)); + if (n < 0) { + fprintf(stderr, "read error %d (%s)\n", + (int)n, ftdi_get_error_string(&ftdictx)); + break; + } + write(STDOUT_FILENO, buf, n); + } + return n; +} + +static void +listdevs(void) +{ + char manufacturer[256]; + char description[256]; + struct ftdi_device_list *iter; + int i; + int ret; + + for (i = 0, iter = devlist; iter; iter = iter->next, i++) { + ret = ftdi_usb_get_strings(&ftdictx, iter->dev, + manufacturer, sizeof(manufacturer), + description, sizeof(description), NULL, 0); + if (ret < 0) { + fprintf(stderr, "ftdi_usb_get_strings: %s\n", + ftdi_get_error_string(&ftdictx)); + exit(EXIT_FAILURE); + } + printf("device %d: manufacturer: %s, description: %s, vendor = %04x, product = %04x\n", + i, manufacturer, description, iter->dev->descriptor.idVendor, + iter->dev->descriptor.idProduct); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-l] [-b baudrate] [-v vendor] [-p product] [-d device]\n", argv0); + fprintf(stderr, "\t-l\tList devices\n"); + fprintf(stderr, "\t-b\tSet baudrate\n"); + fprintf(stderr, "\t-v\tSpecify vendor, defaults to 0x0403\n"); + fprintf(stderr, "\t-p\tSpecify product, defaults to 0x6001\n"); + fprintf(stderr, "\n\tNOTE: The break key is set to ^\\\n"); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + int i; + int baudrate = 115200; + int dev = 0; + int ret; + struct ftdi_device_list *iter; + int vendor = 0x0403; + int product = 0x6001; + + ARGBEGIN { + case 'l': + lflag = 1; + break; + case 'b': + bflag = 1; + baudrate = estrtol(EARGF(usage()), 10); + break; + case 'd': + dflag = 1; + dev = estrtol(EARGF(usage()), 10); + break; + case 'v': + vflag = 1; + vendor = estrtol(EARGF(usage()), 0); + break; + case 'p': + pflag = 1; + product = estrtol(EARGF(usage()), 0); + break; + default: + usage(); + } ARGEND; + + ftdi_init(&ftdictx); + ftdi_set_interface(&ftdictx, INTERFACE_ANY); + + ret = ftdi_usb_find_all(&ftdictx, &devlist, vendor, product); + if (ret < 0) { + fprintf(stderr, "ftdi_usb_find_all: %s\n", + ftdi_get_error_string(&ftdictx)); + exit(EXIT_FAILURE); + } + + if (lflag) { + listdevs(); + exit(EXIT_SUCCESS); + } + + if (dflag) { + for (i = 0, iter = devlist; iter; iter = iter->next, i++) { + if (i == dev) { + vendor = iter->dev->descriptor.idVendor; + product = iter->dev->descriptor.idProduct; + break; + } + } + if (!iter) { + fprintf(stderr, "Can't find device %d\n", dev); + exit(EXIT_FAILURE); + } + } + + if (ftdi_usb_open(&ftdictx, vendor, product) < 0) { + fprintf(stderr, "ftdi_usb_open: %s\n", + ftdi_get_error_string(&ftdictx)); + exit(EXIT_FAILURE); + } + ftdi_set_baudrate(&ftdictx, baudrate); + + ttyinit(); + signal(SIGINT, interrupt); + signal(SIGQUIT, interrupt); + loop(); + ttyrestore(); + exit(EXIT_SUCCESS); +} diff --git a/util.h b/util.h @@ -0,0 +1,16 @@ +/* See LICENSE file for copyright and license details. */ +#include "arg.h" + +#define LEN(x) (sizeof (x) / sizeof *(x)) + +extern char *argv0; + +char *agetcwd(void); +void apathmax(char **, long *); +int devtomajmin(const char *path, int *maj, int *min); +int devtype(const char *majmin); +void enprintf(int, const char *, ...); +void eprintf(const char *, ...); +long estrtol(const char *, int); +void recurse(const char *, void (*)(const char *)); +size_t strlcpy(char *dest, const char *src, size_t size); diff --git a/util/agetcwd.c b/util/agetcwd.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include <unistd.h> + +#include "../util.h" + +char * +agetcwd(void) +{ + char *buf; + long size; + + apathmax(&buf, &size); + if(!getcwd(buf, size)) + eprintf("getcwd:"); + + return buf; +} + diff --git a/util/apathmax.c b/util/apathmax.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "../util.h" + +void +apathmax(char **p, long *size) +{ + errno = 0; + + if((*size = pathconf("/", _PC_PATH_MAX)) == -1) { + if(errno == 0) { + *size = BUFSIZ; + } else { + eprintf("pathconf:"); + } + } + + if(!(*p = malloc(*size))) + eprintf("malloc:"); +} + diff --git a/util/dev.c b/util/dev.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <stdio.h> +#include "../util.h" + +/* Example `path' is /sys/devices/virtual/tty/tty0/dev */ +int +devtomajmin(const char *path, int *maj, int *min) +{ + char buf[BUFSIZ]; + int fd; + ssize_t n; + + fd = open(path, O_RDONLY); + if (fd < 0) + eprintf("open %s:", path); + n = read(fd, buf, sizeof(buf) - 1); + close(fd); + if (n < 0) + eprintf("%s: read error:", path); + if (!n) + return -1; + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + buf[n] = '\0'; + sscanf(buf, "%d:%d", maj, min); + return 0; +} + +/* `majmin' format is maj:min */ +int +devtype(const char *majmin) +{ + char path[PATH_MAX]; + + snprintf(path, sizeof(path), "/sys/dev/block/%s", majmin); + if (!access(path, F_OK)) + return S_IFBLK; + snprintf(path, sizeof(path), "/sys/dev/char/%s", majmin); + if (!access(path, F_OK)) + return S_IFCHR; + return -1; +} diff --git a/util/eprintf.c b/util/eprintf.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "../util.h" + +char *argv0; + +static void venprintf(int, const char *, va_list); + +void +eprintf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + venprintf(EXIT_FAILURE, fmt, ap); + va_end(ap); +} + +void +enprintf(int status, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + venprintf(status, fmt, ap); + va_end(ap); +} + +void +venprintf(int status, const char *fmt, va_list ap) +{ + /*fprintf(stderr, "%s: ", argv0);*/ + + vfprintf(stderr, fmt, ap); + + if(fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } + + exit(status); +} diff --git a/util/estrtol.c b/util/estrtol.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> + +#include "../util.h" + +long +estrtol(const char *s, int base) +{ + char *end; + long n; + + errno = 0; + n = strtol(s, &end, base); + if(*end != '\0') { + if(base == 0) + eprintf("%s: not an integer\n", s); + else + eprintf("%s: not a base %d integer\n", s, base); + } + if(errno != 0) + eprintf("%s:", s); + + return n; +} + diff --git a/util/mkpath.c b/util/mkpath.c @@ -0,0 +1,29 @@ +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> + +int +mkpath(const char *path, mode_t mode) +{ + char tmp[PATH_MAX]; + char *p = NULL; + size_t len; + + snprintf(tmp, sizeof(tmp),"%s",path); + len = strlen(tmp); + if(tmp[len - 1] == '/') + tmp[len - 1] = 0; + for(p = tmp + 1; *p; p++) + if(*p == '/') { + *p = 0; + if (mkdir(tmp, mode) < 0 && errno != EEXIST) + return -1; + *p = '/'; + } + if (mkdir(tmp, mode) < 0 && errno != EEXIST) + return -1; + return 0; +} + diff --git a/util/recurse.c b/util/recurse.c @@ -0,0 +1,40 @@ +/* See LICENSE file for copyright and license details. */ +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> + +#include "../util.h" + +void +recurse(const char *path, void (*fn)(const char *)) +{ + char *cwd; + struct dirent *d; + struct stat st; + DIR *dp; + + if(lstat(path, &st) == -1 || !S_ISDIR(st.st_mode)) { + return; + } else if(!(dp = opendir(path))) { + eprintf("opendir %s:", path); + } + + cwd = agetcwd(); + if(chdir(path) == -1) + eprintf("chdir %s:", path); + + while((d = readdir(dp))) { + if(strcmp(d->d_name, ".") && strcmp(d->d_name, "..")) + fn(d->d_name); + } + + closedir(dp); + if(chdir(cwd) == -1) + eprintf("chdir %s:", cwd); + + free(cwd); +} + diff --git a/util/strlcpy.c b/util/strlcpy.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <string.h> + +size_t +strlcpy(char *dest, const char *src, size_t size) +{ + size_t ret = strlen(src); + + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dest, src, len); + dest[len] = '\0'; + } + return ret; +}