-SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view
+SUBDIRS=lib ao-rawload ao-dbg ao-dumplog ao-bitbang ao-eeprom ao-list ao-load ao-postflight ao-view libaltos altosui
package altosui;
import java.lang.*;
import java.util.*;
+import libaltosJNI.*;
-public class AltosDevice {
- String tty; /* suitable to be passed to AltosSerial.connect */
- String manufacturer;
- String product;
- int serial;
- int idProduct;
- int idVendor;
+public class AltosDevice extends altos_device {
+ public String toString() {
+ return String.format("%-20.20s %4d %s",
+ getProduct(), getSerial(), getPath());
+ }
+
+ static {
+ System.loadLibrary("altos");
+ libaltos.altos_init();
+ }
+ static AltosDevice[] list(String product) {
+ SWIGTYPE_p_altos_list list = libaltos.altos_list_start();
+
+ ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>();
+ if (list != null) {
+ SWIGTYPE_p_altos_file file;
+
+ for (;;) {
+ AltosDevice device = new AltosDevice();
+ if (libaltos.altos_list_next(list, device) == 0)
+ break;
+ device_list.add(device);
+ }
+ libaltos.altos_list_finish(list);
+ }
+
+ AltosDevice[] devices = new AltosDevice[device_list.size()];
+ for (int i = 0; i < device_list.size(); i++)
+ devices[i] = device_list.get(i);
+ return devices;
+ }
}
\ No newline at end of file
import java.lang.*;
import java.util.*;
import javax.swing.*;
+import libaltosJNI.libaltos;
+import libaltosJNI.altos_device;
+import libaltosJNI.SWIGTYPE_p_altos_file;
+import libaltosJNI.SWIGTYPE_p_altos_list;
import altosui.AltosDevice;
-import altosui.AltosDeviceLinux;
public class AltosDeviceDialog {
- static AltosDevice show (JFrame frame, String product) {
- AltosDevice[] devices = null;
- if (System.getProperty("os.name").startsWith("Linux"))
- devices = AltosDeviceLinux.list(product);
+ static altos_device show (JFrame frame, String product) {
+ AltosDevice[] devices;
+ devices = AltosDevice.list(product);
if (devices != null & devices.length > 0) {
Object o = JOptionPane.showInputDialog(frame,
"Select a device",
null,
devices,
devices[0]);
- return (AltosDevice) o;
+ return (altos_device) o;
} else {
return null;
}
+++ /dev/null
-/*
- * Copyright © 2010 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package altosui;
-import java.lang.*;
-import java.io.*;
-import java.util.*;
-import altosui.AltosDevice;
-
-public class AltosDeviceLinux extends AltosDevice {
-
- String load_string(File file) {
- try {
- FileInputStream in = new FileInputStream(file);
- String result = "";
- int c;
- try {
- while ((c = in.read()) != -1) {
- if (c == '\n')
- break;
- result = result + (char) c;
- }
- return result;
- } catch (IOException ee) {
- return "";
- }
- } catch (FileNotFoundException ee) {
- return "";
- }
- }
- String load_string(File dir, String name) {
- return load_string(new File(dir, name));
- }
-
- int load_hex(File file) {
- try {
- return Integer.parseInt(load_string(file).trim(), 16);
- } catch (NumberFormatException ee) {
- return -1;
- }
- }
-
- int load_hex(File dir, String name) {
- return load_hex(new File(dir, name));
- }
-
- int load_dec(File file) {
- try {
- return Integer.parseInt(load_string(file).trim());
- } catch (NumberFormatException ee) {
- return -1;
- }
- }
-
- int load_dec(File dir, String name) {
- return load_dec(new File(dir, name));
- }
-
- String usb_tty(File sys_dir) {
- String base = sys_dir.getName();
- int num_configs = load_hex(sys_dir, "bNumConfigurations");
- int num_inters = load_hex(sys_dir, "bNumInterfaces");
- for (int config = 1; config <= num_configs; config++) {
- for (int inter = 0; inter < num_inters; inter++) {
- String endpoint_base = String.format("%s:%d.%d",
- base, config, inter);
- File endpoint_full = new File(sys_dir, endpoint_base);
-
- File[] namelist;
-
- /* Check for tty:ttyACMx style names */
- class tty_colon_filter implements FilenameFilter {
- public boolean accept(File dir, String name) {
- return name.startsWith("tty:");
- }
- }
- namelist = endpoint_full.listFiles(new tty_colon_filter());
- if (namelist != null && namelist.length > 0)
- return new File ("/dev", namelist[0].getName().substring(4)).getPath();
-
- /* Check for tty/ttyACMx style names */
- class tty_filter implements FilenameFilter {
- public boolean accept(File dir, String name) {
- return name.startsWith("tty");
- }
- }
- File tty_dir = new File(endpoint_full, "tty");
- namelist = tty_dir.listFiles(new tty_filter());
- if (namelist != null && namelist.length > 0)
- return new File ("/dev", namelist[0].getName()).getPath();
- }
- }
- return null;
- }
-
- public AltosDeviceLinux (File sys) {
- sys = sys;
- manufacturer = load_string(sys, "manufacturer");
- product = load_string(sys, "product");
- serial = load_dec(sys, "serial");
- idProduct = load_hex(sys, "idProduct");
- idVendor = load_hex(sys, "idVendor");
- tty = usb_tty(sys);
- }
-
- public String toString() {
- return String.format("%-20s %6d %-15s", product, serial, tty == null ? "" : tty);
- }
- static public AltosDeviceLinux[] list() {
- LinkedList<AltosDeviceLinux> devices = new LinkedList<AltosDeviceLinux>();
-
- class dev_filter implements FilenameFilter{
- public boolean accept(File dir, String name) {
- for (int i = 0; i < name.length(); i++) {
- char c = name.charAt(i);
- if (Character.isDigit(c))
- continue;
- if (c == '-')
- continue;
- if (c == '.' && i != 1)
- continue;
- return false;
- }
- return true;
- }
- }
-
- File usb_devices = new File("/sys/bus/usb/devices");
- File[] devs = usb_devices.listFiles(new dev_filter());
- if (devs != null) {
- for (int e = 0; e < devs.length; e++) {
- AltosDeviceLinux dev = new AltosDeviceLinux(devs[e]);
- if (dev.idVendor == 0xfffe && dev.tty != null) {
- devices.add(dev);
- }
- }
- }
- AltosDeviceLinux[] foo = new AltosDeviceLinux[devices.size()];
- for (int e = 0; e < devices.size(); e++)
- foo[e] = devices.get(e);
- return foo;
- }
-
- static public AltosDeviceLinux[] list(String model) {
- AltosDeviceLinux[] devices = list();
- if (model != null) {
- LinkedList<AltosDeviceLinux> subset = new LinkedList<AltosDeviceLinux>();
- for (int i = 0; i < devices.length; i++) {
- if (devices[i].product.startsWith(model))
- subset.add(devices[i]);
- }
- devices = new AltosDeviceLinux[subset.size()];
- for (int e = 0; e < subset.size(); e++)
- devices[e] = subset.get(e);
- }
- return devices;
- }
-}
gps_connected = true;
gps_time = new AltosGPSTime();
i++;
+ } else if ((words[i]).equals("not-connected")) {
+ gps_time = new AltosGPSTime();
+ i++;
} else if (words.length >= 40) {
gps_locked = true;
gps_connected = true;
v_error = AltosParse.parse_int(AltosParse.strip_suffix(words[i++], "(verr)"));
} else {
gps_time = new AltosGPSTime();
+ i++;
}
AltosParse.word(words[i++], "SAT");
int tracking_channels = 0;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.LinkedList;
import java.util.Iterator;
-import gnu.io.*;
import altosui.AltosSerialMonitor;
+import libaltosJNI.libaltos;
+import libaltosJNI.altos_device;
+import libaltosJNI.SWIGTYPE_p_altos_file;
+import libaltosJNI.SWIGTYPE_p_altos_list;
/*
* This class reads from the serial port and places each received
* line in a queue. Dealing with that queue is left up to other
* threads.
*/
-class AltosSerialReader implements Runnable {
- InputStream serial_in;
+
+public class AltosSerial implements Runnable {
+
+ SWIGTYPE_p_altos_file altos;
LinkedList<LinkedBlockingQueue<String>> monitors;
LinkedBlockingQueue<String> reply_queue;
Thread input_thread;
try {
for (;;) {
- c = serial_in.read();
+ c = libaltos.altos_getchar(altos, 0);
if (Thread.interrupted())
break;
if (c == -1)
}
}
}
- } catch (IOException e) {
} catch (InterruptedException e) {
}
}
}
public boolean opened() {
- return serial_in != null;
+ return altos != null;
}
public void close() {
- if (serial_in != null) {
- try {
- serial_in.close();
- } catch (IOException e) {
- }
- serial_in = null;
+ if (altos != null) {
+ libaltos.altos_close(altos);
+ altos = null;
}
if (input_thread != null) {
try {
}
}
- public void open(File name) throws FileNotFoundException {
- close();
- serial_in = new FileInputStream(name);
- input_thread = new Thread(this);
- input_thread.start();
- }
- public void open(CommPort c) throws IOException {
- close();
- try {
- c.enableReceiveTimeout(1000); /* icky. the read method cannot be interrupted */
- } catch (UnsupportedCommOperationException ee) {
- }
- serial_in = c.getInputStream();
- input_thread = new Thread(this);
- input_thread.start();
- }
- public AltosSerialReader () {
- serial_in = null;
- input_thread = null;
- line = "";
- monitors = new LinkedList<LinkedBlockingQueue<String>> ();
- reply_queue = new LinkedBlockingQueue<String> ();
+ public void putc(char c) {
+ libaltos.altos_putchar(altos, c);
}
-}
-
-public class AltosSerial {
- OutputStream serial_out = null;
- AltosSerialReader reader = null;
-
- CommPort comm_port = null;
-
- public void close() {
- try {
- serial_out.close();
- } catch (IOException ee) {
- }
- reader.close();
- if (comm_port != null) {
- comm_port.close();
- }
- }
-
- public void open(File serial_name) throws FileNotFoundException {
- reader.open(serial_name);
- serial_out = new FileOutputStream(serial_name);
- }
-
- public void open(CommPort c) throws IOException {
- reader.open(c);
- serial_out = c.getOutputStream();
+ public void print(String data) {
+ for (int i = 0; i < data.length(); i++)
+ putc(data.charAt(i));
}
- public void connect(String port_name) throws IOException, NoSuchPortException, PortInUseException {
- comm_port = new RXTXPort(port_name);
- open(comm_port);
+ public void printf(String format, Object ... arguments) {
+ print(String.format(format, arguments));
}
- void init() {
- reader = new AltosSerialReader();
- }
-
- public void add_monitor(LinkedBlockingQueue<String> q) {
- reader.add_monitor(q);
- }
-
- public void remove_monitor(LinkedBlockingQueue<String> q) {
- reader.remove_monitor(q);
+ public void open(altos_device device) throws FileNotFoundException {
+ close();
+ altos = libaltos.altos_open(device);
+ input_thread = new Thread(this);
+ input_thread.start();
}
public AltosSerial() {
- init();
- }
-
- public AltosSerial(File serial_name) throws FileNotFoundException {
- init();
- open(serial_name);
- }
-
- public AltosSerial(CommPort comm_port) throws IOException {
- init();
- open(comm_port);
+ altos = null;
+ input_thread = null;
+ line = "";
+ monitors = new LinkedList<LinkedBlockingQueue<String>> ();
+ reply_queue = new LinkedBlockingQueue<String> ();
}
}
import java.text.*;
import java.util.prefs.*;
import java.util.concurrent.LinkedBlockingQueue;
-import gnu.io.*;
import altosui.AltosSerial;
import altosui.AltosSerialMonitor;
import altosui.AltosLog;
import altosui.AltosVoice;
+import libaltosJNI.*;
+
class AltosFlightStatusTableModel extends AbstractTableModel {
private String[] columnNames = {"Height (m)", "State", "RSSI (dBm)", "Speed (m/s)" };
private Object[] data = { 0, "idle", 0, 0 };
flightInfoModel[col].addRow(name, value);
}
- public void info_add_row(int col, String name, String format, Object value) {
- flightInfoModel[col].addRow(name, String.format(format, value));
- }
-
- public void info_add_row(int col, String name, String format, Object v1, Object v2) {
- flightInfoModel[col].addRow(name, String.format(format, v1, v2));
- }
-
- public void info_add_row(int col, String name, String format, Object v1, Object v2, Object v3) {
- flightInfoModel[col].addRow(name, String.format(format, v1, v2, v3));
+ public void info_add_row(int col, String name, String format, Object... parameters) {
+ flightInfoModel[col].addRow(name, String.format(format, parameters));
}
public void info_add_deg(int col, String name, double v, int pos, int neg) {
class IdleThread extends Thread {
private AltosState state;
+ int reported_landing;
+
+ public void report(boolean last) {
+ if (state == null)
+ return;
+
+ /* reset the landing count once we hear about a new flight */
+ if (state.state < AltosTelemetry.ao_flight_drogue)
+ reported_landing = 0;
+
+ /* Shut up once the rocket is on the ground */
+ if (reported_landing > 2) {
+ return;
+ }
+
+ /* If the rocket isn't on the pad, then report height */
+ if (state.state > AltosTelemetry.ao_flight_pad) {
+ voice.speak("%d meters", (int) (state.height + 0.5));
+ } else {
+ reported_landing = 0;
+ }
+
+ /* If the rocket is coming down, check to see if it has landed;
+ * either we've got a landed report or we haven't heard from it in
+ * a long time
+ */
+ if (!state.ascent &&
+ (last ||
+ System.currentTimeMillis() - state.report_time >= 15000 ||
+ state.state == AltosTelemetry.ao_flight_landed))
+ {
+ if (Math.abs(state.baro_speed) < 20 && state.height < 100)
+ voice.speak("rocket landed safely");
+ else
+ voice.speak("rocket may have crashed");
+ if (state.gps != null)
+ voice.speak("bearing %d degrees, range %d meters",
+ (int) (state.from_pad.bearing + 0.5),
+ (int) (state.from_pad.distance + 0.5));
+ ++reported_landing;
+ }
+ }
public void run () {
- int reported_landing = 0;
+ reported_landing = 0;
state = null;
try {
for (;;) {
Thread.sleep(10000);
- if (state == null)
- continue;
-
- /* reset the landing count once we hear about a new flight */
- if (state.state < AltosTelemetry.ao_flight_drogue)
- reported_landing = 0;
-
- /* Shut up once the rocket is on the ground */
- if (reported_landing > 2)
- continue;
-
- /* If the rocket isn't on the pad, then report height */
- if (state.state > AltosTelemetry.ao_flight_pad) {
- voice.speak(String.format("%d meters", (int) (state.height + 0.5)));
- }
-
- /* If the rocket is coming down, check to see if it has landed;
- * either we've got a landed report or we haven't heard from it in
- * a long time
- */
- if (!state.ascent &&
- (System.currentTimeMillis() - state.report_time > 10000 ||
- state.state == AltosTelemetry.ao_flight_landed))
- {
- if (Math.abs(state.baro_speed) < 20 && state.height < 100)
- voice.speak("rocket landed safely");
- else
- voice.speak("rocket may have crashed");
- if (state.gps != null)
- voice.speak(String.format("bearing %d degrees, range %d meters",
- (int) (state.from_pad.bearing + 0.5),
- (int) (state.from_pad.distance + 0.5)));
- ++reported_landing;
- }
+ report(false);
}
} catch (InterruptedException ie) {
}
private void tell(AltosState state, AltosState old_state) {
if (old_state == null || old_state.state != state.state) {
voice.speak(state.data.state);
- switch (state.state) {
- case AltosTelemetry.ao_flight_fast:
- voice.speak(String.format("max speed %d meters per second",
- (int) (state.max_speed + 0.5)));
- break;
- case AltosTelemetry.ao_flight_drogue:
- voice.speak(String.format("max height %d meters",
- (int) (state.max_height + 0.5)));
- break;
+ if ((old_state == null || old_state.state <= AltosTelemetry.ao_flight_boost) &&
+ state.state > AltosTelemetry.ao_flight_boost) {
+ voice.speak("max speed: %d meters per second.",
+ (int) (state.max_speed + 0.5));
+ } else if ((old_state == null || old_state.state < AltosTelemetry.ao_flight_drogue) &&
+ state.state >= AltosTelemetry.ao_flight_drogue) {
+ voice.speak("max height: %d meters.",
+ (int) (state.max_height + 0.5));
}
}
old_state = state;
}
class DisplayThread extends Thread {
+ IdleThread idle_thread;
+
String read() throws InterruptedException { return null; }
void close() { }
String line;
AltosState state = null;
AltosState old_state = null;
- IdleThread idle_thread = new IdleThread();
+
+ idle_thread = new IdleThread();
info_reset();
info_finish();
idle_thread.interrupt();
}
}
+
+ public void report() {
+ if (idle_thread != null)
+ idle_thread.report(true);
+ }
}
class DeviceThread extends DisplayThread {
}
private void ConnectToDevice() {
- AltosDevice device = AltosDeviceDialog.show(AltosUI.this, "TeleDongle");
+ altos_device device = AltosDeviceDialog.show(AltosUI.this, "TeleDongle");
if (device != null) {
try {
- serial_line.connect(device.tty);
+ serial_line.open(device);
DeviceThread thread = new DeviceThread(serial_line);
run_display(thread);
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(AltosUI.this,
- device.tty,
+ device.getPath(),
"Cannot open serial port",
JOptionPane.ERROR_MESSAGE);
- } catch (NoSuchPortException ee) {
- JOptionPane.showMessageDialog(AltosUI.this,
- device.tty,
- "No such serial port",
- JOptionPane.ERROR_MESSAGE);
- } catch (PortInUseException ee) {
- JOptionPane.showMessageDialog(AltosUI.this,
- device.tty,
- "Port in use",
- JOptionPane.ERROR_MESSAGE);
} catch (IOException ee) {
JOptionPane.showMessageDialog(AltosUI.this,
- device.tty,
+ device.getPath(),
"Unkonwn I/O error",
JOptionPane.ERROR_MESSAGE);
}
while ((c = s.read()) != -1) {
if (c == '\r')
continue;
- if (c == '\n')
+ if (c == '\n') {
return line;
+ }
line = line + (char) c;
}
return null;
replay.close();
} catch (IOException ee) {
}
+ report();
}
void update(AltosState state) throws InterruptedException {
/* Make it run in realtime after the rocket leaves the pad */
if (state.state > AltosTelemetry.ao_flight_pad)
- Thread.sleep((int) (state.time_change * 1000));
+ Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
}
}
} catch (FileNotFoundException ee) {
JOptionPane.showMessageDialog(AltosUI.this,
filename,
- "Cannot open serial port",
+ "Cannot open telemetry file",
JOptionPane.ERROR_MESSAGE);
}
}
}
}
+ public void speak(String format, Object... parameters) {
+ speak(String.format(format, parameters));
+ }
+
public AltosVoice () {
voice_manager = VoiceManager.getInstance();
voice = voice_manager.getVoice(voice_name);
.SUFFIXES: .java .class
-CLASSPATH=..:/usr/share/java/*:/home/keithp/src/freetts/freetts-1.2.2
+CLASSPATH=classes:./*
CLASSFILES=\
AltosConvert.class \
AltosFile.class \
AltosTelemetry.class \
AltosUI.class \
AltosDevice.class \
- AltosDeviceLinux.class \
AltosDeviceDialog.class \
AltosVoice.class
+FREETTSSRC=/home/keithp/src/freetts/freetts-1.2.2
+FREETTSLIB=$(FREETTSSRC)/lib
+FREETTSJAR= \
+ cmudict04.jar \
+ cmulex.jar \
+ cmu_time_awb.jar \
+ cmutimelex.jar \
+ cmu_us_kal.jar \
+ en_us.jar \
+ freetts.jar \
+ freetts-jsapi10.jar \
+ jsapi.jar
+
JAVAFLAGS=-Xlint:unchecked
-all: $(CLASSFILES) altosui altosui.jar
+all: altosui.jar
+
+$(CLASSFILES):
.java.class:
javac -cp "$(CLASSPATH)" $(JAVAFLAGS) $*.java
-altosui: Makefile
- (echo '#!/bin/sh'; \
- echo exec java -cp '"$(CLASSPATH)"' altosui/AltosUI) > $@
- chmod +x $@
+altosui.jar: classes/altosui classes/libaltosJNI $(FREETTSJAR) $(CLASSFILES) Manifest.txt
+ cd ./classes && jar cfm ../$@ altosui/Manifest.txt altosui/*.class libaltosJNI/*.class
+
+classes/altosui:
+ mkdir -p classes
+ ln -s .. classes/altosui
+
+classes/libaltosJNI:
+ mkdir -p classes
+ ln -s ../../libaltos/libaltosJNI classes/libaltosJNI
-altosui.jar: $(CLASSFILES) Manifest.txt
- cd .. && jar cfm altosui/$@ altosui/Manifest.txt altosui/*.class
+$(FREETTSJAR):
+ ln -s $(FREETTSLIB)/$@ .
clean:
- rm -f *.class
+ rm -f *.class $(FREETTSJAR) altosui.jar
+ rm -rf classes
Main-Class: altosui.AltosUI
+Class-Path: freetts.jar
+++ /dev/null
-com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory
--- /dev/null
+<pkg-contents spec="1.12"><f n="AltosUI.app" o="keithp" g="keithp" p="16877" pt="/Users/keithp/AltosUI.app" m="false" t="file"><f n="Contents" o="keithp" g="keithp" p="16877"><f n="Info.plist" o="keithp" g="keithp" p="33188"/><f n="MacOS" o="keithp" g="keithp" p="16877"><f n="JavaApplicationStub" o="keithp" g="keithp" p="33133"/></f><f n="PkgInfo" o="keithp" g="keithp" p="33188"/><f n="Resources" o="keithp" g="keithp" p="16877"><f n="AltosUIIcon.icns" o="keithp" g="keithp" p="33188"/><f n="Java" o="keithp" g="keithp" p="16877"><f n="altosui.jar" o="keithp" g="keithp" p="33188"/><f n="cmu_time_awb.jar" o="keithp" g="keithp" p="33188"/><f n="cmu_us_kal.jar" o="keithp" g="keithp" p="33188"/><f n="cmudict04.jar" o="keithp" g="keithp" p="33188"/><f n="cmulex.jar" o="keithp" g="keithp" p="33188"/><f n="cmutimelex.jar" o="keithp" g="keithp" p="33188"/><f n="en_us.jar" o="keithp" g="keithp" p="33188"/><f n="freetts-jsapi10.jar" o="keithp" g="keithp" p="33188"/><f n="freetts.jar" o="keithp" g="keithp" p="33188"/><f n="libaltos.dylib" o="keithp" g="keithp" p="33188"/></f></f></f></f></pkg-contents>
\ No newline at end of file
--- /dev/null
+<pkgref spec="1.12" uuid="C5762664-2F26-4536-94C4-56F0FBC08D1A"><config><identifier>org.altusmetrum.altosUi.AltosUI.pkg</identifier><version>1.0</version><description></description><post-install type="none"/><installFrom>/Users/keithp/AltosUI.app</installFrom><installTo mod="true" relocatable="true">/Applications/AltosUI.app</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo.path</mod><mod>parent</mod><mod>requireAuthorization</mod><mod>installTo</mod></config><contents><file-list>01altosui-contents.xml</file-list><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents></pkgref>
\ No newline at end of file
--- /dev/null
+<pkmkdoc spec="1.12"><properties><title>AltOS UI</title><build>/Users/keithp/Documents/AltosUI.pkg</build><organization>org.altusmetrum</organization><userSees ui="easy"/><min-target os="3"/><domain anywhere="true"/></properties><distribution><versions min-spec="1.000000"/><scripts></scripts></distribution><contents><choice title="AltosUI" id="choice0" starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="org.altusmetrum.altosUi.AltosUI.pkg"/></choice></contents><resources bg-scale="none" bg-align="topleft"><locale lang="en"/></resources><flags/><item type="file">01altosui.xml</item><mod>properties.title</mod></pkmkdoc>
\ No newline at end of file
--- /dev/null
+OS:=$(shell uname)
+
+ifeq ($(OS),Linux)
+
+JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include
+
+OS_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS)
+
+LIBEXT=so
+
+endif
+
+ifeq ($(OS),Darwin)
+
+DARWIN_CFLAGS=\
+ --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=\
+ -framework IOKit -framework CoreFoundation
+
+OS_CFLAGS = $(DARWIN_CFLAGS) -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64
+LIBEXT=dylib
+
+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
+
+all: libaltos.$(LIBEXT) cjnitest $(CLASSFILES)
+
+.java.class:
+ javac -cp "$(CLASSPATH)" $(JAVAFLAGS) $*.java
+
+CFLAGS=$(OS_CFLAGS) -O0 -g -I.
+
+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 $(CFLAGS) -o $@ $(OBJS) $(LIBS)
+
+clean:
+ rm -f $(CLASSFILES) $(OBJS) libaltos.$(LIBEXT) cjnitest cjnitest.o
+ rm -rf swig_bindings libaltosJNI
+
+$(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)
--- /dev/null
+#include <stdio.h>
+#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();
+}
--- /dev/null
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "libaltos.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+match_dev(char *product, int serial, struct altos_device *device)
+{
+ struct altos_list *list;
+ int i;
+
+ 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;
+}
+
+#ifdef DARWIN
+/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
+static char *
+altos_strndup (const char *s, size_t n)
+{
+ size_t len = strlen (s);
+ char *ret;
+
+ if (len <= n)
+ return strdup (s);
+ ret = malloc(n + 1);
+ strncpy(ret, s, n);
+ ret[n] = '\0';
+ return ret;
+}
+
+#else
+#define altos_strndup strndup
+#endif
+
+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> */
+ serial = strtol(arg, &end, 0);
+ if (end != arg) {
+ if (*end != '\0')
+ return 0;
+ product = NULL;
+ } else {
+ /* check for <product>:<serial> */
+ 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;
+}
+
+#ifdef LINUX
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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;
+ 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");
+ free(endpoint_full);
+ ntty = scandir(tty_dir, &namelist,
+ dir_filter_tty,
+ alphasort);
+ free (tty_dir);
+ 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;
+
+ usbdev = calloc(1, sizeof (struct altos_usbdev));
+ if (!usbdev)
+ return NULL;
+ usbdev->sys = strdup(sys);
+ usbdev->manufacturer = load_string(sys, "manufacturer");
+ usbdev->product = load_string(sys, "product");
+ usbdev->serial = load_dec(sys, "serial");
+ usbdev->idProduct = load_hex(sys, "idProduct");
+ usbdev->idVendor = load_hex(sys, "idVendor");
+ usbdev->tty = usb_tty(sys);
+ return usbdev;
+}
+
+static void
+usbdev_free(struct altos_usbdev *usbdev)
+{
+ free(usbdev->sys);
+ free(usbdev->manufacturer);
+ free(usbdev->product);
+ /* 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;
+};
+
+int
+altos_init(void)
+{
+ return 1;
+}
+
+void
+altos_fini(void)
+{
+}
+
+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);
+ free(dir);
+ if (dev->idVendor == 0xfffe && dev->tty) {
+ 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;
+}
+
+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->product, dev->product);
+ 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);
+}
+
+#endif
+
+#ifdef DARWIN
+
+#include <IOKitLib.h>
+#include <IOKit/usb/USBspec.h>
+#include <sys/param.h>
+#include <paths.h>
+#include <CFNumber.h>
+#include <IOBSD.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct altos_list {
+ io_iterator_t iterator;
+};
+
+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);
+}
+
+#endif
+
+#ifdef POSIX_TTY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+
+#define USB_BUF_SIZE 64
+
+struct altos_file {
+ int fd;
+ 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 = 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 /* POSIX_TTY */
+
+#ifdef USE_LIBUSB
+#include <libusb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+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 */
--- /dev/null
+/*
+ * Copyright © 2010 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#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_ */
--- /dev/null
+%module libaltos
+%{
+#include "libaltos.h"
+%}
+