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