/* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2015-2020 David Sainty * * 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 2 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 . */ /* $Id$ */ /* * avrdude interface for AVR devices Over-The-Air programmable via an * XBee Series 2 device. * * The XBee programmer is STK500v1 (optiboot) encapsulated in the XBee * API protocol. The bootloader supporting this protocol is available at: * * https://github.com/davidsainty/xbeeboot */ #include "ac_cfg.h" #include #include #include #include #include #include "avrdude.h" #include "libavrdude.h" #include "stk500_private.h" #include "stk500.h" #include "xbee.h" /* * After eight seconds the AVR bootloader watchdog will kick in. But * to allow for the possibility of eight seconds upstream and another * eight seconds downstream, allow for 16 retries (of roughly one * second each). */ #ifndef XBEE_MAX_RETRIES #define XBEE_MAX_RETRIES 16 #endif /* * Maximum chunk size, which is the maximum encapsulated payload to be * delivered to the remote CPU. * * There is an additional overhead of 3 bytes encapsulation, one * "REQUEST" byte, one sequence number byte, and one * "FIRMWARE_DELIVER" request type. * * The ZigBee maximum (unfragmented) payload is 84 bytes. Source * routing decreases that by two bytes overhead, plus two bytes per * hop. Maximum hop support is for 11 or 25 hops depending on * firmware. * * Network layer encryption decreases the maximum payload by 18 bytes. * APS end-to-end encryption decreases the maximum payload by 9 bytes. * Both these layers are available in concert, as seen in the section * "Network and APS layer encryption", decreasing our maximum payload * by both 18 bytes and 9 bytes. * * Our maximum payload size should therefore ideally be 84 - 18 - 9 = * 57 bytes, and therefore a chunk size of 54 bytes for zero hops. * * Source: XBee X2C manual: "Maximum RF payload size" section for most * details; "Network layer encryption and decryption" section for the * reference to 18 bytes of overhead; and "Enable APS encryption" for * the reference to 9 bytes of overhead. */ #ifndef XBEEBOOT_MAX_CHUNK #define XBEEBOOT_MAX_CHUNK 54 #endif /* * Maximum source route intermediate hops. This is described in the * documentation variously as 40 hops (routing table); OR 25 hops * (firmware 4x58 or later); OR 11 hops (firmware earlier than 4x58). * * What isn't described is how to know if a given source route length * is actually supported by the mesh for our target device. */ #ifndef XBEE_MAX_INTERMEDIATE_HOPS #define XBEE_MAX_INTERMEDIATE_HOPS 40 #endif /* Protocol */ #define XBEEBOOT_PACKET_TYPE_ACK 0 #define XBEEBOOT_PACKET_TYPE_REQUEST 1 /* * Read signature bytes - Direct copy of the Arduino behaviour to * satisfy Optiboot. */ static int xbee_read_sig_bytes(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *m) { unsigned char buf[32]; /* Signature byte reads are always 3 bytes. */ if (m->size < 3) { pmsg_error("memsize too small for sig byte read\n"); return -1; } buf[0] = Cmnd_STK_READ_SIGN; buf[1] = Sync_CRC_EOP; serial_send(&pgm->fd, buf, 2); if (serial_recv(&pgm->fd, buf, 5) < 0) return -1; if (buf[0] == Resp_STK_NOSYNC) { pmsg_error("programmer is out of sync\n"); return -1; } else if (buf[0] != Resp_STK_INSYNC) { msg_error("\n"); pmsg_error("protocol expects sync byte 0x%02x but got 0x%02x\n", Resp_STK_INSYNC, buf[0]); return -2; } if (buf[4] != Resp_STK_OK) { msg_error("\n"); pmsg_error("protocol expects OK byte 0x%02x but got 0x%02x\n", Resp_STK_OK, buf[4]); return -3; } m->buf[0] = buf[1]; m->buf[1] = buf[2]; m->buf[2] = buf[3]; return 3; } struct XBeeSequenceStatistics { struct timeval sendTime; }; struct XBeeStaticticsSummary { struct timeval minimum; struct timeval maximum; struct timeval sum; unsigned long samples; }; #define XBEE_STATS_GROUPS 4 #define XBEE_STATS_FRAME_LOCAL 0 #define XBEE_STATS_FRAME_REMOTE 1 #define XBEE_STATS_TRANSMIT 2 #define XBEE_STATS_RECEIVE 3 static const char* groupNames[] = { "FRAME_LOCAL", "FRAME_REMOTE", "TRANSMIT", "RECEIVE" }; struct XBeeBootSession { struct serial_device *serialDevice; union filedescriptor serialDescriptor; unsigned char xbee_address[10]; int directMode; unsigned char outSequence; unsigned char inSequence; /* * XBee API frame sequence number. */ unsigned char txSequence; /* * Set to non-zero if the transport is broken to the point it is * considered unusable. */ int transportUnusable; int xbeeResetPin; size_t inInIndex; size_t inOutIndex; unsigned char inBuffer[256]; int sourceRouteHops; /* -1 if unset */ int sourceRouteChanged; /* * The source route is an array of intermediate 16 bit addresses, * starting with the address nearest to the target address, and * finishing with the address closest to our local device. */ unsigned char sourceRoute[2 * XBEE_MAX_INTERMEDIATE_HOPS]; struct XBeeSequenceStatistics sequenceStatistics[256 * XBEE_STATS_GROUPS]; struct XBeeStaticticsSummary groupSummary[XBEE_STATS_GROUPS]; }; static void xbeeStatsReset(struct XBeeStaticticsSummary *summary) { summary->minimum.tv_sec = 0; summary->minimum.tv_usec = 0; summary->maximum.tv_sec = 0; summary->maximum.tv_usec = 0; summary->sum.tv_sec = 0; summary->sum.tv_usec = 0; summary->samples = 0; } static void xbeeStatsAdd(struct XBeeStaticticsSummary *summary, struct timeval const *sample) { summary->sum.tv_usec += sample->tv_usec; if (summary->sum.tv_usec > 1000000) { summary->sum.tv_usec -= 1000000; summary->sum.tv_sec++; } summary->sum.tv_sec += sample->tv_sec; if (summary->samples == 0 || summary->minimum.tv_sec > sample->tv_sec || (summary->minimum.tv_sec == sample->tv_sec && summary->minimum.tv_usec > sample->tv_usec)) { summary->minimum = *sample; } if (summary->maximum.tv_sec < sample->tv_sec || (summary->maximum.tv_sec == sample->tv_sec && summary->maximum.tv_usec < sample->tv_usec)) { summary->maximum = *sample; } summary->samples++; } static void xbeeStatsSummarise(struct XBeeStaticticsSummary const *summary) { pmsg_notice(" Minimum response time: %lu.%06lu\n", summary->minimum.tv_sec, summary->minimum.tv_usec); pmsg_notice(" Maximum response time: %lu.%06lu\n", summary->maximum.tv_sec, summary->maximum.tv_usec); struct timeval average; const unsigned long samples = summary->samples; average.tv_sec = summary->sum.tv_sec / samples; unsigned long long usecs = summary->sum.tv_usec; usecs += (summary->sum.tv_sec % samples) * 1000000; usecs = usecs / samples; average.tv_sec += usecs / 1000000; average.tv_usec = usecs % 1000000; pmsg_notice(" Average response time: %lu.%06lu\n", average.tv_sec, average.tv_usec); } static void XBeeBootSessionInit(struct XBeeBootSession *xbs) { xbs->serialDevice = &serial_serdev; xbs->directMode = 1; xbs->xbeeResetPin = XBEE_DEFAULT_RESET_PIN; xbs->outSequence = 0; xbs->inSequence = 0; xbs->txSequence = 0; xbs->transportUnusable = 0; xbs->inInIndex = 0; xbs->inOutIndex = 0; xbs->sourceRouteHops = -1; xbs->sourceRouteChanged = 0; int group; for (group = 0; group < 3; group++) { int index; for (index = 0; index < 256; index++) xbs->sequenceStatistics[group * 256 + index].sendTime.tv_sec = (time_t)0; xbeeStatsReset(&xbs->groupSummary[group]); } } #define xbeebootsession(fdp) (struct XBeeBootSession*)((fdp)->pfd) static void xbeedev_setresetpin(const union filedescriptor *fdp, int xbeeResetPin) { struct XBeeBootSession *xbs = xbeebootsession(fdp); xbs->xbeeResetPin = xbeeResetPin; } enum xbee_stat_is_retry_enum {XBEE_STATS_NOT_RETRY, XBEE_STATS_IS_RETRY}; typedef enum xbee_stat_is_retry_enum xbee_stat_is_retry; static void xbeedev_stats_send(struct XBeeBootSession *xbs, char const *detail, int detailSequence, unsigned int group, unsigned char sequence, xbee_stat_is_retry retry, struct timeval const *sendTime) { struct XBeeSequenceStatistics *stats = &xbs->sequenceStatistics[group * 256 + sequence]; if (retry == XBEE_STATS_NOT_RETRY) stats->sendTime = *sendTime; if (detailSequence >= 0) { pmsg_notice2("stats: send Group %s Sequence %u : " "Send %lu.%06lu %s Sequence %d\n", groupNames[group], (unsigned int) sequence, (unsigned long) sendTime->tv_sec, (unsigned long) sendTime->tv_usec, detail, detailSequence); } else { pmsg_notice2("stats: send Group %s Sequence %u : " "Send %lu.%06lu %s\n", groupNames[group], (unsigned int) sequence, (unsigned long) sendTime->tv_sec, (unsigned long) sendTime->tv_usec, detail); } } static void xbeedev_stats_receive(struct XBeeBootSession *xbs, char const *detail, unsigned int group, unsigned char sequence, struct timeval const *receiveTime) { struct XBeeSequenceStatistics *stats = &xbs->sequenceStatistics[group * 256 + sequence]; struct timeval delay; time_t secs; long usecs; secs = receiveTime->tv_sec - stats->sendTime.tv_sec; usecs = receiveTime->tv_usec - stats->sendTime.tv_usec; if (usecs < 0) { usecs += 1000000; secs--; } delay.tv_sec = secs; delay.tv_usec = usecs; pmsg_notice2("stats: receive Group %s Sequence %u : " "Send %lu.%06lu Receive %lu.%06lu Delay %lu.%06lu %s\n", groupNames[group], (unsigned int) sequence, (unsigned long) stats->sendTime.tv_sec, (unsigned long) stats->sendTime.tv_usec, (unsigned long) receiveTime->tv_sec, (unsigned long) receiveTime->tv_usec, (unsigned long) secs, (unsigned long) usecs, detail); xbeeStatsAdd(&xbs->groupSummary[group], &delay); } static int sendAPIRequest(struct XBeeBootSession *xbs, unsigned char apiType, int txSequence, int apiOption, int prePayload1, int prePayload2, int packetType, int sequence, int appType, char const *detail, int detailSequence, unsigned int frameGroup, xbee_stat_is_retry retry, unsigned int dataLength, const unsigned char *data) { unsigned char frame[256]; unsigned char *fp = &frame[5]; unsigned char *dataStart = fp; unsigned char checksum = 0xff; unsigned char length = 0; struct timeval time; gettimeofday(&time, NULL); pmsg_notice2("sendAPIRequest(): %lu.%06lu %d, %d, %d, %d %s\n", (unsigned long) time.tv_sec, (unsigned long) time.tv_usec, (int) packetType, (int)sequence, appType, data == NULL ? -1 : (int)*data, detail); #define fpput(x) \ do { \ const unsigned char v = (x); \ if (v == 0x7d || v == 0x7e || v == 0x11 || v == 0x13) { \ *fp++ = 0x7d; \ *fp++ = v ^ 0x20; \ } else { \ *fp++ = v; \ } \ checksum -= v; \ length++; \ } while (0) fpput(apiType); /* ZigBee Receive Packet or ZigBee Transmit Request */ if (apiOption >= 0) fpput(apiOption); /* Receive options (RX) */ if (txSequence >= 0) { fpput(txSequence); /* Delivery sequence (TX/AT) */ /* * Record the frame send time. Note that frame sequences are * never retries. */ xbeedev_stats_send(xbs, detail, detailSequence, frameGroup, txSequence, 0, &time); } if (apiType != 0x08) { /* Automatically inhibit addressing for local AT command requests. */ size_t index; for (index = 0; index < 10; index++) { const unsigned char val = xbs->xbee_address[index]; fpput(val); } /* * If this is an API call with remote address, but is not a Create * Source Route request, consider prefixing it with source routing * instructions. */ if (apiType != 0x21 && xbs->sourceRouteChanged) { pmsg_notice2("sendAPIRequest(): issuing Create Source Route request with %d hops\n", xbs->sourceRouteHops); int rc = sendAPIRequest(xbs, 0x21, /* Create Source Route */ 0, -1, 0, xbs->sourceRouteHops, -1, -1, -1, "Create Source Route for FRAME_REMOTE", txSequence, XBEE_STATS_FRAME_LOCAL, /* Local, no response */ 0, /* Not a retry */ xbs->sourceRouteHops * 2, xbs->sourceRoute); if (rc != 0) return rc; xbs->sourceRouteChanged = 0; } } if (prePayload1 >= 0) fpput(prePayload1); /* Transmit broadcast radius */ if (prePayload2 >= 0) fpput(prePayload2); /* Transmit options */ if (packetType >= 0) fpput(packetType); /* XBEEBOOT_PACKET_TYPE_{ACK,REQUEST} */ if (sequence >= 0) { fpput(sequence); /* Record the send time */ if (packetType == XBEEBOOT_PACKET_TYPE_REQUEST) xbeedev_stats_send(xbs, detail, sequence, XBEE_STATS_TRANSMIT, sequence, retry, &time); } if (appType >= 0) fpput(appType); /* FIRMWARE_DELIVER */ { size_t index; for (index = 0; index < dataLength; index++) fpput(data[index]); } /* Length BEFORE checksum byte */ const unsigned char unescapedLength = length; fpput(checksum); /* Length AFTER checksum byte */ const unsigned int finalLength = fp - dataStart; frame[0] = 0x7e; fp = &frame[1]; fpput(0); fpput(unescapedLength); const unsigned int prefixLength = fp - frame; unsigned char *frameStart = dataStart - prefixLength; memmove(frameStart, frame, prefixLength); return xbs->serialDevice->send(&xbs->serialDescriptor, frameStart, finalLength + prefixLength); } static int sendPacket(struct XBeeBootSession *xbs, const char *detail, unsigned char packetType, unsigned char sequence, xbee_stat_is_retry retry, int appType, unsigned int dataLength, const unsigned char *data) { unsigned char apiType; int prePayload1; int prePayload2; if (xbs->directMode) { /* * In direct mode we are pretending to be an XBee device * forwarding on data received from the transmitting XBee. We * therefore format the data as a remote XBee would, encapsulated * in a 0x90 packet. */ apiType = 0x90; /* ZigBee Receive Packet */ prePayload1 = -1; prePayload2 = -1; } else { /* * In normal mode we are requesting a payload delivery, * encapsulated in a 0x10 packet. */ apiType = 0x10; /* ZigBee Transmit Request */ prePayload1 = 0; prePayload2 = 0; } while ((++xbs->txSequence & 0xff) == 0); return sendAPIRequest(xbs, apiType, xbs->txSequence, -1, prePayload1, prePayload2, packetType, sequence, appType, detail, sequence, XBEE_STATS_FRAME_REMOTE, retry, dataLength, data); } #define XBEE_LENGTH_LEN 2 #define XBEE_CHECKSUM_LEN 1 #define XBEE_APITYPE_LEN 1 #define XBEE_APISEQUENCE_LEN 1 #define XBEE_ADDRESS_64BIT_LEN 8 #define XBEE_ADDRESS_16BIT_LEN 2 #define XBEE_RADIUS_LEN 1 #define XBEE_TXOPTIONS_LEN 1 #define XBEE_RXOPTIONS_LEN 1 static void xbeedev_record16Bit(struct XBeeBootSession *xbs, const unsigned char *rx16Bit) { /* * We don't start out knowing what the 16-bit device address is, but * we should receive it on the return packets, and re-use it from * that point on. */ unsigned char * const tx16Bit = &xbs->xbee_address[XBEE_ADDRESS_64BIT_LEN]; if (memcmp(rx16Bit, tx16Bit, XBEE_ADDRESS_16BIT_LEN) != 0) { pmsg_notice2("xbeedev_record16Bit(): new 16-bit address: %02x%02x\n", (unsigned int)rx16Bit[0], (unsigned int)rx16Bit[1]); memcpy(tx16Bit, rx16Bit, XBEE_ADDRESS_16BIT_LEN); } } /* * Return 0 on success. * Return -1 on generic error (normally serial timeout). * Return -512 + XBee AT Response code */ #define XBEE_AT_RETURN_CODE(x) (((x) >= -512 && (x) <= -256) ? (x) + 512 : -1) static int xbeedev_poll(struct XBeeBootSession *xbs, unsigned char **buf, size_t *buflen, int waitForAck, int waitForSequence) { for (;;) { unsigned char byte; unsigned char frame[256]; unsigned int frameSize; before_frame: do { const int rc = xbs->serialDevice->recv(&xbs->serialDescriptor, &byte, 1); if (rc < 0) return rc; } while (byte != 0x7e); start_of_frame: { size_t index = 0; int escaped = 0; frameSize = XBEE_LENGTH_LEN; do { const int rc = xbs->serialDevice->recv(&xbs->serialDescriptor, &byte, 1); if (rc < 0) return rc; if (byte == 0x7e) /* * No matter when we receive a frame start byte, we should * abort parsing and start a fresh frame. */ goto start_of_frame; if (escaped) { byte ^= 0x20; escaped = 0; } else if (byte == 0x7d) { escaped = 1; continue; } if (index >= sizeof(frame)) goto before_frame; frame[index++] = byte; if (index == XBEE_LENGTH_LEN) { /* Length plus the two length bytes, plus the checksum byte */ frameSize = (frame[0] << 8 | frame[1]) + XBEE_LENGTH_LEN + XBEE_CHECKSUM_LEN; if (frameSize >= sizeof(frame)) /* Too long - immediately give up on this frame */ goto before_frame; } } while (index < frameSize); /* End of frame */ unsigned char checksum = 1; size_t cIndex; for (cIndex = 2; cIndex < index; cIndex++) { checksum += frame[cIndex]; } if (checksum) { /* Checksum didn't match */ pmsg_notice2("xbeedev_poll(): bad checksum %d\n", (int) checksum); continue; } } const unsigned char frameType = frame[2]; struct timeval receiveTime; gettimeofday(&receiveTime, NULL); pmsg_notice2("xbeedev_poll(): %lu.%06lu Received frame type %x\n", (unsigned long) receiveTime.tv_sec, (unsigned long) receiveTime.tv_usec, (unsigned int) frameType); if (frameType == 0x97 && frameSize > 16) { /* Remote command response */ unsigned char txSequence = frame[3]; unsigned char resultCode = frame[16]; xbeedev_stats_receive(xbs, "Remote AT command response", XBEE_STATS_FRAME_REMOTE, txSequence, &receiveTime); pmsg_notice("xbeedev_poll(): remote command %d result code %d\n", (int) txSequence, (int) resultCode); if (waitForSequence >= 0 && waitForSequence == frame[3]) /* Received result for our sequence numbered request */ return -512 + resultCode; } else if (frameType == 0x88 && frameSize > 6) { /* Local command response */ unsigned char txSequence = frame[3]; xbeedev_stats_receive(xbs, "Local AT command response", XBEE_STATS_FRAME_LOCAL, txSequence, &receiveTime); pmsg_notice("xbeedev_poll(): local command %c%c result code %d\n", frame[4], frame[5], (int)frame[6]); if (waitForSequence >= 0 && waitForSequence == txSequence) /* Received result for our sequence numbered request */ return 0; } else if (frameType == 0x8b && frameSize > 7) { /* Transmit status */ unsigned char txSequence = frame[3]; xbeedev_stats_receive(xbs, "Transmit status", XBEE_STATS_FRAME_REMOTE, txSequence, &receiveTime); pmsg_notice2("xbeedev_poll(): transmit status %d result code %d\n", (int) frame[3], (int) frame[7]); } else if (frameType == 0xa1 && frameSize >= XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_ADDRESS_64BIT_LEN + XBEE_ADDRESS_16BIT_LEN + 2 + XBEE_CHECKSUM_LEN) { /* Route Record Indicator */ if (memcmp(&frame[XBEE_LENGTH_LEN + XBEE_APITYPE_LEN], xbs->xbee_address, XBEE_ADDRESS_64BIT_LEN) != 0) { /* Not from our target device */ pmsg_notice2("xbeedev_poll(): route Record Indicator from other XBee\n"); continue; } /* * We don't start out knowing what the 16-bit device address is, * but we should receive it on the return packets, and re-use it * from that point on. */ { const unsigned char *rx16Bit = &frame[XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_ADDRESS_64BIT_LEN]; xbeedev_record16Bit(xbs, rx16Bit); } const unsigned int header = XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_ADDRESS_64BIT_LEN + XBEE_ADDRESS_16BIT_LEN; const unsigned char receiveOptions = frame[header]; const unsigned char hops = frame[header + 1]; pmsg_notice2("xbeedev_poll(): " "Route Record Indicator from target XBee: " "hops=%d options=%d\n", (int)hops, (int)receiveOptions); if (frameSize < header + 2 + hops * 2 + XBEE_CHECKSUM_LEN) /* Bounds check: Frame is too small */ continue; const unsigned int tableOffset = header + 2; unsigned char index; for (index = 0; index < hops; index++) { pmsg_notice2("xbeedev_poll(): " "Route Intermediate Hop %d : %02x%02x\n", (int)index, (int)frame[tableOffset + index * 2], (int)frame[tableOffset + index * 2 + 1]); } if (hops <= XBEE_MAX_INTERMEDIATE_HOPS) { if (xbs->sourceRouteHops != (int)hops || memcmp(&frame[tableOffset], xbs->sourceRoute, hops * 2) != 0) { memcpy(xbs->sourceRoute, &frame[tableOffset], hops * 2); xbs->sourceRouteHops = hops; xbs->sourceRouteChanged = 1; pmsg_notice2("xbeedev_poll(): route has changed\n"); } } } else if (frameType == 0x10 || frameType == 0x90) { unsigned char *dataStart; unsigned int dataLength; if (frameType == 0x10) { /* Direct mode frame */ const unsigned int header = XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_APISEQUENCE_LEN + XBEE_ADDRESS_64BIT_LEN + XBEE_ADDRESS_16BIT_LEN + XBEE_RADIUS_LEN + XBEE_TXOPTIONS_LEN; if (frameSize <= header + XBEE_CHECKSUM_LEN) /* Bounds check: Frame is too small */ continue; dataLength = frameSize - header - XBEE_CHECKSUM_LEN; dataStart = &frame[header]; } else { /* Remote reply frame */ const unsigned int header = XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_ADDRESS_64BIT_LEN + XBEE_ADDRESS_16BIT_LEN + XBEE_RXOPTIONS_LEN; if (frameSize <= header + XBEE_CHECKSUM_LEN) /* Bounds check: Frame is too small */ continue; dataLength = frameSize - header - XBEE_CHECKSUM_LEN; dataStart = &frame[header]; if (memcmp(&frame[XBEE_LENGTH_LEN + XBEE_APITYPE_LEN], xbs->xbee_address, XBEE_ADDRESS_64BIT_LEN) != 0) { /* * This packet is not from our target device. Unlikely * to ever happen, but if it does we have to ignore * it. */ continue; } /* * We don't start out knowing what the 16-bit device address * is, but we should receive it on the return packets, and * re-use it from that point on. */ { const unsigned char *rx16Bit = &frame[XBEE_LENGTH_LEN + XBEE_APITYPE_LEN + XBEE_ADDRESS_64BIT_LEN]; xbeedev_record16Bit(xbs, rx16Bit); } } if (dataLength >= 2) { const unsigned char protocolType = dataStart[0]; const unsigned char sequence = dataStart[1]; pmsg_notice2("xbeedev_poll(): " "%lu.%06lu Packet %d #%d\n", (unsigned long)receiveTime.tv_sec, (unsigned long)receiveTime.tv_usec, (int)protocolType, (int)sequence); if (protocolType == XBEEBOOT_PACKET_TYPE_ACK) { /* ACK */ xbeedev_stats_receive(xbs, "XBeeBoot ACK", XBEE_STATS_TRANSMIT, sequence, &receiveTime); /* * We can't update outSequence here, we already do that * somewhere else. */ if (waitForAck >= 0 && waitForAck == sequence) return 0; } else if (protocolType == XBEEBOOT_PACKET_TYPE_REQUEST && dataLength >= 4 && dataStart[2] == 24) { /* REQUEST FRAME_REPLY */ xbeedev_stats_receive(xbs, "XBeeBoot Receive", XBEE_STATS_RECEIVE, sequence, &receiveTime); unsigned char nextSequence = xbs->inSequence; while ((++nextSequence & 0xff) == 0); if (sequence == nextSequence) { xbs->inSequence = nextSequence; const size_t textLength = dataLength - 3; size_t index; for (index = 0; index < textLength; index++) { const unsigned char data = dataStart[3 + index]; if (buflen != NULL && *buflen > 0) { /* If we are receiving right now, and have a buffer ... */ *(*buf)++ = data; (*buflen)--; } else { xbs->inBuffer[xbs->inInIndex++] = data; if (xbs->inInIndex == sizeof(xbs->inBuffer)) xbs->inInIndex = 0; if (xbs->inInIndex == xbs->inOutIndex) { /* Should be impossible */ pmsg_error("buffer overrun\n"); xbs->transportUnusable = 1; return -1; } } } /*msg_error("ACK %x\n", (unsigned int)sequence);*/ sendPacket(xbs, "Transmit Request ACK for RECEIVE", XBEEBOOT_PACKET_TYPE_ACK, sequence, XBEE_STATS_NOT_RETRY, -1, 0, NULL); if (buf != NULL && *buflen == 0) /* Input buffer has been filled */ return 0; /* * Input buffer has NOT been filled, we are still in a * receive. Not a retry, this is the first point we know * for sure for this sequence number. */ while ((++nextSequence & 0xff) == 0); xbeedev_stats_send(xbs, "poll() implies pending RECEIVE", nextSequence, XBEE_STATS_RECEIVE, nextSequence, XBEE_STATS_NOT_RETRY, &receiveTime); } } } } } } /* * @return * 0 on success, a negative value on failure, or a positive * value indicating the sequence number associated with the * request. */ static int localAsyncAT(struct XBeeBootSession *xbs, char const *detail, unsigned char at1, unsigned char at2, int value) { if (xbs->directMode) /* * Remote XBee AT commands make no sense in direct mode - there is * no XBee device to communicate with. * * Return success, no sequence number. */ return 0; while ((++xbs->txSequence & 0xff) == 0); const unsigned char sequence = xbs->txSequence; unsigned char buf[3]; size_t length = 0; buf[length++] = at1; buf[length++] = at2; if (value >= 0) buf[length++] = (unsigned char)value; pmsg_notice("local AT command: %c%c\n", at1, at2); /* Local AT command 0x08 */ int rc = sendAPIRequest(xbs, 0x08, sequence, -1, -1, -1, -1, -1, -1, detail, -1, XBEE_STATS_FRAME_LOCAL, XBEE_STATS_NOT_RETRY, length, buf); if (rc < 0) /* Failed */ return rc; /* Success, positive sequence number */ return (int)sequence; } static int localAT(struct XBeeBootSession *xbs, char const *detail, unsigned char at1, unsigned char at2, int value) { int result = localAsyncAT(xbs, detail, at1, at2, value); if (result <= 0) /* Failure, or success without a sequence number */ return result; unsigned char sequence = (unsigned char)result; int retries; for (retries = 0; retries < 5; retries++) { const int rc = xbeedev_poll(xbs, NULL, NULL, -1, sequence); if (rc == 0) return 0; } return -1; } /* * Return 0 on success. * Return -1 on generic error (normally serial timeout). * Return -512 + XBee AT Response code */ static int sendAT(struct XBeeBootSession *xbs, char const *detail, unsigned char at1, unsigned char at2, int value) { if (xbs->directMode) /* * Remote XBee AT commands make no sense in direct mode - there is * no XBee device to communicate with. */ return 0; while ((++xbs->txSequence & 0xff) == 0); const unsigned char sequence = xbs->txSequence; unsigned char buf[3]; size_t length = 0; buf[length++] = at1; buf[length++] = at2; if (value >= 0) buf[length++] = (unsigned char)value; pmsg_notice("remote AT command: %c%c\n", at1, at2); /* Remote AT command 0x17 with Apply Changes 0x02 */ sendAPIRequest(xbs, 0x17, sequence, -1, -1, -1, -1, 0x02, -1, detail, -1, XBEE_STATS_FRAME_REMOTE, XBEE_STATS_NOT_RETRY, length, buf); int retries; for (retries = 0; retries < 30; retries++) { const int rc = xbeedev_poll(xbs, NULL, NULL, -1, sequence); const int xbeeRc = XBEE_AT_RETURN_CODE(rc); if (xbeeRc == 0) /* Translate to normal success code */ return 0; if (rc != -1) return rc; } return -1; } /* * Return 0 on no error recognised, 1 if error was detected and reported */ static int xbeeATError(int rc) { const int xbeeRc = XBEE_AT_RETURN_CODE(rc); if (xbeeRc < 0) return 0; if (xbeeRc == 1) { pmsg_error("unable to communicate with remote XBee\n"); } else if (xbeeRc == 2) { pmsg_error("remote XBee: invalid command\n"); } else if (xbeeRc == 3) { pmsg_error("remote XBee: invalid command parameter\n"); } else if (xbeeRc == 4) { pmsg_error("remote XBee: transmission failure\n"); } else { pmsg_error("unrecognised remote XBee error code %d\n", xbeeRc); } return 1; } static void xbeedev_free(struct XBeeBootSession *xbs) { xbs->serialDevice->close(&xbs->serialDescriptor); free(xbs); } static void xbeedev_close(union filedescriptor *fdp) { struct XBeeBootSession *xbs = xbeebootsession(fdp); xbeedev_free(xbs); } static int xbeedev_open(const char *port, union pinfo pinfo, union filedescriptor *fdp) { /* * The syntax for XBee devices is defined as: * * -P @[serialdevice] * * ... or ... * * -P @[serialdevice] * * ... for a direct connection. */ char *ttySeparator = strchr(port, '@'); if (ttySeparator == NULL) { pmsg_error("XBee: bad port syntax, require @\n"); return -1; } struct XBeeBootSession *xbs = malloc(sizeof(struct XBeeBootSession)); if (xbs == NULL) { pmsg_error("out of memory\n"); return -1; } XBeeBootSessionInit(xbs); char *tty = &ttySeparator[1]; if (ttySeparator == port) { /* Direct connection */ memset(xbs->xbee_address, 0, 8); xbs->directMode = 1; } else { size_t addrIndex = 0; int nybble = -1; char const *address = port; while (address != ttySeparator) { char hex = *address++; unsigned int val; if (hex >= '0' && hex <= '9') { val = hex - '0'; } else if (hex >= 'A' && hex <= 'F') { val = hex - 'A' + 10; } else if (hex >= 'a' && hex <= 'f') { val = hex - 'a' + 10; } else { break; } if (nybble == -1) { nybble = val; } else { xbs->xbee_address[addrIndex++] = (nybble * 16) | val; nybble = -1; if (addrIndex == 8) break; } } if (addrIndex != 8 || address != ttySeparator || nybble != -1) { pmsg_error("XBee: bad XBee address, require 16-character hexadecimal address\n"); free(xbs); return -1; } xbs->directMode = 0; } /* Unknown 16 bit address */ xbs->xbee_address[8] = 0xff; xbs->xbee_address[9] = 0xfe; pmsg_trace("XBee address: %02x%02x%02x%02x%02x%02x%02x%02x\n", (unsigned int) xbs->xbee_address[0], (unsigned int) xbs->xbee_address[1], (unsigned int) xbs->xbee_address[2], (unsigned int) xbs->xbee_address[3], (unsigned int) xbs->xbee_address[4], (unsigned int) xbs->xbee_address[5], (unsigned int) xbs->xbee_address[6], (unsigned int) xbs->xbee_address[7]); if (pinfo.serialinfo.baud) { /* * User supplied the correct baud rate. */ } else if (xbs->directMode) { /* * In direct mode, default to 19200. * * Why? * * In this mode, we are NOT talking to an XBee, we are talking * directly to an AVR device that thinks it is talking to an XBee * itself. * * Because, an XBee is a 3.3V device defaulting to 9600baud, and * the Atmel328P is only rated at a maximum clock rate of 8MHz * with a 3.3V supply, so there's a high likelihood a remote * Atmel328P will be clocked at 8MHz. * * With a direct connection, there's a good chance we're talking * to an Arduino clocked at 16MHz with an XBee-enabled chip * plugged in. The doubled clock rate means a doubled serial * rate. Double 9600 baud == 19200 baud. */ pinfo.serialinfo.baud = 19200; } else { /* * In normal mode, default to 9600. * * Why? * * XBee devices default to 9600 baud. In this mode we are talking * to the XBee device, not the far-end device, so it's the local * XBee baud rate we should select. The baud rate of the AVR * device is irrelevant. */ pinfo.serialinfo.baud = 9600; } pinfo.serialinfo.cflags = SERIAL_8N1; pmsg_notice("baud %ld\n", (long)pinfo.serialinfo.baud); { const int rc = xbs->serialDevice->open(tty, pinfo, &xbs->serialDescriptor); if (rc < 0) { free(xbs); return rc; } } if (!xbs->directMode) { /* Attempt to ensure the local XBee is in API mode 2 */ { const int rc = localAT(xbs, "AT AP=2", 'A', 'P', 2); if (rc < 0) { pmsg_error("local XBee is not responding\n"); xbeedev_free(xbs); return rc; } } /* * At this point we want to set the remote XBee parameters as * required for talking to XBeeBoot. Ideally we would start with * an "FR" full reset, but because that causes the XBee to * disappear off the mesh for a significant period and become * unresponsive, we don't do that. */ /* * Issue an "Aggregate Routing Notification" to enable many-to-one * routing to this device. This has two effects: * * - Establishes a route from the remote XBee attached to the CPU * being programmed back to the local XBee. * * - Enables the 0xa1 Route frames so that we can make use of * Source Routing to deliver packets directly to the remote * XBee. * * Under "RF packet routing" subsection "Many-to-One routing", the * XBee S2C manual states "Applications that require multiple data * collectors can also use many-to-one routing. If more than one * data collector device sends a many-to-one broadcast, devices * create one reverse routing table entry for each collector." * * Under "RF packet routing" subsection "Source routing", the XBee * S2C manual states "To use source routing, a device must use the * API mode, and it must send periodic many-to-one route request * broadcasts (AR command) to create a many-to-one route to it on * all devices". */ { const int rc = localAT(xbs, "AT AR=0", 'A', 'R', 0); if (rc < 0) { pmsg_error("local XBee is not responding\n"); xbeedev_free(xbs); return rc; } } /* * Disable RTS input on the remote XBee, just in case it is * enabled by default. XBeeBoot doesn't attempt to support flow * control, and so it may not correctly drive this pin if RTS mode * is the default configuration. * * XBee IO port 6 is the only pin that supports RTS mode, so there * is no need to support any alternative pin. */ const int rc = sendAT(xbs, "AT D6=0", 'D', '6', 0); if (rc < 0) { xbeedev_free(xbs); if (xbeeATError(rc)) return -1; pmsg_error("remote XBee is not responding\n"); return rc; } } fdp->pfd = xbs; return 0; } static int xbeedev_send(const union filedescriptor *fdp, const unsigned char *buf, size_t buflen) { struct XBeeBootSession *xbs = xbeebootsession(fdp); if (xbs->transportUnusable) /* Don't attempt to continue on an unusable transport layer */ return -1; while (buflen > 0) { unsigned char sequence = xbs->outSequence; while ((++sequence & 0xff) == 0); xbs->outSequence = sequence; /* * We are about to send some data, and that might lead potentially * to received data before we see the ACK for this transmission. * As this might be the trigger seen before the next "recv" * operation, record that we have delivered this potential * trigger. */ { unsigned char nextSequence = xbs->inSequence; while ((++nextSequence & 0xff) == 0); struct timeval sendTime; gettimeofday(&sendTime, NULL); /* * Optimistic records should never be treated as retries, * because they might simply be guessing too optimistically. */ xbeedev_stats_send(xbs, "send() hints possible triggered RECEIVE", nextSequence, XBEE_STATS_RECEIVE, nextSequence, 0, &sendTime); } /* * Chunk the data into chunks of up to XBEEBOOT_MAX_CHUNK bytes. */ unsigned char maximum_chunk = XBEEBOOT_MAX_CHUNK; /* * Source routing incurs a two byte fixed overhead, plus a two * byte additional cost per intermediate hop. * * We are attempting to avoid fragmentation here, so resize our * maximum size to anticipate the overhead of the current number * of hops. If our maximum chunk would be less than one, just * give up and hope fragmentation will somehow save us. */ const int hops = xbs->sourceRouteHops; if (hops > 0 && (hops * 2 + 2) < XBEEBOOT_MAX_CHUNK) maximum_chunk -= hops * 2 + 2; const unsigned char blockLength = (buflen > maximum_chunk) ? maximum_chunk : buflen; int pollRc = 0; /* Repeatedly send whilst timing out waiting for ACK responses. */ int retries; for (retries = 0; retries < XBEE_MAX_RETRIES; retries++) { int sendRc = sendPacket(xbs, "Transmit Request Data, expect ACK for TRANSMIT", XBEEBOOT_PACKET_TYPE_REQUEST, sequence, retries > 0 ? XBEE_STATS_IS_RETRY : XBEE_STATS_NOT_RETRY, 23 /* FIRMWARE_DELIVER */, blockLength, buf); if (sendRc < 0) { /* There is no way to recover from a failure mid-send */ xbs->transportUnusable = 1; return sendRc; } pollRc = xbeedev_poll(xbs, NULL, NULL, sequence, -1); if (pollRc == 0) { /* Send was ACK'd */ buflen -= blockLength; buf += blockLength; break; } /* * Test the connection to the local XBee by repeatedly * requesting local configuration details. This functionally * has no effect, but will allow us to measure any reliability * issues on this link. */ localAsyncAT(xbs, "Local XBee ping [send]", 'A', 'P', -1); /* * If we don't receive an ACK it might be because the chip * missed an ACK from us. Resend that too after a timeout, * unless it's zero which is an illegal sequence number. */ if (xbs->inSequence != 0) { int ackRc = sendPacket(xbs, "Transmit Request ACK [Retry in send] " "for RECEIVE", XBEEBOOT_PACKET_TYPE_ACK, xbs->inSequence, XBEE_STATS_IS_RETRY, -1, 0, NULL); if (ackRc < 0) { /* There is no way to recover from a failure mid-send */ xbs->transportUnusable = 1; return ackRc; } } } if (pollRc < 0) { /* There is no way to recover from a failure mid-send */ xbs->transportUnusable = 1; return pollRc; } } return 0; } static int xbeedev_recv(const union filedescriptor *fdp, unsigned char *buf, size_t buflen) { struct XBeeBootSession *xbs = xbeebootsession(fdp); /* * First de-buffer anything previously received in a chunk that * couldn't be immediately delievered. */ while (xbs->inInIndex != xbs->inOutIndex) { *buf++ = xbs->inBuffer[xbs->inOutIndex++]; if (xbs->inOutIndex == sizeof(xbs->inBuffer)) xbs->inOutIndex = 0; if (--buflen == 0) return 0; } if (xbs->transportUnusable) /* Don't attempt to continue on an unusable transport layer */ return -1; /* * When we expect to receive data, that is the time to start the * clock. */ { unsigned char nextSequence = xbs->inSequence; while ((++nextSequence & 0xff) == 0); struct timeval sendTime; gettimeofday(&sendTime, NULL); /* * Not a retry - in fact this is the first stage we know for sure * a RECEIVE is due. */ xbeedev_stats_send(xbs, "recv() implies pending RECEIVE", nextSequence, XBEE_STATS_RECEIVE, nextSequence, XBEE_STATS_NOT_RETRY, &sendTime); } int retries; for (retries = 0; retries < XBEE_MAX_RETRIES; retries++) { const int rc = xbeedev_poll(xbs, &buf, &buflen, -1, -1); if (rc == 0) return 0; if (xbs->transportUnusable) /* Don't attempt to continue on an unusable transport layer */ return -1; /* * Test the connection to the local XBee by repeatedly * requesting local configuration details. This functionally * has no effect, but will allow us to measure any reliability * issues on this link. */ localAsyncAT(xbs, "Local XBee ping [recv]", 'A', 'P', -1); /* * The chip may have missed an ACK from us. Resend after a * timeout. */ if (xbs->inSequence != 0) sendPacket(xbs, "Transmit Request ACK [Retry in recv] for RECEIVE", XBEEBOOT_PACKET_TYPE_ACK, xbs->inSequence, XBEE_STATS_IS_RETRY, -1, 0, NULL); } return -1; } static int xbeedev_drain(const union filedescriptor *fdp, int display) { struct XBeeBootSession *xbs = xbeebootsession(fdp); if (xbs->transportUnusable) /* Don't attempt to continue on an unusable transport layer */ return -1; /* * Flushing the local serial buffer is unhelpful under this protocol */ do { xbs->inOutIndex = xbs->inInIndex = 0; } while (xbeedev_poll(xbs, NULL, NULL, -1, -1) == 0); return 0; } static int xbeedev_set_dtr_rts(const union filedescriptor *fdp, int is_on) { struct XBeeBootSession *xbs = xbeebootsession(fdp); if (xbs->directMode) /* Correct for direct mode */ return xbs->serialDevice->set_dtr_rts(&xbs->serialDescriptor, is_on); /* * For non-direct mode (Over-The-Air) we need to issue XBee commands * to the remote XBee in order to reset the AVR CPU and initiate the * XBeeBoot bootloader. */ const int rc = sendAT(xbs, is_on ? "AT [DTR]=low" : "AT [DTR]=high", 'D', '0' + xbs->xbeeResetPin, is_on ? 5 : 4); if (rc < 0) { if (xbeeATError(rc)) return -1; pmsg_error("remote XBee is not responding\n"); return rc; } return 0; } /* * Device descriptor for XBee framing. */ static struct serial_device xbee_serdev_frame = { .open = xbeedev_open, .close = xbeedev_close, .send = xbeedev_send, .recv = xbeedev_recv, .drain = xbeedev_drain, .set_dtr_rts = xbeedev_set_dtr_rts, .flags = SERDEV_FL_NONE, }; static int xbee_getsync(const PROGRAMMER *pgm) { unsigned char buf[2], resp[2]; /* * Issue sync request as per STK500. Unlike stk500_getsync(), don't * retry here - the underlying protocol will deal with retries for * us in xbeedev_send() and should be reliable. */ buf[0] = Cmnd_STK_GET_SYNC; buf[1] = Sync_CRC_EOP; int sendRc = serial_send(&pgm->fd, buf, 2); if (sendRc < 0) { pmsg_error("unable to deliver STK_GET_SYNC to the remote XBeeBoot bootloader\n"); return sendRc; } /* * The same is true of the receive - it will retry on timeouts until * the response buffer is full. */ int recvRc = serial_recv(&pgm->fd, resp, 2); if (recvRc < 0) { pmsg_error("no response to STK_GET_SYNC from the remote XBeeBoot bootloader\n"); return recvRc; } if (resp[0] != Resp_STK_INSYNC) { pmsg_error("not in sync, resp=0x%02x\n", (unsigned int) resp[0]); return -1; } if (resp[1] != Resp_STK_OK) { pmsg_error("in sync, not OK, resp=0x%02x\n", (unsigned int) resp[1]); return -1; } return 0; } static int xbee_open(PROGRAMMER *pgm, const char *port) { union pinfo pinfo; strcpy(pgm->port, port); pinfo.serialinfo.baud = pgm->baudrate; pinfo.serialinfo.cflags = SERIAL_8N1; /* Wireless is lossier than normal serial */ serial_recv_timeout = 1000; serdev = &xbee_serdev_frame; if (serial_open(port, pinfo, &pgm->fd) == -1) { return -1; } xbeedev_setresetpin(&pgm->fd, PDATA(pgm)->xbeeResetPin); /* Clear DTR and RTS */ serial_set_dtr_rts(&pgm->fd, 0); usleep(250*1000); /* Set DTR and RTS back to high */ serial_set_dtr_rts(&pgm->fd, 1); usleep(50*1000); /* * At this point stk500_drain() and stk500_getsync() calls would * normally be made. But given that we have a transport layer over * the serial command stream, the drain and repeated STK_GET_SYNC * requests are not very helpful. Instead, skip the draining * entirely, and issue the STK_GET_SYNC ourselves. */ if (xbee_getsync(pgm) < 0) return -1; return 0; } static void xbee_close(PROGRAMMER *pgm) { struct XBeeBootSession *xbs = xbeebootsession(&pgm->fd); /* * NB: This request is for the target device, not the locally * connected serial device. */ serial_set_dtr_rts(&pgm->fd, 0); /* * We have tweaked a few settings on the XBee, including the RTS * mode and the reset pin's configuration. Do a soft full reset, * restoring the device to its normal power-on settings. * * Note that this DOES mean that the remote XBee will be * uncontactable until it has restarted and re-established * communications on the mesh. */ if (!xbs->directMode) { const int rc = sendAT(xbs, "AT FR", 'F', 'R', -1); xbeeATError(rc); } pmsg_notice("statistics for FRAME_LOCAL requests - %s->XBee(local)\n", progname); xbeeStatsSummarise(&xbs->groupSummary[XBEE_STATS_FRAME_LOCAL]); pmsg_notice("statistics for FRAME_REMOTE requests - %s->XBee(local)->XBee(target)\n", progname); xbeeStatsSummarise(&xbs->groupSummary[XBEE_STATS_FRAME_REMOTE]); pmsg_notice("statistics for TRANSMIT requests - %s->XBee(local)->XBee(target)->XBeeBoot\n", progname); xbeeStatsSummarise(&xbs->groupSummary[XBEE_STATS_TRANSMIT]); pmsg_notice("statistics for RECEIVE requests - XBeeBoot->XBee(target)->XBee(local)->%s\n", progname); xbeeStatsSummarise(&xbs->groupSummary[XBEE_STATS_RECEIVE]); xbeedev_free(xbs); pgm->fd.pfd = NULL; } static int xbee_parseextparms(const PROGRAMMER *pgm, const LISTID extparms) { LNODEID ln; const char *extended_param; int rc = 0; for (ln = lfirst(extparms); ln; ln = lnext(ln)) { extended_param = ldata(ln); if (strncmp(extended_param, "xbeeresetpin=", 13 /*strlen("xbeeresetpin=")*/) == 0) { int resetpin; if (sscanf(extended_param, "xbeeresetpin=%i", &resetpin) != 1 || resetpin <= 0 || resetpin > 7) { pmsg_error("invalid xbeeresetpin '%s'\n", extended_param); rc = -1; continue; } PDATA(pgm)->xbeeResetPin = resetpin; continue; } pmsg_error("invalid extended parameter '%s'\n", extended_param); rc = -1; } return rc; } const char xbee_desc[] = "XBee Series 2 Over-The-Air (XBeeBoot)"; void xbee_initpgm(PROGRAMMER *pgm) { /* * This behaves like an Arduino, but with packet encapsulation of * the serial streams, XBee device management, and XBee GPIO for the * Auto-Reset feature. stk500.c sets PDATA(pgm)->xbeeResetPin */ stk500_initpgm(pgm); strncpy(pgm->type, "XBee", sizeof(pgm->type)); pgm->read_sig_bytes = xbee_read_sig_bytes; pgm->open = xbee_open; pgm->close = xbee_close; pgm->parseextparams = xbee_parseextparms; }