diff options
Diffstat (limited to 'libaltos')
| -rw-r--r-- | libaltos/Makefile-standalone | 20 | ||||
| -rw-r--r-- | libaltos/Makefile.am | 29 | ||||
| -rw-r--r-- | libaltos/libaltos.c | 1535 | ||||
| -rwxr-xr-x | libaltos/libaltos.dylib | bin | 41596 -> 46228 bytes | |||
| -rw-r--r-- | libaltos/libaltos_common.c | 83 | ||||
| -rw-r--r-- | libaltos/libaltos_darwin.c | 253 | ||||
| -rw-r--r-- | libaltos/libaltos_linux.c | 528 | ||||
| -rw-r--r-- | libaltos/libaltos_posix.c | 201 | ||||
| -rw-r--r-- | libaltos/libaltos_posix.h | 40 | ||||
| -rw-r--r-- | libaltos/libaltos_private.h | 63 | ||||
| -rw-r--r-- | libaltos/libaltos_windows.c | 761 | 
11 files changed, 1963 insertions, 1550 deletions
diff --git a/libaltos/Makefile-standalone b/libaltos/Makefile-standalone index a1f9f5bc..1430aacb 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 \ @@ -29,8 +33,8 @@ ifeq ($(OS),Darwin)  #	-iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \  #	-iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers -XCODE=/Applications/Xcode.app -SDK=$(XCODE)/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk +XCODE=/Applications/Xcode-beta.app +SDK=$(XCODE)/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk  MINVERSION=10.5  OS_LIB_CFLAGS=\ @@ -39,7 +43,7 @@ OS_LIB_CFLAGS=\  	-iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \  	-iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \  	-iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers -OS_APP_CFLAGS=$(OS_LIB_CFLAGS) +OS_APP_CFLAGS=$(OS_LIB_CFLAGS) -O0 -g  OS_LDFLAGS =\  	-framework IOKit -framework CoreFoundation @@ -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 1db2d486..8f69c1ad 100644 --- a/libaltos/Makefile.am +++ b/libaltos/Makefile.am @@ -1,5 +1,5 @@  JAVAC=javac -AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) +AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) -I$(JVM_INCLUDE)/linux  AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -source 6  altoslibdir=$(libdir)/altos @@ -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) -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 97b7b3f9..00000000 --- a/libaltos/libaltos.c +++ /dev/null @@ -1,1535 +0,0 @@ -/* - * Copyright © 2010 Keith Packard <keithp@keithp.com> - * - * 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 <stdio.h> -#include <stdlib.h> -#include <string.h> - -#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 <unistd.h> - -#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 <stdio.h> -#include <stdlib.h> -#include <fcntl.h> -#include <termios.h> -#include <errno.h> - -#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 <poll.h> -#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 <ctype.h> -#include <dirent.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <bluetooth/bluetooth.h> -#include <bluetooth/hci.h> -#include <bluetooth/hci_lib.h> -#include <bluetooth/rfcomm.h> - -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 <dlfcn.h> - -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 <IOKitLib.h> -#include <IOKit/usb/USBspec.h> -#include <sys/param.h> -#include <paths.h> -#include <CFNumber.h> -#include <IOBSD.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> - -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 <stdlib.h> -#include <windows.h> -#include <setupapi.h> - -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 <stdarg.h> - -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.dylib b/libaltos/libaltos.dylib Binary files differindex 8b491c58..c18493ea 100755 --- a/libaltos/libaltos.dylib +++ b/libaltos/libaltos.dylib 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 <keithp@keithp.com> + * + * 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..04194d9a --- /dev/null +++ b/libaltos/libaltos_darwin.c @@ -0,0 +1,253 @@ +/* + * Copyright © 2016 Keith Packard <keithp@keithp.com> + * + * 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 <IOKitLib.h> +#include <IOKit/usb/USBspec.h> +#include <IOKit/serial/IOSerialKeys.h> +#include <usb/IOUSBLib.h> +#include <usb/USBSpec.h> +#include <sys/param.h> +#include <paths.h> +#include <CFNumber.h> +#include <IOBSD.h> + +/* 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 char * +get_cfstring(CFTypeRef string, char result[512]) +{ +	Boolean		got_string; + +	got_string = CFStringGetCString(string, result, 512, kCFStringEncodingASCII); +	if (!got_string) +		strcpy(result, "CFStringGetCString failed"); +	return result; +} + +static int +get_string(io_object_t object, CFStringRef entry, char *result, int result_len) +{ +	CFTypeRef entry_as_string; +	Boolean got_string; +	char entry_string[512]; + +	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; +	char entry_string[512]; + +	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; +	io_iterator_t tdIterator; +	io_object_t tdObject; +	kern_return_t ret; +	int i; + +	matching_dictionary = IOServiceMatching(kIOSerialBSDServiceValue); +	if (matching_dictionary) { +		CFDictionarySetValue(matching_dictionary, +				     CFSTR(kIOSerialBSDTypeKey), +				     CFSTR(kIOSerialBSDAllTypes)); +	} + +	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; +} + +static io_service_t get_usb_object(io_object_t serial_device) +{ +	io_iterator_t iterator; +	io_service_t usb_device; +	io_service_t service; +	IOReturn status; + +	status = IORegistryEntryCreateIterator(serial_device, +				      kIOServicePlane, +				      kIORegistryIterateParents | kIORegistryIterateRecursively, +				      &iterator); + +	if (status != kIOReturnSuccess) +		return 0; + +	while((service = IOIteratorNext(iterator))) { +		io_name_t servicename; +		status = IORegistryEntryGetNameInPlane(service, kIOServicePlane, servicename); + +		if (status == kIOReturnSuccess && IOObjectConformsTo(service, kIOUSBDeviceClassName)) { +			IOObjectRelease(iterator); +			return service; +		} +		IOObjectRelease(service); +	} +	IOObjectRelease(iterator); +	return 0; +} + +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + +	io_object_t object; +	io_service_t usb_device; +	char serial_string[128]; + +	for (;;) { +		object = IOIteratorNext(list->iterator); +		if (!object) { +			return 0; +		} + +		usb_device = get_usb_object(object); + +		if (get_number (usb_device, CFSTR(kUSBVendorID), &device->vendor) && +		    get_number (usb_device, CFSTR(kUSBProductID), &device->product) && +		    get_string (object, CFSTR(kIOCalloutDeviceKey), device->path, sizeof (device->path)) && +		    get_string (usb_device, CFSTR(kUSBProductString), device->name, sizeof (device->name)) && +		    get_string (usb_device, CFSTR(kUSBSerialNumberString), serial_string, sizeof (serial_string))) { +			device->serial = atoi(serial_string); +			IOObjectRelease(object); +			IOObjectRelease(usb_device); +			return 1; +		} +		IOObjectRelease(object); +		IOObjectRelease(usb_device); +	} +} + +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..2065d74a --- /dev/null +++ b/libaltos/libaltos_linux.c @@ -0,0 +1,528 @@ +/* + * Copyright © 2016 Keith Packard <keithp@keithp.com> + * + * 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 <ctype.h> +#include <dirent.h> +#include <bluetooth/bluetooth.h> +#include <bluetooth/hci.h> +#include <bluetooth/hci_lib.h> +#include <bluetooth/rfcomm.h> + +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 <dlfcn.h> + +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->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 <keithp@keithp.com> + * + * 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 <poll.h> +#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 <keithp@keithp.com> + * + * 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 <fcntl.h> +#include <termios.h> +#include <errno.h> +#include <unistd.h> + +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 <keithp@keithp.com> + * + * 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 <unistd.h> + +#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 <keithp@keithp.com> + * + * 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 <winsock2.h> +#include <windows.h> +#include <setupapi.h> + +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 <stdarg.h> + +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 <ws2bth.h> + +#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; +} +  | 
