Initial Commit - Copy from Altus Metrum AltOS
This commit is contained in:
17
libaltos/.gitignore
vendored
Normal file
17
libaltos/.gitignore
vendored
Normal 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/
|
143
libaltos/Makefile-standalone
Normal file
143
libaltos/Makefile-standalone
Normal 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
131
libaltos/Makefile.am
Normal 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
183
libaltos/btletest.c
Normal 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
78
libaltos/cjnitest.c
Normal 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
22
libaltos/gcc
Executable 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
BIN
libaltos/libaltos.dylib
Executable file
Binary file not shown.
124
libaltos/libaltos.h
Normal file
124
libaltos/libaltos.h
Normal 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
9
libaltos/libaltos.i0
Normal 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
128
libaltos/libaltos_common.c
Normal 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
255
libaltos/libaltos_darwin.c
Normal 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
628
libaltos/libaltos_linux.c
Normal 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
210
libaltos/libaltos_posix.c
Normal 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
41
libaltos/libaltos_posix.h
Normal 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_ */
|
72
libaltos/libaltos_private.h
Normal file
72
libaltos/libaltos_private.h
Normal 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
802
libaltos/libaltos_windows.c
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user