/* Process that reads from the USB port and writes to the UART port */ static void NORETURN usb_serial_process(void) { iptr_t type = proc_currentUserData(); KFile *in_fd = (type == USB_TO_SERIAL) ? &usb_port.fd : &ser_port.fd; KFile *out_fd = (type == USB_TO_SERIAL) ? &ser_port.fd : &usb_port.fd; while (1) { int c; c = kfile_getc(in_fd); if (UNLIKELY(c == EOF)) { kfile_clearerr(in_fd); continue; } kfile_putc(c, out_fd); /* * Toggle the STAT LED when some data passes through the * usb-seral link */ LED_TOGGLE(); } }
/** * Receive the tag message from channel, and if * the tag is good put the converted string into given buffer. * The fuction return the len of found tag string, otherwise EOF. */ int keytag_recv(struct TagPacket *pkt, uint8_t *tag, size_t len) { int c; /* Get all chars from buffer */ while ((c = kfile_getc(pkt->tag)) != EOF) { /* Search for STX char in received chars */ if (c == TAG_STX) { /* When STX is found a new packet begins */ if (pkt->sync) LOG_WARN("TAG double sync!\n"); keytag_clearPkt(pkt); pkt->sync = true; } else if (pkt->sync) { /* Check for end of packet */ if (c == TAG_ETX) { /* Terminate the tag string */ size_t tag_len = MIN(len, pkt->len); /* Save read tag */ memcpy(tag, pkt->buf, tag_len); pkt->sync = false; return tag_len; } else { /* Check for buffer overflow */ if (pkt->len >= CONFIG_TAG_MAX_LEN) { LOG_ERR("TAG buffer overflow\n"); pkt->sync = false; } else { /* Add every char after STX to tag reading buffer */ if (pkt->sync) { pkt->buf[pkt->len] = c; pkt->len++; } } } } } if (kfile_error(pkt->tag) != 0) { LOG_ERR("Error %04x\n", kfile_error(pkt->tag)); kfile_clearerr(pkt->tag); } return EOF; }
/** * Discard input to resynchronize with remote end. * * Discard incoming data until the kfile_getc stops receiving * characters for at least \a delay milliseconds. * * \note If the timeout occur, we reset the error before to * quit. */ void kfile_resync(KFile *fd, mtime_t delay) { ticks_t start_time = timer_clock(); for(;;) { if(kfile_getc(fd) != EOF) start_time = timer_clock(); if ((timer_clock() - start_time) > ms_to_ticks(delay)) { kfile_clearerr(fd); break; } } }
static void NORETURN hadarp_process(void) { char buf[16]; while (1) { if (sig_check(SIG_POLIFEMO)) kfile_putc('@', &ser.fd); if (kfile_gets(&ser.fd, buf, sizeof(buf)) == EOF) { hadarp_cnt = -1; kfile_clearerr(&ser.fd); continue; } if (buf[0] == '\0') { /* Discard empty strings */ continue; } char *endptr = buf; int hadarp_raw = strtol(buf, &endptr, 10); if (*endptr != '\0' || hadarp_raw > 10000 || hadarp_raw < 0) hadarp_cnt = -1; else { /* Compensate for geiger tube dead time */ float cps = hadarp_raw / 60.0; hadarp_cnt = ABS(cps / (1.0 - (cps * 1.9e-4)) * 60.0 + 0.5); } LOG_INFO("HADARP cnt:%d\n", hadarp_cnt); } }
/** * Try to read a packet from the pocketBus. * \return true if a packet is received, false otherwise. */ bool pocketbus_recv(struct PocketBusCtx *ctx, struct PocketMsg *msg) { int c; /* Process incoming characters until buffer is not empty */ while ((c = kfile_getc(ctx->fd)) != EOF) { /* Look for STX char */ if (c == POCKETBUS_STX && !ctx->escape) { /* When an STX is found, inconditionally start a new packet */ if (ctx->sync) kprintf("pocketBus double sync!\n"); ctx->sync = true; ctx->len = 0; rotating_init(&ctx->in_cks); continue; } if (ctx->sync) { /* Handle escape mode */ if (c == POCKETBUS_ESC && !ctx->escape) { ctx->escape = true; continue; } /* Handle message end */ if (c == POCKETBUS_ETX && !ctx->escape) { ctx->sync = false; /* Check minimum size */ if (ctx->len < sizeof(PocketBusHdr) + sizeof(rotating_t)) { kprintf("pocketBus short pkt!\n"); continue; } /* Remove checksum bytes from packet len */ ctx->len -= sizeof(rotating_t); /* Compute checksum */ rotating_update(ctx->buf, ctx->len, &ctx->in_cks); uint8_t cks_h = *(ctx->buf + ctx->len); uint8_t cks_l = *(ctx->buf + ctx->len + 1); rotating_t recv_cks = (cks_h << 8) | cks_l; /* Checksum check */ if (recv_cks == ctx->in_cks) { PocketBusHdr *hdr = (PocketBusHdr *)ctx; /* Check packet version */ if (hdr->ver == POCKETBUS_VER) { /* Packet received, set msg fields */ msg->payload = ctx->buf + sizeof(PocketBusHdr); msg->addr = be16_to_cpu(hdr->addr); msg->len = ctx->len - sizeof(PocketBusHdr); msg->ctx = ctx; return true; } else { kprintf("pocketBus version mismatch, here[%d], there[%d]\n", POCKETBUS_VER, hdr->ver); continue; } } else { kprintf("pocketBus cks error, here[%04X], there[%04X]\n", ctx->in_cks, recv_cks); continue; } } ctx->escape = false; /* Check buffer overflow: simply ignore received data and go to unsynced state. */ if (ctx->len >= CONFIG_POCKETBUS_BUFLEN) { kprintf("pocketBus buffer overflow\n"); ctx->sync = false; continue; } /* Put received data in the buffer */ ctx->buf[ctx->len] = c; ctx->len++; } } /* * Check stream status. */ if (kfile_error(ctx->fd)) { LOG_ERR("fd status[%04X]\n", kfile_error(ctx->fd)); kfile_clearerr(ctx->fd); } return false; }
/** * \brief Receive a file using the XModem protocol. * * \param ch Channel to use for transfer * \param fd Destination file * * \note This function allocates a large amount of stack (\see XM_BUFSIZE). */ bool xmodem_recv(KFile *ch, KFile *fd) { char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ int c, i, blocksize; int blocknr = 0, last_block_done = 0, retries = 0; char *buf; uint8_t checksum; uint16_t crc; bool purge = false; bool usecrc = true; LOG_INFO("Starting Transfer...\n"); purge = true; kfile_clearerr(ch); /* Send initial NAK to start transmission */ for(;;) { if (XMODEM_CHECK_ABORT) { kfile_putc(XM_CAN, ch); kfile_putc(XM_CAN, ch); LOG_INFO("Transfer aborted\n"); return false; } /* * Discard incoming input until a timeout occurs, then send * a NAK to the transmitter. */ if (purge) { purge = false; if (kfile_error(ch)) { LOG_ERR("Retries %d\n", retries); } kfile_resync(ch, 200); retries++; if (retries >= CONFIG_XMODEM_MAXRETRIES) { kfile_putc(XM_CAN, ch); kfile_putc(XM_CAN, ch); LOG_INFO("Transfer aborted\n"); return false; } /* Transmission start? */ if (blocknr == 0) { if (retries < CONFIG_XMODEM_MAXCRCRETRIES) { LOG_INFO("Request Tx (CRC)\n"); kfile_putc(XM_C, ch); } else { /* Give up with CRC and fall back to checksum */ usecrc = false; LOG_INFO("Request Tx (BCC)\n"); kfile_putc(XM_NAK, ch); } } else kfile_putc(XM_NAK, ch); } switch (kfile_getc(ch)) { #if XM_BUFSIZE >= 1024 case XM_STX: /* Start of header (1024-byte block) */ blocksize = 1024; goto getblock; #endif case XM_SOH: /* Start of header (128-byte block) */ blocksize = 128; /* Needed to avoid warning if XM_BUFSIZE < 1024 */ getblock: /* Get block number */ c = kfile_getc(ch); /* Check complemented block number */ if ((~c & 0xff) != kfile_getc(ch)) { LOG_WARN("Bad blk (%d)\n", c); purge = true; break; } /* Determine which block is being sent */ if (c == (blocknr & 0xff)) { /* Last block repeated */ LOG_INFO("Repeat blk %d\n", blocknr); } else if (c == ((blocknr + 1) & 0xff)) { /* Next block */ LOG_INFO("Recv blk %d\n", ++blocknr); } else { /* Sync lost */ LOG_WARN("Sync lost (%d/%d)\n", c, blocknr); purge = true; break; } buf = block_buffer; /* Reset pointer to start of buffer */ checksum = 0; crc = 0; for (i = 0; i < blocksize; i++) { if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } /* Store in buffer */ *buf++ = (char)c; /* Calculate block checksum or CRC */ if (usecrc) crc = UPDCRC16(c, crc); else checksum += (char)c; } if (purge) break; /* Get the checksum byte or the CRC-16 MSB */ if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } if (usecrc) { crc = UPDCRC16(c, crc); /* Get CRC-16 LSB */ if ((c = kfile_getc(ch)) == EOF) { purge = true; break; } crc = UPDCRC16(c, crc); if (crc) { LOG_ERR("Bad CRC: %04x\n", crc); purge = true; break; } } /* Compare the checksum */ else if (c != checksum) { LOG_ERR("Bad sum: %04x/%04x\n", checksum, c); purge = true; break; } /* * Avoid flushing the same block twice. * This could happen when the sender does not receive our * acknowledge and resends the same block. */ if (last_block_done < blocknr) { /* Call user function to flush the buffer */ if (kfile_write(fd, block_buffer, blocksize)) { /* Acknowledge block and clear error counter */ kfile_putc(XM_ACK, ch); retries = 0; last_block_done = blocknr; } else { /* User callback failed: abort transfer immediately */ retries = CONFIG_XMODEM_MAXRETRIES; purge = true; } } break; case XM_EOT: /* End of transmission */ kfile_putc(XM_ACK, ch); LOG_INFO("Transfer completed\n"); return true; case EOF: /* Timeout or serial error */ purge = true; break; default: LOG_INFO("Skipping garbage\n"); purge = true; break; } } /* End forever */ }
/** * \brief Transmit some data using the XModem protocol. * * \param ch Channel to use for transfer * \param fd Source file * * \note This function allocates a large amount of stack for * the XModem transfer buffer (\see XM_BUFSIZE). */ bool xmodem_send(KFile *ch, KFile *fd) { char block_buffer[XM_BUFSIZE]; /* Buffer to hold a block of data */ size_t size = -1; int blocknr = 1, retries = 0, c, i; bool proceed, usecrc = false; uint16_t crc; uint8_t sum; /* * Reading a block can be very slow, so we read the first block early * to avoid receiving double XM_C char. * This could happen if we check for XM_C and then read the block, giving * the receiving device time to send another XM_C char misinterpretating * the blocks sent. */ size = kfile_read(fd, block_buffer, XM_BUFSIZE); kfile_clearerr(ch); LOG_INFO("Wait remote host\n"); for(;;) { proceed = false; do { if (XMODEM_CHECK_ABORT) return false; switch (c = kfile_getc(ch)) { case XM_NAK: LOG_INFO("Resend blk %d\n", blocknr); proceed = true; break; case XM_C: if (c == XM_C) { LOG_INFO("Tx start (CRC)\n"); usecrc = true; } else { LOG_INFO("Tx start (BCC)\n"); } proceed = true; break; case XM_ACK: /* End of transfer? */ if (!size) return true; /* Call user function to read in one block */ size = kfile_read(fd, block_buffer, XM_BUFSIZE); LOG_INFO("Send blk %d\n", blocknr); blocknr++; retries = 0; proceed = true; break; case EOF: kfile_clearerr(ch); retries++; LOG_INFO("Retries %d\n", retries); if (retries <= CONFIG_XMODEM_MAXRETRIES) break; /* falling through! */ case XM_CAN: LOG_INFO("Transfer aborted\n"); return false; default: LOG_INFO("Skipping garbage\n"); break; } } while (!proceed); if (!size) { kfile_putc(XM_EOT, ch); continue; } /* Pad block with 0xFF if it's partially full */ memset(block_buffer + size, 0xFF, XM_BUFSIZE - size); /* Send block header (STX, blocknr, ~blocknr) */ #if XM_BUFSIZE == 128 kfile_putc(XM_SOH, ch); #else kfile_putc(XM_STX, ch); #endif kfile_putc(blocknr & 0xFF, ch); kfile_putc(~blocknr & 0xFF, ch); /* Send block and compute its CRC/checksum */ sum = 0; crc = 0; for (i = 0; i < XM_BUFSIZE; i++) { kfile_putc(block_buffer[i], ch); crc = UPDCRC16(block_buffer[i], crc); sum += block_buffer[i]; } /* Send CRC/Checksum */ if (usecrc) { kfile_putc(crc >> 8, ch); kfile_putc(crc & 0xFF, ch); } else kfile_putc(sum, ch); }
void protocol_run(KFile *fd) { /** * \todo to be removed, we could probably access the serial FIFO * directly */ static char linebuf[80]; if (!interactive) { kfile_gets(fd, linebuf, sizeof(linebuf)); // reset serial port error anyway kfile_clearerr(fd); // check message minimum length if (linebuf[0]) { /* If we enter lines beginning with sharp(#) they are stripped out from commands */ if(linebuf[0] != '#') { if (linebuf[0] == 0x1B && linebuf[1] == 0x1B) // ESC { interactive = true; kfile_printf(fd, "Entering interactive mode\r\n"); } else { protocol_parse(fd, linebuf); } } } } else { const char *buf; /* * Read a line from serial. We use a temporary buffer * because otherwise we would have to extract a message * from the port immediately: there might not be any * available, and one might get free while we read * the line. We also add a fake ID at the start to * fool the parser. */ buf = rl_readline(&rl_ctx); /* If we enter lines beginning with sharp(#) they are stripped out from commands */ if(buf && buf[0] != '#') { if (buf[0] != '\0') { // exit special case to immediately change serial input if (!strcmp(buf, "exit") || !strcmp(buf, "quit")) { rl_clear_history(&rl_ctx); kfile_printf(fd, "Leaving interactive mode...\r\n"); interactive = FORCE_INTERACTIVE; } else { //TODO: remove sequence numbers linebuf[0] = '0'; linebuf[1] = ' '; strncpy(linebuf + 2, buf, sizeof(linebuf) - 3); linebuf[sizeof(linebuf) - 1] = '\0'; protocol_parse(fd, linebuf); } } } } }