From c726d8f6eb861801d7543552beab6ee2c920c96f Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Jul 2010 15:41:39 -0700 Subject: [PATCH 1/1] Add libaltos which talks to USB connected altos devices --- ao-tools/libaltos/Makefile | 61 +++++ ao-tools/libaltos/cjnitest.c | 25 ++ ao-tools/libaltos/libaltos.c | 479 ++++++++++++++++++++++++++++++++++ ao-tools/libaltos/libaltos.h | 54 ++++ ao-tools/libaltos/libaltos.i0 | 5 + 5 files changed, 624 insertions(+) create mode 100644 ao-tools/libaltos/Makefile create mode 100644 ao-tools/libaltos/cjnitest.c create mode 100644 ao-tools/libaltos/libaltos.c create mode 100644 ao-tools/libaltos/libaltos.h create mode 100644 ao-tools/libaltos/libaltos.i0 diff --git a/ao-tools/libaltos/Makefile b/ao-tools/libaltos/Makefile new file mode 100644 index 00000000..5ffbdadf --- /dev/null +++ b/ao-tools/libaltos/Makefile @@ -0,0 +1,61 @@ +.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 + +LIBEXT=dylib + +JAVAFILES=\ + $(JNI_SRCS) + +CLASSFILES = $(JAVAFILES:%.java=%.class) + +JAVAFLAGS=-Xlint:unchecked + +all: libaltos.$(LIBEXT) cjnitest $(CLASSFILES) + +.java.class: + javac -cp "$(CLASSPATH)" $(JAVAFLAGS) $*.java + +DARWIN_CFLAGS=\ + -I/System/Library/Frameworks/JavaVM.framework/Headers \ + -I/System/Library/Frameworks/IOKit.framework/Headers \ + -I/System/Library/Frameworks/CoreFoundation.framework/Headers +DARWIN_LIBS=\ + -framework IOKit -framework CoreFoundation + +CFLAGS = $(DARWIN_CFLAGS) -I. -O0 -g + +HEADERS=libaltos.h +SRCS = libaltos.c $(SWIG_WRAP) +OBJS = $(SRCS:%.c=%.o) +LIBS = $(DARWIN_LIBS) + +cjnitest: cjnitest.o $(OBJS) + cc -o $@ $(CFLAGS) cjnitest.o $(OBJS) $(LIBS) + +libaltos.$(LIBEXT): $(OBJS) + gcc -shared -o $@ $(OBJS) $(LIBS) + +clean: + rm -f $(CLASSFILES) $(OBJS) libaltos.$(LIBEXT) cjnitest + +$(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) diff --git a/ao-tools/libaltos/cjnitest.c b/ao-tools/libaltos/cjnitest.c new file mode 100644 index 00000000..cd3898ed --- /dev/null +++ b/ao-tools/libaltos/cjnitest.c @@ -0,0 +1,25 @@ +#include +#include "libaltos.h" + +main () +{ + struct altos_device device; + struct altos_list *list; + + altos_init(); + list = altos_list_start(); + while (altos_list_next(list, &device)) { + struct altos_file *file; + int c; + + file = altos_open(&device); + altos_putchar(file, '?'); altos_putchar(file, '\n'); altos_flush(file); + while ((c = altos_getchar(file, 100)) >= 0) { + putchar (c); + } + printf ("getchar returns %d\n", c); + altos_close(file); + } + altos_list_finish(list); + altos_fini(); +} diff --git a/ao-tools/libaltos/libaltos.c b/ao-tools/libaltos/libaltos.c new file mode 100644 index 00000000..417c8fe5 --- /dev/null +++ b/ao-tools/libaltos/libaltos.c @@ -0,0 +1,479 @@ +/* + * Copyright © 2010 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "libaltos.h" + +#define USE_DARWIN + +#ifdef USE_DARWIN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct altos_list { + io_iterator_t iterator; +}; + +static int +get_string(io_object_t object, CFStringRef entry, char *result, int result_len) +{ + CFTypeRef entry_as_string; + Boolean got_string; + + entry_as_string = IORegistryEntrySearchCFProperty (object, + kIOServicePlane, + entry, + kCFAllocatorDefault, + kIORegistryIterateRecursively); + if (entry_as_string) { + got_string = CFStringGetCString(entry_as_string, + result, result_len, + kCFStringEncodingASCII); + + CFRelease(entry_as_string); + if (got_string) + return 1; + } + return 0; +} + +int +altos_init(void) +{ + return 1; +} + +void +altos_fini(void) +{ +} + +struct altos_list * +altos_list_start(void) +{ + struct altos_list *list = calloc (sizeof (struct altos_list), 1); + CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice"); + UInt32 vendor = 0xfffe, product = 0x000a; + CFNumberRef vendor_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &vendor); + CFNumberRef product_ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &product); + io_iterator_t tdIterator; + io_object_t tdObject; + + CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBVendorID), vendor_ref); + CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBProductID), product_ref); + + IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator); + + CFRelease(vendor_ref); + CFRelease(product_ref); + return list; +} + +int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + io_object_t object; + char serial_string[128]; + + for (;;) { + object = IOIteratorNext(list->iterator); + if (!object) + return 0; + + if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) && + get_string (object, CFSTR("USB Product Name"), device->product, sizeof (device->product)) && + get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) { + device->serial = atoi(serial_string); + return 1; + } + } +} + +void +altos_list_finish(struct altos_list *list) +{ + IOObjectRelease (list->iterator); + free(list); +} + + +#define USB_BUF_SIZE 64 + +struct altos_file { + int fd; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +void +altos_test(char *path) +{ + int n; + char buf[16]; + int fd; + struct termios term; + + fd = open(path, O_RDWR | O_NOCTTY); + if (fd < 0) { + perror(path); + return; + } + if (ioctl(fd, TIOCEXCL, (char *) 0) < 0) { + perror("TIOCEXCL"); + close (fd); + return; + } + + n = tcgetattr(fd, &term); + if (n < 0) { + perror("tcgetattr"); + close(fd); + return; + } + cfmakeraw(&term); + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; + n = tcsetattr(fd, TCSAFLUSH, &term); + if (n < 0) { + perror("tcsetattr"); + close(fd); + return; + } + write(fd, "\n?\n", 3); + for (;;) { + n = read(fd, buf, sizeof (buf)); + if (n < 0) { + perror("read"); + break; + } + if (n == 0) + break; + write(1, buf, n); + } + close(fd); +} + +struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file = calloc (sizeof (struct altos_file), 1); + int ret; + struct termios term; + + if (!file) + return NULL; + + file->fd = open(device->path, O_RDWR | O_NOCTTY); + if (file->fd < 0) { + perror(device->path); + free(file); + return NULL; + } + ret = tcgetattr(file->fd, &term); + if (ret < 0) { + perror("tcgetattr"); + close(file->fd); + free(file); + return NULL; + } + cfmakeraw(&term); + term.c_cc[VMIN] = 0; + term.c_cc[VTIME] = 1; + ret = tcsetattr(file->fd, TCSAFLUSH, &term); + if (ret < 0) { + perror("tcsetattr"); + close(file->fd); + free(file); + return NULL; + } + return file; +} + +void +altos_close(struct altos_file *file) +{ + close(file->fd); + free(file); +} + +int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == USB_BUF_SIZE) { + ret = altos_flush(file); + if (ret) + return ret; + } + file->out_data[file->out_used++] = c; + if (file->out_used == USB_BUF_SIZE) + return altos_flush(file); + return 0; +} + +int +altos_flush(struct altos_file *file) +{ + while (file->out_used) { + int ret; + + ret = write (file->fd, file->out_data, file->out_used); + if (ret < 0) + return -errno; + if (ret) { + memmove(file->out_data, file->out_data + ret, + file->out_used - ret); + file->out_used -= ret; + } + } +} + +int +altos_getchar(struct altos_file *file, int timeout) +{ + while (file->in_read == file->in_used) { + int ret; + + altos_flush(file); + ret = read(file->fd, file->in_data, USB_BUF_SIZE); + if (ret < 0) + return -errno; + file->in_read = 0; + file->in_used = ret; + } + return file->in_data[file->in_read++]; +} + +#endif /* USE_DARWIN */ + +#ifdef USE_LIBUSB +#include +#include +#include +#include + +libusb_context *usb_context; + +int altos_init(void) +{ + int ret; + ret = libusb_init(&usb_context); + if (ret) + return ret; + libusb_set_debug(usb_context, 3); + return 0; +} + +void altos_fini(void) +{ + libusb_exit(usb_context); + usb_context = NULL; +} + +static libusb_device **list; +static ssize_t num, current; + +int altos_list_start(void) +{ + if (list) + altos_list_finish(); + current = 0; + num = libusb_get_device_list(usb_context, &list); + if (num == 0) { + current = num = 0; + list = NULL; + return 0; + } + return 1; +} + +int altos_list_next(struct altos_device *device) +{ + while (current < num) { + struct libusb_device_descriptor descriptor; + libusb_device *usb_device = list[current++]; + + if (libusb_get_device_descriptor(usb_device, &descriptor) == 0) { + if (descriptor.idVendor == 0xfffe) + { + libusb_device_handle *handle; + if (libusb_open(usb_device, &handle) == 0) { + char serial_number[256]; + libusb_get_string_descriptor_ascii(handle, descriptor.iProduct, + device->product, + sizeof(device->product)); + libusb_get_string_descriptor_ascii(handle, descriptor.iSerialNumber, + serial_number, + sizeof (serial_number)); + libusb_close(handle); + device->serial = atoi(serial_number); + device->device = usb_device; + return 1; + } + } + } + } + return 0; +} + +void altos_list_finish(void) +{ + if (list) { + libusb_free_device_list(list, 1); + list = NULL; + } +} + +#define USB_BUF_SIZE 64 + +struct altos_file { + struct libusb_device *device; + struct libusb_device_handle *handle; + int out_ep; + int out_size; + int in_ep; + int in_size; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; + +struct altos_file * +altos_open(struct altos_device *device) +{ + struct altos_file *file; + struct libusb_device_handle *handle; + if (libusb_open(device->device, &handle) == 0) { + int ret; + + ret = libusb_claim_interface(handle, 1); +#if 0 + if (ret) { + libusb_close(handle); + return NULL; + } +#endif + ret = libusb_detach_kernel_driver(handle, 1); +#if 0 + if (ret) { + libusb_close(handle); + return NULL; + } +#endif + + file = calloc(sizeof (struct altos_file), 1); + file->device = libusb_ref_device(device->device); + file->handle = handle; + /* XXX should get these from the endpoint descriptors */ + file->out_ep = 4 | LIBUSB_ENDPOINT_OUT; + file->out_size = 64; + file->in_ep = 5 | LIBUSB_ENDPOINT_IN; + file->in_size = 64; + + return file; + } + return NULL; +} + +void +altos_close(struct altos_file *file) +{ + libusb_close(file->handle); + libusb_unref_device(file->device); + file->handle = NULL; + free(file); +} + +int +altos_putchar(struct altos_file *file, char c) +{ + int ret; + + if (file->out_used == file->out_size) { + ret = altos_flush(file); + if (ret) + return ret; + } + file->out_data[file->out_used++] = c; + if (file->out_used == file->out_size) + return altos_flush(file); + return 0; +} + +int +altos_flush(struct altos_file *file) +{ + while (file->out_used) { + int transferred; + int ret; + + ret = libusb_bulk_transfer(file->handle, + file->out_ep, + file->out_data, + file->out_used, + &transferred, + 0); + if (ret) + return ret; + if (transferred) { + memmove(file->out_data, file->out_data + transferred, + file->out_used - transferred); + file->out_used -= transferred; + } + } +} + +int +altos_getchar(struct altos_file *file, int timeout) +{ + while (file->in_read == file->in_used) { + int ret; + int transferred; + + altos_flush(file); + ret = libusb_bulk_transfer(file->handle, + file->in_ep, + file->in_data, + file->in_size, + &transferred, + (unsigned int) timeout); + if (ret) + return ret; + file->in_read = 0; + file->in_used = transferred; + } + return file->in_data[file->in_read++]; +} + +#endif /* USE_LIBUSB */ diff --git a/ao-tools/libaltos/libaltos.h b/ao-tools/libaltos/libaltos.h new file mode 100644 index 00000000..782f244e --- /dev/null +++ b/ao-tools/libaltos/libaltos.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2010 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#ifndef _LIBALTOS_H_ +#define _LIBALTOS_H_ + +struct altos_device { + //%immutable; + char product[256]; + int serial; + char path[256]; + //%mutable; +}; + +int altos_init(void); + +void altos_fini(void); + +struct altos_list * +altos_list_start(void); + +int altos_list_next(struct altos_list *list, struct altos_device *device); + +void altos_list_finish(struct altos_list *list); + +struct altos_file * +altos_open(struct altos_device *device); + +void altos_close(struct altos_file *file); + +int +altos_putchar(struct altos_file *file, char c); + +int +altos_flush(struct altos_file *file); + +int +altos_getchar(struct altos_file *file, int timeout); + +#endif /* _LIBALTOS_H_ */ diff --git a/ao-tools/libaltos/libaltos.i0 b/ao-tools/libaltos/libaltos.i0 new file mode 100644 index 00000000..d06468f5 --- /dev/null +++ b/ao-tools/libaltos/libaltos.i0 @@ -0,0 +1,5 @@ +%module libaltos +%{ +#include "libaltos.h" +%} + -- 2.30.2