From 1ecdae4fe4bea8892f7eaa72e17159e47aff5840 Mon Sep 17 00:00:00 2001 From: gnawali Date: Wed, 5 Dec 2007 22:49:45 +0000 Subject: [PATCH] moving from sdk/c --- support/sdk/c/sf/Makefile.am | 40 ++ support/sdk/c/sf/README | 51 ++ support/sdk/c/sf/autoconf.h | 23 + support/sdk/c/sf/bootstrap | 5 + support/sdk/c/sf/build.xml | 27 ++ support/sdk/c/sf/configure.ac | 10 + support/sdk/c/sf/message.c | 312 ++++++++++++ support/sdk/c/sf/message.h | 156 ++++++ support/sdk/c/sf/prettylisten.c | 68 +++ support/sdk/c/sf/seriallisten.c | 52 ++ support/sdk/c/sf/serialsend.c | 65 +++ support/sdk/c/sf/serialsource.c | 835 ++++++++++++++++++++++++++++++++ support/sdk/c/sf/serialsource.h | 81 ++++ support/sdk/c/sf/sf.c | 297 ++++++++++++ support/sdk/c/sf/sflisten.c | 35 ++ support/sdk/c/sf/sfsend.c | 47 ++ support/sdk/c/sf/sfsource.c | 161 ++++++ support/sdk/c/sf/sfsource.h | 36 ++ 18 files changed, 2301 insertions(+) create mode 100644 support/sdk/c/sf/Makefile.am create mode 100644 support/sdk/c/sf/README create mode 100644 support/sdk/c/sf/autoconf.h create mode 100755 support/sdk/c/sf/bootstrap create mode 100644 support/sdk/c/sf/build.xml create mode 100644 support/sdk/c/sf/configure.ac create mode 100644 support/sdk/c/sf/message.c create mode 100644 support/sdk/c/sf/message.h create mode 100644 support/sdk/c/sf/prettylisten.c create mode 100644 support/sdk/c/sf/seriallisten.c create mode 100644 support/sdk/c/sf/serialsend.c create mode 100644 support/sdk/c/sf/serialsource.c create mode 100644 support/sdk/c/sf/serialsource.h create mode 100644 support/sdk/c/sf/sf.c create mode 100644 support/sdk/c/sf/sflisten.c create mode 100644 support/sdk/c/sf/sfsend.c create mode 100644 support/sdk/c/sf/sfsource.c create mode 100644 support/sdk/c/sf/sfsource.h diff --git a/support/sdk/c/sf/Makefile.am b/support/sdk/c/sf/Makefile.am new file mode 100644 index 00000000..b26b8059 --- /dev/null +++ b/support/sdk/c/sf/Makefile.am @@ -0,0 +1,40 @@ +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 diff --git a/support/sdk/c/sf/README b/support/sdk/c/sf/README new file mode 100644 index 00000000..100865c2 --- /dev/null +++ b/support/sdk/c/sf/README @@ -0,0 +1,51 @@ +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= + make +in the current directory and, if you wish, "make install" to install the +C-based serial forwarder in /bin. + +This directory contains one utility: +- sf: a C-based serial forwarder: + sf + Starts a serial forwarder listening for TCP connections on port , and + sending and receiving packets on serial port at the specified + . + + 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. + + diff --git a/support/sdk/c/sf/autoconf.h b/support/sdk/c/sf/autoconf.h new file mode 100644 index 00000000..82855083 --- /dev/null +++ b/support/sdk/c/sf/autoconf.h @@ -0,0 +1,23 @@ +/* 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" diff --git a/support/sdk/c/sf/bootstrap b/support/sdk/c/sf/bootstrap new file mode 100755 index 00000000..f35ba820 --- /dev/null +++ b/support/sdk/c/sf/bootstrap @@ -0,0 +1,5 @@ +mkdir config-aux +aclocal +autoheader +autoconf +automake -a -c diff --git a/support/sdk/c/sf/build.xml b/support/sdk/c/sf/build.xml new file mode 100644 index 00000000..b966962d --- /dev/null +++ b/support/sdk/c/sf/build.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/support/sdk/c/sf/configure.ac b/support/sdk/c/sf/configure.ac new file mode 100644 index 00000000..c064fd6d --- /dev/null +++ b/support/sdk/c/sf/configure.ac @@ -0,0 +1,10 @@ +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) diff --git a/support/sdk/c/sf/message.c b/support/sdk/c/sf/message.c new file mode 100644 index 00000000..9739c15a --- /dev/null +++ b/support/sdk/c/sf/message.c @@ -0,0 +1,312 @@ +/* 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 + * Intel Research Berkeley Lab + */ + +#include +#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)); +} diff --git a/support/sdk/c/sf/message.h b/support/sdk/c/sf/message.h new file mode 100644 index 00000000..8655f7b3 --- /dev/null +++ b/support/sdk/c/sf/message.h @@ -0,0 +1,156 @@ +/* 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 + * Intel Research Berkeley Lab + */ +#ifndef MESSAGE_H +#define MESSAGE_H + +#include +#include + +#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 diff --git a/support/sdk/c/sf/prettylisten.c b/support/sdk/c/sf/prettylisten.c new file mode 100644 index 00000000..4a24f7dd --- /dev/null +++ b/support/sdk/c/sf/prettylisten.c @@ -0,0 +1,68 @@ +#include +#include + +#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 - 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); + } +} diff --git a/support/sdk/c/sf/seriallisten.c b/support/sdk/c/sf/seriallisten.c new file mode 100644 index 00000000..f7d5aeb6 --- /dev/null +++ b/support/sdk/c/sf/seriallisten.c @@ -0,0 +1,52 @@ +#include +#include + +#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 - 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); + } +} diff --git a/support/sdk/c/sf/serialsend.c b/support/sdk/c/sf/serialsend.c new file mode 100644 index 00000000..7735f362 --- /dev/null +++ b/support/sdk/c/sf/serialsend.c @@ -0,0 +1,65 @@ +#include +#include + +#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 - 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); +} diff --git a/support/sdk/c/sf/serialsource.c b/support/sdk/c/sf/serialsource.c new file mode 100644 index 00000000..4e4afabd --- /dev/null +++ b/support/sdk/c/sf/serialsource.c @@ -0,0 +1,835 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __CYGWIN__ +#include +#include +#else +#include +#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; +} diff --git a/support/sdk/c/sf/serialsource.h b/support/sdk/c/sf/serialsource.h new file mode 100644 index 00000000..28ba5bdc --- /dev/null +++ b/support/sdk/c/sf/serialsource.h @@ -0,0 +1,81 @@ +#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 diff --git a/support/sdk/c/sf/sf.c b/support/sdk/c/sf/sf.c new file mode 100644 index 00000000..ada9bc5b --- /dev/null +++ b/support/sdk/c/sf/sf.c @@ -0,0 +1,297 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 - act as a serial forwarder on \n" + "(listens to serial port at baud 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); + } + } +} diff --git a/support/sdk/c/sf/sflisten.c b/support/sdk/c/sf/sflisten.c new file mode 100644 index 00000000..845780c6 --- /dev/null +++ b/support/sdk/c/sf/sflisten.c @@ -0,0 +1,35 @@ +#include +#include + +#include "sfsource.h" + +int main(int argc, char **argv) +{ + int fd; + + if (argc != 3) + { + fprintf(stderr, "Usage: %s - 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); + } +} diff --git a/support/sdk/c/sf/sfsend.c b/support/sdk/c/sf/sfsend.c new file mode 100644 index 00000000..73043152 --- /dev/null +++ b/support/sdk/c/sf/sfsend.c @@ -0,0 +1,47 @@ +#include +#include +#include + +#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 - 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); +} diff --git a/support/sdk/c/sf/sfsource.c b/support/sdk/c/sf/sfsource.c new file mode 100644 index 00000000..24df9603 --- /dev/null +++ b/support/sdk/c/sf/sfsource.c @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/support/sdk/c/sf/sfsource.h b/support/sdk/c/sf/sfsource.h new file mode 100644 index 00000000..b6c2b923 --- /dev/null +++ b/support/sdk/c/sf/sfsource.h @@ -0,0 +1,36 @@ +#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 -- 2.39.2