From: Keith Packard Date: Mon, 21 Mar 2016 04:52:53 +0000 (-0700) Subject: libaltos: Add Windows BT support. Split into separate source files. X-Git-Tag: 1.6.3~2^2~79 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=1594691ea88ca84634eea237ac8137a5bdc19f5c libaltos: Add Windows BT support. Split into separate source files. Add Bluetooth support to Windows. Split libaltos into separate files. Signed-off-by: Keith Packard --- diff --git a/libaltos/Makefile-standalone b/libaltos/Makefile-standalone index a1f9f5bc..9a44b8e0 100644 --- a/libaltos/Makefile-standalone +++ b/libaltos/Makefile-standalone @@ -5,9 +5,11 @@ OS:=$(shell uname) # ifeq ($(OS),Linux) -JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include +OS_SRCS=libaltos_posix.c libaltos_linux.c -OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) +JAVA_CFLAGS=-I/usr/lib/jvm/default-java/include -I/usr/lib/jvm/default-java/include/linux + +OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) -shared -fPIC OS_APP_CFLAGS=$(OS_LIB_CFLAGS) @@ -22,6 +24,8 @@ endif # ifeq ($(OS),Darwin) +OS_SRCS=libaltos_posix.c libaltos_darwin.c + #OS_LIB_CFLAGS=\ # -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \ # --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \ @@ -54,6 +58,8 @@ endif # ifneq (,$(findstring MINGW,$(OS))) +OS_SRCS=libaltos_windows.c + CC=gcc OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL @@ -103,7 +109,7 @@ CFLAGS=$(OS_LIB_CFLAGS) -O -I. LDFLAGS=$(OS_LDFLAGS) HEADERS=libaltos.h -SRCS = libaltos.c $(SWIG_WRAP) +SRCS = libaltos_common.c $(OS_SRCS) $(SWIG_WRAP) OBJS = $(SRCS:%.c=%.o) LIBS = $(DARWIN_LIBS) @@ -111,7 +117,7 @@ $(CJNITEST): cjnitest.c $(LIBNAME) $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS) $(LIBNAME): $(OBJS) - $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) + $(CC) -shared -fPIC $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) clean: rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o diff --git a/libaltos/Makefile.am b/libaltos/Makefile.am index ae731633..8f69c1ad 100644 --- a/libaltos/Makefile.am +++ b/libaltos/Makefile.am @@ -8,9 +8,22 @@ altoslib_LTLIBRARIES=libaltos.la libaltos_la_LDFLAGS=-version-info 1:0:1 -Wl,-znoexecstack libaltos_la_SOURCES=\ - libaltos.c \ + libaltos_common.c \ + libaltos_posix.c \ + libaltos_linux.c \ + libaltos_wrap.c \ + libaltos.h \ + libaltos_posix.h \ + libaltos_private.h + +WINDOWS_SRC=\ + libaltos_common.c\ + libaltos_windows.c\ libaltos_wrap.c +WINDOWS_H=\ + libaltos.h + noinst_PROGRAMS=cjnitest cjnitest_SOURCES=cjnitest.c @@ -64,16 +77,16 @@ classlibaltos.stamp: $(SWIG_FILE) MINGCC32=i686-w64-mingw32-gcc MINGCC64=x86_64-w64-mingw32-gcc -MINGFLAGS=-Wall -DWINDOWS -DBUILD_DLL -I$(JVM_INCLUDE) -I$(JVM_INCLUDE)/linux -MINGLIBS=-lsetupapi +MINGFLAGS=-Wall -Wextra -DWINDOWS -DBUILD_DLL -mconsole -I$(JVM_INCLUDE) -I$(JVM_INCLUDE)/linux +MINGLIBS=-lsetupapi -lws2_32 fat: all altos.dll altos64.dll -altos.dll: $(libaltos_la_SOURCES) - $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) +altos.dll: $(WINDOWS_SRC) $(WINDOWS_H) + $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(WINDOWS_SRC) $(MINGLIBS) -altos64.dll: $(libaltos_la_SOURCES) - $(MINGCC64) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS) +altos64.dll: $(WINDOWS_SRC) $(WINDOWS_H) + $(MINGCC64) -o $@ $(MINGFLAGS) -shared $(WINDOWS_SRC) $(MINGLIBS) clean-local: -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c altos.dll altos64.dll diff --git a/libaltos/libaltos.c b/libaltos/libaltos.c deleted file mode 100644 index ebef2e21..00000000 --- a/libaltos/libaltos.c +++ /dev/null @@ -1,1536 +0,0 @@ -/* - * Copyright © 2010 Keith Packard - * - * 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; version 2 of the License. - * - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - */ - -#include "libaltos.h" -#include -#include -#include - -#define BLUETOOTH_PRODUCT_TELEBT "TeleBT" - -#define USE_POLL - -PUBLIC int -altos_init(void) -{ - return LIBALTOS_SUCCESS; -} - -PUBLIC void -altos_fini(void) -{ -} - -static struct altos_error last_error; - -static void -altos_set_last_error(int code, char *string) -{ - last_error.code = code; - strncpy(last_error.string, string, sizeof (last_error.string) -1); - last_error.string[sizeof(last_error.string)-1] = '\0'; -} - -PUBLIC void -altos_get_last_error(struct altos_error *error) -{ - *error = last_error; -} - -#ifdef DARWIN - -#include - -#undef USE_POLL - -/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ -static char * -altos_strndup (const char *s, size_t n) -{ - size_t len = strlen (s); - char *ret; - - if (len <= n) - return strdup (s); - ret = malloc(n + 1); - strncpy(ret, s, n); - ret[n] = '\0'; - return ret; -} - -#else -#define altos_strndup strndup -#endif - -#ifdef POSIX_TTY - -#include -#include -#include -#include -#include -#include - -#define USB_BUF_SIZE 64 - -struct altos_file { - int fd; -#ifdef USE_POLL - int pipe[2]; -#else - int out_fd; -#endif - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; -}; - -static void -altos_set_last_posix_error(void) -{ - altos_set_last_error(errno, strerror(errno)); -} - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (sizeof (struct altos_file), 1); - int ret; - struct termios term; - - if (!file) { - altos_set_last_posix_error(); - return NULL; - } - -// altos_set_last_error(12, "yeah yeah, failed again"); -// free(file); -// return NULL; - - file->fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->fd < 0) { - altos_set_last_posix_error(); - free(file); - return NULL; - } -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = open(device->path, O_RDWR | O_NOCTTY); - if (file->out_fd < 0) { - altos_set_last_posix_error(); - close(file->fd); - free(file); - return NULL; - } -#endif - ret = tcgetattr(file->fd, &term); - if (ret < 0) { - altos_set_last_posix_error(); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - cfmakeraw(&term); - cfsetospeed(&term, B9600); - cfsetispeed(&term, B9600); -#ifdef USE_POLL - term.c_cc[VMIN] = 1; - term.c_cc[VTIME] = 0; -#else - term.c_cc[VMIN] = 0; - term.c_cc[VTIME] = 1; -#endif - ret = tcsetattr(file->fd, TCSAFLUSH, &term); - if (ret < 0) { - altos_set_last_posix_error(); - close(file->fd); -#ifndef USE_POLL - close(file->out_fd); -#endif - free(file); - return NULL; - } - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - if (file->fd != -1) { - int fd = file->fd; - file->fd = -1; -#ifdef USE_POLL - write(file->pipe[1], "\r", 1); -#else - close(file->out_fd); - file->out_fd = -1; -#endif - close(fd); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - if (file->out_used && 0) { - printf ("flush \""); - fwrite(file->out_data, 1, file->out_used, stdout); - printf ("\"\n"); - } - while (file->out_used) { - int ret; - - if (file->fd < 0) - return -EBADF; -#ifdef USE_POLL - ret = write (file->fd, file->out_data, file->out_used); -#else - ret = write (file->out_fd, file->out_data, file->out_used); -#endif - if (ret < 0) { - altos_set_last_posix_error(); - return -last_error.code; - } - if (ret) { - memmove(file->out_data, file->out_data + ret, - file->out_used - ret); - file->out_used -= ret; - } - } - return 0; -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) { - return ret; - } - } - file->out_data[file->out_used++] = c; - ret = 0; - if (file->out_used == USB_BUF_SIZE) - ret = altos_flush(file); - return ret; -} - -#ifdef USE_POLL -#include -#endif - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; -#ifdef USE_POLL - struct pollfd fd[2]; -#endif - - if (timeout == 0) - timeout = -1; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; -#ifdef USE_POLL - fd[0].fd = file->fd; - fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; - fd[1].fd = file->pipe[0]; - fd[1].events = POLLIN; - ret = poll(fd, 2, timeout); - if (ret < 0) { - altos_set_last_posix_error(); - return LIBALTOS_ERROR; - } - if (ret == 0) - return LIBALTOS_TIMEOUT; - - if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) - return LIBALTOS_ERROR; - if (fd[0].revents & POLLIN) -#endif - { - ret = read(file->fd, file->in_data, USB_BUF_SIZE); - if (ret < 0) { - altos_set_last_posix_error(); - return LIBALTOS_ERROR; - } - file->in_read = 0; - file->in_used = ret; -#ifndef USE_POLL - if (ret == 0 && timeout > 0) - return LIBALTOS_TIMEOUT; -#endif - } - } - if (file->in_used && 0) { - printf ("fill \""); - fwrite(file->in_data, 1, file->in_used, stdout); - printf ("\"\n"); - } - return 0; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->fd < 0) - return LIBALTOS_ERROR; - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -#endif /* POSIX_TTY */ - -/* - * Scan for Altus Metrum devices by looking through /sys - */ - -#ifdef LINUX - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static char * -cc_fullname (char *dir, char *file) -{ - char *new; - int dlen = strlen (dir); - int flen = strlen (file); - int slen = 0; - - if (dir[dlen-1] != '/') - slen = 1; - new = malloc (dlen + slen + flen + 1); - if (!new) - return 0; - strcpy(new, dir); - if (slen) - strcat (new, "/"); - strcat(new, file); - return new; -} - -static char * -cc_basename(char *file) -{ - char *b; - - b = strrchr(file, '/'); - if (!b) - return file; - return b + 1; -} - -static char * -load_string(char *dir, char *file) -{ - char *full = cc_fullname(dir, file); - char line[4096]; - char *r; - FILE *f; - int rlen; - - f = fopen(full, "r"); - free(full); - if (!f) - return NULL; - r = fgets(line, sizeof (line), f); - fclose(f); - if (!r) - return NULL; - rlen = strlen(r); - if (r[rlen-1] == '\n') - r[rlen-1] = '\0'; - return strdup(r); -} - -static int -load_hex(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 16); - free(line); - if (end == line) - return -1; - return i; -} - -static int -load_dec(char *dir, char *file) -{ - char *line; - char *end; - long i; - - line = load_string(dir, file); - if (!line) - return -1; - i = strtol(line, &end, 10); - free(line); - if (end == line) - return -1; - return i; -} - -static int -dir_filter_tty_colon(const struct dirent *d) -{ - return strncmp(d->d_name, "tty:", 4) == 0; -} - -static int -dir_filter_tty(const struct dirent *d) -{ - return strncmp(d->d_name, "tty", 3) == 0; -} - -struct altos_usbdev { - char *sys; - char *tty; - char *manufacturer; - char *product_name; - int serial; /* AltOS always uses simple integer serial numbers */ - int idProduct; - int idVendor; -}; - -static char * -usb_tty(char *sys) -{ - char *base; - int num_configs; - int config; - struct dirent **namelist; - int interface; - int num_interfaces; - char endpoint_base[20]; - char *endpoint_full; - char *tty_dir; - int ntty; - char *tty; - - base = cc_basename(sys); - num_configs = load_hex(sys, "bNumConfigurations"); - num_interfaces = load_hex(sys, "bNumInterfaces"); - for (config = 1; config <= num_configs; config++) { - for (interface = 0; interface < num_interfaces; interface++) { - sprintf(endpoint_base, "%s:%d.%d", - base, config, interface); - endpoint_full = cc_fullname(sys, endpoint_base); - - - /* Check for tty:ttyACMx style names - */ - ntty = scandir(endpoint_full, &namelist, - dir_filter_tty_colon, - alphasort); - if (ntty > 0) { - free(endpoint_full); - tty = cc_fullname("/dev", namelist[0]->d_name + 4); - free(namelist); - return tty; - } - - /* Check for tty/ttyACMx style names - */ - tty_dir = cc_fullname(endpoint_full, "tty"); - ntty = scandir(tty_dir, &namelist, - dir_filter_tty, - alphasort); - free (tty_dir); - if (ntty > 0) { - tty = cc_fullname("/dev", namelist[0]->d_name); - free(endpoint_full); - free(namelist); - return tty; - } - - /* Check for ttyACMx style names - */ - ntty = scandir(endpoint_full, &namelist, - dir_filter_tty, - alphasort); - free(endpoint_full); - if (ntty > 0) { - tty = cc_fullname("/dev", namelist[0]->d_name); - free(namelist); - return tty; - } - - } - } - return NULL; -} - -static struct altos_usbdev * -usb_scan_device(char *sys) -{ - struct altos_usbdev *usbdev; - char *tty; - - tty = usb_tty(sys); - if (!tty) - return NULL; - usbdev = calloc(1, sizeof (struct altos_usbdev)); - if (!usbdev) - return NULL; - usbdev->sys = strdup(sys); - usbdev->manufacturer = load_string(sys, "manufacturer"); - usbdev->product_name = load_string(sys, "product"); - usbdev->serial = load_dec(sys, "serial"); - usbdev->idProduct = load_hex(sys, "idProduct"); - usbdev->idVendor = load_hex(sys, "idVendor"); - usbdev->tty = tty; - return usbdev; -} - -static void -usbdev_free(struct altos_usbdev *usbdev) -{ - free(usbdev->sys); - free(usbdev->manufacturer); - free(usbdev->product_name); - /* this can get used as a return value */ - if (usbdev->tty) - free(usbdev->tty); - free(usbdev); -} - -#define USB_DEVICES "/sys/bus/usb/devices" - -static int -dir_filter_dev(const struct dirent *d) -{ - const char *n = d->d_name; - char c; - - while ((c = *n++)) { - if (isdigit(c)) - continue; - if (c == '-') - continue; - if (c == '.' && n != d->d_name + 1) - continue; - return 0; - } - return 1; -} - -struct altos_list { - struct altos_usbdev **dev; - int current; - int ndev; -}; - -struct altos_list * -altos_list_start(void) -{ - int e; - struct dirent **ents; - char *dir; - struct altos_usbdev *dev; - struct altos_list *devs; - int n; - - devs = calloc(1, sizeof (struct altos_list)); - if (!devs) - return NULL; - - n = scandir (USB_DEVICES, &ents, - dir_filter_dev, - alphasort); - if (!n) - return 0; - for (e = 0; e < n; e++) { - dir = cc_fullname(USB_DEVICES, ents[e]->d_name); - dev = usb_scan_device(dir); - if (!dev) - continue; - free(dir); - if (devs->dev) - devs->dev = realloc(devs->dev, - (devs->ndev + 1) * sizeof (struct usbdev *)); - else - devs->dev = malloc (sizeof (struct usbdev *)); - devs->dev[devs->ndev++] = dev; - } - free(ents); - devs->current = 0; - return devs; -} - -PUBLIC struct altos_list * -altos_ftdi_list_start(void) -{ - return altos_list_start(); -} - -int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - struct altos_usbdev *dev; - if (list->current >= list->ndev) { - return 0; - } - dev = list->dev[list->current]; - strcpy(device->name, dev->product_name); - device->vendor = dev->idVendor; - device->product = dev->idProduct; - strcpy(device->path, dev->tty); - device->serial = dev->serial; - list->current++; - return 1; -} - -void -altos_list_finish(struct altos_list *usbdevs) -{ - int i; - - if (!usbdevs) - return; - for (i = 0; i < usbdevs->ndev; i++) - usbdev_free(usbdevs->dev[i]); - free(usbdevs); -} - -#include - -static void *libbt; -static int bt_initialized; - -static int init_bt(void) { - if (!bt_initialized) { - bt_initialized = 1; - libbt = dlopen("libbluetooth.so.3", RTLD_LAZY); - if (!libbt) - printf("failed to find bluetooth library\n"); - } - return libbt != NULL; -} - -#define join(a,b) a ## b -#define bt_func(name, ret, fail, formals, actuals) \ - static ret join(altos_, name) formals { \ - static ret (*name) formals; \ - if (!init_bt()) return fail; \ - name = dlsym(libbt, #name); \ - if (!name) return fail; \ - return name actuals; \ - } - -bt_func(ba2str, int, -1, (const bdaddr_t *ba, char *str), (ba, str)) -#define ba2str altos_ba2str - -bt_func(str2ba, int, -1, (const char *str, bdaddr_t *ba), (str, ba)) -#define str2ba altos_str2ba - -bt_func(hci_read_remote_name, int, -1, (int sock, const bdaddr_t *ba, int len, char *name, int timeout), (sock, ba, len, name, timeout)) -#define hci_read_remote_name altos_hci_read_remote_name - -bt_func(hci_open_dev, int, -1, (int dev_id), (dev_id)) -#define hci_open_dev altos_hci_open_dev - -bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr)) -#define hci_get_route altos_hci_get_route - -bt_func(hci_inquiry, int, -1, (int adapter_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **devs, long flags), (adapter_id, len, max_rsp, lap, devs, flags)) -#define hci_inquiry altos_hci_inquiry - -struct altos_bt_list { - inquiry_info *ii; - int sock; - int dev_id; - int rsp; - int num_rsp; -}; - -#define INQUIRY_MAX_RSP 255 - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - struct altos_bt_list *bt_list; - - bt_list = calloc(1, sizeof (struct altos_bt_list)); - if (!bt_list) - goto no_bt_list; - - bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); - if (!bt_list->ii) - goto no_ii; - bt_list->dev_id = hci_get_route(NULL); - if (bt_list->dev_id < 0) - goto no_dev_id; - - bt_list->sock = hci_open_dev(bt_list->dev_id); - if (bt_list->sock < 0) - goto no_sock; - - bt_list->num_rsp = hci_inquiry(bt_list->dev_id, - inquiry_time, - INQUIRY_MAX_RSP, - NULL, - &bt_list->ii, - IREQ_CACHE_FLUSH); - if (bt_list->num_rsp < 0) - goto no_rsp; - - bt_list->rsp = 0; - return bt_list; - -no_rsp: - close(bt_list->sock); -no_sock: -no_dev_id: - free(bt_list->ii); -no_ii: - free(bt_list); -no_bt_list: - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - inquiry_info *ii; - - if (bt_list->rsp >= bt_list->num_rsp) - return 0; - - ii = &bt_list->ii[bt_list->rsp]; - if (ba2str(&ii->bdaddr, device->addr) < 0) - return 0; - memset(&device->name, '\0', sizeof (device->name)); - if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, - sizeof (device->name), - device->name, 0) < 0) { - strcpy(device->name, "[unknown]"); - } - bt_list->rsp++; - return 1; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ - close(bt_list->sock); - free(bt_list->ii); - free(bt_list); -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - struct sockaddr_rc addr = { 0 }; - int status, i; - struct altos_file *file; - - file = calloc(1, sizeof (struct altos_file)); - if (!file) { - errno = ENOMEM; - altos_set_last_posix_error(); - goto no_file; - } - addr.rc_family = AF_BLUETOOTH; - addr.rc_channel = 1; - if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { - altos_set_last_posix_error(); - goto no_sock; - } - - for (i = 0; i < 5; i++) { - file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); - if (file->fd < 0) { - altos_set_last_posix_error(); - goto no_sock; - } - - status = connect(file->fd, - (struct sockaddr *)&addr, - sizeof(addr)); - if (status >= 0 || errno != EBUSY) - break; - close(file->fd); - usleep(100 * 1000); - } - if (status < 0) { - altos_set_last_posix_error(); - goto no_link; - } - usleep(100 * 1000); - -#ifdef USE_POLL - pipe(file->pipe); -#else - file->out_fd = dup(file->fd); -#endif - return file; -no_link: - close(file->fd); -no_sock: - free(file); -no_file: - return NULL; -} - -#endif - -#ifdef DARWIN - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct altos_list { - io_iterator_t iterator; - int ftdi; -}; - -static int -get_string(io_object_t object, CFStringRef entry, char *result, int result_len) -{ - CFTypeRef entry_as_string; - Boolean got_string; - - entry_as_string = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_string) { - got_string = CFStringGetCString(entry_as_string, - result, result_len, - kCFStringEncodingASCII); - - CFRelease(entry_as_string); - if (got_string) - return 1; - } - return 0; -} - -static int -get_number(io_object_t object, CFStringRef entry, int *result) -{ - CFTypeRef entry_as_number; - Boolean got_number; - - entry_as_number = IORegistryEntrySearchCFProperty (object, - kIOServicePlane, - entry, - kCFAllocatorDefault, - kIORegistryIterateRecursively); - if (entry_as_number) { - got_number = CFNumberGetValue(entry_as_number, - kCFNumberIntType, - result); - if (got_number) - return 1; - } - return 0; -} - -PUBLIC struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc (sizeof (struct altos_list), 1); - CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); - io_iterator_t tdIterator; - io_object_t tdObject; - kern_return_t ret; - int i; - - ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); - if (ret != kIOReturnSuccess) { - free(list); - return NULL; - } - list->ftdi = 0; - return list; -} - -PUBLIC struct altos_list * -altos_ftdi_list_start(void) -{ - struct altos_list *list = altos_list_start(); - - if (list) - list->ftdi = 1; - return list; -} - -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - io_object_t object; - char serial_string[128]; - - for (;;) { - object = IOIteratorNext(list->iterator); - if (!object) - return 0; - - if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || - !get_number (object, CFSTR(kUSBProductID), &device->product)) - continue; - if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && - get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && - get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { - device->serial = atoi(serial_string); - return 1; - } - } -} - -PUBLIC void -altos_list_finish(struct altos_list *list) -{ - IOObjectRelease (list->iterator); - free(list); -} - -struct altos_bt_list { - int sock; - int dev_id; - int rsp; - int num_rsp; -}; - -#define INQUIRY_MAX_RSP 255 - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - return 0; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - return NULL; -} - -#endif - - -#ifdef WINDOWS - -#include -#include -#include - -struct altos_list { - HDEVINFO dev_info; - int index; - int ftdi; -}; - -#define USB_BUF_SIZE 64 - -struct altos_file { - HANDLE handle; - unsigned char out_data[USB_BUF_SIZE]; - int out_used; - unsigned char in_data[USB_BUF_SIZE]; - int in_used; - int in_read; - OVERLAPPED ov_read; - BOOL pend_read; - OVERLAPPED ov_write; -}; - -#include - -static void -log_message(char *fmt, ...) -{ - static FILE *log = NULL; - va_list a; - - if (!log) - log = fopen("\\temp\\altos.txt", "w"); - if (log) { - SYSTEMTIME time; - GetLocalTime(&time); - fprintf (log, "%4d-%02d-%02d %2d:%02d:%02d. ", - time.wYear, time.wMonth, time.wDay, - time.wHour, time.wMinute, time.wSecond); - va_start(a, fmt); - vfprintf(log, fmt, a); - va_end(a); - fflush(log); - } -} - -static void -_altos_set_last_windows_error(char *file, int line) -{ - DWORD error = GetLastError(); - TCHAR message[1024]; - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, - 0, - error, - 0, - message, - sizeof (message) / sizeof (TCHAR), - NULL); - if (error != ERROR_SUCCESS) - log_message ("%s:%d %s\n", file, line, message); - altos_set_last_error(error, message); -} - -#define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__) - -PUBLIC struct altos_list * -altos_list_start(void) -{ - struct altos_list *list = calloc(1, sizeof (struct altos_list)); - - if (!list) - return NULL; - list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, - DIGCF_ALLCLASSES|DIGCF_PRESENT); - if (list->dev_info == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - free(list); - return NULL; - } - list->index = 0; - list->ftdi = 0; - return list; -} - -PUBLIC struct altos_list * -altos_ftdi_list_start(void) -{ - struct altos_list *list = calloc(1, sizeof (struct altos_list)); - - if (!list) - return NULL; - list->dev_info = SetupDiGetClassDevs(NULL, "FTDIBUS", NULL, - DIGCF_ALLCLASSES|DIGCF_PRESENT); - if (list->dev_info == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - free(list); - return NULL; - } - list->index = 0; - list->ftdi = 1; - return list; -} - -PUBLIC int -altos_list_next(struct altos_list *list, struct altos_device *device) -{ - SP_DEVINFO_DATA dev_info_data; - BYTE port[128]; - DWORD port_len; - char friendlyname[256]; - BYTE symbolic[256]; - DWORD symbolic_len; - HKEY dev_key; - unsigned int vid, pid; - int serial; - HRESULT result; - DWORD friendlyname_type; - DWORD friendlyname_len; - char instanceid[1024]; - DWORD instanceid_len; - - dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); - while(SetupDiEnumDeviceInfo(list->dev_info, list->index, - &dev_info_data)) - { - list->index++; - - dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, - DICS_FLAG_GLOBAL, 0, DIREG_DEV, - KEY_READ); - if (dev_key == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - continue; - } - - if (list->ftdi) { - vid = 0x0403; - pid = 0x6015; - serial = 0; - } else { - vid = pid = serial = 0; - /* Fetch symbolic name for this device and parse out - * the vid/pid/serial info */ - symbolic_len = sizeof(symbolic); - result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, - symbolic, &symbolic_len); - if (result != 0) { - altos_set_last_windows_error(); - } else { - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1, - "%04X", &vid); - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, - "%04X", &pid); - sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, - "%d", &serial); - } - if (vid == 0 || pid == 0 || serial == 0) { - if (SetupDiGetDeviceInstanceId(list->dev_info, - &dev_info_data, - instanceid, - sizeof (instanceid), - &instanceid_len)) { - sscanf((char *) instanceid + sizeof("USB\\VID_") - 1, - "%04X", &vid); - sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1, - "%04X", &pid); - sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1, - "%d", &serial); - } else { - altos_set_last_windows_error(); - } - } - if (vid == 0 || pid == 0 || serial == 0) { - RegCloseKey(dev_key); - continue; - } - } - - /* Fetch the com port name */ - port_len = sizeof (port); - result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, - port, &port_len); - RegCloseKey(dev_key); - if (result != 0) { - altos_set_last_windows_error(); - continue; - } - - /* Fetch the device description which is the device name, - * with firmware that has unique USB ids */ - friendlyname_len = sizeof (friendlyname); - if(!SetupDiGetDeviceRegistryProperty(list->dev_info, - &dev_info_data, - SPDRP_FRIENDLYNAME, - &friendlyname_type, - (BYTE *)friendlyname, - sizeof(friendlyname), - &friendlyname_len)) - { - altos_set_last_windows_error(); - continue; - } - device->vendor = vid; - device->product = pid; - device->serial = serial; - strcpy(device->name, friendlyname); - - strcpy(device->path, (char *) port); - return 1; - } - result = GetLastError(); - if (result != ERROR_NO_MORE_ITEMS) - altos_set_last_windows_error(); - return 0; -} - -PUBLIC void -altos_list_finish(struct altos_list *list) -{ - SetupDiDestroyDeviceInfoList(list->dev_info); - free(list); -} - -static int -altos_queue_read(struct altos_file *file) -{ - DWORD got; - if (file->pend_read) - return LIBALTOS_SUCCESS; - - if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) { - if (GetLastError() != ERROR_IO_PENDING) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - file->pend_read = TRUE; - } else { - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_wait_read(struct altos_file *file, int timeout) -{ - DWORD ret; - DWORD got; - - if (!file->pend_read) - return LIBALTOS_SUCCESS; - - if (!timeout) - timeout = INFINITE; - - ret = WaitForSingleObject(file->ov_read.hEvent, timeout); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - file->pend_read = FALSE; - file->in_read = 0; - file->in_used = got; - break; - case WAIT_TIMEOUT: - return LIBALTOS_TIMEOUT; - break; - default: - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - return LIBALTOS_SUCCESS; -} - -static int -altos_fill(struct altos_file *file, int timeout) -{ - int ret; - - if (file->in_read < file->in_used) - return LIBALTOS_SUCCESS; - - file->in_read = file->in_used = 0; - - ret = altos_queue_read(file); - if (ret) - return ret; - ret = altos_wait_read(file, timeout); - if (ret) - return ret; - - return LIBALTOS_SUCCESS; -} - -PUBLIC int -altos_flush(struct altos_file *file) -{ - DWORD put; - unsigned char *data = file->out_data; - int used = file->out_used; - DWORD ret; - - while (used) { - if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { - if (GetLastError() != ERROR_IO_PENDING) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); - switch (ret) { - case WAIT_OBJECT_0: - if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - break; - default: - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - } - data += put; - used -= put; - } - file->out_used = 0; - return LIBALTOS_SUCCESS; -} - -static HANDLE -open_serial(char *full_name) -{ - HANDLE handle; - DCB dcb; - - handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED, NULL); - - if (handle == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - return INVALID_HANDLE_VALUE; - } - - if (!GetCommState(handle, &dcb)) { - altos_set_last_windows_error(); - CloseHandle(handle); - return INVALID_HANDLE_VALUE; - } - dcb.BaudRate = CBR_9600; - dcb.fBinary = TRUE; - dcb.fParity = FALSE; - dcb.fOutxCtsFlow = FALSE; - dcb.fOutxDsrFlow = FALSE; - dcb.fDtrControl = DTR_CONTROL_ENABLE; - dcb.fDsrSensitivity = FALSE; - dcb.fTXContinueOnXoff = FALSE; - dcb.fOutX = FALSE; - dcb.fInX = FALSE; - dcb.fErrorChar = FALSE; - dcb.fNull = FALSE; - dcb.fRtsControl = RTS_CONTROL_ENABLE; - dcb.fAbortOnError = FALSE; - dcb.XonLim = 10; - dcb.XoffLim = 10; - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - dcb.XonChar = 17; - dcb.XoffChar = 19; -#if 0 - dcb.ErrorChar = 0; - dcb.EofChar = 0; - dcb.EvtChar = 0; -#endif - if (!SetCommState(handle, &dcb)) { - altos_set_last_windows_error(); - CloseHandle(handle); - return INVALID_HANDLE_VALUE; - } - return handle; -} - -PUBLIC struct altos_file * -altos_open(struct altos_device *device) -{ - struct altos_file *file = calloc (1, sizeof (struct altos_file)); - char full_name[64]; - COMMTIMEOUTS timeouts; - int i; - - if (!file) - return NULL; - - strcpy(full_name, "\\\\.\\"); - strcat(full_name, device->path); - - file->handle = INVALID_HANDLE_VALUE; - - for (i = 0; i < 5; i++) { - file->handle = open_serial(full_name); - if (file->handle != INVALID_HANDLE_VALUE) - break; - altos_set_last_windows_error(); - Sleep(100); - } - - if (file->handle == INVALID_HANDLE_VALUE) { - free(file); - return NULL; - } - - /* The FTDI driver doesn't appear to work right unless you open it twice */ - if (device->vendor == 0x0403) { - CloseHandle(file->handle); - file->handle = open_serial(full_name); - if (file->handle == INVALID_HANDLE_VALUE) { - free(file); - return NULL; - } - } - - timeouts.ReadIntervalTimeout = MAXDWORD; - timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; - timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ - timeouts.WriteTotalTimeoutMultiplier = 0; - timeouts.WriteTotalTimeoutConstant = 0; - SetCommTimeouts(file->handle, &timeouts); - - file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - - return file; -} - -PUBLIC void -altos_close(struct altos_file *file) -{ - HANDLE handle = file->handle; - if (handle != INVALID_HANDLE_VALUE) { - HANDLE ov_read = file->ov_read.hEvent; - HANDLE ov_write = file->ov_write.hEvent; - file->handle = INVALID_HANDLE_VALUE; - file->ov_read.hEvent = INVALID_HANDLE_VALUE; - file->ov_write.hEvent = INVALID_HANDLE_VALUE; - PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); - Sleep(100); - CloseHandle(handle); - file->handle = INVALID_HANDLE_VALUE; - CloseHandle(ov_read); - CloseHandle(ov_write); - } -} - -PUBLIC void -altos_free(struct altos_file *file) -{ - altos_close(file); - free(file); -} - -PUBLIC int -altos_putchar(struct altos_file *file, char c) -{ - int ret; - - if (file->out_used == USB_BUF_SIZE) { - ret = altos_flush(file); - if (ret) - return ret; - } - file->out_data[file->out_used++] = c; - if (file->out_used == USB_BUF_SIZE) - return altos_flush(file); - return LIBALTOS_SUCCESS; -} - -PUBLIC int -altos_getchar(struct altos_file *file, int timeout) -{ - int ret; - while (file->in_read == file->in_used) { - if (file->handle == INVALID_HANDLE_VALUE) { - altos_set_last_windows_error(); - return LIBALTOS_ERROR; - } - ret = altos_fill(file, timeout); - if (ret) - return ret; - } - return file->in_data[file->in_read++]; -} - -struct altos_bt_list * -altos_bt_list_start(int inquiry_time) -{ - return NULL; -} - -int -altos_bt_list_next(struct altos_bt_list *bt_list, - struct altos_bt_device *device) -{ - return 0; -} - -void -altos_bt_list_finish(struct altos_bt_list *bt_list) -{ - free(bt_list); -} - -void -altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) -{ - strncpy(device->name, name, sizeof (device->name)); - device->name[sizeof(device->name)-1] = '\0'; - strncpy(device->addr, addr, sizeof (device->addr)); - device->addr[sizeof(device->addr)-1] = '\0'; -} - -struct altos_file * -altos_bt_open(struct altos_bt_device *device) -{ - return NULL; -} - -#endif diff --git a/libaltos/libaltos_common.c b/libaltos/libaltos_common.c new file mode 100644 index 00000000..090f03ad --- /dev/null +++ b/libaltos/libaltos_common.c @@ -0,0 +1,83 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "libaltos_private.h" + +PUBLIC int +altos_init(void) +{ + return LIBALTOS_SUCCESS; +} + +PUBLIC void +altos_fini(void) +{ +} + +struct altos_error altos_last_error; + +void +altos_set_last_error(int code, char *string) +{ + altos_last_error.code = code; + strncpy(altos_last_error.string, string, sizeof (altos_last_error.string) -1); + altos_last_error.string[sizeof(altos_last_error.string)-1] = '\0'; +} + +PUBLIC void +altos_get_last_error(struct altos_error *error) +{ + *error = altos_last_error; +} + +PUBLIC int +altos_getchar(struct altos_file *file, int timeout) +{ + int ret; + while (file->in_read == file->in_used) { + ret = altos_fill(file, timeout); + if (ret) + return ret; + } + return file->in_data[file->in_read++]; +} + +PUBLIC int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) { + return ret; + } + } + file->out_data[file->out_used++] = c; + ret = 0; + if (file->out_used == USB_BUF_SIZE) + ret = altos_flush(file); + return ret; +} + + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); + free(file); +} diff --git a/libaltos/libaltos_darwin.c b/libaltos/libaltos_darwin.c new file mode 100644 index 00000000..bf3cf094 --- /dev/null +++ b/libaltos/libaltos_darwin.c @@ -0,0 +1,191 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "libaltos_private.h" +#include "libaltos_posix.h" + +#include +#include +#include +#include +#include +#include + +/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */ +char * +altos_strndup (const char *s, size_t n) +{ + size_t len = strlen (s); + char *ret; + + if (len <= n) + return strdup (s); + ret = malloc(n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +struct altos_list { + io_iterator_t iterator; + int ftdi; +}; + +static int +get_string(io_object_t object, CFStringRef entry, char *result, int result_len) +{ + CFTypeRef entry_as_string; + Boolean got_string; + + entry_as_string = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_string) { + got_string = CFStringGetCString(entry_as_string, + result, result_len, + kCFStringEncodingASCII); + + CFRelease(entry_as_string); + if (got_string) + return 1; + } + return 0; +} + +static int +get_number(io_object_t object, CFStringRef entry, int *result) +{ + CFTypeRef entry_as_number; + Boolean got_number; + + entry_as_number = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_number) { + got_number = CFNumberGetValue(entry_as_number, + kCFNumberIntType, + result); + if (got_number) + return 1; + } + return 0; +} + +PUBLIC struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc (sizeof (struct altos_list), 1); + CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); + io_iterator_t tdIterator; + io_object_t tdObject; + kern_return_t ret; + int i; + + ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); + if (ret != kIOReturnSuccess) { + free(list); + return NULL; + } + list->ftdi = 0; + return list; +} + +PUBLIC struct altos_list * +altos_ftdi_list_start(void) +{ + struct altos_list *list = altos_list_start(); + + if (list) + list->ftdi = 1; + return list; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + io_object_t object; + char serial_string[128]; + + for (;;) { + object = IOIteratorNext(list->iterator); + if (!object) + return 0; + + if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) || + !get_number (object, CFSTR(kUSBProductID), &device->product)) + continue; + if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && + get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) && + get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { + device->serial = atoi(serial_string); + return 1; + } + } +} + +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + IOObjectRelease (list->iterator); + free(list); +} + +struct altos_bt_list { + int sock; + int dev_id; + int rsp; + int num_rsp; +}; + +#define INQUIRY_MAX_RSP 255 + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + return 0; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + return NULL; +} diff --git a/libaltos/libaltos_linux.c b/libaltos/libaltos_linux.c new file mode 100644 index 00000000..d7cd15cf --- /dev/null +++ b/libaltos/libaltos_linux.c @@ -0,0 +1,528 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#define _GNU_SOURCE +#include "libaltos_private.h" +#include "libaltos_posix.h" + +#include +#include +#include +#include +#include +#include + +static char * +cc_fullname (char *dir, char *file) +{ + char *new; + int dlen = strlen (dir); + int flen = strlen (file); + int slen = 0; + + if (dir[dlen-1] != '/') + slen = 1; + new = malloc (dlen + slen + flen + 1); + if (!new) + return 0; + strcpy(new, dir); + if (slen) + strcat (new, "/"); + strcat(new, file); + return new; +} + +static char * +cc_basename(char *file) +{ + char *b; + + b = strrchr(file, '/'); + if (!b) + return file; + return b + 1; +} + +static char * +load_string(char *dir, char *file) +{ + char *full = cc_fullname(dir, file); + char line[4096]; + char *r; + FILE *f; + int rlen; + + f = fopen(full, "r"); + free(full); + if (!f) + return NULL; + r = fgets(line, sizeof (line), f); + fclose(f); + if (!r) + return NULL; + rlen = strlen(r); + if (r[rlen-1] == '\n') + r[rlen-1] = '\0'; + return strdup(r); +} + +static int +load_hex(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 16); + free(line); + if (end == line) + return -1; + return i; +} + +static int +load_dec(char *dir, char *file) +{ + char *line; + char *end; + long i; + + line = load_string(dir, file); + if (!line) + return -1; + i = strtol(line, &end, 10); + free(line); + if (end == line) + return -1; + return i; +} + +static int +dir_filter_tty_colon(const struct dirent *d) +{ + return strncmp(d->d_name, "tty:", 4) == 0; +} + +static int +dir_filter_tty(const struct dirent *d) +{ + return strncmp(d->d_name, "tty", 3) == 0; +} + +struct altos_usbdev { + char *sys; + char *tty; + char *manufacturer; + char *product_name; + int serial; /* AltOS always uses simple integer serial numbers */ + int idProduct; + int idVendor; +}; + +static char * +usb_tty(char *sys) +{ + char *base; + int num_configs; + int config; + struct dirent **namelist; + int interface; + int num_interfaces; + char endpoint_base[20]; + char *endpoint_full; + char *tty_dir; + int ntty; + char *tty; + + base = cc_basename(sys); + num_configs = load_hex(sys, "bNumConfigurations"); + num_interfaces = load_hex(sys, "bNumInterfaces"); + for (config = 1; config <= num_configs; config++) { + for (interface = 0; interface < num_interfaces; interface++) { + sprintf(endpoint_base, "%s:%d.%d", + base, config, interface); + endpoint_full = cc_fullname(sys, endpoint_base); + + + /* Check for tty:ttyACMx style names + */ + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty_colon, + alphasort); + if (ntty > 0) { + free(endpoint_full); + tty = cc_fullname("/dev", namelist[0]->d_name + 4); + free(namelist); + return tty; + } + + /* Check for tty/ttyACMx style names + */ + tty_dir = cc_fullname(endpoint_full, "tty"); + ntty = scandir(tty_dir, &namelist, + dir_filter_tty, + alphasort); + free (tty_dir); + if (ntty > 0) { + tty = cc_fullname("/dev", namelist[0]->d_name); + free(endpoint_full); + free(namelist); + return tty; + } + + /* Check for ttyACMx style names + */ + ntty = scandir(endpoint_full, &namelist, + dir_filter_tty, + alphasort); + free(endpoint_full); + if (ntty > 0) { + tty = cc_fullname("/dev", namelist[0]->d_name); + free(namelist); + return tty; + } + + } + } + return NULL; +} + +static struct altos_usbdev * +usb_scan_device(char *sys) +{ + struct altos_usbdev *usbdev; + char *tty; + + tty = usb_tty(sys); + if (!tty) + return NULL; + usbdev = calloc(1, sizeof (struct altos_usbdev)); + if (!usbdev) + return NULL; + usbdev->sys = strdup(sys); + usbdev->manufacturer = load_string(sys, "manufacturer"); + usbdev->product_name = load_string(sys, "product"); + usbdev->serial = load_dec(sys, "serial"); + usbdev->idProduct = load_hex(sys, "idProduct"); + usbdev->idVendor = load_hex(sys, "idVendor"); + usbdev->tty = tty; + return usbdev; +} + +static void +usbdev_free(struct altos_usbdev *usbdev) +{ + free(usbdev->sys); + free(usbdev->manufacturer); + free(usbdev->product_name); + /* this can get used as a return value */ + if (usbdev->tty) + free(usbdev->tty); + free(usbdev); +} + +#define USB_DEVICES "/sys/bus/usb/devices" + +static int +dir_filter_dev(const struct dirent *d) +{ + const char *n = d->d_name; + char c; + + while ((c = *n++)) { + if (isdigit(c)) + continue; + if (c == '-') + continue; + if (c == '.' && n != d->d_name + 1) + continue; + return 0; + } + return 1; +} + +struct altos_list { + struct altos_usbdev **dev; + int current; + int ndev; +}; + +struct altos_list * +altos_list_start(void) +{ + int e; + struct dirent **ents; + char *dir; + struct altos_usbdev *dev; + struct altos_list *devs; + int n; + + devs = calloc(1, sizeof (struct altos_list)); + if (!devs) + return NULL; + + n = scandir (USB_DEVICES, &ents, + dir_filter_dev, + alphasort); + if (!n) + return 0; + for (e = 0; e < n; e++) { + dir = cc_fullname(USB_DEVICES, ents[e]->d_name); + dev = usb_scan_device(dir); + if (!dev) + continue; + free(dir); + if (devs->dev) + devs->dev = realloc(devs->dev, + (devs->ndev + 1) * sizeof (struct usbdev *)); + else + devs->dev = malloc (sizeof (struct usbdev *)); + devs->dev[devs->ndev++] = dev; + } + free(ents); + devs->current = 0; + return devs; +} + +PUBLIC struct altos_list * +altos_ftdi_list_start(void) +{ + return altos_list_start(); +} + +int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + struct altos_usbdev *dev; + if (list->current >= list->ndev) { + return 0; + } + dev = list->dev[list->current]; + strcpy(device->name, dev->product_name); + device->vendor = dev->idVendor; + device->product = dev->idProduct; + strcpy(device->path, dev->tty); + device->serial = dev->serial; + list->current++; + return 1; +} + +void +altos_list_finish(struct altos_list *usbdevs) +{ + int i; + + if (!usbdevs) + return; + for (i = 0; i < usbdevs->ndev; i++) + usbdev_free(usbdevs->dev[i]); + free(usbdevs); +} + +#include + +static void *libbt; +static int bt_initialized; + +static int init_bt(void) { + if (!bt_initialized) { + bt_initialized = 1; + libbt = dlopen("libbluetooth.so.3", RTLD_LAZY); + if (!libbt) + printf("failed to find bluetooth library\n"); + } + return libbt != NULL; +} + +#define join(a,b) a ## b +#define bt_func(name, ret, fail, formals, actuals) \ + static ret join(altos_, name) formals { \ + static ret (*name) formals; \ + if (!init_bt()) return fail; \ + name = dlsym(libbt, #name); \ + if (!name) return fail; \ + return name actuals; \ + } + +bt_func(ba2str, int, -1, (const bdaddr_t *ba, char *str), (ba, str)) +#define ba2str altos_ba2str + +bt_func(str2ba, int, -1, (const char *str, bdaddr_t *ba), (str, ba)) +#define str2ba altos_str2ba + +bt_func(hci_read_remote_name, int, -1, (int sock, const bdaddr_t *ba, int len, char *name, int timeout), (sock, ba, len, name, timeout)) +#define hci_read_remote_name altos_hci_read_remote_name + +bt_func(hci_open_dev, int, -1, (int dev_id), (dev_id)) +#define hci_open_dev altos_hci_open_dev + +bt_func(hci_get_route, int, -1, (bdaddr_t *bdaddr), (bdaddr)) +#define hci_get_route altos_hci_get_route + +bt_func(hci_inquiry, int, -1, (int adapter_id, int len, int max_rsp, const uint8_t *lap, inquiry_info **devs, long flags), (adapter_id, len, max_rsp, lap, devs, flags)) +#define hci_inquiry altos_hci_inquiry + +struct altos_bt_list { + inquiry_info *ii; + int sock; + int dev_id; + int rsp; + int num_rsp; +}; + +#define INQUIRY_MAX_RSP 255 + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + struct altos_bt_list *bt_list; + + bt_list = calloc(1, sizeof (struct altos_bt_list)); + if (!bt_list) + goto no_bt_list; + + bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info)); + if (!bt_list->ii) + goto no_ii; + bt_list->dev_id = hci_get_route(NULL); + if (bt_list->dev_id < 0) + goto no_dev_id; + + bt_list->sock = hci_open_dev(bt_list->dev_id); + if (bt_list->sock < 0) + goto no_sock; + + bt_list->num_rsp = hci_inquiry(bt_list->dev_id, + inquiry_time, + INQUIRY_MAX_RSP, + NULL, + &bt_list->ii, + IREQ_CACHE_FLUSH); + if (bt_list->num_rsp < 0) + goto no_rsp; + + bt_list->rsp = 0; + return bt_list; + +no_rsp: + close(bt_list->sock); +no_sock: +no_dev_id: + free(bt_list->ii); +no_ii: + free(bt_list); +no_bt_list: + return NULL; +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + inquiry_info *ii; + + if (bt_list->rsp >= bt_list->num_rsp) + return 0; + + ii = &bt_list->ii[bt_list->rsp]; + if (ba2str(&ii->bdaddr, device->addr) < 0) + return 0; + memset(&device->name, '\0', sizeof (device->name)); + if (hci_read_remote_name(bt_list->sock, &ii->bdaddr, + sizeof (device->name), + device->name, 0) < 0) { + strcpy(device->name, "[unknown]"); + } + bt_list->rsp++; + return 1; +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ + close(bt_list->sock); + free(bt_list->ii); + free(bt_list); +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + struct sockaddr_rc addr = { 0 }; + int status, i; + struct altos_file_posix *file; + + file = calloc(1, sizeof (struct altos_file_posix)); + if (!file) { + errno = ENOMEM; + altos_set_last_posix_error(); + goto no_file; + } + addr.rc_family = AF_BLUETOOTH; + addr.rc_channel = 1; + if (str2ba(device->addr, &addr.rc_bdaddr) < 0) { + altos_set_last_posix_error(); + goto no_sock; + } + + for (i = 0; i < 5; i++) { + file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); + if (file->fd < 0) { + altos_set_last_posix_error(); + goto no_sock; + } + + status = connect(file->fd, + (struct sockaddr *)&addr, + sizeof(addr)); + if (status >= 0 || errno != EBUSY) + break; + close(file->fd); + usleep(100 * 1000); + } + if (status < 0) { + altos_set_last_posix_error(); + goto no_link; + } + usleep(100 * 1000); + +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = dup(file->fd); +#endif + return file; +no_link: + close(file->fd); +no_sock: + free(file); +no_file: + return NULL; +} + diff --git a/libaltos/libaltos_posix.c b/libaltos/libaltos_posix.c new file mode 100644 index 00000000..731e9aa1 --- /dev/null +++ b/libaltos/libaltos_posix.c @@ -0,0 +1,201 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "libaltos_private.h" +#include "libaltos_posix.h" + +void +altos_set_last_posix_error(void) +{ + altos_set_last_error(errno, strerror(errno)); +} + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file_posix *file = calloc (sizeof (struct altos_file_posix), 1); + int ret; + struct termios term; + + if (!file) { + altos_set_last_posix_error(); + return NULL; + } + +// altos_set_last_error(12, "yeah yeah, failed again"); +// free(file); +// return NULL; + + file->fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->fd < 0) { + altos_set_last_posix_error(); + free(file); + return NULL; + } +#ifdef USE_POLL + pipe(file->pipe); +#else + file->out_fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->out_fd < 0) { + altos_set_last_posix_error(); + close(file->fd); + free(file); + return NULL; + } +#endif + ret = tcgetattr(file->fd, &term); + if (ret < 0) { + altos_set_last_posix_error(); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + cfmakeraw(&term); + cfsetospeed(&term, B9600); + cfsetispeed(&term, B9600); +#ifdef USE_POLL + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 0; +#else + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; +#endif + ret = tcsetattr(file->fd, TCSAFLUSH, &term); + if (ret < 0) { + altos_set_last_posix_error(); + close(file->fd); +#ifndef USE_POLL + close(file->out_fd); +#endif + free(file); + return NULL; + } + return &file->file; +} + +PUBLIC void +altos_close(struct altos_file *file_common) +{ + struct altos_file_posix *file = (struct altos_file_posix *) file_common; + + if (file->fd != -1) { + int fd = file->fd; + file->fd = -1; +#ifdef USE_POLL + write(file->pipe[1], "\r", 1); +#else + close(file->out_fd); + file->out_fd = -1; +#endif + close(fd); + } +} + +PUBLIC int +altos_flush(struct altos_file *file_common) +{ + struct altos_file_posix *file = (struct altos_file_posix *) file_common; + + if (file->file.out_used && 0) { + printf ("flush \""); + fwrite(file->file.out_data, 1, file->file.out_used, stdout); + printf ("\"\n"); + } + while (file->file.out_used) { + int ret; + + if (file->fd < 0) + return -EBADF; +#ifdef USE_POLL + ret = write (file->fd, file->file.out_data, file->file.out_used); +#else + ret = write (file->out_fd, file->file.out_data, file->file.out_used); +#endif + if (ret < 0) { + altos_set_last_posix_error(); + return -altos_last_error.code; + } + if (ret) { + memmove(file->file.out_data, file->file.out_data + ret, + file->file.out_used - ret); + file->file.out_used -= ret; + } + } + return 0; +} + + +#ifdef USE_POLL +#include +#endif + +int +altos_fill(struct altos_file *file_common, int timeout) +{ + struct altos_file_posix *file = (struct altos_file_posix *) file_common; + + int ret; +#ifdef USE_POLL + struct pollfd fd[2]; +#endif + if (timeout == 0) + timeout = -1; + while (file->file.in_read == file->file.in_used) { + if (file->fd < 0) + return LIBALTOS_ERROR; +#ifdef USE_POLL + fd[0].fd = file->fd; + fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL; + fd[1].fd = file->pipe[0]; + fd[1].events = POLLIN; + ret = poll(fd, 2, timeout); + if (ret < 0) { + altos_set_last_posix_error(); + return LIBALTOS_ERROR; + } + if (ret == 0) + return LIBALTOS_TIMEOUT; + + if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL)) + return LIBALTOS_ERROR; + if (fd[0].revents & POLLIN) +#endif + { + ret = read(file->fd, file->file.in_data, USB_BUF_SIZE); + if (ret < 0) { + altos_set_last_posix_error(); + return LIBALTOS_ERROR; + } + file->file.in_read = 0; + file->file.in_used = ret; +#ifndef USE_POLL + if (ret == 0 && timeout > 0) + return LIBALTOS_TIMEOUT; +#endif + } + } + if (file->file.in_used && 0) { + printf ("fill \""); + fwrite(file->file.in_data, 1, file->file.in_used, stdout); + printf ("\"\n"); + } + return 0; +} + diff --git a/libaltos/libaltos_posix.h b/libaltos/libaltos_posix.h new file mode 100644 index 00000000..dc9bfb62 --- /dev/null +++ b/libaltos/libaltos_posix.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _LIBALTOS_POSIX_H_ +#define _LIBALTOS_POSIX_H_ + +#include +#include +#include +#include + +struct altos_file_posix { + struct altos_file file; + + int fd; +#ifdef USE_POLL + int pipe[2]; +#else + int out_fd; +#endif +}; + +void +altos_set_last_posix_error(void); + +#endif /* _LIBALTOS_POSIX_H_ */ diff --git a/libaltos/libaltos_private.h b/libaltos/libaltos_private.h new file mode 100644 index 00000000..56f6335b --- /dev/null +++ b/libaltos/libaltos_private.h @@ -0,0 +1,63 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _LIBALTOS_PRIVATE_H_ +#define _LIBALTOS_PRIVATE_H_ + +#include "libaltos.h" +#include +#include +#include + +#define BLUETOOTH_PRODUCT_TELEBT "TeleBT" + +#define USB_BUF_SIZE 64 + +struct altos_file { + /* Shared data */ + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +#ifdef LINUX +#define USE_POLL +#endif + +#ifdef DARWIN +#include + +#define strndup(s,n) altos_strndup(s,n) + +char *altos_strndup(const char *s, size_t n); + +#endif + +void +altos_set_last_error(int code, char *string); + +extern struct altos_error altos_last_error; + +PUBLIC int +altos_flush(struct altos_file *file); + +int +altos_fill(struct altos_file *file, int timeout); + +#endif /* _LIBALTOS_PRIVATE_H_ */ diff --git a/libaltos/libaltos_windows.c b/libaltos/libaltos_windows.c new file mode 100644 index 00000000..2c3f41e1 --- /dev/null +++ b/libaltos/libaltos_windows.c @@ -0,0 +1,761 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; version 2 of the License. + * + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "libaltos_private.h" + +#include +#include +#include + +struct altos_list { + HDEVINFO dev_info; + int index; + int ftdi; +}; + +#define USB_BUF_SIZE 64 + +struct altos_file_windows { + struct altos_file file; + + BOOL is_winsock; + /* Data used by the regular I/O */ + HANDLE handle; + OVERLAPPED ov_read; + BOOL pend_read; + OVERLAPPED ov_write; + + /* Data used by winsock */ + SOCKET socket; +}; + +#include + +static void +log_message(char *fmt, ...) +{ + static FILE *log = NULL; + va_list a; + + if (!log) + log = fopen("\\temp\\altos.txt", "w"); + if (log) { + SYSTEMTIME time; + char buffer[4096]; + + GetLocalTime(&time); + sprintf (buffer, "%4d-%02d-%02d %2d:%02d:%02d. ", + time.wYear, time.wMonth, time.wDay, + time.wHour, time.wMinute, time.wSecond); + va_start(a, fmt); + + vsprintf(buffer + strlen(buffer), fmt, a); + va_end(a); + + fputs(buffer, log); + fflush(log); + fputs(buffer, stdout); + fflush(stdout); + } +} + +static void +_altos_set_last_windows_error(char *file, int line, DWORD error) +{ + TCHAR message[1024]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, + error, + 0, + message, + sizeof (message) / sizeof (TCHAR), + NULL); + if (error != ERROR_SUCCESS) + log_message ("%s:%d (%d) %s\n", file, line, error, message); + altos_set_last_error(error, message); +} + +#define altos_set_last_windows_error() _altos_set_last_windows_error(__FILE__, __LINE__, GetLastError()) +#define altos_set_last_winsock_error() _altos_set_last_windows_error(__FILE__, __LINE__, WSAGetLastError()) + +PUBLIC struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc(1, sizeof (struct altos_list)); + + if (!list) + return NULL; + list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, + DIGCF_ALLCLASSES|DIGCF_PRESENT); + if (list->dev_info == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + free(list); + return NULL; + } + list->index = 0; + list->ftdi = 0; + return list; +} + +PUBLIC struct altos_list * +altos_ftdi_list_start(void) +{ + struct altos_list *list = calloc(1, sizeof (struct altos_list)); + + if (!list) + return NULL; + list->dev_info = SetupDiGetClassDevs(NULL, "FTDIBUS", NULL, + DIGCF_ALLCLASSES|DIGCF_PRESENT); + if (list->dev_info == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + free(list); + return NULL; + } + list->index = 0; + list->ftdi = 1; + return list; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + SP_DEVINFO_DATA dev_info_data; + BYTE port[128]; + DWORD port_len; + char friendlyname[256]; + BYTE symbolic[256]; + DWORD symbolic_len; + HKEY dev_key; + unsigned int vid, pid; + int serial; + HRESULT result; + DWORD friendlyname_type; + DWORD friendlyname_len; + char instanceid[1024]; + DWORD instanceid_len; + + dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); + while(SetupDiEnumDeviceInfo(list->dev_info, list->index, + &dev_info_data)) + { + list->index++; + + dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, + DICS_FLAG_GLOBAL, 0, DIREG_DEV, + KEY_READ); + if (dev_key == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + continue; + } + + if (list->ftdi) { + vid = 0x0403; + pid = 0x6015; + serial = 0; + } else { + vid = pid = serial = 0; + /* Fetch symbolic name for this device and parse out + * the vid/pid/serial info */ + symbolic_len = sizeof(symbolic); + result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL, + symbolic, &symbolic_len); + if (result != 0) { + altos_set_last_windows_error(); + } else { + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1, + "%04X", &vid); + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, + "%d", &serial); + } + if (vid == 0 || pid == 0 || serial == 0) { + if (SetupDiGetDeviceInstanceId(list->dev_info, + &dev_info_data, + instanceid, + sizeof (instanceid), + &instanceid_len)) { + sscanf((char *) instanceid + sizeof("USB\\VID_") - 1, + "%04X", &vid); + sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_XXXX\\") - 1, + "%d", &serial); + } else { + altos_set_last_windows_error(); + } + } + if (vid == 0 || pid == 0 || serial == 0) { + RegCloseKey(dev_key); + continue; + } + } + + /* Fetch the com port name */ + port_len = sizeof (port); + result = RegQueryValueEx(dev_key, "PortName", NULL, NULL, + port, &port_len); + RegCloseKey(dev_key); + if (result != 0) { + altos_set_last_windows_error(); + continue; + } + + /* Fetch the device description which is the device name, + * with firmware that has unique USB ids */ + friendlyname_len = sizeof (friendlyname); + if(!SetupDiGetDeviceRegistryProperty(list->dev_info, + &dev_info_data, + SPDRP_FRIENDLYNAME, + &friendlyname_type, + (BYTE *)friendlyname, + sizeof(friendlyname), + &friendlyname_len)) + { + altos_set_last_windows_error(); + continue; + } + device->vendor = vid; + device->product = pid; + device->serial = serial; + strcpy(device->name, friendlyname); + + strcpy(device->path, (char *) port); + return 1; + } + result = GetLastError(); + if (result != ERROR_NO_MORE_ITEMS) + altos_set_last_windows_error(); + return 0; +} + +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + SetupDiDestroyDeviceInfoList(list->dev_info); + free(list); +} + +static int +altos_queue_read(struct altos_file_windows *file) +{ + DWORD got; + if (file->pend_read) + return LIBALTOS_SUCCESS; + + if (!ReadFile(file->handle, file->file.in_data, USB_BUF_SIZE, &got, &file->ov_read)) { + if (GetLastError() != ERROR_IO_PENDING) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + file->pend_read = TRUE; + } else { + file->pend_read = FALSE; + file->file.in_read = 0; + file->file.in_used = got; + } + return LIBALTOS_SUCCESS; +} + +static int +altos_wait_read(struct altos_file_windows *file, int timeout) +{ + DWORD ret; + DWORD got; + + if (!file->pend_read) + return LIBALTOS_SUCCESS; + + if (!timeout) + timeout = INFINITE; + + ret = WaitForSingleObject(file->ov_read.hEvent, timeout); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) { + if (GetLastError () != ERROR_OPERATION_ABORTED) + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + file->pend_read = FALSE; + file->file.in_read = 0; + file->file.in_used = got; + break; + case WAIT_TIMEOUT: + return LIBALTOS_TIMEOUT; + break; + default: + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + return LIBALTOS_SUCCESS; +} + +int +altos_fill(struct altos_file *file_common, int timeout) +{ + struct altos_file_windows *file = (struct altos_file_windows *) file_common; + + int ret; + + if (file->file.in_read < file->file.in_used) + return LIBALTOS_SUCCESS; + + file->file.in_read = file->file.in_used = 0; + + if (file->is_winsock) { + + for (;;) { + fd_set readfds; + TIMEVAL timeval; + int thistimeout; + + /* Check to see if the socket has been closed */ + if (file->socket == INVALID_SOCKET) + return LIBALTOS_ERROR; + +#define POLL_TIMEOUT 10000 + + /* Poll to see if the socket has been closed + * as select doesn't abort when that happens + */ + if (timeout) { + thistimeout = timeout; + if (thistimeout > POLL_TIMEOUT) + thistimeout = POLL_TIMEOUT; + } else { + thistimeout = POLL_TIMEOUT; + } + + timeval.tv_sec = thistimeout / 1000; + timeval.tv_usec = (thistimeout % 1000) * 1000; + + FD_ZERO(&readfds); + FD_SET(file->socket, &readfds); + + ret = select(1, &readfds, NULL, NULL, &timeval); + + if (ret == 0) { + if (timeout) { + timeout -= thistimeout; + if (timeout == 0) + return LIBALTOS_TIMEOUT; + } + } else { + if (ret > 0) + break; + + if (ret < 0) { + altos_set_last_winsock_error(); + return LIBALTOS_ERROR; + } + } + } + + if (file->socket == INVALID_SOCKET) { + altos_set_last_winsock_error(); + return LIBALTOS_ERROR; + } + + ret = recv(file->socket, (char *) file->file.in_data, USB_BUF_SIZE, 0); + + if (ret <= 0) { + altos_set_last_winsock_error(); + return LIBALTOS_ERROR; + } + file->file.in_read = 0; + file->file.in_used = ret; + } else { + if (file->handle == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + + ret = altos_queue_read(file); + if (ret) + return ret; + ret = altos_wait_read(file, timeout); + if (ret) + return ret; + } + + return LIBALTOS_SUCCESS; +} + +PUBLIC int +altos_flush(struct altos_file *file_common) +{ + struct altos_file_windows *file = (struct altos_file_windows *) file_common; + + unsigned char *data = file->file.out_data; + int used = file->file.out_used; + + while (used) { + if (file->is_winsock) { + int put; + + put = send(file->socket, (char *) data, used, 0); + if (put <= 0) { + altos_set_last_winsock_error(); + return LIBALTOS_ERROR; + } + data += put; + used -= put; + } else { + DWORD put; + DWORD ret; + if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) { + if (GetLastError() != ERROR_IO_PENDING) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE); + switch (ret) { + case WAIT_OBJECT_0: + if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) { + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + break; + default: + altos_set_last_windows_error(); + return LIBALTOS_ERROR; + } + } + data += put; + used -= put; + } + } + file->file.out_used = 0; + return LIBALTOS_SUCCESS; +} + +static HANDLE +open_serial(char *full_name) +{ + HANDLE handle; + DCB dcb; + + handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + + if (handle == INVALID_HANDLE_VALUE) { + altos_set_last_windows_error(); + return INVALID_HANDLE_VALUE; + } + + if (!GetCommState(handle, &dcb)) { + altos_set_last_windows_error(); + CloseHandle(handle); + return INVALID_HANDLE_VALUE; + } + dcb.BaudRate = CBR_9600; + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_ENABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fTXContinueOnXoff = FALSE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fRtsControl = RTS_CONTROL_ENABLE; + dcb.fAbortOnError = FALSE; + dcb.XonLim = 10; + dcb.XoffLim = 10; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + dcb.XonChar = 17; + dcb.XoffChar = 19; +#if 0 + dcb.ErrorChar = 0; + dcb.EofChar = 0; + dcb.EvtChar = 0; +#endif + if (!SetCommState(handle, &dcb)) { + altos_set_last_windows_error(); + CloseHandle(handle); + return INVALID_HANDLE_VALUE; + } + return handle; +} + +PUBLIC struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file_windows *file = calloc (1, sizeof (struct altos_file_windows)); + char full_name[64]; + COMMTIMEOUTS timeouts; + int i; + + if (!file) + return NULL; + + strcpy(full_name, "\\\\.\\"); + strcat(full_name, device->path); + + file->handle = INVALID_HANDLE_VALUE; + + for (i = 0; i < 5; i++) { + file->handle = open_serial(full_name); + if (file->handle != INVALID_HANDLE_VALUE) + break; + altos_set_last_windows_error(); + Sleep(100); + } + + if (file->handle == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } + + /* The FTDI driver doesn't appear to work right unless you open it twice */ + if (device->vendor == 0x0403) { + CloseHandle(file->handle); + file->handle = open_serial(full_name); + if (file->handle == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } + } + + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 1 << 30; /* almost forever */ + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + SetCommTimeouts(file->handle, &timeouts); + + file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + + return &file->file; +} + +PUBLIC void +altos_close(struct altos_file *file_common) +{ + struct altos_file_windows *file = (struct altos_file_windows *) file_common; + + if (file->is_winsock) { + SOCKET socket = file->socket; + if (socket != INVALID_SOCKET) { + file->socket = INVALID_SOCKET; + closesocket(socket); + } + } else { + HANDLE handle = file->handle; + if (handle != INVALID_HANDLE_VALUE) { + HANDLE ov_read = file->ov_read.hEvent; + HANDLE ov_write = file->ov_write.hEvent; + file->handle = INVALID_HANDLE_VALUE; + file->ov_read.hEvent = INVALID_HANDLE_VALUE; + file->ov_write.hEvent = INVALID_HANDLE_VALUE; + PurgeComm(handle, PURGE_RXABORT|PURGE_RXCLEAR|PURGE_TXABORT|PURGE_TXCLEAR); + Sleep(100); + CloseHandle(handle); + file->handle = INVALID_HANDLE_VALUE; + CloseHandle(ov_read); + CloseHandle(ov_write); + } + } +} + +#include + +#define LUP_SET (LUP_RETURN_NAME| LUP_CONTAINERS | LUP_RETURN_ADDR | LUP_FLUSHCACHE |\ + LUP_RETURN_TYPE | LUP_RETURN_BLOB | LUP_RES_SERVICE) + +struct altos_bt_list { + WSADATA WSAData; + HANDLE lookup; +}; + +struct altos_bt_list * +altos_bt_list_start(int inquiry_time) +{ + struct altos_bt_list *bt_list; + WSAQUERYSET query_set; + int retCode; + + /* Windows provides no way to set the time */ + (void) inquiry_time; + bt_list = calloc(1, sizeof (struct altos_bt_list)); + if (!bt_list) { + altos_set_last_windows_error(); + return NULL; + } + + if ((retCode = WSAStartup(MAKEWORD(2,2),&bt_list->WSAData)) != 0) { + altos_set_last_winsock_error(); + free(bt_list); + return NULL; + } + + memset(&query_set, '\0', sizeof (query_set)); + query_set.dwSize = sizeof(WSAQUERYSET); + query_set.dwNameSpace = NS_BTH; + + retCode = WSALookupServiceBegin(&query_set, LUP_SET, &bt_list->lookup); + + if (retCode != 0) { + altos_set_last_winsock_error(); + free(bt_list); + return NULL; + } + return bt_list; +} + +static unsigned char get_byte(BTH_ADDR ba, int shift) +{ + return (ba >> ((5 - shift) << 3)) & 0xff; +} + +static BTH_ADDR put_byte(unsigned char c, int shift) +{ + return ((BTH_ADDR) c) << ((5 - shift) << 3); +} + +static void +ba2str(BTH_ADDR ba, char *str) +{ + + sprintf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + get_byte(ba, 0), + get_byte(ba, 1), + get_byte(ba, 2), + get_byte(ba, 3), + get_byte(ba, 4), + get_byte(ba, 5)); +} + +static BTH_ADDR +str2ba(char *str) +{ + unsigned int bytes[6]; + + sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + &bytes[0], + &bytes[1], + &bytes[2], + &bytes[3], + &bytes[4], + &bytes[5]); + return (put_byte(bytes[0], 0) | + put_byte(bytes[1], 1) | + put_byte(bytes[2], 2) | + put_byte(bytes[3], 3) | + put_byte(bytes[4], 4) | + put_byte(bytes[5], 5)); +} + +int +altos_bt_list_next(struct altos_bt_list *bt_list, + struct altos_bt_device *device) +{ + for (;;) { + BYTE buffer[4096]; + DWORD length = sizeof (buffer);; + WSAQUERYSET *results = (WSAQUERYSET *)buffer; + CSADDR_INFO *addr_info; + int retCode; + SOCKADDR_BTH *sockaddr_bth; + + memset(buffer, '\0', sizeof(buffer)); + + retCode = WSALookupServiceNext(bt_list->lookup, LUP_SET, &length, results); + + if (retCode != 0) { + int error = WSAGetLastError(); + if (error != WSAENOMORE && error != WSA_E_NO_MORE) + altos_set_last_winsock_error(); + return 0; + } + + if (results->dwNumberOfCsAddrs > 0) { + + addr_info = results->lpcsaBuffer; + + strncpy(device->name, results->lpszServiceInstanceName, sizeof(device->name)); + device->name[sizeof(device->name)-1] = '\0'; + + sockaddr_bth = (SOCKADDR_BTH *) addr_info->RemoteAddr.lpSockaddr; + + ba2str(sockaddr_bth->btAddr, device->addr); + + return 1; + } + } +} + +void +altos_bt_list_finish(struct altos_bt_list *bt_list) +{ + WSALookupServiceEnd(bt_list->lookup); + free(bt_list); +} + +void +altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device) +{ + strncpy(device->name, name, sizeof (device->name)); + device->name[sizeof(device->name)-1] = '\0'; + strncpy(device->addr, addr, sizeof (device->addr)); + device->addr[sizeof(device->addr)-1] = '\0'; +} + +struct altos_file * +altos_bt_open(struct altos_bt_device *device) +{ + struct altos_file_windows *file; + SOCKADDR_BTH sockaddr_bth; + int ret; + + file = calloc(1, sizeof (struct altos_file_windows)); + if (!file) { + return NULL; + } + + file->is_winsock = TRUE; + file->socket = WSASocket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM, NULL, 0, WSA_FLAG_OVERLAPPED); + + if (file->socket == INVALID_SOCKET) { + altos_set_last_winsock_error(); + free(file); + return NULL; + } + + memset(&sockaddr_bth, '\0', sizeof (sockaddr_bth)); + sockaddr_bth.addressFamily = AF_BTH; + sockaddr_bth.btAddr = str2ba(device->addr); + sockaddr_bth.port = 1; + + ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth)); + + if (ret != 0) { + altos_set_last_winsock_error(); + closesocket(file->socket); + free(file); + return NULL; + } + return &file->file; +} +