r4205 - in developers/werner/ahrt/host: . tmc tmc/examples

werner at sita.openmoko.org werner at sita.openmoko.org
Fri Mar 14 03:43:00 CET 2008


Author: werner
Date: 2008-03-14 03:42:49 +0100 (Fri, 14 Mar 2008)
New Revision: 4205

Added:
   developers/werner/ahrt/host/tmc/
   developers/werner/ahrt/host/tmc/Makefile
   developers/werner/ahrt/host/tmc/README
   developers/werner/ahrt/host/tmc/examples/
   developers/werner/ahrt/host/tmc/examples/telnet.py
   developers/werner/ahrt/host/tmc/examples/tty.py
   developers/werner/ahrt/host/tmc/examples/usbtmc.py
   developers/werner/ahrt/host/tmc/io.c
   developers/werner/ahrt/host/tmc/io.h
   developers/werner/ahrt/host/tmc/python.c
   developers/werner/ahrt/host/tmc/setup.py
   developers/werner/ahrt/host/tmc/telnet.c
   developers/werner/ahrt/host/tmc/tmc.c
   developers/werner/ahrt/host/tmc/tmc.h
   developers/werner/ahrt/host/tmc/tty.c
   developers/werner/ahrt/host/tmc/usbtmc.c
Log:
First try at properly integrating the TMC system. Also adds a "serial"
transport. This is still not quite good enough to obsolete scpinet and
usbtmc, but we'll get there.



Added: developers/werner/ahrt/host/tmc/Makefile
===================================================================
--- developers/werner/ahrt/host/tmc/Makefile	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/Makefile	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,15 @@
+.PHONY:		all install clean spotless
+
+all:		tmc.so
+
+tmc.so:		setup.py python.c tmc.c io.c telnet.c usbtmc.c
+		python setup.py build
+		find build -name tmc.so -exec mv '{}' . \;
+
+install:
+		python setup.py install
+
+clean:
+
+spotless:
+		rm -f tmc.so

Added: developers/werner/ahrt/host/tmc/README
===================================================================
--- developers/werner/ahrt/host/tmc/README	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/README	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,229 @@
+tmc - Test & Measurement Control
+================================
+
+Copyright (C) 2008 by OpenMoko, Inc.
+Written by Werner Almesberger <werner at openmoko.org>
+All Rights Reserved
+
+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.
+
+
+* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *
+
+    This program is still highly experimental. It can accomplish a
+    limited number of tasks but may need manual device resets and
+    such. Also, the Python binding is not smooth at all. (This is
+    my first use of Python, so there's still a bit of learning to
+    do.)
+
+* WARNING * WARNING * WARNING * WARNING * WARNING * WARNING * WARNING *
+
+
+Concept and use
+===============
+
+This suite supports instruments that speak SCPI or a dialect thereof
+using one of the following transport protocols:
+
+- TCP, in a TELNET variant
+- USBTMC
+- SCPI over a TTY (serial port)
+
+Low-level communication is handled by a set of C functions. The system
+is controlled by a Python script. Instruments can operate in two modes:
+
+- synchronous: the control script sends commands and retrieves responses
+  from the instrument.
+
+- asynchronous: the instrument sends a stream of data, which is
+  timestamped and written to a file. The control script executes
+  concurrently.
+
+
+Accessing an instrument
+-----------------------
+
+Communication to an instrument is established by creating an instance
+of the tmc.Instr class:
+
+    import tmc
+
+    instr = tmc.Instr(transport, arg, ...)
+
+
+"transport" is the transport to use, "tcp", "usb", or "serial".
+"arg" is a (possibly empty) list of arguments for the transport protocol
+handler. (See below for details.)
+
+
+Synchronous mode
+----------------
+
+Commands are sent to an instrument with
+
+    instr.send(command, ...)
+
+where "command" is one or more command strings.
+
+    instr.send(command1, command2)
+
+is equivalent to
+
+    instr.send(command1)
+    instr.send(command2)
+
+An instrument response is retrieved with
+
+    response = instr.read()
+
+Note that this function blocks until the complete response has been
+received from the instrument.
+
+
+Asynchronous mode
+-----------------
+
+Asynchronous mode is entered with
+
+    instr.start(filename)
+
+This creates the specified file and start streaming instrument output to
+the file. Instrument output is chopped into separate entries not only at
+message boundaries but also at each comma. Each entry is preceded by a
+timestamp (seconds.milliseconds) and ends with a newline.
+
+In asynchronous mode, instr.read cannot be used. However, messages can
+be send with instr.send, in particular:
+
+    instr.send('*TRG')
+
+The command
+
+    instr.stop()
+
+leaves asynchronous mode, closes the file, and sends a "device clear"
+(DCI) command to the instrument.
+
+
+Transport arguments
+===================
+
+
+tcp
+---
+
+The TCP transport accepts the following arguments:
+["tries=N",] hostname [, port]
+
+  tries=N	The number of times establishing the connection should
+		be attempted. This works around instruments spuriously
+		refusing a new connection, or (worse) resetting a
+		connection on the first read. By default, only one
+		attempt is made.
+
+  hostname	The name or IPv4 address to connect to.
+
+  port		The service name or port number to connect to. The
+		default port number is 3490.
+
+
+usb
+---
+
+The USBTMC transport accepts the following arguments:
+
+[bus=N,] [device=N,] [product=N,] [vendor=N,] [timeout=N,] [retry]
+
+   bus=N	The USB bus number. Default: any.
+   device=N	The number the device has been enumerated as. Default:
+		any.
+   product=N	The USB product ID. Default: any.
+   vendor=N	The USB vendor ID. Default: any.
+   timeout=N	Timeout per USB operation. Default: infinite.
+   retry	Retry after an input timeout.
+
+
+serial
+------
+
+The serial transport accepts the following arguments:
+
+[bps=N,] [crtscts,] device
+
+  bps=N		The speed of the serial interface. Default: use the
+		previous setting.
+  crtscts	Use RTS/CTS flow control: Default: turn it off.
+  device	Device 
+
+
+Instrument quirks
+=================
+
+Fluke 8845A
+-----------
+
+Accepts only one session at a time, and needs a brief pause between
+sessions. Attempts to connect too quickly yield "connection refused".
+Also, sometimes the connection gets reset when trying to read the
+first result from the device.
+
+Setting the option "tries=3" should work around all this.
+
+
+PicoTest M3500
+--------------
+
+When trying to use multiple internal triggers, only the first one is
+taken. It is thus not possible to stream an indefinite number of
+samples. Instead, PicoTest suggest to use a large SAMP:COUN and to
+re-issue READ? when running out of samples.
+
+
+Leaptronix mPP-3040D
+--------------------
+
+Speaks a weird little dialect of SCPI. The main quirks are that there
+is a response to each command, not only to queries, and that the colon
+that resets the command path is mandatory.
+
+
+Known bugs and missing features
+===============================
+
+Only a small part of the error recovery procedures required by USBTMC are
+implemented. More of this will be added later.
+
+The USBTMC specification seems a bit unclear about the treatment of bTag,
+so it may be that just starting with 1 in each session actually violates
+the protocol.
+
+Error handling is almost entirely absent and not at all what Python
+expects to be done. Need to learn more about this.
+
+Checking argument syntax deep down in transport protocol drivers is ugly
+at best.
+
+Passing an argument that doesn't want to be a string yields a segfault.
+
+Re-issuing READ? for the PicoTest M3500 should be implemented in the
+transport protocol driver.
+
+There should be a way to detect whether an asynchronous acquisition has
+completed, and also to synchronize with that event.
+
+The "telnet" protocol handler retries connections even if "tries" is set
+to one.
+
+The way how we select the protocol is probably very non-Python-ish. Should
+there be a per-module method for each protocol that returns a properly
+initialized object of type tmc.Instr instead ? Or maybe even a set of
+subclasses that inherit tmc.Instr ? The latter sounds a bit pompous for
+what little we try to accomplish here.
+
+The USBTMC driver doesn't implement most of the error handling and.
+
+The USBTMC driver doesn't implement DCI, so the device has to be reset
+manually.

Added: developers/werner/ahrt/host/tmc/examples/telnet.py
===================================================================
--- developers/werner/ahrt/host/tmc/examples/telnet.py	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/examples/telnet.py	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,31 @@
+#!/usr/bin/python
+
+#
+# Set up a Fluke 8845A connected via TCP/IP, host name "fluke", to take
+# continuous current measurements, stream them for five seconds to a file,
+# then stop.
+#
+
+import tmc, time
+
+d = tmc.Instr("telnet", "tries=3", "fluke")
+d.send('*IDN?')
+print d.read()
+
+d.send(
+  '*RST;'
+  ':SYST:REM;'
+  ':FUNC "CURR:DC";'
+  ':CURR:DC:RANG 1;'
+  ':CURR:RES 1e-4;'
+  ':CURR:NPLC 0.2;'
+  ':TRIG:SOUR IMM;'
+  ':TRIG:DEL:AUTO ON;'
+  ':TRIG:COUN INF;'
+  ':READ?')
+
+d.start("telnet.out")
+
+time.sleep(5)
+
+d.stop()


Property changes on: developers/werner/ahrt/host/tmc/examples/telnet.py
___________________________________________________________________
Name: svn:executable
   + *

Added: developers/werner/ahrt/host/tmc/examples/tty.py
===================================================================
--- developers/werner/ahrt/host/tmc/examples/tty.py	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/examples/tty.py	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,40 @@
+#!/usr/bin/python
+
+#
+# Set up a Leaptronix mPP-4030D connected via RS-232 over USB, device file
+# /dev/ttyUSB0, to output 1.234V limited to 10mA on its programmable output.
+#
+
+import tmc, time
+
+d = tmc.Instr("tty", "bps=9600", "/dev/ttyUSB0")
+d.send('*IDN?')
+print d.read()
+
+d.send('*RST')
+print d.read()
+
+d.send(':OUTPUT OFF')
+print d.read()
+
+d.send(':VSET #1 1.234')
+print d.read()
+
+# turning on the variable output also turns on the fixed output, so we set it
+# to the lowest possible voltage setting. Note that the voltage really has to
+# be followed by "V".
+
+d.send(':VSET #A 1.5V')
+print d.read()
+
+d.send(':ISET #1 0.010')
+print d.read()
+
+d.send(':OUTPUT ON')
+print d.read()
+
+d.send(':VSET #A OFF')
+print d.read()
+
+d.send(':VOUT? #1')
+print d.read()


Property changes on: developers/werner/ahrt/host/tmc/examples/tty.py
___________________________________________________________________
Name: svn:executable
   + *

Added: developers/werner/ahrt/host/tmc/examples/usbtmc.py
===================================================================
--- developers/werner/ahrt/host/tmc/examples/usbtmc.py	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/examples/usbtmc.py	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+
+#
+# Set up a PicoTest M3500 connected via USB, to take continuous voltage
+# measurements, stream them for five seconds to a file, then stop.
+#
+
+import tmc, time
+
+d = tmc.Instr("usbtmc", "timeout=2", "retry")
+d.send('*IDN?')
+print d.read()
+
+d.send(
+  '*RST;'
+  ':SYST:REM;'
+  ':FUNC "VOLT:DC";'
+  ':VOLT:DC:RANG 10;'
+  ':VOLT:RES 1e-4;'
+  ':VOLT:NPLC 0.2;'
+  ':SAMP:COUN 50000;'
+  ':READ?')
+
+d.start("usbtmc.out")
+
+time.sleep(5)
+
+d.stop()


Property changes on: developers/werner/ahrt/host/tmc/examples/usbtmc.py
___________________________________________________________________
Name: svn:executable
   + *

Added: developers/werner/ahrt/host/tmc/io.c
===================================================================
--- developers/werner/ahrt/host/tmc/io.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/io.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,200 @@
+/*
+ * io.c - I/O handler for SCPI responses
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "io.h"
+
+
+
+/* ----- Asynchronous I/O -------------------------------------------------- */
+
+
+#define BUF_SIZE 8192	/* this should rarely exceed ~16 bytes */
+
+
+struct io_async_buf {
+	int fd;
+	char buf[BUF_SIZE];
+	size_t len;
+	struct timeval tv;
+};
+
+
+void *io_setup_async(const char *file)
+{
+	struct io_async_buf *d;
+
+	d = malloc(sizeof(*d));
+	if (!d) {
+		perror("malloc");
+		return NULL;
+	}
+	d->len = 0;
+	d->fd = creat(file, 0666);
+	if (d->fd >= 0)
+		return d;
+	perror(file);
+	free(d);
+	return NULL;
+}
+
+
+static int do_write(int fd, const void *buf, size_t len)
+{
+	ssize_t wrote;
+
+	while (len) {
+		wrote = write(fd, buf, len);
+		if (wrote < 0) {
+			perror("write");
+			return -1;
+		}
+		if (!wrote) {
+			fprintf(stderr, "can't write\n");
+			return -1;
+		}
+		buf += wrote;
+		len -= wrote;
+	}
+	return 0;
+}
+
+
+static int do_tv(int fd, const struct timeval *tv)
+{
+	char buf[20];
+	ssize_t len;
+
+	len = sprintf(buf, "%lu.%06lu ",
+	    (unsigned long) tv->tv_sec, (unsigned long) tv->tv_usec);
+	return do_write(fd, buf, len);
+}
+
+
+void io_push_async(void *dsc, const struct timeval *tv, const void *buf,
+    size_t len, int end)
+{
+	struct io_async_buf *d = dsc;
+	const void *p;
+
+//fprintf(stderr, "(%*s) %d\n", len, buf, end);
+	while (len) {
+		p = memchr(buf, ',', len);
+		if (!p && end)
+			p = buf+len;
+		if (!p) {
+			if (d->len+len > BUF_SIZE) {
+				fprintf(stderr, "buffer overflow (%u bytes)\n",
+				    (int) (d->len+len));
+				abort();
+			}
+			if (!d->len)
+				d->tv = *tv;
+			memcpy(d->buf+d->len, buf, len);
+			d->len += len;
+			return;
+		}
+		/* @@@FIXME: consider using pwrite */
+		if (!d->len)
+			do_tv(d->fd, tv);
+		else {
+			do_tv(d->fd, &d->tv);
+			do_write(d->fd, d->buf, d->len);
+			d->len = 0;
+		}
+		do_write(d->fd, buf, p-buf);
+		do_write(d->fd, "\n", 1);
+		if (p == buf+len)
+			return;
+		len -= p-buf+1;
+		buf = p+1;
+	}
+}
+
+
+void io_end_async(void *dsc)
+{
+	struct io_async_buf *d = dsc;
+
+	if (close(d->fd) < 0)
+		perror("close");
+	free(d);
+}
+
+
+/* ----- Synchronous I/O --------------------------------------------------- */
+
+
+struct io_sync_buf {
+	char *buf;
+	size_t size;
+};
+
+
+void *io_create_buf(void)
+{
+	struct io_sync_buf *d;
+
+	d = malloc(sizeof(*d));
+	if (!d) {
+		perror("malloc");
+		return NULL;
+	}
+	d->buf = NULL;
+	d->size = 0;
+	return d;
+}
+
+
+void io_push_buf(void *dsc, const struct timeval *tv, const void *buf,
+    size_t len, int end)
+{
+	struct io_sync_buf *d = dsc;
+
+//fprintf(stderr, "(%.*s) %d %d\n", len, buf, len, end);
+	if (!len)
+		return;
+	d->buf = realloc(d->buf, d->size+len);
+	if (!d->buf) {
+		perror("realloc");
+		abort();
+	}
+	memcpy(d->buf+d->size, buf, len);
+	d->size += len;
+}
+
+
+size_t io_read_buf(void *dsc, void *buf, size_t size)
+{
+	struct io_sync_buf *d = dsc;
+	size_t len = d->size;
+
+	if (len > size) {
+		fprintf(stderr,
+		    "io_read_buf: %u byte buffer too small for %u byte "
+		    "message\n", (unsigned) size, (unsigned) len);
+		return -1;
+	}
+	memcpy(buf, d->buf, len);
+//fprintf(stderr, "(%.*s) %d\n", len, d->buf, len);
+	free(d);
+	return len;
+}

Added: developers/werner/ahrt/host/tmc/io.h
===================================================================
--- developers/werner/ahrt/host/tmc/io.h	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/io.h	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,48 @@
+/*
+ * io.h - I/O handler for SCPI responses
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef IO_H
+#define IO_H
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+
+typedef void io_push_fn(void *dsc, const struct timeval *tv,
+    const void *buf, size_t len, int end);
+
+void *io_setup_async(const char *file);
+void io_push_async(void *dsc, const struct timeval *tv, const void *buf,
+    size_t len, int end);
+void io_end_async(void *dsc);
+
+void *io_create_buf(void);
+void io_push_buf(void *dsc, const struct timeval *tv, const void *buf,
+    size_t len, int end);
+size_t io_read_buf(void *dsc, void *buf, size_t size);
+
+
+struct proto_ops {
+	void *(*open)(int argc, const char **argv); 
+	void (*close)(void *dsc);
+	int (*write)(void *dsc, const void *buf, size_t len);
+	int (*read)(void *dsc, io_push_fn *push, void *push_dsc);
+	int (*dci)(void *dsc);
+};
+
+
+extern struct proto_ops telnet_ops; /* TELNET-like */
+extern struct proto_ops usbtmc_ops; /* USBTMC */
+extern struct proto_ops tty_ops; /* TTY */
+
+#endif /* !IO_H */

Added: developers/werner/ahrt/host/tmc/python.c
===================================================================
--- developers/werner/ahrt/host/tmc/python.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/python.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,227 @@
+/*
+ * python.c - Python binding for TMC functions
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include <Python.h>
+
+#include "tmc.h"
+
+
+#define BUF_SIZE 8192
+#define ERROR fprintf(stderr, "ERROR %d\n", __LINE__)
+	/* @@@FIXME: raise exceptions */
+
+struct py_instr {
+	PyObject_HEAD
+	struct tmc_dsc *instr;
+};
+
+
+
+static char **make_argv(int skip, PyObject *args, int *argc)
+{
+	char **argv;
+	PyObject *obj;
+	int i;
+
+	*argc = PyTuple_Size(args)-skip;
+	if (*argc < 0)
+		return NULL;
+	argv = malloc(sizeof(char *)* *argc);
+	if (!argv) {
+		PyErr_NoMemory();
+		return NULL;
+	}
+	for (i = 0; i != *argc; i++) {
+		obj = PyTuple_GetItem(args, skip+i);
+		argv[i] = PyString_AsString(obj);
+	}
+	return argv;
+}
+
+
+static PyObject *tmc_tp_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+	struct py_instr *s;
+
+fprintf(stderr, "tmc_tp_new\n");
+	s = (struct py_instr *) type->tp_alloc(type, 0);
+	if (s)
+		s->instr = NULL;
+	return (PyObject *) s;
+}
+
+
+static int tmc_tp_init(PyObject *self, PyObject *args, PyObject *kwds)
+{
+	struct py_instr *s = (struct py_instr *) self;
+	char **argv;
+	int argc;
+	const struct proto_ops *ops;
+
+	if (s->instr) {
+		ERROR;
+		fprintf(stderr, "tmc_tp_init: already initialized\n");
+		return -1;
+	}
+	argv = make_argv(0, args, &argc);
+	if (!argv) {
+		ERROR;
+		return -1;
+	}
+	if (!argc) {
+		ERROR;
+		free(argv);
+		return -1;
+	}
+	ops = tmc_ops(*argv);
+	if (!ops) {
+		ERROR;
+		free(argv);
+		return -1;
+	}
+	s->instr = tmc_open(ops, argc-1, (const char **) argv+1);
+	free(argv);
+	if (s->instr)
+		return 0;
+	ERROR;
+	return -1;
+}
+
+
+static void tmc_tp_dealloc(PyObject *self)
+{
+	struct py_instr *s = (struct py_instr *) self;
+
+	if (s->instr)
+		tmc_close(s->instr);
+}
+
+
+static PyObject *tmc_py_send(PyObject *self, PyObject *args)
+{
+	struct py_instr *s = (struct py_instr *) self;
+	char **argv;
+	int argc;
+
+//fprintf(stderr, "tmc_py_send\n");
+	if (!s->instr) {
+		ERROR;
+		return NULL;
+	}
+	argv = make_argv(0, args, &argc);
+	if (!argv) {
+		ERROR;
+		return NULL;
+	}
+	if (tmc_send(s->instr, argc, (const char **) argv)) {
+		ERROR;
+		return NULL;
+	}
+	free(argv);
+	return Py_BuildValue("");
+}
+
+
+static PyObject *tmc_py_read(PyObject *self, PyObject *noarg)
+{
+	struct py_instr *s = (struct py_instr *) self;
+	static char buf[BUF_SIZE+1];
+	int len;
+
+//fprintf(stderr, "tmc_py_read\n");
+	if (!s->instr) {
+		ERROR;
+		return NULL;
+	}
+	len = tmc_read(s->instr, buf, BUF_SIZE);
+	if (len < 0) {
+		ERROR;
+		return NULL;
+	}
+	buf[len] = 0;
+	return Py_BuildValue("s", buf);
+}
+
+
+static PyObject *tmc_py_start(PyObject *self, PyObject *arg)
+{
+	struct py_instr *s = (struct py_instr *) self;
+
+	if (!s->instr) {
+		ERROR;
+		return NULL;
+	}
+	if (tmc_start(s->instr, PyString_AsString(arg))) {
+		ERROR;
+		return NULL;
+	}
+	return Py_BuildValue("");
+}
+
+
+static PyObject *tmc_py_stop(PyObject *self, PyObject *noarg)
+{
+	struct py_instr *s = (struct py_instr *) self;
+
+	if (!s->instr) {
+		ERROR;
+		return NULL;
+	}
+	if (tmc_stop(s->instr)) {
+		ERROR;
+		return NULL;
+	}
+	return Py_BuildValue("");
+}
+
+
+static PyMethodDef tmc_tp_methods[] = {
+	{ "send",	tmc_py_send,	METH_VARARGS,	"Send commands" },
+	{ "read",	tmc_py_read,	METH_NOARGS,	"Read a response" },
+	{ "start",	tmc_py_start,	METH_O,		"Start async logging" },
+	{ "stop",	tmc_py_stop,	METH_NOARGS,	"Stop async logging" },
+	{ NULL, NULL, 0, NULL }
+};
+
+
+static PyTypeObject tmc_instr_type = {
+	PyObject_HEAD_INIT(NULL)
+	.tp_name	= "tmc.Instr",
+	.tp_basicsize	= sizeof(struct py_instr),
+	.tp_flags	= Py_TPFLAGS_DEFAULT,
+	.tp_doc		= "TMC objects",
+	.tp_new		= tmc_tp_new,
+	.tp_init	= tmc_tp_init,
+	.tp_dealloc	= tmc_tp_dealloc,
+	.tp_methods	= tmc_tp_methods,
+};
+
+
+static PyMethodDef tmc_methods[] = {
+	{ "stop",	tmc_py_stop,	METH_NOARGS,	"Stop async logging" },
+	{ NULL, NULL, 0, NULL }
+};
+
+
+PyMODINIT_FUNC inittmc(void)
+{
+	PyObject *m;
+
+	tmc_instr_type.tp_new = PyType_GenericNew;
+	if (PyType_Ready(&tmc_instr_type) < 0)
+		return;
+	m = Py_InitModule3("tmc", tmc_methods, "Test & Mesurement Control");
+	Py_INCREF(&tmc_instr_type);
+	PyModule_AddObject(m, "Instr", (PyObject *) &tmc_instr_type);
+}

Added: developers/werner/ahrt/host/tmc/setup.py
===================================================================
--- developers/werner/ahrt/host/tmc/setup.py	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/setup.py	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,9 @@
+from distutils.core import setup, Extension
+
+setup(name="tmc",
+    version="1.0",
+    ext_modules=[
+	Extension("tmc",
+            ["python.c", "tmc.c", "io.c", "telnet.c", "usbtmc.c", "tty.c"],
+            extra_compile_args=["-Wall"],
+	    libraries=["usb"])])

Added: developers/werner/ahrt/host/tmc/telnet.c
===================================================================
--- developers/werner/ahrt/host/tmc/telnet.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/telnet.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,302 @@
+/*
+ * telnet.c - Send and receive SCPI messages over a TELNET (TCP) connection
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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 code differs from generic handling of a data transfer over TCP in the
+ * following ways:
+ *
+ * - adds CRLF when sending,
+ * - strips CRLF when receiving,
+ * - tried to connect multiple times if the connection is refused,
+ * - reconnects and retransmits the last message if the connection is reset.
+ *
+ * The last two features are needed to reliably begin talking to a Fluke 8845A.
+ * Once the session is established, no such anomalies have been observed.
+ */
+
+/*
+ * Known issues:
+ * - recovery logic may allow more retries than configured (need to check)
+ * - retry delay should be configurable
+ * - argument passing concept is a bit ugly
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "io.h"
+
+
+#define BUF_SIZE 1024
+
+#define DEFAULT_PORT "3490"
+#define DEFAULT_TRIES 1
+
+
+struct telnet_dsc {
+	int sd;		/* socket descriptor */
+	int crlf;	/* 0 = in message, 1 = CR seen, 2 = CRLF seen */
+	int max_tries;	/* number of times we try to set up the connection */
+	int tries_left;	/* number of attempts left */
+	struct sockaddr_in addr; /* peer address */
+	void *last_buf;	/* last buffer sent */
+	int last_len;
+};
+
+
+static void usage(void)
+{
+	fprintf(stderr, "usage: \"telnet\", [tries=N,] host [, port]\n");
+}
+
+
+static int do_connect(struct telnet_dsc *d)
+{
+	while (1) {
+		d->sd = socket(PF_INET, SOCK_STREAM, 0);
+		if (d->sd < 0) {
+			perror("socket");
+			return -1;
+		}
+
+		if (connect(d->sd,
+		    (struct sockaddr *) &d->addr, sizeof(d->addr)) >= 0)
+			return 0;
+		if (errno != ECONNREFUSED || d->tries_left == 1) {
+			perror("connect");
+			if (close(d->sd) < 0)
+				perror("close");
+			return -1;
+		}
+		if (d->tries_left)
+			d->tries_left--;
+		sleep(1);
+	}
+}
+
+
+static void *telnet_open(int argc, const char **argv)
+{
+	struct telnet_dsc *d;
+	const char *host, *port;
+	int tries = DEFAULT_TRIES;
+	const struct hostent *he;
+	const struct servent *se;
+	unsigned long num;
+	char *end;
+
+	if (argc && strchr(argv[0], '=')) {
+		if (strncmp(argv[0], "tries=", 6)) {
+			usage();
+			return NULL;
+		}
+		tries = strtoul(argv[0]+6, &end, 0);
+		if (*end) {
+			usage();
+			return NULL;
+		}
+		argc--;
+		argv++;
+	}
+	if (!argc || argc > 2) {
+		usage();
+		return NULL;
+	}
+	host = argv[0];
+	port = argc == 2 ? argv[1] : DEFAULT_PORT;
+
+	d = malloc(sizeof(struct telnet_dsc));
+	if (!d) {
+		perror("malloc");
+		return NULL;
+	}
+	d->crlf = 0;
+
+	d->addr.sin_family = AF_INET;
+	d->addr.sin_addr.s_addr = inet_addr(host);
+	if (d->addr.sin_addr.s_addr == INADDR_NONE) {
+		he = gethostbyname(host);
+		if (!he) {
+			fprintf(stderr, "unknown host \"%s\"\n", host);
+			goto fail;
+		}
+		d->addr.sin_addr = *(struct in_addr *) he->h_addr;
+	}
+
+	se = getservbyname(port, "tcp");
+	if (se)
+		d->addr.sin_port = se->s_port;
+	else {
+		num = strtoul(port, &end, 0);
+		if (!num || num > 0xffff || *end) {
+			fprintf(stderr, "invalid port \"%s\"\n", port);
+			goto fail;
+		}
+		d->addr.sin_port = htons(num);
+	}
+	
+	d->max_tries = tries;
+	d->tries_left = tries;
+	d->last_buf = NULL;
+	if (!do_connect(d))
+		return d;
+
+fail:
+	free(d);
+	return NULL;
+}
+
+
+static void telnet_close(void *dsc)
+{
+	struct telnet_dsc *d = dsc;
+
+	if (close(d->sd) < 0)
+		perror("close");
+	if (d->last_buf)
+		free(d->last_buf);
+}
+
+
+static int do_write(int sd, const void *buf, size_t len)
+{
+	ssize_t wrote;
+
+	while (len) {
+		wrote = write(sd, buf, len);
+		if (wrote < 0) {
+			perror("write");
+			return -1;
+		}
+		if (!wrote) {
+			fprintf(stderr, "write: wrote 0 bytes\n");
+			return -1;
+		}
+		buf += wrote;
+		len -= wrote;
+	}
+	return 0;
+}
+
+
+static int telnet_write(void *dsc, const void *buf, size_t len)
+{
+	struct telnet_dsc *d = dsc;
+	void *tmp;
+
+	tmp = malloc(len);
+	if (!tmp) {
+		perror("malloc");
+		return -1;
+	}
+	memcpy(tmp, buf, len);
+	if (d->last_buf)
+		free(d->last_buf);
+	d->last_buf = tmp;
+	d->last_len = len;
+
+	/* "buf" may be d->last_buf, so use the copy now */
+	if (do_write(d->sd, tmp, len) < 0)
+		return -1;
+	return do_write(d->sd, "\r\n", 2);
+}
+
+
+static int telnet_read(void *dsc, io_push_fn *push, void *push_dsc)
+{
+	struct telnet_dsc *d = dsc;
+	char buf[BUF_SIZE];
+	struct timeval tv;
+	ssize_t got;
+	char *p;
+
+	do {
+		got = read(d->sd, buf, BUF_SIZE);
+		if (got < 0 && errno == ECONNRESET &&
+		    (d->tries_left || !d->max_tries)) {
+			(void) close(d->sd);
+			if (d->tries_left)
+				d->tries_left--;
+			if (do_connect(d) < 0)
+				return -1;
+			if (telnet_write(dsc, d->last_buf, d->last_len) < 0)
+				return -1;
+			continue;
+		}
+		if (got < 0) {
+			perror("read");
+			return -1;
+		}
+		if (!got) {
+			fprintf(stderr, "read: unexpected EOF\n");
+			return -1;
+		}
+		if (gettimeofday(&tv, NULL) < 0) {
+			perror("gettimeofday");
+			return -1;
+		}
+		for (p = buf; p != buf+got; p++)
+			switch (*p) {
+			case '\r':
+				if (d->crlf)
+					fprintf(stderr,
+					    "warning: unexpected \\r\n");
+				d->crlf = 1;
+				break;
+			case '\n':
+				if (d->crlf != 1)
+					fprintf(stderr,
+					    "warning: unexpected \\n\n");
+				d->crlf = 2;
+				break;
+			default:
+				if (d->crlf)
+					fprintf(stderr,
+					    "warning: data after \\r\\n\n");
+				d->crlf = 0;
+				break;
+			}
+		if (d->crlf <= got)
+			push(push_dsc, &tv, buf, got-d->crlf, d->crlf == 2);
+	}
+	while (d->crlf != 2);
+	d->crlf = 0;
+	return 0;
+}
+
+
+static int telnet_dci(void *dsc)
+{
+	struct telnet_dsc *d = dsc;
+
+	return do_write(d->sd, "\003", 1);
+}
+
+
+struct proto_ops telnet_ops = {
+	.open	= telnet_open,
+	.close	= telnet_close,
+	.read	= telnet_read,
+	.write	= telnet_write,
+	.dci	= telnet_dci,
+};

Added: developers/werner/ahrt/host/tmc/tmc.c
===================================================================
--- developers/werner/ahrt/host/tmc/tmc.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/tmc.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,146 @@
+/*
+ * tmc.c - Device-independent TMC functions
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "io.h"
+#include "tmc.h"
+
+
+struct tmc_dsc {
+	const struct proto_ops *ops;	/* protocol operations */
+	void *proto_dsc;		/* protocol descriptor */
+	void *io_dsc;			/* I/O descriptor */
+	int running;
+	pthread_t thread;
+};
+
+
+struct tmc_dsc *tmc_open(const struct proto_ops *ops,
+    int argc, const char **argv)
+{
+	struct tmc_dsc *dsc;
+
+	dsc = malloc(sizeof(*dsc));
+	if (!dsc) {
+		perror("malloc");
+		return NULL;
+	}
+	dsc->ops = ops;
+	dsc->proto_dsc = ops->open(argc, argv);
+	dsc->running = 0;
+	if (dsc->proto_dsc)
+		return dsc;
+	free(dsc);
+	return NULL;
+}
+
+
+int tmc_close(struct tmc_dsc *dsc)
+{
+	if (dsc->running)
+		tmc_stop(dsc);
+	dsc->ops->close(dsc->proto_dsc);
+	return 0;
+}
+
+
+int tmc_send(struct tmc_dsc *dsc, int argc, const char **argv)
+{
+	int i;
+
+	for (i = 0; i != argc; i++)
+		if (dsc->ops->write(dsc->proto_dsc, argv[i], strlen(argv[i])))
+			return -1;
+	return 0;
+}
+
+
+int tmc_read(struct tmc_dsc *dsc, void *buf, ssize_t size)
+{
+	void *io_dsc;
+
+	if (dsc->running) {
+		fprintf(stderr, "can't read in asynchronous mode\n");
+		return -1;
+	}
+	io_dsc = io_create_buf();
+	/* @@@FIXME: handle read error */
+	dsc->ops->read(dsc->proto_dsc, io_push_buf, io_dsc);
+	return io_read_buf(io_dsc, buf, size);
+}
+
+
+static void *launch(void *arg)
+{
+	struct tmc_dsc *dsc = arg;
+
+	dsc->ops->read(dsc->proto_dsc, io_push_async, dsc->io_dsc);
+	return NULL;
+}
+
+
+int tmc_start(struct tmc_dsc *dsc, const char *file)
+{
+	if (dsc->running) {
+		fprintf(stderr, "tmc_start: already running\n");
+		return -1;
+	}
+	dsc->io_dsc = io_setup_async(file);
+	if (!dsc->io_dsc)
+		return -1;
+	dsc->running = 1;
+	pthread_create(&dsc->thread, NULL, launch, dsc);
+	return 0;
+}
+
+
+int tmc_stop(struct tmc_dsc *dsc)
+{
+	int err;
+
+	if (!dsc->running) {
+		fprintf(stderr, "tmc_stop: not running\n");
+		return -1;
+	}
+	err = pthread_cancel(dsc->thread);
+	if (err) {
+		fprintf(stderr, "pthread_cancel: %s\n", strerror(err));
+	}
+	dsc->running = 0;
+	dsc->ops->dci(dsc->proto_dsc);
+	return 0;
+}
+
+
+const struct proto_ops *tmc_ops(const char *name)
+{
+	static struct {
+		const char *name;
+		const struct proto_ops *ops;
+	} ops[] = {
+		{ "tty",	&tty_ops },
+		{ "telnet",	&telnet_ops },
+		{ "usbtmc", 	&usbtmc_ops },
+		{ NULL,		NULL }
+	}, *p;
+
+	for (p = ops; p->name; p++)
+		if (!strcmp(p->name, name))
+			return p->ops;
+	return NULL;
+}

Added: developers/werner/ahrt/host/tmc/tmc.h
===================================================================
--- developers/werner/ahrt/host/tmc/tmc.h	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/tmc.h	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,38 @@
+/*
+ * tmc.h - Device-independent TMC functions
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef TMC_H
+#define TMC_H
+
+#include <sys/types.h>
+
+#include "io.h"
+
+
+struct tmc_dsc;
+
+struct tmc_dsc *tmc_open(const struct proto_ops *ops,
+    int argc, const char **argv);
+int tmc_close(struct tmc_dsc *dsc);
+int tmc_send(struct tmc_dsc *dsc, int argc, const char **argv);
+int tmc_read(struct tmc_dsc *dsc, void *buf, ssize_t size);
+int tmc_start(struct tmc_dsc *dsc, const char *file);
+int tmc_stop(struct tmc_dsc *dsc);
+
+/*
+ * This should probably go somewhere else ...
+ */
+
+const struct proto_ops *tmc_ops(const char *name);
+
+#endif /* !TMC_H */

Added: developers/werner/ahrt/host/tmc/tty.c
===================================================================
--- developers/werner/ahrt/host/tmc/tty.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/tty.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,291 @@
+/*
+ * tty.c - Send and receive SCPI messages over a TTY device
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/types.h>
+
+#include "io.h"
+
+
+#define BUF_SIZE 1024
+
+
+struct tty_dsc {
+	int fd;		/* file descriptor */
+	int crlf;	/* 0 = in message, 1 = CR seen, 2 = CRLF seen */
+	struct termios attr;
+};
+
+
+static int bps_to_speed(speed_t *speed, unsigned long bps)
+{
+	static struct {
+		speed_t speed;
+		int bps;
+	} table[] = {
+		{ B300,	      300 },
+		{ B1200,     1200 },
+		{ B2400,     2400 },
+		{ B9600,     9600 },
+		{ B19200,   19200 },
+		{ B38400,   38400 },
+		{ B57600,   57600 },
+		{ B115200, 115200 },
+		{      0,       0 }
+	}, *p;
+
+	for (p = table; p->bps; p++)
+		if (p->bps == bps) {
+			*speed = p->speed;
+			return 0;
+		}
+	fprintf(stderr, "unknown speed: %lubps\n", bps);
+	return -1;
+}
+
+
+static void *tty_open(int argc, const char **argv)
+{
+	struct tty_dsc *d;
+	unsigned long bps = 0;
+	speed_t speed = 0; /* initialize to keep gcc from complaining */
+	int crtscts = 0;
+	char *end;
+	int i;
+	struct termios attr;
+
+	for (i = 0; i != argc; i++) {
+		if (!strcmp(argv[i], "crtscts"))
+			crtscts = 1;
+		else if (!strncmp(argv[i], "bps=", 4)) {
+			bps = strtoul(argv[i]+4, &end, 0);
+			if (*end)
+				goto usage;
+			if (bps_to_speed(&speed, bps) < 0)
+				goto usage;
+		}
+		else if (strchr(argv[i], '='))
+			goto usage;
+		else
+			break;
+	}
+	if (i != argc-1)
+		goto usage;
+
+	d = malloc(sizeof(struct tty_dsc));
+	if (!d) {
+		perror("malloc");
+		return NULL;
+	}
+	d->crlf = 0;
+
+	d->fd = open(argv[i], O_RDWR | O_NOCTTY, 0);
+	if (d->fd < 0) {
+		perror(argv[1]);
+		goto fail;
+	}
+
+	if (tcgetattr(d->fd, &d->attr) < 0) {
+		perror("tcgetattr");
+		goto fail_fd;
+	}
+	attr = d->attr;
+	cfmakeraw(&attr);
+	if (bps &&
+	    (cfsetispeed(&attr, speed) < 0 || cfsetospeed(&attr, speed) < 0)) {
+		perror("cfsetispeed");
+		goto fail_fd;
+	}
+	attr.c_cflag = (attr.c_cflag & ~CRTSCTS) | (crtscts ? CRTSCTS : 0);
+	if (tcsetattr(d->fd, TCSAFLUSH, &attr) < 0) {
+		perror("tcsetattr");
+		goto fail_fd;
+	}
+	return d;
+
+usage:
+	fprintf(stderr, "usage: \"tty\", [crtscts,] [bps=N,] device\n");
+	return NULL;
+
+fail_fd:
+	if (close(d->fd) < 0)
+		perror("close");
+fail:
+	free(d);
+	return NULL;
+}
+
+
+static void tty_close(void *dsc)
+{
+	struct tty_dsc *d = dsc;
+
+	if (tcsetattr(d->fd, TCSAFLUSH, &d->attr) < 0)
+		perror("tcsetattr");
+	if (close(d->fd) < 0)
+		perror("close");
+}
+
+
+static int do_write(int fd, const void *buf, size_t len)
+{
+	ssize_t wrote;
+
+	while (len) {
+		wrote = write(fd, buf, len);
+		if (wrote < 0) {
+			perror("write");
+			return -1;
+		}
+		if (!wrote) {
+			fprintf(stderr, "write: wrote 0 bytes\n");
+			return -1;
+		}
+		buf += wrote;
+		len -= wrote;
+	}
+	return 0;
+}
+
+
+/*
+ * We carefully check the echo to make sure nothing evil has happened to the
+ * command we just sent. E.g., if this is a power supply, we wouldn't want it
+ * to output, say, 9.2V instead of 1.2V, just because a bit was flipped.
+ */
+
+
+static int expect(int fd, const void *msg, size_t len)
+{
+	char buf[BUF_SIZE];
+	ssize_t got;
+
+	while (len) {
+		got = read(fd, buf, sizeof(buf));
+		if (got < 0) {
+			perror("read");
+			return -1;
+		}
+		if (!got) {
+			fprintf(stderr, "read: unexpected EOF\n");
+			return -1;
+		}
+		if (got > len) {
+			fprintf(stderr, "got %d, expected no more than %d\n",
+			    (int) got, (int) len);
+			return -1;
+		}
+		if (memcmp(buf, msg, got)) {
+			fprintf(stderr, "echo does not match message\n");
+			return -1;
+		}
+		msg += got;	
+		len -= got;
+	}
+	return 0;
+}
+
+
+static int tty_write(void *dsc, const void *buf, size_t len)
+{
+	struct tty_dsc *d = dsc;
+
+	if (do_write(d->fd, buf, len) < 0)
+		return -1;
+#if 0
+	if (expect(d->fd, buf, len) < 0)
+		return -1;
+#endif
+	if (do_write(d->fd, "\r\n", 2) < 0)
+		return -1;
+#if 0
+	if (expect(d->fd, "\r\n", 2) < 0)
+		return -1;
+#endif
+	return 0;
+}
+
+
+static int tty_read(void *dsc, io_push_fn *push, void *push_dsc)
+{
+	struct tty_dsc *d = dsc;
+	char buf[BUF_SIZE];
+	struct timeval tv;
+	ssize_t got;
+	char *p;
+
+	do {
+		got = read(d->fd, buf, BUF_SIZE);
+		if (got < 0) {
+			perror("read");
+			return -1;
+		}
+		if (!got) {
+			fprintf(stderr, "read: unexpected EOF\n");
+			return -1;
+		}
+		if (gettimeofday(&tv, NULL) < 0) {
+			perror("gettimeofday");
+			return -1;
+		}
+		for (p = buf; p != buf+got; p++)
+			switch (*p) {
+			case '\r':
+				if (d->crlf)
+					fprintf(stderr,
+					    "warning: unexpected \\r\n");
+				d->crlf = 1;
+				break;
+			case '\n':
+				if (d->crlf != 1)
+					fprintf(stderr,
+					    "warning: unexpected \\n\n");
+				d->crlf = 2;
+				break;
+			default:
+				if (d->crlf)
+					fprintf(stderr,
+					    "warning: data after \\r\\n\n");
+				d->crlf = 0;
+				break;
+			}
+		if (d->crlf <= got)
+			push(push_dsc, &tv, buf, got-d->crlf, d->crlf == 2);
+	}
+	while (d->crlf != 2);
+	d->crlf = 0;
+	return 0;
+}
+
+
+static int tty_dci(void *dsc)
+{
+	struct tty_dsc *d = dsc;
+
+	return do_write(d->fd, "\003", 1);
+}
+
+
+struct proto_ops tty_ops = {
+	.open	= tty_open,
+	.close	= tty_close,
+	.read	= tty_read,
+	.write	= tty_write,
+	.dci	= tty_dci,
+};

Added: developers/werner/ahrt/host/tmc/usbtmc.c
===================================================================
--- developers/werner/ahrt/host/tmc/usbtmc.c	2008-03-13 15:38:39 UTC (rev 4204)
+++ developers/werner/ahrt/host/tmc/usbtmc.c	2008-03-14 02:42:49 UTC (rev 4205)
@@ -0,0 +1,555 @@
+/*
+ * usbtmc.c - Partial implementation of the Universal Serial Bus Test and
+ *            Measurement Class Specification (USBTMC)
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <werner at openmoko.org>
+ * All Rights Reserved
+ *
+ * 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.
+ *
+ * Based on the Agilent USBTMC kernel driver by Stefan Kopp:
+ * http://www.home.agilent.com/upload/cmc_upload/All/usbtmc.html
+ * http://www.home.agilent.com/upload/cmc_upload/All/usbtmc.tar
+ */
+
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <usb.h>
+
+#include "io.h"
+
+
+#ifndef USB_CLASS_APP_SPEC
+#define USB_CLASS_APP_SPEC 0xfe
+#endif
+
+#ifndef USB_SUBCLASS_USBTMC
+#define USB_SUBCLASS_USBTMC 0x03
+#endif
+
+#ifndef USB_PROTOCOL_USB488
+#define USB_PROTOCOL_USB488 1
+#endif
+
+#define SIZE_IOBUFFER 4096
+#define DEV_DEP_MSG_OUT	1
+#define DEV_DEP_MSG_IN	2
+#define INITIATE_ABORT_BULK_IN 3
+
+
+struct usbtmc_dsc {
+	usb_dev_handle *handle;		/* USB device handle */
+	uint8_t ep_int_in, ep_bulk_in, ep_bulk_out; /* endpoints */
+	uint8_t bTag;			/* sequence number */
+	unsigned long timeout;		/* timeout in milliseconds */
+	int retry;			/* retry after timeouts */
+	void *last_buf;			/* last buffer sent */
+	int last_len;
+};
+
+
+static int debug = 0;
+
+
+/* ----- Debugging --------------------------------------------------------- */
+
+
+static void dump(const char *label, void *data, size_t size)
+{
+	int i;
+
+	if (!debug)
+		return;
+	fprintf(stderr, "%s (%d)", label, (int) size);
+	for (i = 0; i != size; i++) {
+		if (!(i & 15))
+			fprintf(stderr, "\n%04x ", i);
+		fprintf(stderr, " %02x", ((uint8_t *) data)[i]);
+	}
+	fprintf(stderr, "\n");
+}
+
+
+/* ----- Device selection -------------------------------------------------- */
+
+
+static void multiple_ep(const char *type)
+{
+	fprintf(stderr,
+	    "multiple %s endpoints found\n", type);
+}
+
+
+static void multiple_dev(void)
+{
+	fprintf(stderr,
+	    "multiple devices found. Please add more qualifiers\n");
+}
+
+
+static int get_eps(struct usbtmc_dsc *d,
+    const struct usb_interface_descriptor *alt)
+{
+	const struct usb_endpoint_descriptor *ep;
+	uint8_t type_dir;
+
+	for (ep = alt->endpoint;
+	    ep != alt->endpoint+alt->bNumEndpoints;
+	    ep++) {
+		type_dir = (ep->bmAttributes & USB_ENDPOINT_TYPE_MASK) |
+		  (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK);
+		switch (type_dir) {
+		case USB_ENDPOINT_TYPE_INTERRUPT | USB_ENDPOINT_IN:
+			if (d->ep_int_in) {
+				multiple_ep("Interrupt-IN");
+				return -1;
+			}
+			d->ep_int_in = ep->bEndpointAddress;
+			break;
+		case USB_ENDPOINT_TYPE_BULK | USB_ENDPOINT_IN:
+			if (d->ep_bulk_in) {
+				multiple_ep("Bulk-IN");
+				return -1;
+			}
+			d->ep_bulk_in = ep->bEndpointAddress;
+			break;
+		case USB_ENDPOINT_TYPE_BULK:
+			if (d->ep_bulk_out) {
+				multiple_ep("Bulk-OUT");
+				return -1;
+			}
+			d->ep_bulk_out = ep->bEndpointAddress;
+			break;
+		default:
+			fprintf(stderr,
+			    "endpoint type 0x%02x is not supported\n",
+			    type_dir);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+static int open_usbtmc_alt(struct usbtmc_dsc *d,
+    struct usb_device *dev,
+    const struct usb_config_descriptor *cfg,
+    const struct usb_interface *itf,
+    struct usb_interface_descriptor *alt)
+{
+	int error;
+
+	if (alt->bDescriptorType != USB_DT_INTERFACE)
+		return 0;
+	if (alt->bAlternateSetting)
+		return 0;
+	if (alt->bInterfaceClass != USB_CLASS_APP_SPEC)
+		return 0;
+	if (alt->bInterfaceSubClass != USB_SUBCLASS_USBTMC)
+		return 0;
+	if (alt->bInterfaceProtocol &&
+	    alt->bInterfaceProtocol != USB_PROTOCOL_USB488)
+		return 0;
+
+	if (d->handle) {
+		multiple_dev();
+		return -1;
+	}
+	d->handle = usb_open(dev);
+	if (!d->handle) {
+		fprintf(stderr, "cannot open device\n");
+		return -1;
+	}
+	error = usb_set_configuration(d->handle, cfg->bConfigurationValue);
+	if (error) {
+		fprintf(stderr, "cannot set configuration (error %d)\n",
+		    error);
+		return -1;
+	}
+	error = usb_claim_interface(d->handle, alt->bInterfaceNumber);
+	if (error) {
+		fprintf(stderr, "cannot claim interface (error %d)\n", error);
+		return -1;
+	}
+	error = usb_set_altinterface(d->handle, alt->bAlternateSetting);
+	if (error) {
+		fprintf(stderr, "cannot set alternate setting (error %d)\n",
+		    error);
+		return -1;
+	}
+
+	if (get_eps(d, alt) < 0)
+		return -1;
+
+	/* amazingly enough, after all this searching, we have a device */
+	return 0;
+}
+
+  
+static int open_usbtmc_dev(struct usbtmc_dsc *d,
+    struct usb_device *dev)
+{
+	const struct usb_config_descriptor *cfg;
+	const struct usb_interface *itf;
+	struct usb_interface_descriptor *alt;
+
+	if (dev->descriptor.bDeviceClass ||
+	    dev->descriptor.bDeviceSubClass ||
+	    dev->descriptor.bDeviceProtocol)
+		return 0;
+	/* seems that libusb doesn't provide the Device_Qualifier
+	   descriptor, so we can't sanity-check that one */
+
+	for (cfg = dev->config;
+	    cfg != dev->config+dev->descriptor.bNumConfigurations;
+	    cfg++)
+		for (itf = cfg->interface;
+		    itf != cfg->interface+cfg->bNumInterfaces;
+		    itf++)
+			for (alt = itf->altsetting;
+			    alt != itf->altsetting+itf->num_altsetting;
+			    alt++)
+				if (open_usbtmc_alt(d, dev, cfg, itf, alt) < 0)
+					return -1;
+	return 0;
+}
+
+
+static int open_usbtmc(struct usbtmc_dsc *d,
+    uint16_t vendor, uint16_t product, uint8_t bus_id, uint8_t dev_id)
+{
+	const struct usb_bus *bus;
+	struct usb_device *dev;
+
+	usb_init();
+	usb_find_busses();
+	usb_find_devices();
+
+	for (bus = usb_get_busses(); bus; bus = bus->next) {
+		if (bus_id && atoi(bus->dirname) != bus_id)
+			continue;
+		for (dev = bus->devices; dev; dev = dev->next) {
+			if (dev_id && dev->devnum != dev_id)
+				continue;
+			if (vendor && dev->descriptor.idVendor != vendor)
+				continue;
+			if (product && dev->descriptor.idProduct != product)
+				continue;
+			if (open_usbtmc_dev(d, dev) < 0)
+				return -1;
+			if (debug)
+				fprintf(stderr,
+				    "USBTMC device %04X:%04X at %s/%s\n",
+				    dev->descriptor.idVendor,
+				    dev->descriptor.idProduct,
+				    bus->dirname, dev->filename);
+		}
+	}
+	return 0;
+}
+
+
+/* ----- USBTMC Communication ---------------------------------------------- */
+
+
+static void usbtmc_abort_in(struct usbtmc_dsc *d)
+{
+	uint8_t buf[2];
+	ssize_t got;
+
+	got = usb_control_msg(d->handle,
+	    USB_ENDPOINT_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
+	    INITIATE_ABORT_BULK_IN,
+	    d->bTag,
+	    d->ep_bulk_in,
+	    (void *) buf, 2, d->timeout);
+	if (got < 0) {
+		fprintf(stderr, "INITIATE_ABORT_BULK_IN error %d\n",
+		    (int) got);
+		return;
+	}
+	fprintf(stderr, "status 0x%02x bTag 0x%02x\n", buf[0], buf[1]);
+
+	/* depending on status, do more */
+}
+
+
+static void usage(void)
+{
+	fprintf(stderr,
+            "usage: \"usbtmc\" [, retry] [, timeout=N]\n"
+            "       [, bus=N] [, device=N] [, vendor=N] [, product=N]\n");
+}
+
+
+static void *usbtmc_open(int argc, const char **argv)
+{
+	struct usbtmc_dsc *d;
+	unsigned long vendor = 0, product = 0;
+        unsigned long bus = 0, device = 0;
+	int i;
+	char *end;
+
+	d = malloc(sizeof(*d));
+	if (!d) {
+		perror("malloc");
+		return NULL;
+	}
+	memset(d, 0, sizeof(*d));
+	for (i = 0; i != argc; i++) {
+		if (!strcmp(argv[i], "retry"))
+			d->retry = 1;
+		else if (!strncmp(argv[i], "timeout=", 8)) {
+			d->timeout = strtoul(argv[i]+8, &end, 0);
+			if (*end || !d->timeout || d->timeout > INT_MAX/1000)
+				goto usage;
+			d->timeout *= 1000;
+		}
+		else if (!strncmp(argv[i], "bus=", 4)) {
+			bus = strtoul(argv[i]+4, &end, 0);
+			if (*end || !bus || bus > 0xff)
+				goto usage;
+		}
+		else if (!strncmp(argv[i], "device=", 7)) {
+			device = strtoul(argv[i]+7, &end, 0);
+			if (*end || !device || device > 0xff)
+				goto usage;
+		}
+		else if (!strncmp(argv[i], "vendor=", 7)) {
+			device = strtoul(argv[i]+7, &end, 0);
+			if (*end || !vendor || vendor > 0xffff)
+				goto usage;
+		}
+		else if (!strncmp(argv[i], "product=", 8)) {
+			product = strtoul(argv[i]+8, &end, 0);
+			if (*end || !product || product > 0xffff)
+				goto usage;
+		}
+		else goto usage;
+	}
+	if (open_usbtmc(d, vendor, product, bus, device) < 0)
+		goto fail;
+	if (!d->handle) {
+		fprintf(stderr, "no device found\n");
+		goto fail;
+	}
+	return d;
+
+usage:
+	usage();
+fail:
+	free(d);
+	return NULL;
+}
+
+
+static void usbtmc_close(void *dsc)
+{
+	struct usbtmc_dsc *d = dsc;
+	int error;
+
+	error = usb_close(d->handle);
+	if (error)
+		fprintf(stderr, "usb_close: %d\n", error);
+	if (d->last_buf)
+		free(d->last_buf);
+}
+
+
+static int usbtmc_write(void *dsc, const void *buf, size_t len)
+{
+	struct usbtmc_dsc *d = dsc;
+	uint8_t tmp[SIZE_IOBUFFER];
+	char *msg;
+	size_t left, size, send_size;
+	ssize_t sent;
+
+	msg = malloc(len);
+	if (!msg) {
+		perror("malloc");
+		return -1;
+	}
+	memcpy(msg, buf, len);
+	if (d->last_buf)
+		free(d->last_buf);
+	d->last_buf = msg;
+	d->last_len = len;
+	/* "buf" may be d->last_buf, so use the copy ("tmp") from now on */
+
+	for (left = len; left; left -= size) {
+		tmp[8] = left <= SIZE_IOBUFFER-12;
+		size = tmp[8] ? left : SIZE_IOBUFFER-12;
+
+		d->bTag++;
+		if (!d->bTag)
+			d->bTag++;
+		tmp[0] = DEV_DEP_MSG_OUT;
+		tmp[1] = d->bTag;
+		tmp[2] = ~d->bTag;
+		tmp[4] = size;
+		tmp[5] = size >> 8;
+		tmp[6] = size >> 16;
+		tmp[7] = size >> 24;
+		tmp[9] = tmp[10] = tmp[11] = 0;
+
+		memcpy(tmp+12, msg, size);
+		msg += size;
+
+		send_size = 12+((size+3) & ~3);
+
+		dump("SEND: DEV_DEP_MSG_OUT", tmp, send_size);
+		sent = usb_bulk_write(d->handle, d->ep_bulk_out, (void *) tmp,
+		    send_size, d->timeout);
+		if (sent < 0) {
+			fprintf(stderr, "write error %d\n", (int) sent);
+			return -1;
+		}
+		if (sent != send_size) {
+			fprintf(stderr, "sent %d instead of %d bytes\n",
+			    (int) sent, (int) send_size);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+
+static int usbtmc_read(void *dsc, io_push_fn *push, void *push_dsc)
+{
+	struct usbtmc_dsc *d = dsc;
+	uint8_t tmp[SIZE_IOBUFFER];
+	size_t size;
+	ssize_t sent, got;
+	uint32_t payload;
+	struct timeval tv;
+	int end = 0;
+
+	do {
+		size = SIZE_IOBUFFER-12;
+		d->bTag++;
+		if (!d->bTag)
+			d->bTag++;
+		tmp[0] = DEV_DEP_MSG_IN;
+		tmp[1] = d->bTag;
+		tmp[2] = ~d->bTag;
+		tmp[4] = size;
+		tmp[5] = size >> 8;
+		tmp[6] = size >> 16;
+		tmp[7] = size >> 24;
+		tmp[8] = tmp[9] = tmp[10] = tmp[11] = 0;
+
+		dump("SEND: DEV_DEP_MSG_IN", tmp, 12);
+		sent =
+		    usb_bulk_write(d->handle, d->ep_bulk_out, (void *) tmp, 12,
+		    d->timeout);
+		if (sent < 0) {
+			fprintf(stderr, "write error %d\n", (int) sent);
+			return -1;
+		}
+		if (sent != 12) {
+			fprintf(stderr, "sent %d instead of 12 bytes\n",
+			    (int) sent);
+			return -1;
+		}
+
+		got = usb_bulk_read(d->handle, d->ep_bulk_in, (void *) tmp,
+		    size, d->timeout);
+		if (got < 0) {
+			usbtmc_abort_in(d);
+			fprintf(stderr, "read error %d\n", (int) got);
+			if (d->retry) {
+				if (usbtmc_write(d, d->last_buf, d->last_len)
+				    < 0)
+					return -1;
+				continue;
+			}
+			return got;
+		}
+		dump("RECV: DEV_DEP_MSG_IN", tmp, got);
+		if (got < 12) {
+			/* ABORT */
+			fprintf(stderr,
+			    "received %d bytes, need at least 12\n", (int) got);
+			return -1;
+		}
+		if (got > size) {
+			/* ABORT */
+			fprintf(stderr,
+			    "received %d bytes, wanted no more than %d\n",
+			    (int) got, (int) size);
+			return -1;
+		}
+
+		if (gettimeofday(&tv, NULL) < 0) {
+			perror("gettimeofday");
+			return -1;
+		}
+
+		if (tmp[0] != DEV_DEP_MSG_IN) {
+			/* ABORT */
+			fprintf(stderr, "unexpected message type 0x%02x\n",
+			    tmp[0]);
+			return -1;
+		}
+		if (tmp[1] != d->bTag || tmp[2] != (~d->bTag & 0xff)) {
+			/* ABORT */
+			fprintf(stderr,
+			    "received bTag 0x%02x/0x%02x, "
+			    "expected 0x%02x/0x%02x\n",
+			    tmp[1], tmp[2], d->bTag, ~d->bTag & 0xff);
+			return -1;
+		}
+
+		if (tmp[8] & 2) {
+			fprintf(stderr, "unexpected TermChar\n");
+			return -1;
+		}
+
+		payload =
+		    tmp[4] | (tmp[5] << 8) | (tmp[6] << 16) | (tmp[7] << 24);
+		if (!payload) {
+			fprintf(stderr,
+			    "zero-length DEV_DEP_MSG_IN payload\n");
+			return -1;
+		}
+		if (payload > size) {
+			fprintf(stderr,
+			    "DEV_DEP_MSG_IN payload has %lu bytes, "
+			    "we have space for %d\n",
+			    (unsigned long) payload, (int) size);
+			return -1;
+		}
+
+		end = tmp[8] & 1;
+		push(push_dsc, &tv, tmp+12, payload, end);
+	}
+	while (!end);
+	return 0;
+}
+
+
+static int usbtmc_dci(void *dsc)
+{
+	/* @@@FIXME: implement later */
+	return 0;
+}
+
+
+struct proto_ops usbtmc_ops = {
+        .open   = usbtmc_open,
+        .close  = usbtmc_close,
+        .read   = usbtmc_read,
+        .write  = usbtmc_write,
+        .dci    = usbtmc_dci,
+};





More information about the commitlog mailing list