From d14c96663a1027164fa30ed89b53f5a9d3fdb82b Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 17 Aug 2010 18:19:43 -0700 Subject: [PATCH] libaltos: integrate Windows support. This adds Windows support for discovery and I/O. The API to the library is mostly unchanged, except that it now exports product and vendor USB IDs as Win7 doesn't expose the product name anywhere that we've been able to find, so we'll be updating the firmware to use unique idProduct values for each product. Signed-off-by: Keith Packard --- ao-tools/libaltos/Makefile | 53 +++- ao-tools/libaltos/cjnitest.c | 21 +- ao-tools/libaltos/libaltos.c | 501 ++++++++++++++++++----------------- ao-tools/libaltos/libaltos.h | 68 ++++- 4 files changed, 383 insertions(+), 260 deletions(-) diff --git a/ao-tools/libaltos/Makefile b/ao-tools/libaltos/Makefile index fa5127eb..a251e54e 100644 --- a/ao-tools/libaltos/Makefile +++ b/ao-tools/libaltos/Makefile @@ -1,27 +1,56 @@ OS:=$(shell uname) +# +# Linux +# ifeq ($(OS),Linux) JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include OS_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS) -LIBEXT=so +OS_LDFLAGS= + +LIBNAME=libaltos.so +EXEEXT= endif +# +# Darwin (Mac OS X) +# ifeq ($(OS),Darwin) -DARWIN_CFLAGS=\ +OS_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 -DARWIN_LIBS=\ + +OS_LDFLAGS =\ -framework IOKit -framework CoreFoundation -OS_CFLAGS = $(DARWIN_CFLAGS) -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 -LIBEXT=dylib +LIBNAME=libaltos.dylib +EXEEXT= + +endif + +# +# Windows +# +ifneq (,$(findstring MINGW,$(OS))) + +CC=gcc + +OS_CFLAGS = -DWINDOWS -mconsole + +OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \ + -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias + +LIBNAME=altos.dll + +EXEEXT=.exe endif @@ -48,26 +77,30 @@ CLASSFILES = $(JAVAFILES:%.java=%.class) JAVAFLAGS=-Xlint:unchecked -all: libaltos.$(LIBEXT) cjnitest $(CLASSFILES) +CJNITEST=cjnitest$(EXEEXT) + +all: $(LIBNAME) $(CJNITEST) $(CLASSFILES) .java.class: javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java CFLAGS=$(OS_CFLAGS) -O0 -g -I. +LDFLAGS=$(OS_LDFLAGS) + HEADERS=libaltos.h SRCS = libaltos.c $(SWIG_WRAP) OBJS = $(SRCS:%.c=%.o) LIBS = $(DARWIN_LIBS) -cjnitest: cjnitest.o $(OBJS) +$(CJNITEST): cjnitest.o $(OBJS) cc -o $@ $(CFLAGS) cjnitest.o $(OBJS) $(LIBS) -libaltos.$(LIBEXT): $(OBJS) - gcc -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) +$(LIBNAME): $(OBJS) + gcc -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS) clean: - rm -f $(CLASSFILES) $(OBJS) libaltos.$(LIBEXT) cjnitest cjnitest.o + rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o rm -rf swig_bindings libaltosJNI $(JNI_FILE): libaltos.i0 $(HEADERS) diff --git a/ao-tools/libaltos/cjnitest.c b/ao-tools/libaltos/cjnitest.c index cd3898ed..93d1f376 100644 --- a/ao-tools/libaltos/cjnitest.c +++ b/ao-tools/libaltos/cjnitest.c @@ -1,6 +1,15 @@ #include #include "libaltos.h" +static void +altos_puts(struct altos_file *file, char *string) +{ + char c; + + while ((c = *string++)) + altos_putchar(file, c); +} + main () { struct altos_device device; @@ -12,12 +21,20 @@ main () 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); - altos_putchar(file, '?'); altos_putchar(file, '\n'); altos_flush(file); + if (!file) { + printf("altos_open failed\n"); + continue; + } + altos_puts(file,"v\nc s\n"); while ((c = altos_getchar(file, 100)) >= 0) { putchar (c); } - printf ("getchar returns %d\n", c); + if (c != LIBALTOS_TIMEOUT) + printf ("getchar returns %d\n", c); altos_close(file); } altos_list_finish(list); diff --git a/ao-tools/libaltos/libaltos.c b/ao-tools/libaltos/libaltos.c index 00fb2125..3e8485e4 100644 --- a/ao-tools/libaltos/libaltos.c +++ b/ao-tools/libaltos/libaltos.c @@ -15,29 +15,21 @@ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ +#define BUILD_DLL #include "libaltos.h" #include #include #include -static int -match_dev(char *product, int serial, struct altos_device *device) +PUBLIC int +altos_init(void) { - struct altos_list *list; - int i; + return LIBALTOS_SUCCESS; +} - list = altos_list_start(); - if (!list) - return 0; - while ((i = altos_list_next(list, device)) != 0) { - if (product && strncmp (product, device->product, strlen(product)) != 0) - continue; - if (serial && serial != device->serial) - continue; - break; - } - altos_list_finish(list); - return i; +PUBLIC void +altos_fini(void) +{ } #ifdef DARWIN @@ -60,48 +52,9 @@ altos_strndup (const char *s, size_t n) #define altos_strndup strndup #endif -int -altos_find_by_arg(char *arg, char *default_product, struct altos_device *device) -{ - char *product; - int serial; - char *end; - char *colon; - int ret; - - if (arg) - { - /* check for */ - serial = strtol(arg, &end, 0); - if (end != arg) { - if (*end != '\0') - return 0; - product = NULL; - } else { - /* check for : */ - colon = strchr(arg, ':'); - if (colon) { - product = altos_strndup(arg, colon - arg); - serial = strtol(colon + 1, &end, 0); - if (*end != '\0') - return 0; - } else { - product = arg; - serial = 0; - } - } - } else { - product = NULL; - serial = 0; - } - if (!product && default_product) - ret = match_dev(default_product, serial, device); - if (!ret) - ret = match_dev(product, serial, device); - if (product && product != arg) - free(product); - return ret; -} +/* + * Scan for Altus Metrum devices by looking through /sys + */ #ifdef LINUX @@ -216,7 +169,7 @@ struct altos_usbdev { char *sys; char *tty; char *manufacturer; - char *product; + char *product_name; int serial; /* AltOS always uses simple integer serial numbers */ int idProduct; int idVendor; @@ -286,7 +239,7 @@ usb_scan_device(char *sys) return NULL; usbdev->sys = strdup(sys); usbdev->manufacturer = load_string(sys, "manufacturer"); - usbdev->product = load_string(sys, "product"); + 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"); @@ -299,7 +252,7 @@ usbdev_free(struct altos_usbdev *usbdev) { free(usbdev->sys); free(usbdev->manufacturer); - free(usbdev->product); + free(usbdev->product_name); /* this can get used as a return value */ if (usbdev->tty) free(usbdev->tty); @@ -332,17 +285,6 @@ struct altos_list { int ndev; }; -int -altos_init(void) -{ - return 1; -} - -void -altos_fini(void) -{ -} - struct altos_list * altos_list_start(void) { @@ -366,7 +308,7 @@ altos_list_start(void) dir = cc_fullname(USB_DEVICES, ents[e]->d_name); dev = usb_scan_device(dir); free(dir); - if (dev->idVendor == 0xfffe && dev->tty) { + if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) { if (devs->dev) devs->dev = realloc(devs->dev, devs->ndev + 1 * sizeof (struct usbdev *)); @@ -387,7 +329,9 @@ altos_list_next(struct altos_list *list, struct altos_device *device) if (list->current >= list->ndev) return 0; dev = list->dev[list->current]; - strcpy(device->product, dev->product); + 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++; @@ -447,17 +391,6 @@ get_string(io_object_t object, CFStringRef entry, char *result, int result_len) return 0; } -int -altos_init(void) -{ - return 1; -} - -void -altos_fini(void) -{ -} - struct altos_list * altos_list_start(void) { @@ -566,15 +499,16 @@ altos_open(struct altos_device *device) void altos_close(struct altos_file *file) { - close(file->fd); - file->fd = -1; + if (file->fd != -1) { + close(file->fd); + file->fd = -1; + } } void altos_free(struct altos_file *file) { - if (file->fd != -1) - close(file->fd); + altos_close(file); free(file); } @@ -613,6 +547,8 @@ altos_flush(struct altos_file *file) } } +#include + int altos_getchar(struct altos_file *file, int timeout) { @@ -622,9 +558,18 @@ altos_getchar(struct altos_file *file, int timeout) altos_flush(file); if (file->fd < 0) return -EBADF; + if (timeout) { + struct pollfd fd; + int ret; + fd.fd = file->fd; + fd.events = POLLIN; + ret = poll(&fd, 1, timeout); + if (ret == 0) + return LIBALTOS_TIMEOUT; + } ret = read(file->fd, file->in_data, USB_BUF_SIZE); if (ret < 0) - return -errno; + return LIBALTOS_ERROR; file->in_read = 0; file->in_used = ret; } @@ -633,143 +578,255 @@ altos_getchar(struct altos_file *file, int timeout) #endif /* POSIX_TTY */ -#ifdef USE_LIBUSB -#include -#include -#include -#include +#ifdef WINDOWS -libusb_context *usb_context; +#include +#include -int altos_init(void) -{ - int ret; - ret = libusb_init(&usb_context); - if (ret) - return ret; - libusb_set_debug(usb_context, 3); - return 0; -} +struct altos_list { + HDEVINFO dev_info; + int index; +}; -void altos_fini(void) -{ - libusb_exit(usb_context); - usb_context = NULL; -} +#define USB_BUF_SIZE 64 -static libusb_device **list; -static ssize_t num, current; +struct altos_file { + HANDLE handle; + unsigned char out_data[USB_BUF_SIZE]; + int out_used; + unsigned char in_data[USB_BUF_SIZE]; + int in_used; + int in_read; +}; -int altos_list_start(void) + +PUBLIC struct altos_list * +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; + 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) { + printf("SetupDiGetClassDevs failed %d\n", GetLastError()); + free(list); + return NULL; } - return 1; + list->index = 0; + return list; } -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; - } - } +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device) +{ + SP_DEVINFO_DATA dev_info_data; + char port[128]; + DWORD port_len; + char location[256]; + char symbolic[256]; + DWORD symbolic_len; + HKEY dev_key; + int vid, pid; + int serial; + HRESULT result; + DWORD location_type; + DWORD location_len; + + dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA); + while(SetupDiEnumDeviceInfo(list->dev_info, list->index, + &dev_info_data)) + { + list->index++; + + dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data, + DICS_FLAG_GLOBAL, 0, DIREG_DEV, + KEY_READ); + if (dev_key == INVALID_HANDLE_VALUE) { + printf("cannot open device registry key\n"); + continue; } + + /* 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) { + printf("cannot find SymbolicName value\n"); + RegCloseKey(dev_key); + continue; + } + vid = pid = serial = 0; + sscanf(symbolic + sizeof("\\??\\USB#VID_") - 1, + "%04X", &vid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1, + "%04X", &pid); + sscanf(symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1, + "%d", &serial); + if (!USB_IS_ALTUSMETRUM(vid, pid)) { + printf("Not Altus Metrum symbolic name: %s\n", + symbolic); + 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) { + printf("failed to get PortName\n"); + continue; + } + + /* Fetch the 'location information' which is the device name, + * at least on XP */ + location_len = sizeof (location); + if(!SetupDiGetDeviceRegistryProperty(list->dev_info, + &dev_info_data, + SPDRP_LOCATION_INFORMATION, + &location_type, + (BYTE *)location, + sizeof(location), + &location_len)) + { + printf("Failed to get location\n"); + continue; + } + device->vendor = vid; + device->product = pid; + device->serial = serial; + + if (strcasestr(location, "tele")) + strcpy(device->name, location); + else + strcpy(device->name, ""); + + strcpy(device->path, port); + printf ("product: %04x:%04x (%s) path: %s serial %d\n", + device->vendor, device->product, device->name, + device->path, device->serial); + return 1; } + result = GetLastError(); + if (result != ERROR_NO_MORE_ITEMS) + printf ("SetupDiEnumDeviceInfo failed error %d\n", result); return 0; } -void altos_list_finish(void) +PUBLIC void +altos_list_finish(struct altos_list *list) +{ + SetupDiDestroyDeviceInfoList(list->dev_info); + free(list); +} + +static int +altos_fill(struct altos_file *file, int timeout) { - if (list) { - libusb_free_device_list(list, 1); - list = NULL; + DWORD result; + DWORD got; + COMMTIMEOUTS timeouts; + + if (file->in_read < file->in_used) + return LIBALTOS_SUCCESS; + file->in_read = file->in_used = 0; + + if (timeout) { + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = timeout; + } else { + timeouts.ReadIntervalTimeout = 0; + timeouts.ReadTotalTimeoutMultiplier = 0; + timeouts.ReadTotalTimeoutConstant = 0; + } + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 0; + + if (!SetCommTimeouts(file->handle, &timeouts)) { + printf("SetCommTimeouts failed %d\n", GetLastError()); } + + if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, NULL)) { + result = GetLastError(); + printf ("read failed %d\n", result); + return LIBALTOS_ERROR; + got = 0; + } + if (got) + return LIBALTOS_SUCCESS; + return LIBALTOS_TIMEOUT; } -#define USB_BUF_SIZE 64 +PUBLIC int +altos_flush(struct altos_file *file) +{ + DWORD put; + char *data = file->out_data; + char used = file->out_used; + DWORD result; -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; -}; + while (used) { + if (!WriteFile(file->handle, data, used, &put, NULL)) { + result = GetLastError(); + printf ("write failed %d\n", result); + return LIBALTOS_ERROR; + } + data += put; + used -= put; + } + file->out_used = 0; + return LIBALTOS_SUCCESS; +} -struct altos_file * +PUBLIC 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; + struct altos_file *file = calloc (sizeof (struct altos_file), 1); + char full_name[64]; - 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 + if (!file) + return NULL; - 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; + strcpy(full_name, "\\\\.\\"); + strcat(full_name, device->path); + file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file->handle == INVALID_HANDLE_VALUE) { + free(file); + return NULL; + } - return file; + timeouts.ReadIntervalTimeout = MAXDWORD; + timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; + timeouts.ReadTotalTimeoutConstant = 100; + timeouts.WriteTotalTimeoutMultiplier = 0; + timeouts.WriteTotalTimeoutConstant = 10000; + if (!SetCommTimeouts(file->handle, &timeouts)) { + printf("SetCommTimeouts failed %d\n", GetLastError()); } - return NULL; + + return file; } -void +PUBLIC void altos_close(struct altos_file *file) { - libusb_close(file->handle); - libusb_unref_device(file->device); - file->handle = NULL; + if (file->handle != INVALID_HANDLE_VALUE) { + CloseHandle(file->handle); + file->handle = INVALID_HANDLE_VALUE; + } +} + +PUBLIC void +altos_free(struct altos_file *file) +{ + altos_close(file); free(file); } @@ -778,60 +835,32 @@ altos_putchar(struct altos_file *file, char c) { int ret; - if (file->out_used == file->out_size) { + 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 == file->out_size) + 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 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; - } - } + return LIBALTOS_SUCCESS; } int altos_getchar(struct altos_file *file, int timeout) { + int ret; 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); + ret = altos_flush(file); + if (ret) + return ret; + if (file->handle == INVALID_HANDLE_VALUE) + return LIBALTOS_ERROR; + ret = altos_fill(file, timeout); if (ret) return ret; - file->in_read = 0; - file->in_used = transferred; } return file->in_data[file->in_read++]; } -#endif /* USE_LIBUSB */ +#endif diff --git a/ao-tools/libaltos/libaltos.h b/ao-tools/libaltos/libaltos.h index 53026e0a..fe2c483c 100644 --- a/ao-tools/libaltos/libaltos.h +++ b/ao-tools/libaltos/libaltos.h @@ -18,39 +18,83 @@ #ifndef _LIBALTOS_H_ #define _LIBALTOS_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 + +#define USB_VENDOR_FSF 0xfffe +#define USB_VENDOR_ALTUSMETRUM USB_VENDOR_FSF +#define USB_PRODUCT_ALTUSMETRUM 0x000a +#define USB_PRODUCT_TELEMETRUM 0x000b +#define USB_PRODUCT_TELEDONGLE 0x000c +#define USB_PRODUCT_TELETERRA 0x000d +#define USB_PRODUCT_ALTUSMETRUM_MIN 0x000a +#define USB_PRODUCT_ALTUSMETRUM_MAX 0x0013 + +#define USB_IS_ALTUSMETRUM(v,p) ((v) == USB_VENDOR_ALTUSMETRUM && \ + (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \ + (p) <= USB_PRODUCT_ALTUSMETRUM_MAX)) + struct altos_device { //%immutable; - char product[256]; + int vendor; + int product; int serial; + char name[256]; char path[256]; //%mutable; }; -int altos_init(void); +#define LIBALTOS_SUCCESS 0 +#define LIBALTOS_ERROR -1 +#define LIBALTOS_TIMEOUT -2 + +/* Returns 0 for success, < 0 on error */ +PUBLIC int +altos_init(void); -void altos_fini(void); +PUBLIC void +altos_fini(void); -struct altos_list * +PUBLIC struct altos_list * altos_list_start(void); -int altos_list_next(struct altos_list *list, struct altos_device *device); +/* Returns 1 for success, zero on end of list */ +PUBLIC int +altos_list_next(struct altos_list *list, struct altos_device *device); -void altos_list_finish(struct altos_list *list); +PUBLIC void +altos_list_finish(struct altos_list *list); -struct altos_file * +PUBLIC struct altos_file * altos_open(struct altos_device *device); -void altos_close(struct altos_file *file); +PUBLIC void +altos_close(struct altos_file *file); -void altos_free(struct altos_file *file); +PUBLIC void +altos_free(struct altos_file *file); -int +/* Returns < 0 for error */ +PUBLIC int altos_putchar(struct altos_file *file, char c); -int +/* Returns < 0 for error */ +PUBLIC int altos_flush(struct altos_file *file); -int +/* Returns < 0 for error or timeout. timeout of 0 == wait forever */ +PUBLIC int altos_getchar(struct altos_file *file, int timeout); #endif /* _LIBALTOS_H_ */ -- 2.30.2