/*
libxbee - a C library to aid the use of Digi's Series 1 XBee modules
running in API mode (AP=2).
Copyright (C) 2009 Attie Grande (attie@attie.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
const char *SVN_REV = "$Id: api.c 508 2011-06-12 23:22:34Z attie@attie.co.uk $";
char svn_rev[128] = "\0";
#include "api.h"
const char *xbee_svn_version(void) {
if (svn_rev[0] == '\0') {
char *t;
sprintf(svn_rev,"r%s",&SVN_REV[11]);
t = strrchr(svn_rev,' ');
if (t) {
t[0] = '\0';
}
}
return svn_rev;
}
const char *xbee_build_info(void) {
return "Built on " __DATE__ " @ " __TIME__ " for " HOST_OS;
}
/* ################################################################# */
/* ### Memory Handling ############################################# */
/* ################################################################# */
/* malloc wrapper function */
static void *Xmalloc2(xbee_hnd xbee, size_t size) {
void *t;
t = malloc(size);
if (!t) {
/* uhoh... thats pretty bad... */
xbee_perror("libxbee:malloc()");
exit(1);
}
return t;
}
/* calloc wrapper function */
static void *Xcalloc2(xbee_hnd xbee, size_t size) {
void *t;
t = calloc(1, size);
if (!t) {
/* uhoh... thats pretty bad... */
xbee_perror("libxbee:calloc()");
exit(1);
}
return t;
}
/* realloc wrapper function */
static void *Xrealloc2(xbee_hnd xbee, void *ptr, size_t size) {
void *t;
t = realloc(ptr,size);
if (!t) {
/* uhoh... thats pretty bad... */
fprintf(stderr,"libxbee:realloc(): Returned NULL\n");
exit(1);
}
return t;
}
/* free wrapper function (uses the Xfree macro and sets the pointer to NULL after freeing it) */
static void Xfree2(void **ptr) {
if (!*ptr) return;
free(*ptr);
*ptr = NULL;
}
/* ################################################################# */
/* ### Helper Functions ############################################ */
/* ################################################################# */
/* #################################################################
returns 1 if the packet has data for the digital input else 0 */
int xbee_hasdigital(xbee_pkt *pkt, int sample, int input) {
int mask = 0x0001;
if (input < 0 || input > 7) return 0;
if (sample >= pkt->samples) return 0;
mask <<= input;
return !!(pkt->IOdata[sample].IOmask & mask);
}
/* #################################################################
returns 1 if the digital input is high else 0 (or 0 if no digital data present) */
int xbee_getdigital(xbee_pkt *pkt, int sample, int input) {
int mask = 0x0001;
if (!xbee_hasdigital(pkt,sample,input)) return 0;
mask <<= input;
return !!(pkt->IOdata[sample].IOdigital & mask);
}
/* #################################################################
returns 1 if the packet has data for the analog input else 0 */
int xbee_hasanalog(xbee_pkt *pkt, int sample, int input) {
int mask = 0x0200;
if (input < 0 || input > 5) return 0;
if (sample >= pkt->samples) return 0;
mask <<= input;
return !!(pkt->IOdata[sample].IOmask & mask);
}
/* #################################################################
returns analog input as a voltage if vRef is non-zero, else raw value (or 0 if no analog data present) */
double xbee_getanalog(xbee_pkt *pkt, int sample, int input, double Vref) {
if (!xbee_hasanalog(pkt,sample,input)) return 0;
if (Vref) return (Vref / 1023) * pkt->IOdata[sample].IOanalog[input];
return pkt->IOdata[sample].IOanalog[input];
}
/* ################################################################# */
/* ### XBee Functions ############################################## */
/* ################################################################# */
static void xbee_logf(xbee_hnd xbee, const char *logformat, const char *file,
const int line, const char *function, char *format, ...) {
char buf[128];
va_list ap;
if (!xbee) return;
if (!xbee->log) return;
va_start(ap,format);
vsnprintf(buf,127,format,ap);
va_end(ap);
fprintf(xbee->log,logformat,file,line,function,buf);
}
void xbee_logitf(char *format, ...) {
char buf[128];
va_list ap;
va_start(ap,format);
vsnprintf(buf,127,format,ap);
va_end(ap);
xbee_logit(buf);
}
void _xbee_logitf(xbee_hnd xbee, char *format, ...) {
char buf[128];
va_list ap;
va_start(ap,format);
vsnprintf(buf,127,format,ap);
va_end(ap);
_xbee_logit(xbee, buf);
}
void xbee_logit(char *str) {
_xbee_logit(default_xbee, str);
}
void _xbee_logit(xbee_hnd xbee, char *str) {
if (!xbee) return;
if (!xbee->log) return;
xbee_mutex_lock(xbee->logmutex);
fprintf(xbee->log,LOG_FORMAT"\n",__FILE__,__LINE__,__FUNCTION__,str);
xbee_mutex_unlock(xbee->logmutex);
}
/* #################################################################
xbee_sendAT - INTERNAL
allows for an at command to be send, and the reply to be captured */
static int xbee_sendAT(xbee_hnd xbee, char *command, char *retBuf, int retBuflen) {
return xbee_sendATdelay(xbee, 0, command, retBuf, retBuflen);
}
static int xbee_sendATdelay(xbee_hnd xbee, int guardTime, char *command, char *retBuf, int retBuflen) {
struct timeval to;
int ret;
int bufi = 0;
/* if there is a guardTime given, then use it and a bit more */
if (guardTime) usleep(guardTime * 1200);
/* get rid of any pre-command sludge... */
memset(&to, 0, sizeof(to));
ret = xbee_select(xbee,&to);
if (ret > 0) {
char t[128];
while (xbee_read(xbee,t,127));
}
/* send the requested command */
xbee_log("sendATdelay: Sending '%s'", command);
xbee_write(xbee,command, strlen(command));
/* if there is a guardTime, then use it */
if (guardTime) {
usleep(guardTime * 900);
/* get rid of any post-command sludge... */
memset(&to, 0, sizeof(to));
ret = xbee_select(xbee,&to);
if (ret > 0) {
char t[128];
while (xbee_read(xbee,t,127));
}
}
/* retrieve the data */
memset(retBuf, 0, retBuflen);
memset(&to, 0, sizeof(to));
if (guardTime) {
/* select on the xbee fd... wait at most 0.2 the guardTime for the response */
to.tv_usec = guardTime * 200;
} else {
/* or 250ms */
to.tv_usec = 250000;
}
if ((ret = xbee_select(xbee,&to)) == -1) {
xbee_perror("libxbee:xbee_sendATdelay()");
exit(1);
}
if (!ret) {
/* timed out, and there is nothing to be read */
xbee_log("sendATdelay: No Data to read - Timeout...");
return 1;
}
/* check for any dribble... */
do {
/* if there is actually no space in the retBuf then break out */
if (bufi >= retBuflen - 1) {
break;
}
/* read as much data as is possible into retBuf */
if ((ret = xbee_read(xbee,&retBuf[bufi], retBuflen - bufi - 1)) == 0) {
break;
}
/* advance the 'end of string' pointer */
bufi += ret;
/* wait at most 150ms for any more data */
memset(&to, 0, sizeof(to));
to.tv_usec = 150000;
if ((ret = xbee_select(xbee,&to)) == -1) {
xbee_perror("libxbee:xbee_sendATdelay()");
exit(1);
}
/* loop while data was read */
} while (ret);
if (!bufi) {
xbee_log("sendATdelay: No response...");
return 1;
}
/* terminate the string */
retBuf[bufi] = '\0';
xbee_log("sendATdelay: Recieved '%s'",retBuf);
return 0;
}
/* #################################################################
xbee_start
sets up the correct API mode for the xbee
cmdSeq = CC
cmdTime = GT */
static int xbee_startAPI(xbee_hnd xbee) {
char buf[256];
if (xbee->cmdSeq == 0 || xbee->cmdTime == 0) return 1;
/* setup the command sequence string */
memset(buf,xbee->cmdSeq,3);
buf[3] = '\0';
/* try the command sequence */
if (xbee_sendATdelay(xbee, xbee->cmdTime, buf, buf, sizeof(buf))) {
/* if it failed... try just entering 'AT' which should return OK */
if (xbee_sendAT(xbee, "AT\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1;
} else if (strncmp(&buf[strlen(buf)-3],"OK\r",3)) {
/* if data was returned, but it wasn't OK... then something went wrong! */
return 1;
}
/* get the current API mode */
if (xbee_sendAT(xbee, "ATAP\r", buf, 3)) return 1;
buf[1] = '\0';
xbee->oldAPI = atoi(buf);
if (xbee->oldAPI != 2) {
/* if it wasnt set to mode 2 already, then set it to mode 2 */
if (xbee_sendAT(xbee, "ATAP2\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1;
}
/* quit from command mode, ready for some packets! :) */
if (xbee_sendAT(xbee, "ATCN\r", buf, 4) || strncmp(buf,"OK\r",3)) return 1;
return 0;
}
/* #################################################################
xbee_end
resets the API mode to the saved value - you must have called xbee_setup[log]API */
int xbee_end(void) {
return _xbee_end(default_xbee);
}
int _xbee_end(xbee_hnd xbee) {
int ret = 1;
xbee_con *con, *ncon;
xbee_pkt *pkt, *npkt;
xbee_hnd xbeet;
ISREADYR(0);
xbee_log("Stopping libxbee instance...");
/* unlink the instance from list... */
xbee_log("Unlinking instance from list...");
xbee_mutex_lock(xbee_hnd_mutex);
if (xbee == default_xbee) {
default_xbee = default_xbee->next;
if (!default_xbee) {
xbee_mutex_destroy(xbee_hnd_mutex);
}
} else {
xbeet = default_xbee;
while (xbeet) {
if (xbeet->next == xbee) {
xbeet->next = xbee->next;
break;
}
xbeet = xbeet->next;
}
}
if (default_xbee) xbee_mutex_unlock(xbee_hnd_mutex);
/* if the api mode was not 2 to begin with then put it back */
if (xbee->oldAPI == 2) {
xbee_log("XBee was already in API mode 2, no need to reset");
ret = 0;
} else {
int to = 5;
con = _xbee_newcon(xbee,'I',xbee_localAT);
con->callback = NULL;
con->waitforACK = 1;
_xbee_senddata(xbee,con,"AP%c",xbee->oldAPI);
pkt = NULL;
while (!pkt && to--) {
pkt = _xbee_getpacketwait(xbee,con);
}
if (pkt) {
ret = pkt->status;
Xfree(pkt);
}
_xbee_endcon(xbee,con);
}
/* xbee_* functions may no longer run... */
xbee->xbee_ready = 0;
/* nullify everything */
/* stop listening for data... either after timeout or next char read which ever is first */
xbee->run = 0;
xbee_thread_cancel(xbee->listent,0);
xbee_thread_join(xbee->listent);
xbee_thread_cancel(xbee->threadt,0);
xbee_thread_join(xbee->threadt);
/* free all connections */
con = xbee->conlist;
xbee->conlist = NULL;
while (con) {
ncon = con->next;
Xfree(con);
con = ncon;
}
/* free all packets */
xbee->pktlast = NULL;
pkt = xbee->pktlist;
xbee->pktlist = NULL;
while (pkt) {
npkt = pkt->next;
Xfree(pkt);
pkt = npkt;
}
/* destroy mutexes */
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
/* close the serial port */
Xfree(xbee->path);
if (xbee->tty) xbee_close(xbee->tty);
/* close log and tty */
if (xbee->log) {
fflush(xbee->log);
xbee_close(xbee->log);
}
xbee_mutex_destroy(xbee->logmutex);
Xfree(xbee);
return ret;
}
/* #################################################################
xbee_setup
opens xbee serial port & creates xbee listen thread
the xbee must be configured for API mode 2
THIS MUST BE CALLED BEFORE ANY OTHER XBEE FUNCTION */
int xbee_setup(char *path, int baudrate) {
return xbee_setuplogAPI(path,baudrate,0,0,0);
}
xbee_hnd _xbee_setup(char *path, int baudrate) {
return _xbee_setuplogAPI(path,baudrate,0,0,0);
}
int xbee_setuplog(char *path, int baudrate, int logfd) {
return xbee_setuplogAPI(path,baudrate,logfd,0,0);
}
xbee_hnd _xbee_setuplog(char *path, int baudrate, int logfd) {
return _xbee_setuplogAPI(path,baudrate,logfd,0,0);
}
int xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime) {
return xbee_setuplogAPI(path,baudrate,0,cmdSeq,cmdTime);
}
xbee_hnd _xbee_setupAPI(char *path, int baudrate, char cmdSeq, int cmdTime) {
return _xbee_setuplogAPI(path,baudrate,0,cmdSeq,cmdTime);
}
int xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime) {
if (default_xbee) return 0;
default_xbee = _xbee_setuplogAPI(path,baudrate,logfd,cmdSeq,cmdTime);
return (default_xbee?0:-1);
}
xbee_hnd _xbee_setuplogAPI(char *path, int baudrate, int logfd, char cmdSeq, int cmdTime) {
int ret;
xbee_hnd xbee = NULL;
/* create a new instance */
xbee = Xcalloc(sizeof(struct xbee_hnd));
xbee->next = NULL;
xbee_mutex_init(xbee->logmutex);
#ifdef DEBUG
if (!logfd) logfd = 2;
#endif
if (logfd) {
xbee->logfd = dup(logfd);
xbee->log = fdopen(xbee->logfd,"w");
if (!xbee->log) {
/* errno == 9 is bad file descriptor (probrably not provided) */
if (errno != 9) xbee_perror("xbee_setup(): Failed opening logfile");
xbee->logfd = 0;
} else {
#ifdef __GNUC__ /* ---- */
/* set to line buffer - ensure lines are written to file when complete */
setvbuf(xbee->log,NULL,_IOLBF,BUFSIZ);
#else /* -------------- */
/* Win32 is rubbish... so we have to completely disable buffering... */
setvbuf(xbee->log,NULL,_IONBF,BUFSIZ);
#endif /* ------------- */
}
}
xbee_logS("---------------------------------------------------------------------");
xbee_logI("libxbee Starting...");
xbee_logI("SVN Info: %s",xbee_svn_version());
xbee_logI("Build Info: %s",xbee_build_info());
xbee_logE("---------------------------------------------------------------------");
/* setup the connection stuff */
xbee->conlist = NULL;
/* setup the packet stuff */
xbee->pktlist = NULL;
xbee->pktlast = NULL;
xbee->pktcount = 0;
xbee->run = 1;
/* setup the mutexes */
if (xbee_mutex_init(xbee->conmutex)) {
xbee_perror("xbee_setup():xbee_mutex_init(conmutex)");
if (xbee->log) xbee_close(xbee->log);
Xfree(xbee);
return NULL;
}
if (xbee_mutex_init(xbee->pktmutex)) {
xbee_perror("xbee_setup():xbee_mutex_init(pktmutex)");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
Xfree(xbee);
return NULL;
}
if (xbee_mutex_init(xbee->sendmutex)) {
xbee_perror("xbee_setup():xbee_mutex_init(sendmutex)");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
Xfree(xbee);
return NULL;
}
/* take a copy of the XBee device path */
if ((xbee->path = Xmalloc(sizeof(char) * (strlen(path) + 1))) == NULL) {
xbee_perror("xbee_setup():Xmalloc(path)");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
Xfree(xbee);
return NULL;
}
strcpy(xbee->path,path);
if (xbee->log) xbee_log("Opening serial port '%s'...",xbee->path);
/* call the relevant init function */
if ((ret = init_serial(xbee,baudrate)) != 0) {
xbee_log("Something failed while opening the serial port...");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
Xfree(xbee->path);
Xfree(xbee);
return NULL;
}
/* when xbee_end() is called, if this is not 2 then ATAP will be set to this value */
xbee->oldAPI = 2;
xbee->cmdSeq = cmdSeq;
xbee->cmdTime = cmdTime;
if (xbee->cmdSeq && xbee->cmdTime) {
if (xbee_startAPI(xbee)) {
if (xbee->log) {
xbee_log("Couldn't communicate with XBee...");
xbee_close(xbee->log);
}
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
Xfree(xbee->path);
#ifdef __GNUC__ /* ---- */
close(xbee->ttyfd);
#endif /* ------------- */
xbee_close(xbee->tty);
Xfree(xbee);
return NULL;
}
}
/* allow the listen thread to start */
xbee->xbee_ready = -1;
/* can start xbee_listen thread now */
if (xbee_thread_create(xbee->listent, xbee_listen_wrapper, xbee)) {
xbee_perror("xbee_setup():xbee_thread_create(listent)");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
Xfree(xbee->path);
#ifdef __GNUC__ /* ---- */
close(xbee->ttyfd);
#endif /* ------------- */
xbee_close(xbee->tty);
Xfree(xbee);
return NULL;
}
/* can start xbee_thread_watch thread thread now */
if (xbee_thread_create(xbee->threadt, xbee_thread_watch, xbee)) {
xbee_perror("xbee_setup():xbee_thread_create(threadt)");
if (xbee->log) xbee_close(xbee->log);
xbee_mutex_destroy(xbee->conmutex);
xbee_mutex_destroy(xbee->pktmutex);
xbee_mutex_destroy(xbee->sendmutex);
Xfree(xbee->path);
#ifdef __GNUC__ /* ---- */
close(xbee->ttyfd);
#endif /* ------------- */
xbee_close(xbee->tty);
Xfree(xbee);
return NULL;
}
usleep(500);
while (xbee->xbee_ready != -2) {
usleep(500);
xbee_log("Waiting for xbee_listen() to be ready...");
}
/* allow other functions to be used! */
xbee->xbee_ready = 1;
xbee_log("Linking xbee instance...");
if (!default_xbee) {
xbee_mutex_init(xbee_hnd_mutex);
xbee_mutex_lock(xbee_hnd_mutex);
default_xbee = xbee;
xbee_mutex_unlock(xbee_hnd_mutex);
} else {
xbee_hnd xbeet;
xbee_mutex_lock(xbee_hnd_mutex);
xbeet = default_xbee;
while (xbeet->next) {
xbeet = xbeet->next;
}
xbeet->next = xbee;
xbee_mutex_unlock(xbee_hnd_mutex);
}
xbee_log("libxbee: Started!");
return xbee;
}
/* #################################################################
xbee_con
produces a connection to the specified device and frameID
if a connection had already been made, then this connection will be returned */
xbee_con *xbee_newcon(unsigned char frameID, xbee_types type, ...) {
xbee_con *ret;
va_list ap;
/* xbee_vnewcon() wants a va_list... */
va_start(ap, type);
/* hand it over :) */
ret = _xbee_vnewcon(default_xbee, frameID, type, ap);
va_end(ap);
return ret;
}
xbee_con *_xbee_newcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, ...) {
xbee_con *ret;
va_list ap;
/* xbee_vnewcon() wants a va_list... */
va_start(ap, type);
/* hand it over :) */
ret = _xbee_vnewcon(xbee, frameID, type, ap);
va_end(ap);
return ret;
}
xbee_con *_xbee_vnewcon(xbee_hnd xbee, unsigned char frameID, xbee_types type, va_list ap) {
xbee_con *con, *ocon;
unsigned char tAddr[8];
int t;
int i;
ISREADYR(NULL);
if (!type || type == xbee_unknown) type = xbee_localAT; /* default to local AT */
else if (type == xbee_remoteAT) type = xbee_64bitRemoteAT; /* if remote AT, default to 64bit */
/* if: 64 bit address expected (2 ints) */
if ((type == xbee_64bitRemoteAT) ||
(type == xbee_64bitData) ||
(type == xbee_64bitIO) ||
(type == xbee2_data)) {
t = va_arg(ap, int);
tAddr[0] = (t >> 24) & 0xFF;
tAddr[1] = (t >> 16) & 0xFF;
tAddr[2] = (t >> 8) & 0xFF;
tAddr[3] = (t ) & 0xFF;
t = va_arg(ap, int);
tAddr[4] = (t >> 24) & 0xFF;
tAddr[5] = (t >> 16) & 0xFF;
tAddr[6] = (t >> 8) & 0xFF;
tAddr[7] = (t ) & 0xFF;
/* if: 16 bit address expected (1 int) */
} else if ((type == xbee_16bitRemoteAT) ||
(type == xbee_16bitData) ||
(type == xbee_16bitIO)) {
t = va_arg(ap, int);
tAddr[0] = (t >> 8) & 0xFF;
tAddr[1] = (t ) & 0xFF;
tAddr[2] = 0;
tAddr[3] = 0;
tAddr[4] = 0;
tAddr[5] = 0;
tAddr[6] = 0;
tAddr[7] = 0;
/* otherwise clear the address */
} else {
memset(tAddr,0,8);
}
/* lock the connection mutex */
xbee_mutex_lock(xbee->conmutex);
/* are there any connections? */
if (xbee->conlist) {
con = xbee->conlist;
while (con) {
/* if: looking for a modemStatus, and the types match! */
if ((type == xbee_modemStatus) &&
(con->type == type)) {
xbee_mutex_unlock(xbee->conmutex);
return con;
/* if: looking for a txStatus and frameIDs match! */
} else if ((type == xbee_txStatus) &&
(con->type == type) &&
(frameID == con->frameID)) {
xbee_mutex_unlock(xbee->conmutex);
return con;
/* if: looking for a localAT, and the frameIDs match! */
} else if ((type == xbee_localAT) &&
(con->type == type) &&
(frameID == con->frameID)) {
xbee_mutex_unlock(xbee->conmutex);
return con;
/* if: connection types match, the frameIDs match, and the addresses match! */
} else if ((type == con->type) &&
(frameID == con->frameID) &&
(!memcmp(tAddr,con->tAddr,8))) {
xbee_mutex_unlock(xbee->conmutex);
return con;
}
/* if there are more, move along, dont want to loose that last item! */
if (con->next == NULL) break;
con = con->next;
}
/* keep hold of the last connection... we will need to link it up later */
ocon = con;
}
/* unlock the connection mutex */
xbee_mutex_unlock(xbee->conmutex);
/* create a new connection and set its attributes */
con = Xcalloc(sizeof(xbee_con));
con->type = type;
/* is it a 64bit connection? */
if ((type == xbee_64bitRemoteAT) ||
(type == xbee_64bitData) ||
(type == xbee_64bitIO) ||
(type == xbee2_data)) {
con->tAddr64 = TRUE;
}
con->atQueue = 0; /* queue AT commands? */
con->txDisableACK = 0; /* disable ACKs? */
con->txBroadcastPAN = 0; /* broadcast? */
con->frameID = frameID;
con->waitforACK = 0;
memcpy(con->tAddr,tAddr,8); /* copy in the remote address */
xbee_mutex_init(con->callbackmutex);
xbee_mutex_init(con->callbackListmutex);
xbee_mutex_init(con->Txmutex);
xbee_sem_init(con->waitforACKsem);
if (xbee->log) {
switch(type) {
case xbee_localAT:
xbee_log("New local AT connection!");
break;
case xbee_16bitRemoteAT:
case xbee_64bitRemoteAT:
xbee_logc("New %d-bit remote AT connection! (to: ",(con->tAddr64?64:16));
for (i=0;i<(con->tAddr64?8:2);i++) {
fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]);
}
fprintf(xbee->log,")");
xbee_logcf();
break;
case xbee_16bitData:
case xbee_64bitData:
xbee_logc("New %d-bit data connection! (to: ",(con->tAddr64?64:16));
for (i=0;i<(con->tAddr64?8:2);i++) {
fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]);
}
fprintf(xbee->log,")");
xbee_logcf();
break;
case xbee_16bitIO:
case xbee_64bitIO:
xbee_logc("New %d-bit IO connection! (to: ",(con->tAddr64?64:16));
for (i=0;i<(con->tAddr64?8:2);i++) {
fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]);
}
fprintf(xbee->log,")");
xbee_logcf();
break;
case xbee2_data:
xbee_logc("New Series 2 data connection! (to: ");
for (i=0;i<8;i++) {
fprintf(xbee->log,(i?":%02X":"%02X"),tAddr[i]);
}
fprintf(xbee->log,")");
xbee_logcf();
break;
case xbee_txStatus:
xbee_log("New Tx status connection!");
break;
case xbee_modemStatus:
xbee_log("New modem status connection!");
break;
case xbee_unknown:
default:
xbee_log("New unknown connection!");
}
}
/* lock the connection mutex */
xbee_mutex_lock(xbee->conmutex);
/* make it the last in the list */
con->next = NULL;
/* add it to the list */
if (xbee->conlist) {
ocon->next = con;
} else {
xbee->conlist = con;
}
/* unlock the mutex */
xbee_mutex_unlock(xbee->conmutex);
return con;
}
/* #################################################################
xbee_conflush
removes any packets that have been collected for the specified
connection */
void xbee_purgecon(xbee_con *con) {
_xbee_purgecon(default_xbee, con);
}
void _xbee_purgecon(xbee_hnd xbee, xbee_con *con) {
xbee_pkt *r, *p, *n;
ISREADYP();
/* lock the packet mutex */
xbee_mutex_lock(xbee->pktmutex);
/* if: there are packets */
if ((p = xbee->pktlist) != NULL) {
r = NULL;
/* get all packets for this connection */
do {
/* does the packet match the connection? */
if (xbee_matchpktcon(xbee,p,con)) {
/* if it was the first packet */
if (!r) {
/* move the chain along */
xbee->pktlist = p->next;
} else {
/* otherwise relink the list */
r->next = p->next;
}
xbee->pktcount--;
/* free this packet! */
n = p->next;
Xfree(p);
/* move on */
p = n;
} else {
/* move on */
r = p;
p = p->next;
}
} while (p);
xbee->pktlast = r;
}
/* unlock the packet mutex */
xbee_mutex_unlock(xbee->pktmutex);
}
/* #################################################################
xbee_endcon
close the unwanted connection
free wrapper function (uses the Xfree macro and sets the pointer to NULL after freeing it) */
void xbee_endcon2(xbee_con **con, int alreadyUnlinked) {
_xbee_endcon2(default_xbee, con, alreadyUnlinked);
}
void _xbee_endcon2(xbee_hnd xbee, xbee_con **con, int alreadyUnlinked) {
xbee_con *t, *u;
ISREADYP();
/* lock the connection mutex */
xbee_mutex_lock(xbee->conmutex);
u = t = xbee->conlist;
while (t && t != *con) {
u = t;
t = t->next;
}
if (!t) {
/* this could be true if comming from the destroySelf signal... */
if (!alreadyUnlinked) {
/* invalid connection given... */
if (xbee->log) {
xbee_log("Attempted to close invalid connection...");
}
/* unlock the connection mutex */
xbee_mutex_unlock(xbee->conmutex);
return;
}
} else {
/* extract this connection from the list */
if (t == xbee->conlist) {
xbee->conlist = t->next;
} else {
u->next = t->next;
}
}
/* unlock the connection mutex */
xbee_mutex_unlock(xbee->conmutex);
/* check if a callback thread is running... */
if (t->callback && xbee_mutex_trylock(t->callbackmutex)) {
/* if it is running... tell it to destroy the connection on completion */
xbee_log("Attempted to close a connection with active callbacks... "
"Connection will be destroyed when callbacks have completeted...");
t->destroySelf = 1;
return;
}
/* remove all packets for this connection */
_xbee_purgecon(xbee,t);
/* destroy the callback mutex */
xbee_mutex_destroy(t->callbackmutex);
xbee_mutex_destroy(t->callbackListmutex);
xbee_mutex_destroy(t->Txmutex);
xbee_sem_destroy(t->waitforACKsem);
/* free the connection! */
Xfree(*con);
}
/* #################################################################
xbee_senddata
send the specified data to the provided connection */
int xbee_senddata(xbee_con *con, char *format, ...) {
int ret;
va_list ap;
/* xbee_vsenddata() wants a va_list... */
va_start(ap, format);
/* hand it over :) */
ret = _xbee_vsenddata(default_xbee, con, format, ap);
va_end(ap);
return ret;
}
int _xbee_senddata(xbee_hnd xbee, xbee_con *con, char *format, ...) {
int ret;
va_list ap;
/* xbee_vsenddata() wants a va_list... */
va_start(ap, format);
/* hand it over :) */
ret = _xbee_vsenddata(xbee, con, format, ap);
va_end(ap);
return ret;
}
int xbee_vsenddata(xbee_con *con, char *format, va_list ap) {
return _xbee_vsenddata(default_xbee, con, format, ap);
}
int _xbee_vsenddata(xbee_hnd xbee, xbee_con *con, char *format, va_list ap) {
unsigned char data[128]; /* max payload is 100 bytes... plus a bit of fluff... */
int length;
/* make up the data and keep the length, its possible there are nulls in there */
length = vsnprintf((char *)data, 128, format, ap);
/* hand it over :) */
return _xbee_nsenddata(xbee, con, (char *)data, length);
}
/* returns:
1 - if NAC was recieved
0 - if packet was successfully sent (or just sent if waitforACK is off)
-1 - if there was an error building the packet
-2 - if the connection type was unknown */
int xbee_nsenddata(xbee_con *con, char *data, int length) {
return _xbee_nsenddata(default_xbee, con, data, length);
}
int _xbee_nsenddata(xbee_hnd xbee, xbee_con *con, char *data, int length) {
t_data *pkt;
int i;
unsigned char buf[128]; /* max payload is 100 bytes... plus a bit for the headers etc... */
ISREADYR(-1);
if (!con) return -1;
if (con->type == xbee_unknown) return -1;
if (length > 127) return -1;
if (xbee->log) {
xbee_logS("--== TX Packet ============--");
xbee_logIc("Connection Type: ");
switch (con->type) {
case xbee_unknown: fprintf(xbee->log,"Unknown"); break;
case xbee_localAT: fprintf(xbee->log,"Local AT"); break;
case xbee_remoteAT: fprintf(xbee->log,"Remote AT"); break;
case xbee_16bitRemoteAT: fprintf(xbee->log,"Remote AT (16-bit)"); break;
case xbee_64bitRemoteAT: fprintf(xbee->log,"Remote AT (64-bit)"); break;
case xbee_16bitData: fprintf(xbee->log,"Data (16-bit)"); break;
case xbee_64bitData: fprintf(xbee->log,"Data (64-bit)"); break;
case xbee_16bitIO: fprintf(xbee->log,"IO (16-bit)"); break;
case xbee_64bitIO: fprintf(xbee->log,"IO (64-bit)"); break;
case xbee2_data: fprintf(xbee->log,"Series 2 Data"); break;
case xbee2_txStatus: fprintf(xbee->log,"Series 2 Tx Status"); break;
case xbee_txStatus: fprintf(xbee->log,"Tx Status"); break;
case xbee_modemStatus: fprintf(xbee->log,"Modem Status"); break;
}
xbee_logIcf();
switch (con->type) {
case xbee_localAT: case xbee_remoteAT: case xbee_txStatus: case xbee_modemStatus:
break;
default:
xbee_logIc("Destination: ");
for (i=0;i<(con->tAddr64?8:2);i++) {
fprintf(xbee->log,(i?":%02X":"%02X"),con->tAddr[i]);
}
xbee_logIcf();
}
xbee_logI("Length: %d",length);
for (i=0;i 32) && (data[i] < 127)) {
fprintf(xbee->log,"'%c'",data[i]);
} else{
fprintf(xbee->log," _");
}
xbee_logIcf();
}
xbee_logEf();
}
/* ########################################## */
/* if: local AT */
if (con->type == xbee_localAT) {
/* AT commands are 2 chars long (plus optional parameter) */
if (length < 2) return -1;
if (length > 32) return -1;
/* use the command? */
buf[0] = ((!con->atQueue)?XBEE_LOCAL_ATREQ:XBEE_LOCAL_ATQUE);
buf[1] = con->frameID;
/* copy in the data */
for (i=0;itype == xbee_16bitRemoteAT) ||
(con->type == xbee_64bitRemoteAT)) {
if (length < 2) return -1; /* at commands are 2 chars long (plus optional parameter) */
if (length > 32) return -1;
buf[0] = XBEE_REMOTE_ATREQ;
buf[1] = con->frameID;
/* copy in the relevant address */
if (con->tAddr64) {
memcpy(&buf[2],con->tAddr,8);
buf[10] = 0xFF;
buf[11] = 0xFE;
} else {
memset(&buf[2],0,8);
memcpy(&buf[10],con->tAddr,2);
}
/* queue the command? */
buf[12] = ((!con->atQueue)?0x02:0x00);
/* copy in the data */
for (i=0;itype == xbee_16bitData) ||
(con->type == xbee_64bitData)) {
int offset;
if (length > 100) return -1;
/* if: 16bit Data */
if (con->type == xbee_16bitData) {
buf[0] = XBEE_16BIT_DATATX;
offset = 5;
/* copy in the address */
memcpy(&buf[2],con->tAddr,2);
/* if: 64bit Data */
} else { /* 64bit Data */
buf[0] = XBEE_64BIT_DATATX;
offset = 11;
/* copy in the address */
memcpy(&buf[2],con->tAddr,8);
}
/* copy frameID */
buf[1] = con->frameID;
/* disable ack? broadcast? */
buf[offset-1] = ((con->txDisableACK)?0x01:0x00) | ((con->txBroadcastPAN)?0x04:0x00);
/* copy in the data */
for (i=0;itype == xbee_64bitIO) ||
(con->type == xbee_16bitIO)) {
/* not currently implemented... is it even allowed? */
if (xbee->log) {
xbee_log("******* TODO ********\n");
}
/* ########################################## */
/* if: Series 2 Data */
} else if (con->type == xbee2_data) {
if (length > 72) return -1;
buf[0] = XBEE2_DATATX;
buf[1] = con->frameID;
/* copy in the relevant address */
memcpy(&buf[2],con->tAddr,8);
buf[10] = 0xFF;
buf[11] = 0xFE;
/* Maximum Radius/hops */
buf[12] = 0x00;
/* Options */
buf[13] = 0x00;
/* copy in the data */
for (i=0;ipktmutex);
/* if: there are no packets */
if ((p = xbee->pktlist) == NULL) {
xbee_mutex_unlock(xbee->pktmutex);
/*if (xbee->log) {
xbee_log("No packets avaliable...");
}*/
return NULL;
}
l = NULL;
q = NULL;
/* get the first avaliable packet for this connection */
do {
/* does the packet match the connection? */
if (xbee_matchpktcon(xbee, p, con)) {
q = p;
break;
}
/* move on */
l = p;
p = p->next;
} while (p);
/* if: no packet was found */
if (!q) {
xbee_mutex_unlock(xbee->pktmutex);
if (xbee->log) {
struct timeval tv;
xbee_logS("--== Get Packet ==========--");
gettimeofday(&tv,NULL);
xbee_logE("Didn't get a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec);
}
return NULL;
}
/* if it was the first packet */
if (l) {
/* relink the list */
l->next = p->next;
if (!l->next) xbee->pktlast = l;
} else {
/* move the chain along */
xbee->pktlist = p->next;
if (!xbee->pktlist) {
xbee->pktlast = NULL;
} else if (!xbee->pktlist->next) {
xbee->pktlast = xbee->pktlist;
}
}
xbee->pktcount--;
/* unlink this packet from the chain! */
q->next = NULL;
if (xbee->log) {
struct timeval tv;
xbee_logS("--== Get Packet ==========--");
gettimeofday(&tv,NULL);
xbee_logI("Got a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec);
xbee_logE("Packets left: %d",xbee->pktcount);
}
/* unlock the packet mutex */
xbee_mutex_unlock(xbee->pktmutex);
/* and return the packet (must be free'd by caller!) */
return q;
}
/* #################################################################
xbee_matchpktcon - INTERNAL
checks if the packet matches the connection */
static int xbee_matchpktcon(xbee_hnd xbee, xbee_pkt *pkt, xbee_con *con) {
/* if: the connection type matches the packet type OR
the connection is 16/64bit remote AT, and the packet is a remote AT response */
if ((pkt->type == con->type) || /* -- */
((pkt->type == xbee_remoteAT) && /* -- */
((con->type == xbee_16bitRemoteAT) ||
(con->type == xbee_64bitRemoteAT)))) {
/* if: is a modem status (there can only be 1 modem status connection) */
if (pkt->type == xbee_modemStatus) return 1;
/* if: the packet is a txStatus or localAT and the frameIDs match */
if ((pkt->type == xbee_txStatus) ||
(pkt->type == xbee_localAT)) {
if (pkt->frameID == con->frameID) {
return 1;
}
/* if: the packet was sent as a 16bit remoteAT, and the 16bit addresss match */
} else if ((pkt->type == xbee_remoteAT) &&
(con->type == xbee_16bitRemoteAT) &&
!memcmp(pkt->Addr16,con->tAddr,2)) {
return 1;
/* if: the packet was sent as a 64bit remoteAT, and the 64bit addresss match */
} else if ((pkt->type == xbee_remoteAT) &&
(con->type == xbee_64bitRemoteAT) &&
!memcmp(pkt->Addr64,con->tAddr,8)) {
return 1;
/* if: the packet is 64bit addressed, and the addresses match */
} else if (pkt->sAddr64 && !memcmp(pkt->Addr64,con->tAddr,8)) {
return 1;
/* if: the packet is 16bit addressed, and the addresses match */
} else if (!pkt->sAddr64 && !memcmp(pkt->Addr16,con->tAddr,2)) {
return 1;
} else if (con->type == pkt->type &&
(con->type == xbee_16bitData || con->type == xbee_64bitData) &&
(pkt->isBroadcastADR || pkt->isBroadcastPAN)) {
unsigned char t[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
if ((con->tAddr64 && !memcmp(con->tAddr,t,8)) ||
(!con->tAddr64 && !memcmp(con->tAddr,t,2))) {
return 1;
}
}
}
return 0;
}
/* #################################################################
xbee_parse_io - INTERNAL
parses the data given into the packet io information */
static int xbee_parse_io(xbee_hnd xbee, xbee_pkt *p, unsigned char *d,
int maskOffset, int sampleOffset, int sample) {
xbee_sample *s = &(p->IOdata[sample]);
/* copy in the I/O data mask */
s->IOmask = (((d[maskOffset]<<8) | d[maskOffset + 1]) & 0x7FFF);
/* copy in the digital I/O data */
s->IOdigital = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x01FF);
/* advance over the digital data, if its there */
sampleOffset += ((s->IOmask & 0x01FF)?2:0);
/* copy in the analog I/O data */
if (s->IOmask & 0x0200) {
s->IOanalog[0] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (s->IOmask & 0x0400) {
s->IOanalog[1] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (s->IOmask & 0x0800) {
s->IOanalog[2] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (s->IOmask & 0x1000) {
s->IOanalog[3] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (s->IOmask & 0x2000) {
s->IOanalog[4] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (s->IOmask & 0x4000) {
s->IOanalog[5] = (((d[sampleOffset]<<8) | d[sampleOffset+1]) & 0x03FF);
sampleOffset+=2;
}
if (xbee->log) {
if (s->IOmask & 0x0001)
xbee_logI("Digital 0: %c",((s->IOdigital & 0x0001)?'1':'0'));
if (s->IOmask & 0x0002)
xbee_logI("Digital 1: %c",((s->IOdigital & 0x0002)?'1':'0'));
if (s->IOmask & 0x0004)
xbee_logI("Digital 2: %c",((s->IOdigital & 0x0004)?'1':'0'));
if (s->IOmask & 0x0008)
xbee_logI("Digital 3: %c",((s->IOdigital & 0x0008)?'1':'0'));
if (s->IOmask & 0x0010)
xbee_logI("Digital 4: %c",((s->IOdigital & 0x0010)?'1':'0'));
if (s->IOmask & 0x0020)
xbee_logI("Digital 5: %c",((s->IOdigital & 0x0020)?'1':'0'));
if (s->IOmask & 0x0040)
xbee_logI("Digital 6: %c",((s->IOdigital & 0x0040)?'1':'0'));
if (s->IOmask & 0x0080)
xbee_logI("Digital 7: %c",((s->IOdigital & 0x0080)?'1':'0'));
if (s->IOmask & 0x0100)
xbee_logI("Digital 8: %c",((s->IOdigital & 0x0100)?'1':'0'));
if (s->IOmask & 0x0200)
xbee_logI("Analog 0: %d (~%.2fv)",s->IOanalog[0],(3.3/1023)*s->IOanalog[0]);
if (s->IOmask & 0x0400)
xbee_logI("Analog 1: %d (~%.2fv)",s->IOanalog[1],(3.3/1023)*s->IOanalog[1]);
if (s->IOmask & 0x0800)
xbee_logI("Analog 2: %d (~%.2fv)",s->IOanalog[2],(3.3/1023)*s->IOanalog[2]);
if (s->IOmask & 0x1000)
xbee_logI("Analog 3: %d (~%.2fv)",s->IOanalog[3],(3.3/1023)*s->IOanalog[3]);
if (s->IOmask & 0x2000)
xbee_logI("Analog 4: %d (~%.2fv)",s->IOanalog[4],(3.3/1023)*s->IOanalog[4]);
if (s->IOmask & 0x4000)
xbee_logI("Analog 5: %d (~%.2fv)",s->IOanalog[5],(3.3/1023)*s->IOanalog[5]);
}
return sampleOffset;
}
/* #################################################################
xbee_listen_stop
stops the listen thread after the current packet has been processed */
void xbee_listen_stop(xbee_hnd xbee) {
ISREADYP();
xbee->run = 0;
}
/* #################################################################
xbee_listen_wrapper - INTERNAL
the xbee_listen wrapper. Prints an error when xbee_listen ends */
static void xbee_listen_wrapper(xbee_hnd xbee) {
int ret;
/* just falls out if the proper 'go-ahead' isn't given */
if (xbee->xbee_ready != -1) return;
/* now allow the parent to continue */
xbee->xbee_ready = -2;
#ifdef _WIN32 /* ---- */
/* win32 requires this delay... no idea why */
usleep(1000000);
#endif /* ----------- */
while (xbee->run) {
ret = xbee_listen(xbee);
if (!xbee->run) break;
xbee_log("xbee_listen() returned [%d]... Restarting in 25ms!",ret);
usleep(25000);
}
}
/* xbee_listen - INTERNAL
the xbee xbee_listen thread
reads data from the xbee and puts it into a linked list to keep the xbee buffers free */
static int xbee_listen(xbee_hnd xbee) {
#define LISTEN_BUFLEN 1024
unsigned char c, t, d[LISTEN_BUFLEN];
unsigned int l, i, chksum, o;
int j;
xbee_pkt *p = NULL, *q;
xbee_con *con;
int hasCon;
/* do this forever :) */
while (xbee->run) {
/* clean up any undesired storage */
if (p) Xfree(p);
/* wait for a valid start byte */
if ((c = xbee_getrawbyte(xbee)) != 0x7E) {
if (xbee->log) xbee_log("***** Unexpected byte (0x%02X)... *****",c);
continue;
}
if (!xbee->run) return 0;
xbee_logSf();
if (xbee->log) {
struct timeval tv;
xbee_logI("--== RX Packet ===========--");
gettimeofday(&tv,NULL);
xbee_logI("Got a packet @ %ld.%06ld",tv.tv_sec,tv.tv_usec);
}
/* get the length */
l = xbee_getbyte(xbee) << 8;
l += xbee_getbyte(xbee);
/* check it is a valid length... */
if (!l) {
if (xbee->log) {
xbee_logI("Recived zero length packet!");
}
continue;
}
if (l > 100) {
if (xbee->log) {
xbee_logI("Recived oversized packet! Length: %d",l - 1);
}
}
if (l > LISTEN_BUFLEN) {
if (xbee->log) {
xbee_logI("Recived packet larger than buffer! Discarding...");
}
continue;
}
if (xbee->log) {
xbee_logI("Length: %d",l - 1);
}
/* get the packet type */
t = xbee_getbyte(xbee);
/* start the checksum */
chksum = t;
/* suck in all the data */
for (i = 0; l > 1 && i < LISTEN_BUFLEN; l--, i++) {
/* get an unescaped byte */
c = xbee_getbyte(xbee);
d[i] = c;
chksum += c;
if (xbee->log) {
xbee_logIc("%3d | 0x%02X | ",i,c);
if ((c > 32) && (c < 127)) fprintf(xbee->log,"'%c'",c); else fprintf(xbee->log," _ ");
if ((t == XBEE_LOCAL_AT && i == 4) ||
(t == XBEE_REMOTE_AT && i == 14) ||
(t == XBEE_64BIT_DATARX && i == 10) ||
(t == XBEE_16BIT_DATARX && i == 4) ||
(t == XBEE_64BIT_IO && i == 13) ||
(t == XBEE_16BIT_IO && i == 7)) {
/* mark the beginning of the 'data' bytes */
fprintf(xbee->log," <-- data starts");
} else if (t == XBEE_64BIT_IO) {
if (i == 10) fprintf(xbee->log," <-- sample count");
else if (i == 11) fprintf(xbee->log," <-- mask (msb)");
else if (i == 12) fprintf(xbee->log," <-- mask (lsb)");
} else if (t == XBEE_16BIT_IO) {
if (i == 4) fprintf(xbee->log," <-- sample count");
else if (i == 5) fprintf(xbee->log," <-- mask (msb)");
else if (i == 6) fprintf(xbee->log," <-- mask (lsb)");
}
xbee_logIcf();
}
}
i--; /* it went up too many times!... */
/* add the checksum */
chksum += xbee_getbyte(xbee);
/* check if the whole packet was recieved, or something else occured... unlikely... */
if (l>1) {
if (xbee->log) {
xbee_logE("Didn't get whole packet... :(");
}
continue;
}
/* check the checksum */
if ((chksum & 0xFF) != 0xFF) {
if (xbee->log) {
chksum &= 0xFF;
xbee_logE("Invalid Checksum: 0x%02X",chksum);
}
continue;
}
/* make a new packet */
p = Xcalloc(sizeof(xbee_pkt));
q = NULL;
p->datalen = 0;
/* ########################################## */
/* if: modem status */
if (t == XBEE_MODEM_STATUS) {
if (xbee->log) {
xbee_logI("Packet type: Modem Status (0x8A)");
xbee_logIc("Event: ");
switch (d[0]) {
case 0x00: fprintf(xbee->log,"Hardware reset"); break;
case 0x01: fprintf(xbee->log,"Watchdog timer reset"); break;
case 0x02: fprintf(xbee->log,"Associated"); break;
case 0x03: fprintf(xbee->log,"Disassociated"); break;
case 0x04: fprintf(xbee->log,"Synchronization lost"); break;
case 0x05: fprintf(xbee->log,"Coordinator realignment"); break;
case 0x06: fprintf(xbee->log,"Coordinator started"); break;
}
fprintf(xbee->log,"... (0x%02X)",d[0]);
xbee_logIcf();
}
p->type = xbee_modemStatus;
p->sAddr64 = FALSE;
p->dataPkt = FALSE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = TRUE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
/* modem status can only ever give 1 'data' byte */
p->datalen = 1;
p->data[0] = d[0];
/* ########################################## */
/* if: local AT response */
} else if (t == XBEE_LOCAL_AT) {
if (xbee->log) {
xbee_logI("Packet type: Local AT Response (0x88)");
xbee_logI("FrameID: 0x%02X",d[0]);
xbee_logI("AT Command: %c%c",d[1],d[2]);
xbee_logIc("Status: ");
if (d[3] == 0x00) fprintf(xbee->log,"OK");
else if (d[3] == 0x01) fprintf(xbee->log,"Error");
else if (d[3] == 0x02) fprintf(xbee->log,"Invalid Command");
else if (d[3] == 0x03) fprintf(xbee->log,"Invalid Parameter");
fprintf(xbee->log," (0x%02X)",d[3]);
xbee_logIcf();
}
p->type = xbee_localAT;
p->sAddr64 = FALSE;
p->dataPkt = FALSE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
p->frameID = d[0];
p->atCmd[0] = d[1];
p->atCmd[1] = d[2];
p->status = d[3];
/* copy in the data */
p->datalen = i-3;
for (;i>3;i--) p->data[i-4] = d[i];
/* ########################################## */
/* if: remote AT response */
} else if (t == XBEE_REMOTE_AT) {
if (xbee->log) {
xbee_logI("Packet type: Remote AT Response (0x97)");
xbee_logI("FrameID: 0x%02X",d[0]);
xbee_logIc("64-bit Address: ");
for (j=0;j<8;j++) {
fprintf(xbee->log,(j?":%02X":"%02X"),d[1+j]);
}
xbee_logIcf();
xbee_logIc("16-bit Address: ");
for (j=0;j<2;j++) {
fprintf(xbee->log,(j?":%02X":"%02X"),d[9+j]);
}
xbee_logIcf();
xbee_logI("AT Command: %c%c",d[11],d[12]);
xbee_logIc("Status: ");
if (d[13] == 0x00) fprintf(xbee->log,"OK");
else if (d[13] == 0x01) fprintf(xbee->log,"Error");
else if (d[13] == 0x02) fprintf(xbee->log,"Invalid Command");
else if (d[13] == 0x03) fprintf(xbee->log,"Invalid Parameter");
else if (d[13] == 0x04) fprintf(xbee->log,"No Response");
fprintf(xbee->log," (0x%02X)",d[13]);
xbee_logIcf();
}
p->type = xbee_remoteAT;
p->sAddr64 = FALSE;
p->dataPkt = FALSE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = TRUE;
p->IOPkt = FALSE;
p->frameID = d[0];
p->Addr64[0] = d[1];
p->Addr64[1] = d[2];
p->Addr64[2] = d[3];
p->Addr64[3] = d[4];
p->Addr64[4] = d[5];
p->Addr64[5] = d[6];
p->Addr64[6] = d[7];
p->Addr64[7] = d[8];
p->Addr16[0] = d[9];
p->Addr16[1] = d[10];
p->atCmd[0] = d[11];
p->atCmd[1] = d[12];
p->status = d[13];
p->samples = 1;
if (p->status == 0x00 && p->atCmd[0] == 'I' && p->atCmd[1] == 'S') {
/* parse the io data */
xbee_logI("--- Sample -----------------");
xbee_parse_io(xbee, p, d, 15, 17, 0);
xbee_logI("----------------------------");
} else {
/* copy in the data */
p->datalen = i-13;
for (;i>13;i--) p->data[i-14] = d[i];
}
/* ########################################## */
/* if: TX status */
} else if (t == XBEE_TX_STATUS) {
if (xbee->log) {
xbee_logI("Packet type: TX Status Report (0x89)");
xbee_logI("FrameID: 0x%02X",d[0]);
xbee_logIc("Status: ");
if (d[1] == 0x00) fprintf(xbee->log,"Success");
else if (d[1] == 0x01) fprintf(xbee->log,"No ACK");
else if (d[1] == 0x02) fprintf(xbee->log,"CCA Failure");
else if (d[1] == 0x03) fprintf(xbee->log,"Purged");
fprintf(xbee->log," (0x%02X)",d[1]);
xbee_logIcf();
}
p->type = xbee_txStatus;
p->sAddr64 = FALSE;
p->dataPkt = FALSE;
p->txStatusPkt = TRUE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
p->frameID = d[0];
p->status = d[1];
/* never returns data */
p->datalen = 0;
/* check for any connections waiting for a status update */
/* lock the connection mutex */
xbee_mutex_lock(xbee->conmutex);
xbee_logI("Looking for a connection that wants a status update...");
con = xbee->conlist;
while (con) {
if ((con->frameID == p->frameID) &&
(con->ACKstatus == 0xFF)) {
xbee_logI("Found @ 0x%08X!",con);
con->ACKstatus = p->status;
xbee_sem_post(con->waitforACKsem);
}
con = con->next;
}
/* unlock the connection mutex */
xbee_mutex_unlock(xbee->conmutex);
/* ########################################## */
/* if: 16 / 64bit data recieve */
} else if ((t == XBEE_64BIT_DATARX) ||
(t == XBEE_16BIT_DATARX)) {
int offset;
if (t == XBEE_64BIT_DATARX) { /* 64bit */
offset = 8;
} else { /* 16bit */
offset = 2;
}
if (xbee->log) {
xbee_logI("Packet type: %d-bit RX Data (0x%02X)",((t == XBEE_64BIT_DATARX)?64:16),t);
xbee_logIc("%d-bit Address: ",((t == XBEE_64BIT_DATARX)?64:16));
for (j=0;jlog,(j?":%02X":"%02X"),d[j]);
}
xbee_logIcf();
xbee_logI("RSSI: -%ddB",d[offset]);
if (d[offset + 1] & 0x02) xbee_logI("Options: Address Broadcast");
if (d[offset + 1] & 0x04) xbee_logI("Options: PAN Broadcast");
}
p->isBroadcastADR = !!(d[offset+1] & 0x02);
p->isBroadcastPAN = !!(d[offset+1] & 0x04);
p->dataPkt = TRUE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
if (t == XBEE_64BIT_DATARX) { /* 64bit */
p->type = xbee_64bitData;
p->sAddr64 = TRUE;
p->Addr64[0] = d[0];
p->Addr64[1] = d[1];
p->Addr64[2] = d[2];
p->Addr64[3] = d[3];
p->Addr64[4] = d[4];
p->Addr64[5] = d[5];
p->Addr64[6] = d[6];
p->Addr64[7] = d[7];
} else { /* 16bit */
p->type = xbee_16bitData;
p->sAddr64 = FALSE;
p->Addr16[0] = d[0];
p->Addr16[1] = d[1];
}
/* save the RSSI / signal strength
this can be used with printf as:
printf("-%ddB\n",p->RSSI); */
p->RSSI = d[offset];
p->status = d[offset + 1];
/* copy in the data */
p->datalen = i-(offset + 1);
for (;i>offset + 1;i--) p->data[i-(offset + 2)] = d[i];
/* ########################################## */
/* if: 16 / 64bit I/O recieve */
} else if ((t == XBEE_64BIT_IO) ||
(t == XBEE_16BIT_IO)) {
int offset,i2;
if (t == XBEE_64BIT_IO) { /* 64bit */
p->type = xbee_64bitIO;
p->sAddr64 = TRUE;
p->Addr64[0] = d[0];
p->Addr64[1] = d[1];
p->Addr64[2] = d[2];
p->Addr64[3] = d[3];
p->Addr64[4] = d[4];
p->Addr64[5] = d[5];
p->Addr64[6] = d[6];
p->Addr64[7] = d[7];
offset = 8;
p->samples = d[10];
} else { /* 16bit */
p->type = xbee_16bitIO;
p->sAddr64 = FALSE;
p->Addr16[0] = d[0];
p->Addr16[1] = d[1];
offset = 2;
p->samples = d[4];
}
if (p->samples > 1) {
p = Xrealloc(p, sizeof(xbee_pkt) + (sizeof(xbee_sample) * (p->samples - 1)));
}
if (xbee->log) {
xbee_logI("Packet type: %d-bit RX I/O Data (0x%02X)",((t == XBEE_64BIT_IO)?64:16),t);
xbee_logIc("%d-bit Address: ",((t == XBEE_64BIT_IO)?64:16));
for (j = 0; j < offset; j++) {
fprintf(xbee->log,(j?":%02X":"%02X"),d[j]);
}
xbee_logIcf();
xbee_logI("RSSI: -%ddB",d[offset]);
xbee_logI("Samples: %d",d[offset + 2]);
}
i2 = offset + 5;
/* never returns data */
p->datalen = 0;
p->dataPkt = FALSE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = TRUE;
/* save the RSSI / signal strength
this can be used with printf as:
printf("-%ddB\n",p->RSSI); */
p->RSSI = d[offset];
p->status = d[offset + 1];
/* each sample is split into its own packet here, for simplicity */
for (o = 0; o < p->samples; o++) {
if (i2 >= i) {
xbee_logI("Invalid I/O data! Actually contained %d samples...",o);
p = Xrealloc(p, sizeof(xbee_pkt) + (sizeof(xbee_sample) * ((o>1)?o:1)));
p->samples = o;
break;
}
xbee_logI("--- Sample %3d -------------", o);
/* parse the io data */
i2 = xbee_parse_io(xbee, p, d, offset + 3, i2, o);
}
xbee_logI("----------------------------");
/* ########################################## */
/* if: Series 2 Transmit status */
} else if (t == XBEE2_TX_STATUS) {
if (xbee->log) {
xbee_logI("Packet type: Series 2 Transmit Status (0x%02X)", t);
xbee_logI("FrameID: 0x%02X",d[0]);
xbee_logI("16-bit Delivery Address: %02X:%02X",d[1],d[2]);
xbee_logI("Transmit Retry Count: %02X",d[3]);
xbee_logIc("Delivery Status: ");
if (d[4] == 0x00) fprintf(xbee->log,"Success");
else if (d[4] == 0x02) fprintf(xbee->log,"CCA Failure");
else if (d[4] == 0x15) fprintf(xbee->log,"Invalid Destination");
else if (d[4] == 0x21) fprintf(xbee->log,"Network ACK Failure");
else if (d[4] == 0x22) fprintf(xbee->log,"Not Joined to Network");
else if (d[4] == 0x23) fprintf(xbee->log,"Self-Addressed");
else if (d[4] == 0x24) fprintf(xbee->log,"Address Not Found");
else if (d[4] == 0x25) fprintf(xbee->log,"Route Not Found");
else if (d[4] == 0x74) fprintf(xbee->log,"Data Payload Too Large"); /* ??? */
fprintf(xbee->log," (0x%02X)",d[4]);
xbee_logIcf();
xbee_logIc("Discovery Status: ");
if (d[5] == 0x00) fprintf(xbee->log,"No Discovery Overhead");
else if (d[5] == 0x01) fprintf(xbee->log,"Address Discovery");
else if (d[5] == 0x02) fprintf(xbee->log,"Route Discovery");
else if (d[5] == 0x03) fprintf(xbee->log,"Address & Route Discovery");
fprintf(xbee->log," (0x%02X)",d[5]);
xbee_logIcf();
}
p->type = xbee2_txStatus;
p->sAddr64 = FALSE;
p->dataPkt = FALSE;
p->txStatusPkt = TRUE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
p->frameID = d[0];
p->status = d[4];
/* never returns data */
p->datalen = 0;
/* ########################################## */
/* if: Series 2 data recieve */
} else if (t == XBEE2_DATARX) {
int offset;
offset = 10;
if (xbee->log) {
xbee_logI("Packet type: Series 2 Data Rx (0x%02X)", t);
xbee_logIc("64-bit Address: ");
for (j=0;j<8;j++) {
fprintf(xbee->log,(j?":%02X":"%02X"),d[j]);
}
xbee_logIcf();
xbee_logIc("16-bit Address: ");
for (j=0;j<2;j++) {
fprintf(xbee->log,(j?":%02X":"%02X"),d[j+8]);
}
xbee_logIcf();
if (d[offset] & 0x01) xbee_logI("Options: Packet Acknowledged");
if (d[offset] & 0x02) xbee_logI("Options: Packet was a broadcast packet");
if (d[offset] & 0x20) xbee_logI("Options: Packet Encrypted"); /* ??? */
if (d[offset] & 0x40) xbee_logI("Options: Packet from end device"); /* ??? */
}
p->dataPkt = TRUE;
p->txStatusPkt = FALSE;
p->modemStatusPkt = FALSE;
p->remoteATPkt = FALSE;
p->IOPkt = FALSE;
p->type = xbee2_data;
p->sAddr64 = TRUE;
p->Addr64[0] = d[0];
p->Addr64[1] = d[1];
p->Addr64[2] = d[2];
p->Addr64[3] = d[3];
p->Addr64[4] = d[4];
p->Addr64[5] = d[5];
p->Addr64[6] = d[6];
p->Addr64[7] = d[7];
p->Addr16[0] = d[8];
p->Addr16[1] = d[9];
p->status = d[offset];
/* copy in the data */
p->datalen = i - (offset + 1);
for (;i>offset;i--) {
p->data[i-(offset + 1)] = d[i];
}
/* ########################################## */
/* if: Unknown */
} else {
xbee_logE("Packet type: Unknown (0x%02X)",t);
continue;
}
p->next = NULL;
/* lock the connection mutex */
xbee_mutex_lock(xbee->conmutex);
hasCon = 0;
if (p->isBroadcastADR || p->isBroadcastPAN) {
unsigned char t[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/* if the packet was broadcast, search for a broadcast accepting connection */
con = xbee->conlist;
while (con) {
if (con->type == p->type &&
(con->type == xbee_16bitData || con->type == xbee_64bitData) &&
((con->tAddr64 && !memcmp(con->tAddr,t,8)) ||
(!con->tAddr64 && !memcmp(con->tAddr,t,2)))) {
hasCon = 1;
xbee_logI("Found broadcasting connection @ 0x%08X",con);
break;
}
con = con->next;
}
}
if (!hasCon || !con) {
con = xbee->conlist;
while (con) {
if (xbee_matchpktcon(xbee, p, con)) {
hasCon = 1;
break;
}
con = con->next;
}
}
/* unlock the connection mutex */
xbee_mutex_unlock(xbee->conmutex);
/* if the packet doesn't have a connection, don't add it! */
if (!hasCon) {
xbee_logE("Connectionless packet... discarding!");
continue;
}
/* if the connection has a callback function then it is passed the packet
and the packet is not added to the list */
if (con && con->callback) {
t_callback_list *l, *q;
xbee_mutex_lock(con->callbackListmutex);
l = con->callbackList;
q = NULL;
while (l) {
q = l;
l = l->next;
}
l = Xcalloc(sizeof(t_callback_list));
l->pkt = p;
if (!con->callbackList || q == NULL) {
con->callbackList = l;
} else {
q->next = l;
}
xbee_mutex_unlock(con->callbackListmutex);
xbee_logI("Using callback function!");
xbee_logI(" info block @ 0x%08X",l);
xbee_logI(" function @ 0x%08X",con->callback);
xbee_logI(" connection @ 0x%08X",con);
xbee_logE(" packet @ 0x%08X",p);
/* if the callback thread not still running, then start a new one! */
if (!xbee_mutex_trylock(con->callbackmutex)) {
xbee_thread_t t;
int ret;
t_threadList *p, *q;
t_CBinfo info;
info.xbee = xbee;
info.con = con;
xbee_log("Starting new callback thread!");
if ((ret = xbee_thread_create(t,xbee_callbackWrapper,&info)) != 0) {
xbee_mutex_unlock(con->callbackmutex);
/* this MAY help with future attempts... */
xbee_sem_post(xbee->threadsem);
xbee_logS("An error occured while starting thread (%d)... Out of resources?", ret);
xbee_logE("This packet has been lost!");
continue;
}
xbee_log("Started thread 0x%08X!", t);
xbee_mutex_lock(xbee->threadmutex);
p = xbee->threadList;
q = NULL;
while (p) {
q = p;
p = p->next;
}
p = Xcalloc(sizeof(t_threadList));
if (q == NULL) {
xbee->threadList = p;
} else {
q->next = p;
}
p->thread = t;
p->next = NULL;
xbee_mutex_unlock(xbee->threadmutex);
} else {
xbee_logE("Using existing callback thread... callback has been scheduled.");
}
/* prevent the packet from being free'd */
p = NULL;
continue;
}
/* lock the packet mutex, so we can safely add the packet to the list */
xbee_mutex_lock(xbee->pktmutex);
/* if: the list is empty */
if (!xbee->pktlist) {
/* start the list! */
xbee->pktlist = p;
} else if (xbee->pktlast) {
/* add the packet to the end */
xbee->pktlast->next = p;
} else {
/* pktlast wasnt set... look for the end and then set it */
i = 0;
q = xbee->pktlist;
while (q->next) {
q = q->next;
i++;
}
q->next = p;
xbee->pktcount = i;
}
xbee->pktlast = p;
xbee->pktcount++;
/* unlock the packet mutex */
xbee_mutex_unlock(xbee->pktmutex);
xbee_logI("--========================--");
xbee_logE("Packets: %d",xbee->pktcount);
p = q = NULL;
}
return 0;
}
static void xbee_callbackWrapper(t_CBinfo *info) {
xbee_hnd xbee;
xbee_con *con;
xbee_pkt *pkt;
t_callback_list *temp;
xbee = info->xbee;
con = info->con;
/* dont forget! the callback mutex is already locked... by the parent thread :) */
xbee_mutex_lock(con->callbackListmutex);
while (con->callbackList) {
/* shift the list along 1 */
temp = con->callbackList;
con->callbackList = temp->next;
xbee_mutex_unlock(con->callbackListmutex);
/* get the packet */
pkt = temp->pkt;
xbee_logS("Starting callback function...");
xbee_logI(" info block @ 0x%08X",temp);
xbee_logI(" function @ 0x%08X",con->callback);
xbee_logI(" connection @ 0x%08X",con);
xbee_logE(" packet @ 0x%08X",pkt);
Xfree(temp);
if (con->callback) {
con->callback(con,pkt);
xbee_log("Callback complete!");
if (!con->noFreeAfterCB) Xfree(pkt);
} else {
xbee_pkt *q;
int i;
xbee_log("Callback function was removed! Appending packet to main list...");
/* lock the packet mutex, so we can safely add the packet to the list */
xbee_mutex_lock(xbee->pktmutex);
/* if: the list is empty */
if (!xbee->pktlist) {
/* start the list! */
xbee->pktlist = pkt;
} else if (xbee->pktlast) {
/* add the packet to the end */
xbee->pktlast->next = pkt;
} else {
/* pktlast wasnt set... look for the end and then set it */
i = 0;
q = xbee->pktlist;
while (q->next) {
q = q->next;
i++;
}
q->next = pkt;
xbee->pktcount = i;
}
xbee->pktlast = pkt;
xbee->pktcount++;
/* unlock the packet mutex */
xbee_mutex_unlock(xbee->pktmutex);
}
xbee_mutex_lock(con->callbackListmutex);
}
xbee_mutex_unlock(con->callbackListmutex);
xbee_log("Callback thread ending...");
/* releasing the thread mutex is the last thing we do! */
xbee_mutex_unlock(con->callbackmutex);
if (con->destroySelf) {
_xbee_endcon2(xbee,&con,1);
}
xbee_sem_post(xbee->threadsem);
}
/* #################################################################
xbee_thread_watch - INTERNAL
watches for dead threads and tidies up */
static void xbee_thread_watch(xbee_hnd xbee) {
#ifdef _WIN32 /* ---- */
/* win32 requires this delay... no idea why */
usleep(1000000);
#endif /* ----------- */
xbee_mutex_init(xbee->threadmutex);
xbee_sem_init(xbee->threadsem);
while (xbee->run) {
t_threadList *p, *q, *t;
xbee_mutex_lock(xbee->threadmutex);
p = xbee->threadList;
q = NULL;
while (p) {
t = p;
p = p->next;
if (!(xbee_thread_tryjoin(t->thread))) {
xbee_log("Joined with thread 0x%08X...",t->thread);
if (t == xbee->threadList) {
xbee->threadList = t->next;
} else if (q) {
q->next = t->next;
}
free(t);
} else {
q = t;
}
}
xbee_mutex_unlock(xbee->threadmutex);
xbee_sem_wait(xbee->threadsem);
usleep(100000); /* 100ms to allow the thread to end before we try to join */
}
xbee_mutex_destroy(xbee->threadmutex);
xbee_sem_destroy(xbee->threadsem);
}
/* #################################################################
xbee_getbyte - INTERNAL
waits for an escaped byte of data */
static unsigned char xbee_getbyte(xbee_hnd xbee) {
unsigned char c;
/* take a byte */
c = xbee_getrawbyte(xbee);
/* if its escaped, take another and un-escape */
if (c == 0x7D) c = xbee_getrawbyte(xbee) ^ 0x20;
return (c & 0xFF);
}
/* #################################################################
xbee_getrawbyte - INTERNAL
waits for a raw byte of data */
static unsigned char xbee_getrawbyte(xbee_hnd xbee) {
int ret;
unsigned char c = 0x00;
/* the loop is just incase there actually isnt a byte there to be read... */
do {
/* wait for a read to be possible */
if ((ret = xbee_select(xbee,NULL)) == -1) {
xbee_perror("libxbee:xbee_getrawbyte()");
exit(1);
}
if (!xbee->run) break;
if (ret == 0) continue;
/* read 1 character */
if (xbee_read(xbee,&c,1) == 0) {
/* for some reason no characters were read... */
if (xbee_ferror(xbee) || xbee_feof(xbee)) {
xbee_log("Error or EOF detected");
fprintf(stderr,"libxbee:xbee_read(): Error or EOF detected\n");
exit(1); /* this should have something nicer... */
}
/* no error... try again */
usleep(10);
continue;
}
} while (0);
return (c & 0xFF);
}
/* #################################################################
_xbee_send_pkt - INTERNAL
sends a complete packet of data */
static int _xbee_send_pkt(xbee_hnd xbee, t_data *pkt, xbee_con *con) {
int retval = 0;
/* lock connection mutex */
xbee_mutex_lock(con->Txmutex);
/* lock the send mutex */
xbee_mutex_lock(xbee->sendmutex);
/* write and flush the data */
xbee_write(xbee,pkt->data,pkt->length);
/* unlock the mutex */
xbee_mutex_unlock(xbee->sendmutex);
xbee_logSf();
if (xbee->log) {
int i,x,y;
/* prints packet in hex byte-by-byte */
xbee_logIc("TX Packet:");
for (i=0,x=0,y=0;ilength;i++,x--) {
if (x == 0) {
fprintf(xbee->log,"\n 0x%04X | ",y);
x = 0x8;
y += x;
}
if (x == 4) {
fprintf(xbee->log," ");
}
fprintf(xbee->log,"0x%02X ",pkt->data[i]);
}
xbee_logIcf();
}
xbee_logEf();
if (con->waitforACK &&
((con->type == xbee_16bitData) ||
(con->type == xbee_64bitData))) {
con->ACKstatus = 0xFF; /* waiting */
xbee_log("Waiting for ACK/NAK response...");
xbee_sem_wait1sec(con->waitforACKsem);
switch (con->ACKstatus) {
case 0: xbee_log("ACK recieved!"); break;
case 1: xbee_log("NAK recieved..."); break;
case 2: xbee_log("CCA failure..."); break;
case 3: xbee_log("Purged..."); break;
case 255: default: xbee_log("Timeout...");
}
if (con->ACKstatus) retval = 1; /* error */
}
/* unlock connection mutex */
xbee_mutex_unlock(con->Txmutex);
/* free the packet */
Xfree(pkt);
return retval;
}
/* #################################################################
xbee_make_pkt - INTERNAL
adds delimiter field
calculates length and checksum
escapes bytes */
static t_data *xbee_make_pkt(xbee_hnd xbee, unsigned char *data, int length) {
t_data *pkt;
unsigned int l, i, o, t, x, m;
char d = 0;
/* check the data given isnt too long
100 bytes maximum payload + 12 bytes header information */
if (length > 100 + 12) return NULL;
/* calculate the length of the whole packet
start, length (MSB), length (LSB), DATA, checksum */
l = 3 + length + 1;
/* prepare memory */
pkt = Xcalloc(sizeof(t_data));
/* put start byte on */
pkt->data[0] = 0x7E;
/* copy data into packet */
for (t = 0, i = 0, o = 1, m = 1; i <= length; o++, m++) {
/* if: its time for the checksum */
if (i == length) d = M8((0xFF - M8(t)));
/* if: its time for the high length byte */
else if (m == 1) d = M8(length >> 8);
/* if: its time for the low length byte */
else if (m == 2) d = M8(length);
/* if: its time for the normal data */
else if (m > 2) d = data[i];
x = 0;
/* check for any escapes needed */
if ((d == 0x11) || /* XON */
(d == 0x13) || /* XOFF */
(d == 0x7D) || /* Escape */
(d == 0x7E)) { /* Frame Delimiter */
l++;
pkt->data[o++] = 0x7D;
x = 1;
}
/* move data in */
pkt->data[o] = ((!x)?d:d^0x20);
if (m > 2) {
i++;
t += d;
}
}
/* remember the length */
pkt->length = l;
return pkt;
}