--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+
+TOS=$(shell ncc -print-tosdir)
+SERIAL_H = $(TOS)/lib/serial/Serial.h
+
+BUILT_SOURCES = serialpacket.h serialprotocol.h
+
+bin_PROGRAMS=sf
+noinst_PROGRAMS=prettylisten sflisten sfsend seriallisten serialsend
+noinst_LIBRARIES=libmote.a
+
+sf_SOURCES = sf.c
+sf_LDADD = libmote.a
+
+prettylisten_SOURCES = prettylisten.c
+prettylisten_LDADD = libmote.a
+
+sflisten_SOURCES = sflisten.c
+sflisten_LDADD = libmote.a
+
+sfsend_SOURCES = sfsend.c
+sfsend_LDADD = libmote.a
+
+seriallisten_SOURCES = seriallisten.c
+seriallisten_LDADD = libmote.a
+
+serialsend_SOURCES = serialsend.c
+serialsend_LDADD = libmote.a
+
+libmote_a_SOURCES = \
+ message.c \
+ serialpacket.c \
+ serialsource.c \
+ sfsource.c
+
+serialpacket.c serialpacket.h: $(SERIAL_H)
+ mig -o serialpacket.h -c-prefix=spacket c $(SERIAL_H) serial_packet
+
+serialprotocol.h: $(SERIAL_H)
+ ncg -o $@ -c-prefix=SERIAL c $(SERIAL_H) Serial.h
--- /dev/null
+Mini C-SDK for TinyOS
+=====================
+
+This directory contains a mini-SDK for C, for communicating with motes
+running TinyOS 2.0. To build this SDK, run
+ ./bootstrap
+ ./configure --prefix=<somewhere>
+ make
+in the current directory and, if you wish, "make install" to install the
+C-based serial forwarder in <somewhere>/bin.
+
+This directory contains one utility:
+- sf: a C-based serial forwarder:
+ sf <port> <device> <baudrate>
+ Starts a serial forwarder listening for TCP connections on port <port>, and
+ sending and receiving packets on serial port <device> at the specified
+ <baudrate>.
+
+ This serial forwarder implements the standard TinyOS 2.0 serial forwarder
+ protocol (see comments in support/sdk/java/net/tinyos/packet/SFProtocol.java
+ for a brief overview).
+
+a library (libmote.a) supporting mote communication:
+- serialsource.h: send and receive packets over a serial port (supports
+ non-blocking I/O)
+- sfsource.h: send and receive packets using the serial forwarder
+ protocol
+- message.h: support functions for mig, to encode and decode bitfields of
+ arbitrary size and endianness
+- serialpacket.h: mig-generated code to encode and decode the header of
+ TinyOS serial active-message packets (the packets sent and received by the
+ BaseStation application)
+- serialprotocol.h: ncg-generated code containing the constants describing
+ TinyOS serial packets (from tos/lib/serial/Serial.h)
+
+and four example programs that use that library:
+- seriallisten: print packets received from a serial port
+- sflisten: print packets received from a serial forwarder
+- prettylisten: print packets received from a serial forwarder, using
+ mig-generated code to decode the standard serial-active-message header
+- sfsend: send a packet (specified on the command line) to a serial forwarder
+
+Note that sflisten prints, and sfsend sends, raw packets. In particular,
+the first byte indicates the packet type (e.g., 00 for the AM-over-serial
+packets). For more information on serial communication to and from motes,
+see TEP113.
+
+For more information on using ncg and mig with C, see the nescc-mig and
+nescc-ncg man pages.
+
+
--- /dev/null
+/* autoconf.h. Generated from autoconf.h.in by configure. */
+/* autoconf.h.in. Generated from configure.ac by autoheader. */
+
+/* Name of package */
+#define PACKAGE "cmotesdk"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "cmotesdk"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "cmotesdk 1.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "cmotesdk"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.0"
+
+/* Version number of package */
+#define VERSION "1.0"
--- /dev/null
+mkdir config-aux
+aclocal
+autoheader
+autoconf
+automake -a -c
--- /dev/null
+<project name="tinyos-2.x support sdk c" default="all">
+
+ <target name="all" >
+ <echo message = "Building support sdk c" />
+ <exec executable="./bootstrap" failonerror="true">
+ </exec>
+ <exec executable="./configure" failonerror="true">
+ <arg line="--quiet" />
+ </exec>
+ <exec executable="make" failonerror="true">
+ <arg line="all" />
+ </exec>
+ </target>
+
+ <target name="install" >
+ <echo message = "Installing tinyos-2.x support sdk c" />
+ <exec executable="./bootstrap" failonerror="true">
+ </exec>
+ <exec executable="./configure" failonerror="true">
+ <arg line="--prefix=$TOSTOOLS_PREFIX --quiet" />
+ </exec>
+ <exec executable="make" failonerror="true">
+ <arg line="install" />
+ </exec>
+ </target>
+
+</project>
--- /dev/null
+AC_INIT(cmotesdk, 1.0)
+AC_CONFIG_SRCDIR(sfsource.c)
+AM_CONFIG_HEADER(autoconf.h)
+AC_CONFIG_AUX_DIR(config-aux)
+AM_INIT_AUTOMAKE
+
+AC_PROG_CC
+AC_PROG_RANLIB
+
+AC_OUTPUT(Makefile)
--- /dev/null
+/* Copyright (c) 2006 Intel Corporation
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached INTEL-LICENSE
+ * file. If you do not find these files, copies can be found by writing to
+ * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
+ * 94704. Attention: Intel License Inquiry.
+ */
+/* Authors: David Gay <dgay@intel-research.net>
+ * Intel Research Berkeley Lab
+ */
+
+#include <stdlib.h>
+#include "message.h"
+
+struct tmsg {
+ uint8_t *data;
+ size_t len;
+};
+
+tmsg_t *new_tmsg(void *packet, size_t len)
+{
+ tmsg_t *x = malloc(sizeof(tmsg_t));
+
+ if (x)
+ {
+ x->data = packet;
+ x->len = len;
+ }
+ return x;
+}
+
+void free_tmsg(tmsg_t *msg)
+{
+ if (msg)
+ free(msg);
+}
+
+void *tmsg_data(tmsg_t *msg)
+{
+ return msg->data;
+}
+
+size_t tmsg_length(tmsg_t *msg)
+{
+ return msg->len;
+}
+
+static void (*failfn)(void);
+
+void tmsg_fail(void)
+{
+ if (failfn)
+ failfn();
+}
+
+void (*tmsg_set_fail(void (*fn)(void)))(void)
+{
+ void (*oldfn)(void) = failfn;
+
+ failfn = fn;
+
+ return oldfn;
+}
+
+/* Check if a specified bit field is in range for a buffer, and invoke
+ tmsg_fail if not. Return TRUE if in range, FALSE otherwise */
+static int boundsp(tmsg_t *msg, size_t offset, size_t length)
+{
+ if (offset + length <= msg->len * 8)
+ return 1;
+
+ tmsg_fail();
+ return 0;
+}
+
+/* Convert 2's complement 'length' bit integer 'x' from unsigned to signed
+ */
+static int64_t u2s(uint64_t x, size_t length)
+{
+ if (x & 1ULL << (length - 1))
+ return (int64_t)x - (1LL << length);
+ else
+ return x;
+}
+
+uint64_t tmsg_read_ule(tmsg_t *msg, size_t offset, size_t length)
+{
+ uint64_t x = 0;
+
+ if (boundsp(msg, offset, length))
+ {
+ size_t byte_offset = offset >> 3;
+ size_t bit_offset = offset & 7;
+ size_t shift = 0;
+
+ /* all in one byte case */
+ if (length + bit_offset <= 8)
+ return (msg->data[byte_offset] >> bit_offset) & ((1 << length) - 1);
+
+ /* get some high order bits */
+ if (offset > 0)
+ {
+ x = msg->data[byte_offset] >> bit_offset;
+ byte_offset++;
+ shift += 8 - bit_offset;
+ length -= 8 - bit_offset;
+ }
+
+ while (length >= 8)
+ {
+ x |= (uint64_t)msg->data[byte_offset++] << shift;
+ shift += 8;
+ length -= 8;
+ }
+
+ /* data from last byte */
+ if (length > 0)
+ x |= (uint64_t)(msg->data[byte_offset] & ((1 << length) - 1)) << shift;
+ }
+
+ return x;
+}
+
+int64_t tmsg_read_le(tmsg_t *msg, size_t offset, size_t length)
+{
+ return u2s(tmsg_read_ule(msg, offset, length), length);
+}
+
+void tmsg_write_ule(tmsg_t *msg, size_t offset, size_t length, uint64_t x)
+{
+ if (boundsp(msg, offset, length))
+ {
+ size_t byte_offset = offset >> 3;
+ size_t bit_offset = offset & 7;
+ size_t shift = 0;
+
+ /* all in one byte case */
+ if (length + bit_offset <= 8)
+ {
+ msg->data[byte_offset] =
+ ((msg->data[byte_offset] & ~(((1 << length) - 1) << bit_offset))
+ | x << bit_offset);
+ return;
+ }
+
+ /* set some high order bits */
+ if (bit_offset > 0)
+ {
+ msg->data[byte_offset] =
+ ((msg->data[byte_offset] & ((1 << bit_offset) - 1)) | x << bit_offset);
+ byte_offset++;
+ shift += 8 - bit_offset;
+ length -= 8 - bit_offset;
+ }
+
+ while (length >= 8)
+ {
+ msg->data[byte_offset++] = x >> shift;
+ shift += 8;
+ length -= 8;
+ }
+
+ /* data for last byte */
+ if (length > 0)
+ msg->data[byte_offset] =
+ (msg->data[byte_offset] & ~((1 << length) - 1)) | x >> shift;
+ }
+}
+
+void tmsg_write_le(tmsg_t *msg, size_t offset, size_t length, int64_t value)
+{
+ tmsg_write_ule(msg, offset, length, value);
+}
+
+uint64_t tmsg_read_ube(tmsg_t *msg, size_t offset, size_t length)
+{
+ uint64_t x = 0;
+
+ if (boundsp(msg, offset, length))
+ {
+ size_t byte_offset = offset >> 3;
+ size_t bit_offset = offset & 7;
+
+ /* All in one byte case */
+ if (length + bit_offset <= 8)
+ return (msg->data[byte_offset] >> (8 - bit_offset - length)) &
+ ((1 << length) - 1);
+
+ /* get some high order bits */
+ if (bit_offset > 0)
+ {
+ length -= 8 - bit_offset;
+ x = (uint64_t)(msg->data[byte_offset] & ((1 << (8 - bit_offset)) - 1)) << length;
+ byte_offset++;
+ }
+
+ while (length >= 8)
+ {
+ length -= 8;
+ x |= (uint64_t)msg->data[byte_offset++] << length;
+ }
+
+ /* data from last byte */
+ if (length > 0)
+ x |= msg->data[byte_offset] >> (8 - length);
+
+ return x;
+ }
+
+ return x;
+}
+
+int64_t tmsg_read_be(tmsg_t *msg, size_t offset, size_t length)
+{
+ return u2s(tmsg_read_ube(msg, offset, length), length);
+}
+
+void tmsg_write_ube(tmsg_t *msg, size_t offset, size_t length, uint64_t x)
+{
+ if (boundsp(msg, offset, length))
+ {
+ size_t byte_offset = offset >> 3;
+ size_t bit_offset = offset & 7;
+
+ /* all in one byte case */
+ if (length + bit_offset <= 8) {
+ size_t mask = ((1 << length) - 1) << (8 - bit_offset - length);
+
+ msg->data[byte_offset] =
+ ((msg->data[byte_offset] & ~mask) | x << (8 - bit_offset - length));
+ return;
+ }
+
+ /* set some high order bits */
+ if (bit_offset > 0)
+ {
+ size_t mask = (1 << (8 - bit_offset)) - 1;
+
+ length -= 8 - bit_offset;
+ msg->data[byte_offset] =
+ ((msg->data[byte_offset] & ~mask) | x >> length);
+ byte_offset++;
+ }
+
+ while (length >= 8)
+ {
+ length -= 8;
+ msg->data[byte_offset++] = x >> length;
+ }
+
+ /* data for last byte */
+ if (length > 0)
+ {
+ size_t mask = (1 << (8 - length)) - 1;
+
+ msg->data[byte_offset] =
+ ((msg->data[byte_offset] & mask) | x << (8 - length));
+ }
+ }
+}
+
+void tmsg_write_be(tmsg_t *msg, size_t offset, size_t length, int64_t value)
+{
+ tmsg_write_ube(msg, offset, length, value);
+}
+
+/* u2f and f2u convert raw 32-bit values to/from float. This code assumes
+ that the floating point rep in the uint32_t values:
+ bit 31: sign, bits 30-23: exponent, bits 22-0: mantissa
+ matches that of a floating point value when such a value is stored in
+ memory.
+*/
+
+/* Note that C99 wants us to use the union approach rather than the
+ cast-a-pointer approach... */
+union f_and_u {
+ uint32_t u;
+ float f;
+};
+
+static float u2f(uint32_t x)
+{
+ union f_and_u y = { .u = x};
+ return y.f;
+}
+
+static uint32_t f2u(float x)
+{
+ union f_and_u y = { .f = x};
+ return y.u;
+}
+
+float tmsg_read_float_le(tmsg_t *msg, size_t offset)
+{
+ return u2f(tmsg_read_ule(msg, offset, 32));
+}
+
+void tmsg_write_float_le(tmsg_t *msg, size_t offset, float x)
+{
+ tmsg_write_ule(msg, offset, 32, f2u(x));
+}
+
+float tmsg_read_float_be(tmsg_t *msg, size_t offset)
+{
+ return u2f(tmsg_read_ube(msg, offset, 32));
+}
+
+void tmsg_write_float_be(tmsg_t *msg, size_t offset, float x)
+{
+ tmsg_write_ube(msg, offset, 32, f2u(x));
+}
--- /dev/null
+/* Copyright (c) 2006 Intel Corporation
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached INTEL-LICENSE
+ * file. If you do not find these files, copies can be found by writing to
+ * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA,
+ * 94704. Attention: Intel License Inquiry.
+ */
+/* Authors: David Gay <dgay@intel-research.net>
+ * Intel Research Berkeley Lab
+ */
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** The type of message buffers */
+typedef struct tmsg tmsg_t;
+
+/** Invoke the function set by tmsg_set_fail.
+ * tmsg_fail is called by the tmsg_read and tmsg_write functions when an
+ * out-of-buffer access is attempted.
+*/
+void tmsg_fail(void);
+
+/** Set the function that tmsg_fail should call, and return the previous
+ * function. If the function is NULL, tmsg_fail does nothing.
+*/
+void (*tmsg_set_fail(void (*fn)(void)))(void);
+
+/**
+ * Create a message buffer from array 'packet' of 'len' bytes
+ */
+tmsg_t *new_tmsg(void *packet, size_t len);
+
+/**
+ * Free a message buffer. This does NOT free the underlying array.
+ */
+void free_tmsg(tmsg_t *msg);
+
+/**
+ * Return underlying array of a message buffer
+ */
+void *tmsg_data(tmsg_t *msg);
+
+/**
+ * Return length of a message buffer
+ */
+size_t tmsg_length(tmsg_t *msg);
+
+/**
+ * Read an unsigned little-endian integer of 'bit_length' bits from bit offset
+ * 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+uint64_t tmsg_read_ule(tmsg_t *msg, size_t bit_offset, size_t bit_length);
+
+/**
+ * Read a signed little-endian integer of 'bit_length' bits from bit offset
+ * 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+int64_t tmsg_read_le(tmsg_t *msg, size_t bit_offset, size_t bit_length);
+
+/**
+ * Write an unsigned little-endian integer of 'bit_length' bits to bit offset
+ * 'bit_offset'.
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_ule(tmsg_t *msg, size_t bit_offset, size_t bit_length, uint64_t value);
+
+/**
+ * Write a signed little-endian integer of 'bit_length' bits to bit offset
+ * 'bit_offset'.
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_le(tmsg_t *msg, size_t bit_offset, size_t bit_length, int64_t value);
+
+/**
+ * Read an unsigned big-endian integer of 'bit_length' bits from bit offset
+ * 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+uint64_t tmsg_read_ube(tmsg_t *msg, size_t bit_offset, size_t bit_length);
+
+/**
+ * Read a signed big-endian integer of 'bit_length' bits from bit offset
+ * 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+int64_t tmsg_read_be(tmsg_t *msg, size_t bit_offset, size_t bit_length);
+
+/**
+ * Write an unsigned big-endian integer of 'bit_length' bits to bit offset
+ * 'bit_offset'.
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_ube(tmsg_t *msg, size_t bit_offset, size_t bit_length, uint64_t value);
+
+/**
+ * Write a signed big-endian integer of 'bit_length' bits to bit offset
+ * 'bit_offset'.
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_be(tmsg_t *msg, size_t bit_offset, size_t bit_length, int64_t value);
+
+/**
+ * Read a 32-bit IEEE float stored in little-endian format (bit 31: sign,
+ * bits 30-23: exponent, bits 22-0: mantissa) from bit offset 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+float tmsg_read_float_le(tmsg_t *msg, size_t offset);
+
+/**
+ * Write a 32-bit IEEE float in little-endian format (bit 31: sign,
+ * bits 30-23: exponent, bits 22-0: mantissa) to bit offset 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_float_le(tmsg_t *msg, size_t offset, float x);
+
+/**
+ * Read a 32-bit IEEE float stored in big-endian format (bit 31: sign,
+ * bits 30-23: exponent, bits 22-0: mantissa) from bit offset 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and 0 is returned.
+ */
+float tmsg_read_float_be(tmsg_t *msg, size_t offset);
+
+/**
+ * Write a 32-bit IEEE float in big-endian format (bit 31: sign,
+ * bits 30-23: exponent, bits 22-0: mantissa) to bit offset 'bit_offset'
+ * If the specified field is out of range for the buffer, tmsg_fail is called
+ * and no write occurs.
+ */
+void tmsg_write_float_be(tmsg_t *msg, size_t offset, float x);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sfsource.h"
+#include "serialpacket.h"
+#include "serialprotocol.h"
+
+void hexprint(uint8_t *packet, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ printf("%02x ", packet[i]);
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: %s <host> <port> - dump packets from a serial forwarder\n", argv[0]);
+ exit(2);
+ }
+ fd = open_sf_source(argv[1], atoi(argv[2]));
+ if (fd < 0)
+ {
+ fprintf(stderr, "Couldn't open serial forwarder at %s:%s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+ for (;;)
+ {
+ int len, i;
+ uint8_t *packet = read_sf_packet(fd, &len);
+
+ if (!packet)
+ exit(0);
+
+ if (len >= 1 + SPACKET_SIZE &&
+ packet[0] == SERIAL_TOS_SERIAL_ACTIVE_MESSAGE_ID)
+ {
+ tmsg_t *msg = new_tmsg(packet + 1, len - 1);
+
+ if (!msg)
+ exit(0);
+
+ printf("dest %u, src %u, length %u, group %u, type %u\n ",
+ spacket_header_dest_get(msg),
+ spacket_header_src_get(msg),
+ spacket_header_length_get(msg),
+ spacket_header_group_get(msg),
+ spacket_header_type_get(msg));
+ hexprint((uint8_t *)tmsg_data(msg) + spacket_data_offset(0),
+ tmsg_length(msg) - spacket_data_offset(0));
+
+ free(msg);
+ }
+ else
+ {
+ printf("non-AM packet: ");
+ hexprint(packet, len);
+ }
+ putchar('\n');
+ fflush(stdout);
+ free((void *)packet);
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "serialsource.h"
+
+static char *msgs[] = {
+ "unknown_packet_type",
+ "ack_timeout" ,
+ "sync" ,
+ "too_long" ,
+ "too_short" ,
+ "bad_sync" ,
+ "bad_crc" ,
+ "closed" ,
+ "no_memory" ,
+ "unix_error"
+};
+
+void stderr_msg(serial_source_msg problem)
+{
+ fprintf(stderr, "Note: %s\n", msgs[problem]);
+}
+
+int main(int argc, char **argv)
+{
+ serial_source src;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: %s <device> <rate> - dump packets from a serial port\n", argv[0]);
+ exit(2);
+ }
+ src = open_serial_source(argv[1], platform_baud_rate(argv[2]), 0, stderr_msg);
+ if (!src)
+ {
+ fprintf(stderr, "Couldn't open serial port at %s:%s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+ for (;;)
+ {
+ int len, i;
+ const unsigned char *packet = read_serial_packet(src, &len);
+
+ if (!packet)
+ exit(0);
+ for (i = 0; i < len; i++)
+ printf("%02x ", packet[i]);
+ putchar('\n');
+ free((void *)packet);
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "serialsource.h"
+
+static char *msgs[] = {
+ "unknown_packet_type",
+ "ack_timeout" ,
+ "sync" ,
+ "too_long" ,
+ "too_short" ,
+ "bad_sync" ,
+ "bad_crc" ,
+ "closed" ,
+ "no_memory" ,
+ "unix_error"
+};
+
+void stderr_msg(serial_source_msg problem)
+{
+ fprintf(stderr, "Note: %s\n", msgs[problem]);
+}
+
+void send_packet(serial_source src, char **bytes, int count)
+{
+ int i;
+ unsigned char *packet;
+
+ packet = malloc(count);
+ if (!packet)
+ exit(2);
+
+ for (i = 0; i < count; i++)
+ packet[i] = strtol(bytes[i], NULL, 0);
+
+ fprintf(stderr,"Sending ");
+ for (i = 0; i < count; i++)
+ fprintf(stderr, " %02x", packet[i]);
+ fprintf(stderr, "\n");
+
+ if (write_serial_packet(src, packet, count) == 0)
+ printf("ack\n");
+ else
+ printf("noack\n");
+}
+
+int main(int argc, char **argv)
+{
+ serial_source src;
+
+ if (argc < 3)
+ {
+ fprintf(stderr, "Usage: %s <device> <rate> <bytes> - send a raw packet to a serial port\n", argv[0]);
+ exit(2);
+ }
+ src = open_serial_source(argv[1], platform_baud_rate(argv[2]), 0, stderr_msg);
+ if (!src)
+ {
+ fprintf(stderr, "Couldn't open serial port at %s:%s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+
+ send_packet(src, argv + 3, argc - 3);
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <stdio.h>
+#ifdef __CYGWIN__
+#include <windows.h>
+#include <io.h>
+#else
+#include <stdint.h>
+#endif
+
+/* C implementation of the mote serial protocol. See
+ net.tinyos.packet.Packetizer for more details */
+
+#undef DEBUG
+
+#include "serialsource.h"
+#include "serialprotocol.h"
+
+typedef int bool;
+
+enum {
+#ifndef __CYGWIN__
+ FALSE = 0,
+ TRUE = 1,
+#endif
+ BUFSIZE = 256,
+ MTU = 256,
+ ACK_TIMEOUT = 1000000, /* in us */
+ SYNC_BYTE = SERIAL_HDLC_FLAG_BYTE,
+ ESCAPE_BYTE = SERIAL_HDLC_CTLESC_BYTE,
+
+ P_ACK = SERIAL_SERIAL_PROTO_ACK,
+ P_PACKET_ACK = SERIAL_SERIAL_PROTO_PACKET_ACK,
+ P_PACKET_NO_ACK = SERIAL_SERIAL_PROTO_PACKET_NOACK,
+ P_UNKNOWN = SERIAL_SERIAL_PROTO_PACKET_UNKNOWN
+};
+
+struct packet_list
+{
+ uint8_t *packet;
+ int len;
+ struct packet_list *next;
+};
+
+struct serial_source {
+ int fd;
+ bool non_blocking;
+ void (*message)(serial_source_msg problem);
+
+ /* Receive state */
+ struct {
+ uint8_t buffer[BUFSIZE];
+ int bufpos, bufused;
+ uint8_t packet[MTU];
+ bool in_sync, escaped;
+ int count;
+ struct packet_list *queue[256]; // indexed by protocol
+ } recv;
+ struct {
+ uint8_t seqno;
+ uint8_t *escaped;
+ int escapeptr;
+ uint16_t crc;
+ } send;
+};
+
+static tcflag_t parse_baudrate(int requested)
+{
+ int baudrate;
+
+ switch (requested)
+ {
+#ifdef B50
+ case 50: baudrate = B50; break;
+#endif
+#ifdef B75
+ case 75: baudrate = B75; break;
+#endif
+#ifdef B110
+ case 110: baudrate = B110; break;
+#endif
+#ifdef B134
+ case 134: baudrate = B134; break;
+#endif
+#ifdef B150
+ case 150: baudrate = B150; break;
+#endif
+#ifdef B200
+ case 200: baudrate = B200; break;
+#endif
+#ifdef B300
+ case 300: baudrate = B300; break;
+#endif
+#ifdef B600
+ case 600: baudrate = B600; break;
+#endif
+#ifdef B1200
+ case 1200: baudrate = B1200; break;
+#endif
+#ifdef B1800
+ case 1800: baudrate = B1800; break;
+#endif
+#ifdef B2400
+ case 2400: baudrate = B2400; break;
+#endif
+#ifdef B4800
+ case 4800: baudrate = B4800; break;
+#endif
+#ifdef B9600
+ case 9600: baudrate = B9600; break;
+#endif
+#ifdef B19200
+ case 19200: baudrate = B19200; break;
+#endif
+#ifdef B38400
+ case 38400: baudrate = B38400; break;
+#endif
+#ifdef B57600
+ case 57600: baudrate = B57600; break;
+#endif
+#ifdef B115200
+ case 115200: baudrate = B115200; break;
+#endif
+#ifdef B230400
+ case 230400: baudrate = B230400; break;
+#endif
+#ifdef B460800
+ case 460800: baudrate = B460800; break;
+#endif
+#ifdef B500000
+ case 500000: baudrate = B500000; break;
+#endif
+#ifdef B576000
+ case 576000: baudrate = B576000; break;
+#endif
+#ifdef B921600
+ case 921600: baudrate = B921600; break;
+#endif
+#ifdef B1000000
+ case 1000000: baudrate = B1000000; break;
+#endif
+#ifdef B1152000
+ case 1152000: baudrate = B1152000; break;
+#endif
+#ifdef B1500000
+ case 1500000: baudrate = B1500000; break;
+#endif
+#ifdef B2000000
+ case 2000000: baudrate = B2000000; break;
+#endif
+#ifdef B2500000
+ case 2500000: baudrate = B2500000; break;
+#endif
+#ifdef B3000000
+ case 3000000: baudrate = B3000000; break;
+#endif
+#ifdef B3500000
+ case 3500000: baudrate = B3500000; break;
+#endif
+#ifdef B4000000
+ case 4000000: baudrate = B4000000; break;
+#endif
+ default:
+ baudrate = 0;
+ }
+ return baudrate;
+}
+
+#ifdef DEBUG
+static void dump(const char *msg, unsigned char *packet, int len)
+{
+ int i;
+
+ printf("%s", msg);
+ for (i = 0; i < len; i++)
+ printf(" %02x", packet[i]);
+ putchar('\n');
+}
+#endif
+
+static void message(serial_source src, serial_source_msg msg)
+{
+ if (src->message)
+ src->message(msg);
+}
+
+static int serial_read(serial_source src, int non_blocking, void *buffer, int n)
+{
+ fd_set fds;
+ int cnt;
+
+ if (non_blocking)
+ {
+ cnt = read(src->fd, buffer, n);
+
+ /* Work around buggy usb serial driver (returns 0 when no data
+ is available). Mac OS X seems to like to do this too (at
+ least with a Keyspan 49WG).
+ */
+ if (cnt == 0)
+ {
+ cnt = -1;
+ errno = EAGAIN;
+ }
+ return cnt;
+ }
+ else
+ for (;;)
+ {
+ FD_ZERO(&fds);
+ FD_SET(src->fd, &fds);
+ cnt = select(src->fd + 1, &fds, NULL, NULL, NULL);
+ if (cnt < 0)
+ return -1;
+
+ cnt = read(src->fd, buffer, n);
+ if (cnt != 0)
+ return cnt;
+ }
+}
+
+serial_source open_serial_source(const char *device, int baud_rate,
+ int non_blocking,
+ void (*message)(serial_source_msg problem))
+/* Effects: opens serial port device at specified baud_rate. If non_blocking
+ is true, read_serial_packet calls will be non-blocking (writes are
+ always blocking, for now at least)
+ Returns: descriptor for serial forwarder at host:port, or
+ NULL for failure (bad device or bad baud rate)
+ */
+{
+ struct termios newtio;
+ int fd;
+ tcflag_t baudflag = parse_baudrate(baud_rate);
+
+ if (!baudflag)
+ return NULL;
+
+ fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0)
+ return NULL;
+
+#ifdef __CYGWIN__
+ /* For some very mysterious reason, this incantation is necessary to make
+ the serial port work under some windows machines */
+ HANDLE handle = (HANDLE)get_osfhandle(fd);
+ DCB dcb;
+ if (!(GetCommState(handle, &dcb) && SetCommState(handle, &dcb)))
+ {
+ close(fd);
+ return NULL;
+ }
+#endif
+ /* Serial port setting */
+ memset(&newtio, 0, sizeof(newtio));
+ newtio.c_cflag = CS8 | CLOCAL | CREAD;
+ newtio.c_iflag = IGNPAR | IGNBRK;
+ cfsetispeed(&newtio, baudflag);
+ cfsetospeed(&newtio, baudflag);
+
+ /* Raw output_file */
+ newtio.c_oflag = 0;
+
+ if (tcflush(fd, TCIFLUSH) >= 0 &&
+ tcsetattr(fd, TCSANOW, &newtio) >= 0)
+ {
+ serial_source src = malloc(sizeof *src);
+
+ if (src)
+ {
+ memset(src, 0, sizeof *src);
+ src->fd = fd;
+ src->non_blocking = non_blocking;
+ src->message = message;
+ src->send.seqno = 37;
+
+ return src;
+ }
+ }
+ close(fd);
+
+ return NULL;
+}
+
+int serial_source_fd(serial_source src)
+/* Returns: the file descriptor used by serial source src (useful when
+ non-blocking reads were requested)
+*/
+{
+ return src->fd;
+}
+
+int close_serial_source(serial_source src)
+/* Effects: closes serial source src
+ Returns: 0 if successful, -1 if some problem occured (but source is
+ considered closed anyway)
+ */
+{
+ int ok = close(src->fd);
+
+ free(src);
+
+ return ok;
+}
+
+static int source_wait(serial_source src, struct timeval *deadline)
+/* Effects: waits until deadline for some data on source. deadline
+ can be NULL for indefinite waiting.
+ Returns: 0 if data is available, -1 if the deadline expires
+*/
+{
+ struct timeval tv;
+ fd_set fds;
+ int cnt;
+
+ if (src->recv.bufpos < src->recv.bufused)
+ return 0;
+
+ for (;;)
+ {
+ if (deadline)
+ {
+ gettimeofday(&tv, NULL);
+ tv.tv_sec = deadline->tv_sec - tv.tv_sec;
+ tv.tv_usec = deadline->tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0)
+ {
+ tv.tv_usec += 1000000;
+ tv.tv_sec--;
+ }
+ if (tv.tv_sec < 0)
+ return -1;
+ }
+
+ FD_ZERO(&fds);
+ FD_SET(src->fd, &fds);
+ cnt = select(src->fd + 1, &fds, NULL, NULL, deadline ? &tv : NULL);
+ if (cnt < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ message(src, msg_unix_error);
+ return -1;
+ }
+ if (cnt == 0)
+ return -1;
+ return 0;
+ }
+}
+
+static int source_write(serial_source src, const void *buffer, int count)
+{
+ int actual = 0;
+
+ if (fcntl(src->fd, F_SETFL, 0) < 0)
+ {
+ message(src, msg_unix_error);
+ return -1;
+ }
+ while (count > 0)
+ {
+ int n = write(src->fd, buffer, count);
+
+ if (n < 0 && errno == EINTR)
+ continue;
+ if (n < 0)
+ {
+ message(src, msg_unix_error);
+ actual = -1;
+ break;
+ }
+
+ count -= n;
+ actual += n;
+ buffer += n;
+ }
+ if (fcntl(src->fd, F_SETFL, O_NONBLOCK) < 0)
+ {
+ message(src, msg_unix_error);
+ /* We're in trouble, but there's no obvious fix. */
+ }
+ return actual;
+}
+
+static void push_protocol_packet(serial_source src,
+ uint8_t type, uint8_t *packet, uint8_t len)
+{
+ /* I'm assuming short queues */
+ struct packet_list *entry = malloc(sizeof *entry), **last;
+
+ if (!entry)
+ {
+ message(src, msg_no_memory);
+ free(packet);
+ return;
+ }
+
+ entry->packet = packet;
+ entry->len = len;
+ entry->next = NULL;
+
+ last = &src->recv.queue[type];
+ while (*last)
+ last = &(*last)->next;
+ *last = entry;
+}
+
+static struct packet_list *pop_protocol_packet(serial_source src, uint8_t type)
+{
+ struct packet_list *entry = src->recv.queue[type];
+
+ if (entry)
+ src->recv.queue[type] = entry->next;
+
+ return entry;
+}
+
+static bool packet_available(serial_source src, uint8_t type)
+{
+ return src->recv.queue[type] != NULL;
+}
+
+int serial_source_empty(serial_source src)
+/* Returns: true if serial source does not contain any pending data, i.e.,
+ if the result is true and there is no data available on the source's
+ file descriptor, then read_serial_packet will:
+ - return NULL if the source is non-blocking
+ - block if it is blocking
+
+ (Note: the presence of this calls allows the serial_source to do some
+ internal buffering)
+*/
+{
+ return src->recv.bufpos >= src->recv.bufused &&
+ !packet_available(src, P_PACKET_NO_ACK);
+}
+
+/* Slow implementation of crc function */
+static uint16_t crc_byte(uint16_t crc, uint8_t b)
+{
+ uint8_t i;
+
+ crc = crc ^ b << 8;
+ i = 8;
+ do
+ if (crc & 0x8000)
+ crc = crc << 1 ^ 0x1021;
+ else
+ crc = crc << 1;
+ while (--i);
+
+ return crc;
+}
+
+static uint16_t crc_packet(uint8_t *data, int len)
+{
+ uint16_t crc = 0;
+
+ while (len-- > 0)
+ crc = crc_byte(crc, *data++);
+
+ return crc;
+}
+
+static int read_byte(serial_source src, int non_blocking)
+/* Returns: next byte (>= 0), or -1 if no data available and non-blocking is true.
+*/
+{
+ if (src->recv.bufpos >= src->recv.bufused)
+ {
+ for (;;)
+ {
+ int n = serial_read(src, non_blocking, src->recv.buffer, sizeof src->recv.buffer);
+
+ if (n == 0) /* Can't occur because of serial_read bug workaround */
+ {
+ message(src, msg_closed);
+ return -1;
+ }
+ if (n > 0)
+ {
+#ifdef DEBUG
+ dump("raw", src->recv.buffer, n);
+#endif
+ src->recv.bufpos = 0;
+ src->recv.bufused = n;
+ break;
+ }
+ if (errno == EAGAIN)
+ return -1;
+ if (errno != EINTR)
+ message(src, msg_unix_error);
+ }
+ }
+ //printf("in %02x\n", src->recv.buffer[src->recv.bufpos]);
+ return src->recv.buffer[src->recv.bufpos++];
+}
+
+static void process_packet(serial_source src, uint8_t *packet, int len);
+static int write_framed_packet(serial_source src,
+ uint8_t packet_type, uint8_t first_byte,
+ const uint8_t *packet, int count);
+
+static void read_and_process(serial_source src, int non_blocking)
+/* Effects: reads and processes up to one packet.
+*/
+{
+ uint8_t *packet = src->recv.packet;
+
+ for (;;)
+ {
+ int byte = read_byte(src, non_blocking);
+
+ if (byte < 0)
+ return;
+
+ if (!src->recv.in_sync)
+ {
+ if (byte == SYNC_BYTE)
+ {
+ src->recv.in_sync = TRUE;
+ message(src, msg_sync);
+ src->recv.count = 0;
+ src->recv.escaped = FALSE;
+ }
+ continue;
+ }
+ if (src->recv.count >= MTU)
+ {
+ message(src, msg_too_long);
+ src->recv.in_sync = FALSE;
+ continue;
+ }
+ if (src->recv.escaped)
+ {
+ if (byte == SYNC_BYTE)
+ {
+ /* sync byte following escape is an error, resync */
+ message(src, msg_bad_sync);
+ src->recv.in_sync = FALSE;
+ continue;
+ }
+ byte ^= 0x20;
+ src->recv.escaped = FALSE;
+ }
+ else if (byte == ESCAPE_BYTE)
+ {
+ src->recv.escaped = TRUE;
+ continue;
+ }
+ else if (byte == SYNC_BYTE)
+ {
+ int count = src->recv.count;
+ uint8_t *received;
+ uint16_t read_crc, computed_crc;
+
+ src->recv.count = 0; /* ready for next packet */
+
+ if (count < 4)
+ /* frames that are too small are ignored */
+ continue;
+
+ received = malloc(count - 2);
+ if (!received)
+ {
+ message(src, msg_no_memory);
+ continue;
+ }
+ memcpy(received, packet, count - 2);
+
+ read_crc = packet[count - 2] | packet[count - 1] << 8;
+ computed_crc = crc_packet(received, count - 2);
+
+#ifdef DEBUG
+ dump("received", packet, count);
+ printf(" crc %x comp %x\n", read_crc, computed_crc);
+#endif
+ if (read_crc == computed_crc)
+ {
+ process_packet(src, received, count - 2);
+ return; /* give rest of world chance to do something */
+ }
+ else
+ {
+ message(src, msg_bad_crc);
+ free(received);
+ /* We don't lose sync here. If we did, garbage on the line
+ at startup will cause loss of the first packet. */
+ continue;
+ }
+ }
+ packet[src->recv.count++] = byte;
+ }
+}
+
+static void process_packet(serial_source src, uint8_t *packet, int len)
+{
+ int packet_type = packet[0], offset = 1;
+
+ if (packet_type == P_PACKET_ACK)
+ {
+ /* send ack */
+ write_framed_packet(src, P_ACK, packet[1], NULL, 0);
+ /* And merge with un-acked packets */
+ packet_type = P_PACKET_NO_ACK;
+ offset = 2;
+ }
+ /* packet must remain a valid pointer to pass to free. So we move the
+ data rather than pass an internal pointer */
+ memmove(packet, packet + offset, len - offset);
+ push_protocol_packet(src, packet_type, packet, len - offset);
+}
+
+void *read_serial_packet(serial_source src, int *len)
+/* Effects: Read the serial source src. If a packet is available, return it.
+ If in blocking mode and no packet is available, wait for one.
+ Returns: the packet read (in newly allocated memory), with *len is
+ set to the packet length, or NULL if no packet is yet available and
+ the serial source is in non-blocking mode
+*/
+{
+ read_and_process(src, TRUE);
+ for (;;)
+ {
+ struct packet_list *entry;
+
+ entry = pop_protocol_packet(src, P_PACKET_NO_ACK);
+ if (entry)
+ {
+ uint8_t *packet = entry->packet;
+
+ *len = entry->len;
+ free(entry);
+
+ return packet;
+ }
+ if (src->non_blocking && serial_source_empty(src))
+ return NULL;
+ source_wait(src, NULL);
+ read_and_process(src, src->non_blocking);
+ }
+}
+
+/* The escaper does the sync bytes+escape-like encoding+crc of packets */
+
+static void escape_add(serial_source src, uint8_t b)
+{
+ src->send.escaped[src->send.escapeptr++] = b;
+}
+
+static int init_escaper(serial_source src, int count)
+{
+ src->send.escaped = malloc(count * 2 + 2);
+ if (!src->send.escaped)
+ {
+ message(src, msg_no_memory);
+ return -1;
+ }
+ src->send.escapeptr = 0;
+ src->send.crc = 0;
+
+ escape_add(src, SYNC_BYTE);
+
+ return 0;
+}
+
+static void terminate_escaper(serial_source src)
+{
+ escape_add(src, SYNC_BYTE);
+}
+
+static void escape_byte(serial_source src, uint8_t b)
+{
+ src->send.crc = crc_byte(src->send.crc, b);
+ if (b == SYNC_BYTE || b == ESCAPE_BYTE)
+ {
+ escape_add(src, ESCAPE_BYTE);
+ escape_add(src, b ^ 0x20);
+ }
+ else
+ escape_add(src, b);
+}
+
+static void free_escaper(serial_source src)
+{
+ free(src->send.escaped);
+}
+
+// Write a packet of type 'packetType', first byte 'firstByte'
+// and bytes 2..'count'+1 in 'packet'
+static int write_framed_packet(serial_source src,
+ uint8_t packet_type, uint8_t first_byte,
+ const uint8_t *packet, int count)
+{
+ int i, crc;
+
+#ifdef DEBUG
+ printf("writing %02x %02x", packet_type, first_byte);
+ dump("", packet, count);
+#endif
+
+ if (init_escaper(src, count + 4) < 0)
+ return -1;
+
+ escape_byte(src, packet_type);
+ escape_byte(src, first_byte);
+ for (i = 0; i < count; i++)
+ escape_byte(src, packet[i]);
+
+ crc = src->send.crc;
+ escape_byte(src, crc & 0xff);
+ escape_byte(src, crc >> 8);
+
+ terminate_escaper(src);
+
+#ifdef DEBUG
+ dump("encoded", src->send.escaped, src->send.escapeptr);
+#endif
+
+ if (source_write(src, src->send.escaped, src->send.escapeptr) < 0)
+ {
+ free_escaper(src);
+ return -1;
+ }
+ free_escaper(src);
+ return 0;
+}
+
+static void add_timeval(struct timeval *tv, long us)
+/* Specialised for this app */
+{
+ tv->tv_sec += us / 1000000;
+ tv->tv_usec += us % 1000000;
+ if (tv->tv_usec > 1000000)
+ {
+ tv->tv_usec -= 1000000;
+ tv->tv_sec++;
+ }
+}
+
+int write_serial_packet(serial_source src, const void *packet, int len)
+/* Effects: writes len byte packet to serial source src
+ Returns: 0 if packet successfully written, 1 if successfully written
+ but not acknowledged, -1 otherwise
+*/
+{
+ struct timeval deadline;
+
+ src->send.seqno++;
+ if (write_framed_packet(src, P_PACKET_ACK, src->send.seqno, packet, len) < 0)
+ return -1;
+
+ gettimeofday(&deadline, NULL);
+ add_timeval(&deadline, ACK_TIMEOUT);
+ for (;;)
+ {
+ struct packet_list *entry;
+
+ read_and_process(src, TRUE);
+ entry = pop_protocol_packet(src, P_ACK);
+ if (entry)
+ {
+ uint8_t acked = entry->packet[0];
+
+ free(entry->packet);
+ free(entry);
+ if (acked == src->send.seqno)
+ return 0;
+ }
+ else if (source_wait(src, &deadline) < 0)
+ return 1;
+ }
+}
+
+/* This somewhat convoluted code allows us to use a common baudrate table
+ with the Java code. This could be improved if we generated the Java
+ code from a common table.
+*/
+
+struct pargs {
+ char *name;
+ int rate;
+};
+
+static void padd(struct pargs *args, const char *name, int baudrate)
+{
+ if (!strcmp(args->name, name))
+ args->rate = baudrate;
+}
+
+static void init(void) { }
+
+int platform_baud_rate(char *platform_name)
+/* Returns: The baud rate of the specified platform, or -1 for unknown
+ platforms
+*/
+{
+ /* The Java code looks like Platform.add(Platform.x, "name", baudrate);
+ Fake up some C stuff which will make that work right. */
+ struct pargs args;
+ struct {
+ void (*add)(struct pargs *args, const char *name, int baudrate);
+ struct pargs *x;
+ } Platform = { padd, &args };
+ static struct {
+ struct {
+ int packet;
+ } tinyos;
+ } net;
+
+ if (isdigit(platform_name[0]))
+ return atoi(platform_name);
+
+ args.name = platform_name;
+ args.rate = -1;
+
+#define class
+#define BaudRate
+#define static
+#define void
+#define throws ;
+#define Exception
+#define package
+#include "../java/net/tinyos/packet/BaudRate.java"
+
+ return args.rate;
+}
--- /dev/null
+#ifndef SERIALSOURCE_H
+#define SERIALSOURCE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct serial_source *serial_source;
+
+typedef enum {
+ msg_unknown_packet_type, /* packet of unknown type received */
+ msg_ack_timeout, /* ack not received within timeout */
+ msg_sync, /* sync achieved */
+ msg_too_long, /* greater than MTU (256 bytes) */
+ msg_too_short, /* less than 4 bytes */
+ msg_bad_sync, /* unexpected sync byte received */
+ msg_bad_crc, /* received packet has bad crc */
+ msg_closed, /* serial port closed itself */
+ msg_no_memory, /* malloc failed */
+ msg_unix_error /* check errno for details */
+} serial_source_msg;
+
+serial_source open_serial_source(const char *device, int baud_rate,
+ int non_blocking,
+ void (*message)(serial_source_msg problem));
+/* Effects: opens serial port device at specified baud_rate. If non_blocking
+ is true, read_serial_packet calls will be non-blocking (writes are
+ always blocking, for now at least)
+ If non-null, message will be called to signal various problems during
+ execution.
+ Returns: descriptor for serial forwarder at host:port, or
+ NULL for failure
+ */
+
+int serial_source_fd(serial_source src);
+/* Returns: the file descriptor used by serial source src (useful when
+ non-blocking reads were requested)
+*/
+
+int serial_source_empty(serial_source src);
+/* Returns: true if serial source does not contain any pending data, i.e.,
+ if the result is true and there is no data available on the source's
+ file descriptor, then read_serial_packet will:
+ - return NULL if the source is non-blocking
+ - block if it is blocking
+
+ (Note: the presence of this calls allows the serial_source to do some
+ internal buffering)
+*/
+
+int close_serial_source(serial_source src);
+/* Effects: closes serial source src
+ Returns: 0 if successful, -1 if some problem occured (but source is
+ considered closed anyway)
+ */
+
+void *read_serial_packet(serial_source src, int *len);
+/* Effects: Read the serial source src. If a packet is available, return it.
+ If in blocking mode and no packet is available, wait for one.
+ Returns: the packet read (in newly allocated memory), with *len is
+ set to the packet length, or NULL if no packet is yet available and
+ the serial source is in non-blocking mode
+*/
+
+int write_serial_packet(serial_source src, const void *packet, int len);
+/* Effects: writes len byte packet to serial source src
+ Returns: 0 if packet successfully written, 1 if successfully written
+ but not acknowledged, -1 otherwise
+*/
+
+int platform_baud_rate(char *platform_name);
+/* Returns: The baud rate of the specified platform, or -1 for unknown
+ platforms. If platform_name starts with a digit, just return
+ atoi(platform_name).
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+
+#include "sfsource.h"
+#include "serialsource.h"
+
+serial_source src;
+int server_socket;
+int packets_read, packets_written, num_clients;
+
+struct client_list
+{
+ struct client_list *next;
+ int fd;
+} *clients;
+
+int unix_check(const char *msg, int result)
+{
+ if (result < 0)
+ {
+ perror(msg);
+ exit(2);
+ }
+
+ return result;
+}
+
+void *xmalloc(size_t s)
+{
+ void *p = malloc(s);
+
+ if (!p)
+ {
+ fprintf(stderr, "out of memory\n");
+ exit(2);
+ }
+ return p;
+}
+
+void fd_wait(fd_set *fds, int *maxfd, int fd)
+{
+ if (fd > *maxfd)
+ *maxfd = fd;
+ FD_SET(fd, fds);
+}
+
+void pstatus(void)
+{
+ printf("clients %d, read %d, wrote %d\n", num_clients, packets_read,
+ packets_written);
+}
+
+void forward_packet(const void *packet, int len);
+
+
+void add_client(int fd)
+{
+ struct client_list *c = xmalloc(sizeof *c);
+
+ c->next = clients;
+ clients = c;
+ num_clients++;
+ pstatus();
+
+ c->fd = fd;
+}
+
+void rem_client(struct client_list **c)
+{
+ struct client_list *dead = *c;
+
+ *c = dead->next;
+ num_clients--;
+ pstatus();
+ close(dead->fd);
+ free(dead);
+}
+
+void new_client(int fd)
+{
+ fcntl(fd, F_SETFL, 0);
+ if (init_sf_source(fd) < 0)
+ close(fd);
+ else
+ add_client(fd);
+}
+
+void check_clients(fd_set *fds)
+{
+ struct client_list **c;
+
+ for (c = &clients; *c; )
+ {
+ int next = 1;
+
+ if (FD_ISSET((*c)->fd, fds))
+ {
+ int len;
+ const void *packet = read_sf_packet((*c)->fd, &len);
+
+ if (packet)
+ {
+ forward_packet(packet, len);
+ free((void *)packet);
+ }
+ else
+ {
+ rem_client(c);
+ next = 0;
+ }
+ }
+ if (next)
+ c = &(*c)->next;
+ }
+}
+
+void wait_clients(fd_set *fds, int *maxfd)
+{
+ struct client_list *c;
+
+ for (c = clients; c; c = c->next)
+ fd_wait(fds, maxfd, c->fd);
+}
+
+void dispatch_packet(const void *packet, int len)
+{
+ struct client_list **c;
+
+ for (c = &clients; *c; )
+ if (write_sf_packet((*c)->fd, packet, len) >= 0)
+ c = &(*c)->next;
+ else
+ rem_client(c);
+}
+
+void open_server_socket(int port)
+{
+ struct sockaddr_in me;
+ int opt;
+
+ server_socket = unix_check("socket", socket(AF_INET, SOCK_STREAM, 0));
+ unix_check("socket", fcntl(server_socket, F_SETFL, O_NONBLOCK));
+ memset(&me, 0, sizeof me);
+ me.sin_family = AF_INET;
+ me.sin_port = htons(port);
+
+ opt = 1;
+ unix_check("setsockopt", setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&opt, sizeof(opt)));
+
+ unix_check("bind", bind(server_socket, (struct sockaddr *)&me, sizeof me));
+ unix_check("listen", listen(server_socket, 5));
+}
+
+void check_new_client(void)
+{
+ int clientfd = accept(server_socket, NULL, NULL);
+
+ if (clientfd >= 0)
+ new_client(clientfd);
+}
+
+
+
+
+
+void stderr_msg(serial_source_msg problem)
+{
+ static char *msgs[] = {
+ "unknown_packet_type",
+ "ack_timeout" ,
+ "sync" ,
+ "too_long" ,
+ "too_short" ,
+ "bad_sync" ,
+ "bad_crc" ,
+ "closed" ,
+ "no_memory" ,
+ "unix_error"
+ };
+
+ fprintf(stderr, "Note: %s\n", msgs[problem]);
+}
+
+void open_serial(const char *dev, int baud)
+{
+ char ldev[80];
+#ifdef __CYGWIN__
+ int portnum;
+ if (strncasecmp(dev, "COM", 3) == 0)
+ {
+ fprintf(stderr, "Warning: you're attempting to open a Windows rather that a Cygwin device. Retrying with ");
+ portnum=atoi(dev+3);
+ sprintf(ldev, "/dev/ttyS%d",portnum-1);
+ fprintf(stderr,ldev);
+ fprintf(stderr, "\n");
+ }
+ else
+#endif
+ strcpy(ldev, dev);
+
+ src = open_serial_source(ldev, baud, 1, stderr_msg);
+ if (!src)
+ {
+ fprintf(stderr, "Couldn't open serial port at %s:%d\n", dev, baud);
+ exit(1);
+ }
+}
+
+void check_serial(void)
+{
+ int len;
+ const unsigned char *packet = read_serial_packet(src, &len);
+
+ if (packet)
+ {
+ packets_read++;
+ dispatch_packet(packet, len);
+ free((void *)packet);
+ }
+}
+
+void forward_packet(const void *packet, int len)
+{
+ int ok = write_serial_packet(src, packet, len);
+
+ packets_written++;
+ if (ok < 0)
+ exit(2);
+ if (ok > 0)
+ fprintf(stderr, "Note: write failed\n");
+}
+
+int main(int argc, char **argv)
+{
+ int serfd;
+
+ if (argc != 4)
+ {
+ fprintf(stderr,
+ "Usage: %s <port> <device> <rate> - act as a serial forwarder on <port>\n"
+ "(listens to serial port <device> at baud rate <rate>)\n" ,
+ argv[0]);
+ exit(2);
+ }
+
+ if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+ fprintf(stderr, "Warning: failed to ignore SIGPIPE.\n");
+
+ open_serial(argv[2], platform_baud_rate(argv[3]));
+ serfd = serial_source_fd(src);
+ open_server_socket(atoi(argv[1]));
+
+ for (;;)
+ {
+ fd_set rfds;
+ int maxfd = -1;
+ struct timeval zero;
+ int serial_empty;
+ int ret;
+
+ zero.tv_sec = zero.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ fd_wait(&rfds, &maxfd, serfd);
+ fd_wait(&rfds, &maxfd, server_socket);
+ wait_clients(&rfds, &maxfd);
+
+ serial_empty = serial_source_empty(src);
+ if (serial_empty)
+ ret = select(maxfd + 1, &rfds, NULL, NULL, NULL);
+ else
+ {
+ ret = select(maxfd + 1, &rfds, NULL, NULL, &zero);
+ check_serial();
+ }
+ if (ret >= 0)
+ {
+ if (FD_ISSET(serfd, &rfds))
+ check_serial();
+
+ if (FD_ISSET(server_socket, &rfds))
+ check_new_client();
+
+ check_clients(&rfds);
+ }
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sfsource.h"
+
+int main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc != 3)
+ {
+ fprintf(stderr, "Usage: %s <host> <port> - dump packets from a serial forwarder\n", argv[0]);
+ exit(2);
+ }
+ fd = open_sf_source(argv[1], atoi(argv[2]));
+ if (fd < 0)
+ {
+ fprintf(stderr, "Couldn't open serial forwarder at %s:%s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+ for (;;)
+ {
+ int len, i;
+ const unsigned char *packet = read_sf_packet(fd, &len);
+
+ if (!packet)
+ exit(0);
+ for (i = 0; i < len; i++)
+ printf("%02x ", packet[i]);
+ putchar('\n');
+ fflush(stdout);
+ free((void *)packet);
+ }
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "sfsource.h"
+
+void send_packet(int fd, char **bytes, int count)
+{
+ int i;
+ unsigned char *packet;
+
+ packet = malloc(count);
+ if (!packet)
+ exit(2);
+
+ for (i = 0; i < count; i++)
+ packet[i] = strtol(bytes[i], NULL, 0);
+
+ fprintf(stderr,"Sending ");
+ for (i = 0; i < count; i++)
+ fprintf(stderr, " %02x", packet[i]);
+ fprintf(stderr, "\n");
+
+ write_sf_packet(fd, packet, count);
+}
+
+int main(int argc, char **argv)
+{
+ int fd;
+
+ if (argc < 4)
+ {
+ fprintf(stderr, "Usage: %s <host> <port> <bytes> - send a raw packet to a serial forwarder\n", argv[0]);
+ exit(2);
+ }
+ fd = open_sf_source(argv[1], atoi(argv[2]));
+ if (fd < 0)
+ {
+ fprintf(stderr, "Couldn't open serial forwarder at %s:%s\n",
+ argv[1], argv[2]);
+ exit(1);
+ }
+
+ send_packet(fd, argv + 3, argc - 3);
+
+ close(fd);
+}
--- /dev/null
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "sfsource.h"
+
+int saferead(int fd, void *buffer, int count)
+{
+ int actual = 0;
+
+ while (count > 0)
+ {
+ int n = read(fd, buffer, count);
+
+ if (n == -1 && errno == EINTR)
+ continue;
+ if (n == -1)
+ return -1;
+ if (n == 0)
+ return actual;
+
+ count -= n;
+ actual += n;
+ buffer = (char*)buffer + n;
+ }
+ return actual;
+}
+
+int safewrite(int fd, const void *buffer, int count)
+{
+ int actual = 0;
+
+ while (count > 0)
+ {
+ int n = write(fd, buffer, count);
+
+ if (n == -1 && errno == EINTR)
+ continue;
+ if (n == -1)
+ return -1;
+
+ count -= n;
+ actual += n;
+ buffer = (char*)buffer + n;
+ }
+ return actual;
+}
+
+int open_sf_source(const char *host, int port)
+/* Returns: file descriptor for serial forwarder at host:port
+ */
+{
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
+ struct hostent *entry;
+ struct sockaddr_in addr;
+
+ if (fd < 0)
+ return fd;
+
+ entry = gethostbyname(host);
+ if (!entry)
+ {
+ close(fd);
+ return -1;
+ }
+
+ addr.sin_family = entry->h_addrtype;
+ memcpy(&addr.sin_addr, entry->h_addr, entry->h_length);
+ addr.sin_port = htons(port);
+ if (connect(fd, (struct sockaddr *)&addr, sizeof addr) < 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ if (init_sf_source(fd) < 0)
+ {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int init_sf_source(int fd)
+/* Effects: Checks that fd is following the TinyOS 2.0 serial forwarder
+ protocol. Use this if you obtain your file descriptor from some other
+ source than open_sf_source (e.g., you're a server)
+ Returns: 0 if it is, -1 otherwise
+ */
+{
+ char check[2], us[2];
+ int version;
+
+ /* Indicate version and check if a TinyOS 2.0 serial forwarder on the
+ other end */
+ us[0] = 'U'; us[1] = ' ';
+ if (safewrite(fd, us, 2) != 2 ||
+ saferead(fd, check, 2) != 2 ||
+ check[0] != 'U')
+ return -1;
+
+ version = check[1];
+ if (us[1] < version)
+ version = us[1];
+
+ /* Add other cases here for later protocol versions */
+ switch (version)
+ {
+ case ' ': break;
+ default: return -1; /* not a valid version */
+ }
+
+ return 0;
+}
+
+void *read_sf_packet(int fd, int *len)
+/* Effects: reads packet from serial forwarder on file descriptor fd
+ Returns: the packet read (in newly allocated memory), and *len is
+ set to the packet length, or NULL for failure
+*/
+{
+ unsigned char l;
+ void *packet;
+
+ if (saferead(fd, &l, 1) != 1)
+ return NULL;
+
+ packet = malloc(l);
+ if (!packet)
+ return NULL;
+
+ if (saferead(fd, packet, l) != l)
+ {
+ free(packet);
+ return NULL;
+ }
+ *len = l;
+
+ return packet;
+}
+
+int write_sf_packet(int fd, const void *packet, int len)
+/* Effects: writes len byte packet to serial forwarder on file descriptor
+ fd
+ Returns: 0 if packet successfully written, -1 otherwise
+*/
+{
+ unsigned char l = len;
+
+ if (safewrite(fd, &l, 1) != 1 ||
+ safewrite(fd, packet, l) != l)
+ return -1;
+
+ return 0;
+}
--- /dev/null
+#ifndef SFSOURCE_H
+#define SFSOURCE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int open_sf_source(const char *host, int port);
+/* Returns: file descriptor for TinyOS 2.0 serial forwarder at host:port, or
+ -1 for failure
+ */
+
+int init_sf_source(int fd);
+/* Effects: Checks that fd is following the TinyOS 2.0 serial forwarder
+ protocol. Use this if you obtain your file descriptor from some other
+ source than open_sf_source (e.g., you're a server)
+ Returns: 0 if it is, -1 otherwise
+ */
+
+void *read_sf_packet(int fd, int *len);
+/* Effects: reads packet from serial forwarder on file descriptor fd
+ Returns: the packet read (in newly allocated memory), and *len is
+ set to the packet length
+*/
+
+int write_sf_packet(int fd, const void *packet, int len);
+/* Effects: writes len byte packet to serial forwarder on file descriptor
+ fd
+ Returns: 0 if packet successfully written, -1 otherwise
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif