Show tosnic_lower.c syntax highlighted
/*
*
* Copyright (c) 2005 The Regents of the University of California. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "tosnic_i.h"
#include <termios.h>
#include <sys/uio.h>
/*
* tosnic_lower.c
*
* Connects to local serial or remote tcp server, and handles integration with
* simulation and emulation
*
*/
/*
* interface to framer
*/
/* does NOT delete buffer */
void tosnic_lower_write(tosnic_state_t *s, buf_t *framed)
{
elog(LOG_DEBUG(5), "Writing bytes to port:");
elog_raw(LOG_DEBUG(5), framed->buf, framed->len);
s->serial_tx_count += framed->len;
if (s->net_port)
ev_tcp_peer_write(s->net_port, framed);
else {
if (write_to_fd(s->serial_fd, framed->buf, framed->len) < 0) {
elog(LOG_WARNING, "Error on underlying serial connection: %m");
tosnic_reset(s);
}
}
}
/*
* serial handler
*/
static
int tosnic_lower_serial_data_ready(void *data, int fd, int cond, g_event_t *event)
{
tosnic_state_t *s = (tosnic_state_t *)data;
if (cond == FUSD_NOTIFY_INPUT) {
buf_t *serial_data = buf_new();
bufcpy(serial_data, NULL, TOSNIC_READ_LEN);
int status = read(fd, serial_data->buf, TOSNIC_READ_LEN);
if (status < 0) {
if (errno == EINTR || errno == EAGAIN)
goto done;
elog(LOG_CRIT, "Read error from serial %s: %m", s->local_device);
exit(1);
}
if (status > 0) {
serial_data->len = status;
tosnic_framer_data_from_lower(s, serial_data);
}
done:
return EVENT_RENEW;
}
elog(LOG_WARNING, "Exception on serial %s.. USB flaked?: %m",
s->local_device);
close(fd);
elog(LOG_WARNING, "Will attempt to reconnect after one second...");
sleep(1);
tosnic_reset(s);
return EVENT_DONE;
}
/*
* tcp handlers
*/
static
int tosnic_lower_tcp_data_ready(ev_tcp_peer_t *peer, buf_t *data)
{
tosnic_state_t *s = (tosnic_state_t *)ev_tcp_peer_get_peer_data(peer);
tosnic_framer_data_from_lower(s, data);
return EVENT_RENEW;
}
static
void tosnic_lower_tcp_close(ev_tcp_peer_t *peer, int errno_value)
{
tosnic_state_t *s = (tosnic_state_t *)ev_tcp_peer_get_peer_data(peer);
elog(LOG_CRIT, "exiting: connection to %s dropped: %s",
s->serial_devname, strerror(errno_value));
exit(1);
}
/*
* initialization
*/
int tosnic_lower_reset(tosnic_state_t *s)
{
/* if host:port specified, connect to remote tcp port.. */
if (s->hostname) {
/* close if previously connected */
if (s->net_port) ev_tcp_peer_close(s->net_port, 0);
ev_tcp_peer_opts_t opts = {
data_ready: tosnic_lower_tcp_data_ready,
on_close: tosnic_lower_tcp_close,
peer_info: s
};
s->net_port = ev_tcp_connect(NULL, s->hostname, s->port_num, &opts);
if (s->net_port == NULL) {
elog(LOG_CRIT, "Can't connect to %s: %m", s->serial_devname);
exit(1);
}
}
/* otherwise, open serial */
else if (s->local_device) {
/* close if previously open */
if (s->serial) {
close(s->serial_fd);
g_event_destroy(s->serial);
}
/* Try to open the port */
if ((s->serial_fd = open(s->local_device, O_RDWR | O_NOCTTY)) < 0) {
elog(LOG_CRIT, "can't open %s: %m", s->local_device);
exit(1);
}
/* configure the serial port: raw, set baud, no rtscts */
misc_config_serial(s->serial_fd, s->baud, s->rtscts);
/* flush output */
if (tcflush(s->serial_fd, TCIFLUSH) < 0)
elog(LOG_WARNING, "can't tcflush serial port %s: %m", s->local_device);
/* Set this FD to be nonblocking */
set_nonblock(s->serial_fd, 1);
/* Add an event that fires when the serial port is readable */
if (g_event_add(s->serial_fd, FUSD_NOTIFY_INPUT | FUSD_NOTIFY_EXCEPT,
tosnic_lower_serial_data_ready, s, NULL, &s->serial) < 0) {
elog(LOG_CRIT, "can't create event for %s: %m", s->local_device);
exit(1);
}
}
else return -1;
return 0;
}
static
void tosnic_parse_moteid(tosnic_state_t *s)
{
char *dup = strdup(s->serial_devname);
/* is it a host:port? */
char *colon;
if ((colon = strchr(dup, ':'))) {
*colon = 0;
colon++;
s->port_num = atoi(colon);
s->hostname = dup;
}
else if (strncmp(dup, "/dev/", 5) == 0) {
s->local_device = dup;
}
else {
int id;
if (sscanf(dup, "%d", &id) == 1) {
char port[20];
sprintf(port, "/dev/tty%d", id);
s->local_device = strdup(port);
}
else {
elog(LOG_WARNING, "*** Error parsing mote config string '%s'", dup);
exit(1);
}
}
}
int tosnic_lower_init(tosnic_state_t *s, int *argc, char **argv)
{
/* parse lower configuration args */
s->serial_devname = misc_parse_out_option(argc, argv, "port", 0);
s->baud_requested = (0 == misc_parse_option_as_uint(argc, argv, "baud", 'b', &s->baud));
s->rtscts = misc_parse_out_switch(argc, argv, "rtscts", 0);
s->power_requested = (0 == misc_parse_option_as_uint(argc, argv, "power", 'p', &s->power));
s->mote_type_str = misc_parse_out_option(argc, argv, "mote", 'm');
int debug_in_sim = misc_parse_out_switch(argc, argv, "debug_in_sim", 0);
/* parse the mote type */
if (s->mote_type_str) {
if (strcmp(s->mote_type_str, "mica2") == 0) {
s->mote_type = TOSNIC_MICA2;
}
else if (strcmp(s->mote_type_str, "mica2dot") == 0) {
s->mote_type = TOSNIC_MICA2DOT;
}
else if (strcmp(s->mote_type_str, "mica") == 0) {
s->mote_type = TOSNIC_MICA1;
}
else if (strcmp(s->mote_type_str, "telos") == 0) {
s->mote_type = TOSNIC_TELOS;
}
else if (strcmp(s->mote_type_str, "cricket") == 0) {
s->mote_type = TOSNIC_CRICKET;
}
else {
elog(LOG_WARNING, "Unknown mote type '%s'.. aborting", s->mote_type_str);
exit(1);
}
}
/* mica2 is default */
else {
s->mote_type_str = "mica2";
s->mote_type = TOSNIC_MICA2;
}
/* if a mote type is set, autoconf baud rate */
if (!s->baud_requested) {
switch (s->mote_type) {
case TOSNIC_MICA2:
s->baud = 57600;
break;
case TOSNIC_MICA1:
case TOSNIC_MICA2DOT:
s->baud = 19200;
break;
case TOSNIC_CRICKET:
s->baud = 115200;
break;
case TOSNIC_TELOS:
s->baud = 262144;
break;
}
}
/* if a mote type is set, autoconf power */
if (!s->power_requested) {
s->power = 15;
if (s->mote_type == TOSNIC_MICA1)
s->power = 67;
}
/* are we in sim mode? */
if (in_sim) {
/* if we're in proxy mode, we don't do anything locally */
char *proxy_host = misc_sim_get_proxy_host(s->dev_name, argc, argv);
if (proxy_host) {
/* ok, we connect to the remote host.. */
elog(LOG_NOTICE, "Connecting to remote host %s, link device %s", proxy_host, s->dev_name);
elog(LOG_NOTICE, "Mapping to group %d, node %d, device %s", my_sim_group, my_node_id, s->dev_name);
lp_register_in_class(s->class_name, s->dev_name);
if (!(fusdd_proxy_connect(proxy_host, link_name_s_nosim(s->dev_name, LINK_DATA_SUBDEV),
link_name_s(s->dev_name, LINK_DATA_SUBDEV), NULL) == 0 &&
fusdd_proxy_connect(proxy_host, link_name_s_nosim(s->dev_name, LINK_STATUS_SUBDEV),
link_name_s(s->dev_name, LINK_STATUS_SUBDEV), NULL) == 0 &&
fusdd_proxy_connect(proxy_host, link_name_s_nosim(s->dev_name, LINK_COMMAND_SUBDEV),
link_name_s(s->dev_name, LINK_COMMAND_SUBDEV), NULL) == 0)) {
/* $$$ expose the other mote devices.. */
elog(LOG_CRIT, "Failed to connect to remote host: %m");
exit(1);
}
return 1;
}
/* or do we have a mote ID env var? */
char *str = getenv("MOTE_ID");
if (str) {
elog(LOG_NOTICE, "In ceiling mode... overriding port argument with %s", str);
s->serial_devname = strdup(str);
}
/* if not, check to see if there's an active channel model. */
else {
lp_opts_t opts = {
opts: {
name: s->dev_name,
if_class: s->class_name
}
};
if (lp_check_sim(&opts))
return 1;
if (debug_in_sim) {
elog(LOG_CRIT, "****************SIM DEBUGGING MODE!!!!");
s->serial_devname = sim_path(s->serial_devname);
elog(LOG_CRIT, "Will actually open lower device %s", s->serial_devname);
}
else {
elog(LOG_CRIT, "***In SIM mode, but no simulated channel model or ceiling config present!");
elog(LOG_CRIT, "***Please check your configuration!! Exiting!");
exit(1);
}
}
}
/* OK, now we will parse the port argument and open the lower connection */
if (s->serial_devname) {
tosnic_parse_moteid(s);
if (tosnic_lower_reset(s) < 0) {
/* nothing to open..? */
elog(LOG_CRIT, "?? no hostname or local device to open?");
exit(1);
}
}
else {
elog(LOG_CRIT, "Must specify --port argument or provide via MOTE_ID environment variable!");
usage(argv[0]);
exit(1);
}
/* and continue to provide upper.. */
return 0;
}
See more files for this project here