xbee_err xbee_rx(struct xbee *xbee, int *restart, void *arg) { xbee_err ret; struct xbee_rxInfo *info; struct xbee_tbuf *buf; info = arg; if (!info->bufList || !info->ioFunc) { *restart = 0; return XBEE_EINVAL; } while (!xbee->die) { buf = NULL; if ((ret = info->ioFunc(xbee, info->ioArg, &buf)) != XBEE_ENONE) { if (ret == XBEE_EEOF) { *restart = 0; if (info->eofCallback) info->eofCallback(xbee, info); return XBEE_EEOF; } else if (ret == XBEE_ESHUTDOWN && xbee->die) { break; } xbee_log(1, "rx() returned %d (%s)... retrying in 10 ms", ret, xbee_errorToStr(ret)); usleep(10000); /* 10 ms */ continue; } #ifndef XBEE_DISABLE_LOGGING #ifndef XBEE_LOG_NO_RX if (xbee->log->enable_rx) { /* format: tx[0x0000000000000000] */ char label[42]; /* enough space for a 64-bit pointer and ANSI color codes */ #ifndef XBEE_LOG_NO_COLOR if (xbee->log->use_color) { snprintf(label, sizeof(label), "Rx[%c[%dm%p%c[0m]", 27, 30 + info->logColor, info, 27); } else { #endif /* !XBEE_LOG_NO_COLOR */ snprintf(label, sizeof(label), "Rx[%p]", info); #ifndef XBEE_LOG_NO_COLOR } #endif /* !XBEE_LOG_NO_COLOR */ xbee_logData(25, label, buf->data, buf->len); } #endif /* !XBEE_LOG_NO_RX */ #endif /* !XBEE_DISABLE_LOGGING */ if (xbee_ll_add_tail(info->bufList, buf) != XBEE_ENONE) return XBEE_ELINKEDLIST; buf = NULL; if (xsys_sem_post(&info->sem) != 0) return XBEE_ESEMAPHORE; } return XBEE_ESHUTDOWN; }
xbee_err xbee_free(struct xbee *xbee) { int i; xbee_ll_ext_item(xbeeList, xbee); xbee->die = 1; if (xbee->iface.rx) { xsys_sem_post(&xbee->iface.rx->sem); } if (xbee->iface.tx) { xsys_sem_post(&xbee->iface.tx->sem); } /* sleep for 4 seconds because: the rx thread should timeout every 2-ish econds the rxHandler thread will need to run round one more time to clean up the tx thread will need to run round one more time to clean up */ for (i = 0; i < 4; i++) usleep(1000000); xbee_threadDestroyMine(xbee); if (xbee->netInfo) xbee_netStop(xbee); if (xbee->mode && xbee->mode->shutdown) xbee->mode->shutdown(xbee); xbee_modeCleanup(xbee->iface.conTypes); xbee_rxFree(xbee->iface.rx); xbee_txFree(xbee->iface.tx); #ifndef XBEE_DISABLE_LOGGING xbee_logFree(xbee->log); #endif /* !XBEE_DISABLE_LOGGING */ xbee_frameBlockFree(xbee->fBlock); free(xbee); return XBEE_ENONE; }
/* start a monitored thread. If it dies, it will be restarted the thread identification information will be stored in the thread parameter */ int _xbee_threadStartMonitored(struct xbee *xbee, xsys_thread *thread, void*(*start_routine)(void*), void *arg, char *funcName) { struct threadInfo *tinfo; /* check parameters */ if (!xbee) { if (!xbee_default) return XBEE_ENOXBEE; xbee = xbee_default; } if (!xbee_validate(xbee)) return XBEE_EINVAL; if (!thread) return XBEE_EMISSINGPARAM; if (!start_routine) return XBEE_EMISSINGPARAM; if (!arg) return XBEE_EMISSINGPARAM; if (!funcName) return XBEE_EMISSINGPARAM; /* find out if we are already monitoring that function, and with the same argument */ for (tinfo = NULL; (tinfo = ll_get_next(&xbee->threadList, tinfo)) != NULL;) { /* is that handle already being used? */ if (tinfo->thread == thread) return XBEE_EINUSE; /* is that exact func/arg combo already being used? that would be a bit silly... */ if (tinfo->start_routine == start_routine && tinfo->arg == arg) { return XBEE_EEXISTS; } } /* create a new block */ if ((tinfo = calloc(1, sizeof(struct threadInfo))) == NULL) { return XBEE_ENOMEM; } /* setup all the details */ tinfo->funcName = funcName; tinfo->start_routine = start_routine; tinfo->arg = arg; tinfo->thread = thread; /* add it to the threadList */ if (ll_add_tail(&xbee->threadList, tinfo)) { return XBEE_ELINKEDLIST; } /* and prod the monitor thread (who will start the thread for us! */ xsys_sem_post(&xbee->semMonitor); return XBEE_ENONE; }
xbee_err xbee_framePost(struct xbee_frameBlock *fBlock, unsigned char frameId, unsigned char retVal) { xbee_err ret; struct xbee_frame *frame; int i; if (!fBlock) return XBEE_EMISSINGPARAM; if (frameId == 0) return XBEE_ENONE; xbee_mutex_lock(&fBlock->mutex); frame = NULL; for (i = 0; i < fBlock->numFrames; i++) { if (fBlock->frame[i].id != frameId) continue; if (fBlock->frame[i].status != 0) { frame = &fBlock->frame[i]; } break; } if (!frame) { ret = XBEE_EINVAL; } else if (frame->con && (frame->status & XBEE_FRAME_STATUS_WAITING)) { ret = XBEE_ENONE; frame->status |= XBEE_FRAME_STATUS_COMPLETE; frame->retVal = retVal; xsys_sem_post(&frame->sem); } else { if (!(frame->status & XBEE_FRAME_STATUS_ABANDONED)) { ret = XBEE_ETIMEOUT; } if (frame->con) { frame->con->frameId = 0; frame->con = NULL; } frame->status = 0; } xbee_mutex_unlock(&fBlock->mutex); return ret; }
/* end a connection the userData parameter can be used to retrieve the data stored with the connection */ EXPORT int xbee_conEnd(struct xbee *xbee, struct xbee_con *con, void **userData) { struct xbee_conType *conType; struct xbee_pkt *pkt; int i; /* check parameters */ if (!xbee) { if (!xbee_default) return XBEE_ENOXBEE; xbee = xbee_default; } if (!xbee_validate(xbee)) return XBEE_ENOXBEE; if (!con) return XBEE_EMISSINGPARAM; /* check the connection */ if (_xbee_conValidate(xbee, con, &conType)) return XBEE_EINVAL; /* remove the connection from the list */ if (ll_ext_item(&(conType->conList), con)) return XBEE_EINVAL; /* chop up any queued packets */ for (i = 0; (pkt = ll_ext_head(&(con->rxList))) != NULL; i++) { xbee_pktFree(pkt); } xbee_log(2,"Ended '%s' connection @ %p (destroyed %d packet%s)", conType->name, con, i, (i!=1)?"s":""); /* if the userData parameter is provided, then give the con's userData back to the caller */ if (userData) *userData = con->userData; /* if there is a callback thread running, then kill it off */ if (con->callbackRunning) { con->destroySelf = 1; xsys_sem_post(&con->callbackSem); /* inform the caller that we are waiting for the callback to complete */ return XBEE_ECALLBACK; } /* it is only safe to call _xbee_conEnd2() once the callback thread has completed, this will finish tidying up the connection */ return _xbee_conEnd2(xbee, con); }
/* transmit a message on the provided connection this function takes the raw data and its length */ EXPORT int xbee_connTx(struct xbee *xbee, struct xbee_con *con, char *data, int length) { int ret = XBEE_ENONE; struct bufData *buf; struct xbee_conType *conType; int waitForAckEnabled; /* check parameters */ if (!xbee) { if (!xbee_default) return XBEE_ENOXBEE; xbee = xbee_default; } if (!xbee_validate(xbee)) return XBEE_ENOXBEE; if (!con) return XBEE_EMISSINGPARAM; /* check the provided connection */ if (_xbee_conValidate(xbee, con, &conType)) return XBEE_EINVAL; /* check that we are able to send the message, if there is no xbee->f->connTx mapping, then we need a conType->txHandler */ if (!conType->txHandler && !xbee->f->connTx) return XBEE_ECANTTX; /* allocate a buffer, 1 byte is already held within the bufData struct, and we don't send the trailing '\0' */ if ((buf = calloc(1, sizeof(struct bufData) + (sizeof(unsigned char) * (length - 1)))) == NULL) { ret = XBEE_ENOMEM; goto die1; } /* populate the buffer */ buf->len = length; memcpy(buf->buf, data, length); /* cache the value of waitForAck, because it may change halfway through! */ waitForAckEnabled = con->options.waitForAck; /* if the connection has 'waitForAck' enabled, then we need to get a free FrameID that can be used */ if (waitForAckEnabled) { /* if we are configured to wait for an Ack, then we need to lock down the connection */ xbee_log(4,"Locking txMutex for con @ %p", con); xsys_mutex_lock(&con->txMutex); if ((con->frameID = xbee_frameIdGet(xbee, con)) == 0) { /* currently we don't inform the user (BAD), but this is unlikely unless you are communicating with >256 remote nodes */ xbee_log(1,"No avaliable frame IDs... we can't validate delivery"); xbee_log(4,"Unlocking txMutex for con @ %p (failed to get FrameID)", con); xsys_mutex_unlock(&con->txMutex); } else { /* mark the FrameID as present */ con->frameID_enabled = 1; } } /* if there is a custom mapping for connTx, then the handlers are skipped (but can be called from within the mapping!) */ if (!xbee->f->connTx) { struct bufData *oBuf; oBuf = buf; /* execute the conType handler, this should take the data provided, and convert it into an XBee formatted block of data this is given, and returned via the 'buf' argument */ xbee_log(6,"Executing handler (%s)...", conType->txHandler->handlerName); if ((ret = conType->txHandler->handler(xbee, conType->txHandler, 0, &buf, con, NULL)) != XBEE_ENONE) goto die2; /* a bit of sanity checking... */ if (!buf || buf == oBuf) { ret = XBEE_EUNKNOWN; goto die2; } free(oBuf); } if (!xbee->f->connTx) { /* if there is no connTx mapped, then add the packet to libxbee's txlist, and prod the tx thread */ ll_add_tail(&xbee->txList, buf); xsys_sem_post(&xbee->txSem); } else { /* same as before, if a mapping is registered, then the packet isn't queued for Tx, at least not here instead we execute the mapped function */ if ((ret = xbee->f->connTx(xbee, con, buf)) != XBEE_ENONE) goto die2; } /* if we should be waiting for an Ack, we now need to wait */ if (waitForAckEnabled && con->frameID) { xbee_log(4,"Waiting for txSem for con @ %p", con); /* the wait occurs inside xbee_frameIdGetACK() */ ret = xbee_frameIdGetACK(xbee, con, con->frameID); if (ret) xbee_log(4,"--- xbee_frameIdGetACK() returned: %d",ret); /* unlock the connection so that other transmitters may follow on */ xbee_log(4,"Unlocking txMutex for con @ %p", con); xsys_mutex_unlock(&con->txMutex); } /* disable the frameID */ con->frameID_enabled = 0; goto done; die2: free(buf); die1: done: return ret; }