Initial Commit - Copy from Altus Metrum AltOS

This commit is contained in:
2024-06-25 19:03:04 +02:00
commit 13fc49c923
2048 changed files with 1206748 additions and 0 deletions

17
libaltos/.gitignore vendored Normal file
View File

@@ -0,0 +1,17 @@
*.so
*.lo
*.la
*.java
*.class
*.dll
.libs/
classlibaltos.stamp
libaltos_wrap.c
libaltosJNI
cjnitest
cjnitest32
cjnitest64
cjnitest_*
btletest
libaltos.swig
swig_bindings/

View File

@@ -0,0 +1,143 @@
OS:=$(shell uname)
#
# Linux
#
ifeq ($(OS),Linux)
OS_SRCS=libaltos_posix.c libaltos_linux.c
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)
OS_LDFLAGS=
LIBNAME=libaltos.so
EXEEXT=
endif
#
# Darwin (Mac OS X)
#
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 \
# -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \
# -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/MacOSX11.1.sdk
MINVERSION=10.5
OS_LIB_CFLAGS=\
-DDARWIN -DPOSIX_TTY -arch x86_64 -arch arm64 -isysroot $(SDK) \
-mmacosx-version-min=10.5 \
-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) -O0 -g
OS_LDFLAGS =\
-framework IOKit -framework CoreFoundation
LIBNAME=libaltos.dylib
EXEEXT=
endif
#
# Windows
#
ifneq (,$(findstring MINGW,$(OS)))
OS_SRCS=libaltos_windows.c
CC=gcc
OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL
OS_APP_CFLAGS = -DWINDOWS -mconsole
OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \
-ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias
LIBNAME=altos.dll
EXEEXT=.exe
endif
.SUFFIXES: .java .class
CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*"
SWIG_DIR=swig_bindings/java
SWIG_FILE=$(SWIG_DIR)/libaltos.swig
SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c
JNI_DIR=libaltosJNI
JNI_FILE=$(JNI_DIR)/libaltosJNI.java
JNI_SRCS=$(JNI_FILE) \
$(JNI_DIR)/SWIGTYPE_p_altos_file.java \
$(JNI_DIR)/SWIGTYPE_p_altos_list.java \
$(JNI_DIR)/altos_device.java \
$(JNI_DIR)/libaltos.java
JAVAFILES=\
$(JNI_SRCS)
CLASSFILES = $(JAVAFILES:%.java=%.class)
JAVAFLAGS=-Xlint:unchecked
CJNITEST=cjnitest$(EXEEXT)
all: $(LIBNAME) $(CJNITEST) $(CLASSFILES)
.java.class:
javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java
CFLAGS=$(OS_LIB_CFLAGS) -O -I.
LDFLAGS=$(OS_LDFLAGS)
HEADERS=libaltos.h
SRCS = libaltos_common.c $(OS_SRCS) $(SWIG_WRAP)
OBJS = $(SRCS:%.c=%.o)
LIBS = $(DARWIN_LIBS)
$(CJNITEST): cjnitest.c $(LIBNAME)
$(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS)
$(LIBNAME): $(OBJS)
$(CC) -shared -fPIC $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
clean:
rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o
rm -rf swig_bindings libaltosJNI
distclean: clean
$(JNI_FILE): libaltos.i0 $(HEADERS)
mkdir -p $(SWIG_DIR)
mkdir -p libaltosJNI
sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE)
swig -java -package libaltosJNI $(SWIG_FILE)
cp swig_bindings/java/*.java libaltosJNI
$(SWIG_WRAP): $(JNI_FILE)
ifeq ($(OS),Linux)
install: $(LIBNAME)
install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME)
endif
.NOTPARALLEL:

131
libaltos/Makefile.am Normal file
View File

@@ -0,0 +1,131 @@
export PATH=$(shell echo "$$PWD:$$PATH")
AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE) -I$(JVM_INCLUDE)/linux
AM_JAVACFLAGS=$(JAVAC_VERSION_FLAGS) -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked
altoslibdir=$(libdir)/altos
altoslib_LTLIBRARIES=libaltos.la
libaltos_la_LDFLAGS=-version-info 1:0:1 -Wl,-znoexecstack
libaltos_la_SOURCES=\
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 btletest
cjnitest_SOURCES=cjnitest.c
cjnitest_LDADD=libaltos.la
btletest_SOURCES=btletest.c
btletest_LDADD=-lbluetooth
if MULTI_ARCH
altoslib_LTLIBRARIES+=libaltos_i686.la libaltos_amd64.la libaltos_aarch64.la libaltos_armel.la libaltos_armhf.la
I686=i686-linux-gnu
libaltos_i686_la_LDFLAGS=-Wl,arch=$(I686) $(libaltos_la_LDFLAGS)
libaltos_i686_la_CFLAGS=-Warch=i686-linux-gnu $(AM_CFLAGS)
libaltos_i686_la_SOURCES=$(libaltos_la_SOURCES)
AMD64=x86_64-linux-gnu
libaltos_amd64_la_LDFLAGS=-Wl,arch=$(AMD64) $(libaltos_la_LDFLAGS)
libaltos_amd64_la_CFLAGS=-Warch=x86_64-linux-gnu $(AM_CFLAGS)
libaltos_amd64_la_SOURCES=$(libaltos_la_SOURCES)
AARCH64=aarch64-linux-gnu
libaltos_aarch64_la_LDFLAGS=-Wl,arch=$(AARCH64) $(libaltos_la_LDFLAGS)
libaltos_aarch64_la_CFLAGS=-Warch=$(AARCH64) $(AM_CFLAGS)
libaltos_aarch64_la_SOURCES=$(libaltos_la_SOURCES)
ARMEL=arm-linux-gnueabi
libaltos_armel_la_LDFLAGS=-Wl,arch=$(ARMEL) $(libaltos_la_LDFLAGS)
libaltos_armel_la_CFLAGS=-Warch=$(ARMEL) $(AM_CFLAGS)
libaltos_armel_la_SOURCES=$(libaltos_la_SOURCES)
ARMHF=arm-linux-gnueabihf
libaltos_armhf_la_LDFLAGS=-Wl,arch=$(ARMHF) $(libaltos_la_LDFLAGS)
libaltos_armhf_la_CFLAGS=-Warch=$(ARMHF) $(AM_CFLAGS)
libaltos_armhf_la_SOURCES=$(libaltos_la_SOURCES)
noinst_PROGRAMS+=cjnitest_i686 cjnitest_amd64 cjnitest_aarch64 cjnitest_armel cjnitest_armhf
cjnitest_i686_CFLAGS=$(libaltos_i686_la_CFLAGS)
cjnitest_i686_LDFLAGS=-Wl,arch=$(I686)
cjnitest_i686_SOURCES=$(cjnitest_SOURCES)
cjnitest_i686_LDADD=libaltos_i686.la
cjnitest_amd64_CFLAGS=$(libaltos_amd64_la_CFLAGS)
cjnitest_amd64_LDFLAGS=-Wl,arch=$(AMD64)
cjnitest_amd64_SOURCES=$(cjnitest_SOURCES)
cjnitest_amd64_LDADD=libaltos_amd64.la
cjnitest_aarch64_CFLAGS=$(libaltos_aarch64_la_CFLAGS)
cjnitest_aarch64_LDFLAGS=-Wl,arch=$(AARCH64)
cjnitest_aarch64_SOURCES=$(cjnitest_SOURCES)
cjnitest_aarch64_LDADD=libaltos_aarch64.la
cjnitest_armel_CFLAGS=$(libaltos_armel_la_CFLAGS)
cjnitest_armel_LDFLAGS=-Wl,arch=$(ARMEL)
cjnitest_armel_SOURCES=$(cjnitest_SOURCES)
cjnitest_armel_LDADD=libaltos_armel.la
cjnitest_armhf_CFLAGS=$(libaltos_armhf_la_CFLAGS)
cjnitest_armhf_LDFLAGS=-Wl,arch=$(ARMHF)
cjnitest_armhf_SOURCES=$(cjnitest_SOURCES)
cjnitest_armhf_LDADD=libaltos_armhf.la
endif
LIBS=-ldl
HFILES=libaltos.h
SWIG_FILE=libaltos.swig
CLASSDIR=libaltosJNI
$(SWIG_FILE): libaltos.i0 $(HFILES)
sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE)
all-local: classlibaltos.stamp
test:
which gcc
libaltos_wrap.c: classlibaltos.stamp
classlibaltos.stamp: $(SWIG_FILE)
swig -java -package libaltosJNI $(SWIG_FILE)
mkdir -p libaltosJNI
$(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \
touch classlibaltos.stamp
MINGCC32=i686-w64-mingw32-gcc
MINGCC64=x86_64-w64-mingw32-gcc
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: $(WINDOWS_SRC) $(WINDOWS_H)
SOURCE_DATE_EPOCH=0 $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(WINDOWS_SRC) $(MINGLIBS)
altos64.dll: $(WINDOWS_SRC) $(WINDOWS_H)
SOURCE_DATE_EPOCH=0 $(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

183
libaltos/btletest.c Normal file
View File

@@ -0,0 +1,183 @@
/*
* Copyright © 2017 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, either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <dirent.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
#include <bluetooth/l2cap.h>
#include <poll.h>
#define ATT_OP_MTU_REQ 0x02
#define ATT_OP_MTU_RESP 0x03
#define ATT_OP_WRITE_CMD 0x52
#define ATT_OP_HANDLE_NOTIFY 0x1b
#define CID_ATT 0x0004
#define TX_ENDPOINT 0x003a
#define RX_ENDPOINT 0x0037
#define RX_NOTIFY 0x0038
int
main(int argc, char **argv)
{
int sk;
int psm;
struct sockaddr_l2 src_addr = { 0 };
struct sockaddr_l2 dst_addr = { 0 };
char buf[1024];
struct pollfd fd[2];
int n, i;
char *btaddr;
int mtu;
btaddr = argc > 1 ? argv[1] : "D8:80:39:F3:4E:A5";
sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if (sk < 0) {
perror("socket");
exit(1);
}
src_addr.l2_family = AF_BLUETOOTH;
/* Leave src_addr.l2_bdaddr all zeros */
src_addr.l2_cid = htobs(CID_ATT);
src_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
if (bind(sk, (struct sockaddr *) &src_addr, sizeof (src_addr)) < 0) {
perror("bind");
exit(1);
}
dst_addr.l2_family = AF_BLUETOOTH;
str2ba(btaddr, &dst_addr.l2_bdaddr);
dst_addr.l2_cid = htobs(CID_ATT);
dst_addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
if (connect(sk, (struct sockaddr *) &dst_addr, sizeof(dst_addr)) < 0) {
perror("connect");
exit(1);
}
buf[0] = ATT_OP_MTU_REQ;
buf[1] = sizeof(buf) & 0xff;
buf[2] = sizeof(buf) >> 8;
n = write(sk, buf, 3);
if (n != 3) {
perror("write mtu\n");
exit(1);
}
fd[0].fd = sk;
fd[0].events = POLLIN;
for (;;) {
n = poll(fd, 1, 3000);
if (n <= 0) {
printf("timeout waiting for MTU response\n");
exit(1);
}
if (fd[0].revents & POLLIN) {
n = read(sk, buf, sizeof(buf));
printf("read %d\n", n);
for (i = 0; i < n; i++)
printf("%02x\n", buf[i]);
if (buf[0] == ATT_OP_MTU_RESP) {
mtu = (buf[1] & 0xff) | ((buf[2] & 0xff) << 8);
break;
}
}
}
printf("mtu %d\n", mtu);
buf[0] = ATT_OP_WRITE_CMD;
buf[1] = RX_NOTIFY & 0xff;
buf[2] = RX_NOTIFY >> 8;
buf[3] = 1;
n = write(sk, buf, 4);
if (n != 4) {
perror("write notify");
exit(1);
}
fd[0].fd = 0;
fd[0].events = POLLIN;
fd[1].fd = sk;
fd[1].events = POLLIN;
for (;;) {
n = poll(fd, 2, -1);
if (n == 0)
continue;
if (fd[0].revents & POLLIN) {
char *b;
n = read(0, buf+3, sizeof(buf)-3);
if (n < 0) {
perror("read stdin");
exit(1);
}
if (n == 0)
break;
b = buf;
while (n > 0) {
int this = n;
if (this > mtu - 3)
this = mtu - 3;
b[0] = ATT_OP_WRITE_CMD;
b[1] = TX_ENDPOINT;
b[2] = TX_ENDPOINT >> 8;
if (write(sk, b, this + 3) != this + 3) {
perror("write sk");
exit(1);
}
b += this;
n -= this;
}
}
if (fd[1].revents & POLLIN) {
uint16_t ch;
n = read(sk, buf, sizeof(buf));
if (n < 0) {
perror("read sk");
exit(1);
}
if (n == 0)
continue;
ch = buf[1] | (buf[2] << 8);
switch (buf[0]) {
case ATT_OP_HANDLE_NOTIFY:
if (ch == RX_ENDPOINT)
write(1, buf+3, n-3);
break;
}
}
if (fd[1].revents & (POLLERR|POLLHUP))
break;
}
close(sk);
return 0;
}

78
libaltos/cjnitest.c Normal file
View File

@@ -0,0 +1,78 @@
#include <stdio.h>
#include "libaltos.h"
#include <string.h>
#define HAS_BLUETOOTH 1
#define HAS_USB 1
static void
altos_puts(struct altos_file *file, char *string)
{
char c;
while ((c = *string++))
altos_putchar(file, c);
}
int
main (int argc, char **argv)
{
struct altos_device device;
struct altos_list *list;
struct altos_bt_device bt_device;
struct altos_bt_list *bt_list;
altos_init();
#if HAS_USB
list = altos_list_start();
while (altos_list_next(list, &device)) {
struct altos_file *file;
int c;
printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product,
device.name, device.serial, device.path);
file = altos_open(&device);
if (!file) {
printf("altos_open failed\n");
continue;
}
altos_puts(file,"v\nc s\n");
altos_flush(file);
while ((c = altos_getchar(file, 100)) >= 0) {
putchar (c);
}
if (c != LIBALTOS_TIMEOUT)
printf ("getchar returns %d\n", c);
altos_close(file);
}
altos_list_finish(list);
#endif
#if HAS_BLUETOOTH
bt_list = altos_bt_list_start(8);
while (altos_bt_list_next(bt_list, &bt_device)) {
printf ("%s %s\n", bt_device.name, bt_device.addr);
if (strncmp(bt_device.name, "TeleBT", 6) == 0) {
struct altos_file *file;
int c;
file = altos_bt_open(&bt_device);
if (!file) {
printf("altos_bt_open failed\n");
continue;
}
altos_puts(file,"v\nc s\n");
altos_flush(file);
while ((c = altos_getchar(file, 100)) >= 0) {
putchar(c);
}
if (c != LIBALTOS_TIMEOUT)
printf("getchar returns %d\n", c);
altos_close(file);
}
}
altos_bt_list_finish(bt_list);
#endif
altos_fini();
return 0;
}

22
libaltos/gcc Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
NEW_PATH=`echo $PATH | sed 's/^[^:]*://'`
PATH=$NEW_PATH
CC=gcc
args=()
for i in "$@"; do
case "$i" in
-Wl,arch=*)
arch=`echo "$i" | sed -e 's/^-Wl,arch=//'`
CC="$arch"-gcc
;;
-Warch=*)
arch=`echo "$i" | sed -e 's/^-Warch=//'`
CC="$arch"-gcc
;;
*)
args+=( "$i" )
;;
esac
done
echo " " "$CC" "${args[@]}"
exec "$CC" "${args[@]}"

BIN
libaltos/libaltos.dylib Executable file

Binary file not shown.

124
libaltos/libaltos.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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_H_
#define _LIBALTOS_H_
#include <stdlib.h>
#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# ifndef BUILD_STATIC
# ifdef BUILD_DLL
# define PUBLIC __declspec(dllexport)
# else
# define PUBLIC __declspec(dllimport)
# endif
# endif /* BUILD_STATIC */
#endif
#ifndef PUBLIC
# define PUBLIC
#endif
struct altos_device {
//%immutable;
int vendor;
int product;
int serial;
char name[256];
char path[256];
int (*method_1)(int x, int y);
//%mutable;
};
struct altos_bt_device {
//%immutable;
char name[256];
char addr[20];
//%mutable;
};
struct altos_error {
int code;
char string[1024];
};
#define LIBALTOS_SUCCESS 0
#define LIBALTOS_ERROR -1
#define LIBALTOS_TIMEOUT -2
/* Returns 0 for success, < 0 on error */
PUBLIC int
altos_init(void);
PUBLIC void
altos_fini(void);
PUBLIC void
altos_get_last_error(struct altos_error *error);
PUBLIC struct altos_list *
altos_list_start(void);
PUBLIC struct altos_list *
altos_ftdi_list_start(void);
/* Returns 1 for success, zero on end of list */
PUBLIC int
altos_list_next(struct altos_list *list, struct altos_device *device);
PUBLIC void
altos_list_finish(struct altos_list *list);
PUBLIC struct altos_file *
altos_open(struct altos_device *device);
PUBLIC void
altos_close(struct altos_file *file);
PUBLIC void
altos_free(struct altos_file *file);
/* Returns < 0 for error */
PUBLIC int
altos_putchar(struct altos_file *file, char c);
/* Returns < 0 for error */
PUBLIC int
altos_flush(struct altos_file *file);
/* Returns < 0 for error or timeout. timeout of 0 == wait forever */
PUBLIC int
altos_getchar(struct altos_file *file, int timeout);
PUBLIC struct altos_bt_list *
altos_bt_list_start(int inquiry_time);
PUBLIC int
altos_bt_list_next(struct altos_bt_list *list, struct altos_bt_device *device);
PUBLIC void
altos_bt_list_finish(struct altos_bt_list *list);
PUBLIC void
altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device);
PUBLIC struct altos_file *
altos_bt_open(struct altos_bt_device *device);
#endif /* _LIBALTOS_H_ */

9
libaltos/libaltos.i0 Normal file
View File

@@ -0,0 +1,9 @@
%module libaltos
%{
#include "libaltos.h"
%}
%extend altos_device {
int method_1(int x, int y) {
return ($self->method_1)(x, y);
}
}

128
libaltos/libaltos_common.c Normal file
View File

@@ -0,0 +1,128 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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;
file->busy = 1;
while (file->in_read == file->in_used) {
ret = altos_fill(file, timeout);
if (ret)
goto done;
}
ret = file->in_data[file->in_read++];
done:
file->busy = 0;
return ret;
}
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;
}
struct bt_vendor_map {
const char vendor[10];
int port;
};
static const struct bt_vendor_map altos_bt_vendor_map[] = {
{ .vendor = "00:12:6f:", 1 }, /* Rayson */
{ .vendor = "8c:de:52:", 6 }, /* ISSC */
{ .vendor = "d8:80:39:", 6 }, /* Microchip */
{ .vendor = "04:91:62:", 6 }, /* New Microchip */
};
#define NUM_BT_VENDOR_MAP (sizeof altos_bt_vendor_map / sizeof altos_bt_vendor_map[0])
static inline int
ao_tolower(int c) {
if ('A' <= c && c <= 'Z')
return c + 'a' - 'A';
return c;
}
int altos_bt_port(struct altos_bt_device *device) {
unsigned i, j;
for (i = 0; i < NUM_BT_VENDOR_MAP; i++) {
const char *vendor = altos_bt_vendor_map[i].vendor;
for (j = 0; ; j++) {
if (vendor[j] == '\0')
return altos_bt_vendor_map[i].port;
if (device->addr[j] == '\0')
break;
if (ao_tolower(device->addr[j]) != vendor[j])
break;
}
}
return 0;
}
PUBLIC void
altos_free(struct altos_file *file)
{
int i;
altos_close(file);
for (i = 0; i < 10 && file->busy; i++)
altos_pause_one_second();
free(file);
}

255
libaltos/libaltos_darwin.c Normal file
View File

@@ -0,0 +1,255 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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(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;
}

628
libaltos/libaltos_linux.c Normal file
View File

@@ -0,0 +1,628 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.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
bt_func(sdp_connect, sdp_session_t *, 0, (const bdaddr_t *src, const bdaddr_t *dst, uint32_t flags), (src, dst, flags))
#define sdp_connect altos_sdp_connect
bt_func(sdp_uuid16_create, uuid_t *, 0, (uuid_t *uuid, uint16_t data), (uuid, data))
#define sdp_uuid16_create altos_sdp_uuid16_create
bt_func(sdp_list_append, sdp_list_t *, 0, (sdp_list_t *list, void *d), (list, d))
#define sdp_list_append altos_sdp_list_append
bt_func(sdp_service_search_attr_req, int, -1, (sdp_session_t *session, const sdp_list_t *search, sdp_attrreq_type_t reqtype, const sdp_list_t *attrid_list, sdp_list_t **rsp_list), (session, search, reqtype, attrid_list, rsp_list))
#define sdp_service_search_attr_req altos_sdp_service_search_attr_req
bt_func(sdp_uuid_to_proto, int, 0, (uuid_t *uuid), (uuid))
#define sdp_uuid_to_proto altos_sdp_uuid_to_proto
bt_func(sdp_get_access_protos, int, 0, (const sdp_record_t *rec, sdp_list_t **protos), (rec, protos))
#define sdp_get_access_protos altos_sdp_get_access_protos
bt_func(sdp_get_proto_port, int, 0, (const sdp_list_t *list, int proto), (list, proto))
#define sdp_get_proto_port altos_sdp_get_proto_port
bt_func(sdp_close, int, 0, (sdp_session_t *session), (session))
#define sdp_close altos_sdp_close
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;
sdp_session_t *session = NULL;
int channel = 0;
if (str2ba(device->addr, &addr.rc_bdaddr) < 0) {
altos_set_last_posix_error();
goto no_file;
}
/* Try the built-in vendor list */
channel = altos_bt_port(device);
/* Not present, try to discover an RFCOMM service */
if (channel == 0) {
/*
* Search for the RFCOMM service to get the right channel
*/
session = sdp_connect(BDADDR_ANY, &addr.rc_bdaddr, SDP_RETRY_IF_BUSY);
if (session) {
static const uint8_t svc_uuid_int[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0x11, 0x01
};
int err;
uuid_t svc_uuid;
uint32_t range;
sdp_list_t *search_list, *attrid_list;
sdp_list_t *response_list = NULL, *r;
sdp_uuid16_create(&svc_uuid, PUBLIC_BROWSE_GROUP);
search_list = sdp_list_append(NULL, &svc_uuid);
range = 0x0000ffff;
attrid_list = sdp_list_append(NULL, &range);
err = sdp_service_search_attr_req(session, search_list,
SDP_ATTR_REQ_RANGE, attrid_list, &response_list);
if (err >= 0) {
for (r = response_list; r; r = r->next) {
sdp_record_t *rec = (sdp_record_t*) r->data;
sdp_list_t *proto_list;
sdp_list_t *access = NULL;
int proto;
proto = sdp_uuid_to_proto(&rec->svclass);
if (proto == SERIAL_PORT_SVCLASS_ID) {
sdp_get_access_protos(rec, &access);
if (access) {
int this_chan = sdp_get_proto_port(access, RFCOMM_UUID);
if (this_chan) {
printf("found service on channel %d\n", this_chan);
channel = this_chan;
}
}
}
}
}
/* Leave the session open so we don't disconnect from the device before opening
* the RFCOMM channel
*/
}
}
/* Still nothing, try the default */
if (channel == 0)
channel = BT_PORT_DEFAULT;
/* Connect to the channel */
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 = channel;
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;
}
if (session)
sdp_close(session);
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:
if (session)
sdp_close(session);
return NULL;
}

210
libaltos/libaltos_posix.c Normal file
View File

@@ -0,0 +1,210 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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;
}
#include <time.h>
void
altos_pause_one_second(void)
{
struct timespec delay = { .tv_sec = 1, .tv_nsec = 0 };
nanosleep(&delay, NULL);
}

41
libaltos/libaltos_posix.h Normal file
View File

@@ -0,0 +1,41 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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_ */

View File

@@ -0,0 +1,72 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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 BT_PORT_DEFAULT 6
#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;
int busy;
};
#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);
int
altos_bt_port(struct altos_bt_device *device);
void
altos_pause_one_second(void);
#endif /* _LIBALTOS_PRIVATE_H_ */

802
libaltos/libaltos_windows.c Normal file
View File

@@ -0,0 +1,802 @@
/*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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);
__ms_sprintf (buffer, "%4d-%02d-%02d %2d:%02d:%02d. ",
time.wYear, time.wMonth, time.wDay,
time.wHour, time.wMinute, time.wSecond);
va_start(a, fmt);
__ms_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;
}
static struct {
unsigned int vid, pid;
char *name;
} name_map[] = {
{ .vid = 0xfffe, .pid = 0x000d, .name = "EasyTimer" },
{ .vid = 0xfffe, .pid = 0x0028, .name = "EasyMega" },
{ .vid = 0xfffe, .pid = 0x002c, .name = "EasyMotor" },
{ .name = NULL },
};
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;
int i;
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 {
__ms_sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
"%04X", &vid);
__ms_sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
"%04X", &pid);
__ms_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)) {
__ms_sscanf((char *) instanceid + sizeof("USB\\VID_") - 1,
"%04X", &vid);
__ms_sscanf((char *) instanceid + sizeof("USB\\VID_XXXX&PID_") - 1,
"%04X", &pid);
__ms_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;
}
char *space = friendlyname;
while (*space) {
if (*space == ' ') {
*space = '\0';
break;
}
space++;
}
for (i = 0; name_map[i].name; i++) {
if (name_map[i].vid == vid && name_map[i].pid == pid) {
strcpy(friendlyname, name_map[i].name);
break;
}
}
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)
{
__ms_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];
__ms_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;
int channel = 0;
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);
channel = altos_bt_port(device);
if (channel == 0)
channel = BT_PORT_DEFAULT;
sockaddr_bth.port = channel;
ret = connect(file->socket, (SOCKADDR *) &sockaddr_bth, sizeof (sockaddr_bth));
if (ret != 0) {
altos_set_last_winsock_error();
closesocket(file->socket);
free(file);
log_message("Connection attempted to address %s port %d\n", device->addr, sockaddr_bth.port);
return NULL;
}
return &file->file;
}
void
altos_pause_one_second(void)
{
Sleep(1000);
}