int rr_dev_resend (rr_dev dev, unsigned long lineno, const char *reply, size_t nbytes) { int resent = 0; blocknode *node; /* sent cache slot 0 is most recent */ while (1) { if (!(node = rr_dev_pop_from_queue (dev, RR_PRIO_SENTCACHE))) break; rr_dev_log (dev, RR_DEBUG_HIGH, "pop sent node line %d '%s' (%d bytes)\n", (int)node->line, node->block, node->blocksize); if (node->line >= lineno) { rr_dev_prepend_to_queue (dev, RR_PRIO_RESEND, node); resent++; } else { /* put it back and look elsewhere */ rr_dev_prepend_to_queue (dev, RR_PRIO_SENTCACHE, node); break; } } if (resent == 0) { /* Perhaps line is in the resend queue, and we got an: * rs: 3 * rs: 6 * type sequence so try peel forward the resend queue. */ while (1) { if (!(node = rr_dev_pop_from_queue (dev, RR_PRIO_RESEND))) break; rr_dev_log (dev, RR_DEBUG_HIGH, "pop resend node line %d '%s' (%d bytes)\n", (int)node->line, node->block, node->blocksize); if (node->line < lineno) { rr_dev_prepend_to_queue (dev, RR_PRIO_SENTCACHE, node); resent++; } else { /* put it back and give up */ rr_dev_prepend_to_queue (dev, RR_PRIO_RESEND, node); break; } } if (resent == 0) { rr_dev_log (dev, RR_DEBUG_ALWAYS, "re-send request for unknown (too old) line %ld from cache size %d\n", lineno, dev->sendsize[RR_PRIO_SENTCACHE]); rr_dev_emit_error (dev, RR_E_UNCACHED_RESEND, reply, nbytes); } } dev->send_next = 1; dev->wait_wr_cb (dev, 1, dev->wait_wr_cl); return 0; }
void rr_dev_reset (rr_dev dev) { empty_buffers (dev); rr_dev_log (dev, RR_DEBUG_MEDIUM, "reset dev size to zero\n"); rr_dev_reset_lineno (dev); dev->recvbuf_fill = 0; }
static void debug_log_block (rr_dev dev, const char *block, int nbytes) { char buffer[4096], *p; int num = nbytes >= 4096 ? 4095 : nbytes; strncpy (buffer, block, num); buffer[num] = '\0'; if ((p = strchr (buffer, '\n'))) *p = '\0'; if ((p = strchr (buffer, '\r'))) *p = '\0'; rr_dev_log (dev, RR_DEBUG_MEDIUM, "API enqueue of '%s' queue size %d\n", buffer, rr_dev_buffered_lines (dev)); }
int rr_dev_open (rr_dev dev, const char *port, long speed) { char *error = NULL; dev->fd = serial_open (port, speed, &error); if (SERIAL_INVALID_CHECK(dev->fd) < 0) { rr_dev_log (dev, RR_DEBUG_ALWAYS, "Failed to open device %s", error ? error : "<no error>"); fprintf (stderr, "%s\n", error ? error : "<null>"); return -1; } else return 0; }
void rr_dev_handle_ok (rr_dev dev) { int buffered = rr_dev_buffered_lines (dev); /* Send as many commands as we get ok's */ if (dev->init_send_count > 0) dev->init_send_count--; dev->send_next = 1; if (buffered < dev->dev_cmdqueue_size) { rr_dev_log (dev, RR_DEBUG_MEDIUM, "request more %d < %d\n", buffered, dev->dev_cmdqueue_size); dev->more_cb (dev, dev->more_cl); } dev->wait_wr_cb (dev, 1, dev->wait_wr_cl); if (dev->debug_output > RR_DEBUG_ALWAYS) { /* Check the sendsize accounts add up */ int i = 0; for (i = 0; i < RR_PRIO_COUNT; ++i) { blocknode *p; int count = 0; for (p = dev->sendhead[i]; p; p = p->next) { if (!p->next && dev->sendtail[i] != p) rr_dev_log (dev, RR_DEBUG_MEDIUM, "Error: queue (%d) broken tail pointer %p vs %p\n", i, p, dev->sendtail[i]); count++; } if (count != dev->sendsize[i]) rr_dev_log (dev, RR_DEBUG_MEDIUM, "Error: queue (%d) size mismatch: %d vs %d\n", i, count, dev->sendsize[i]); } } }
int fived_handle_reply (rr_dev dev, const char *reply, size_t nbytes) { if (!strncasecmp ("ok", reply, 2)) { rr_dev_handle_ok (dev); /* Parse values */ char *i; for (i = (char*)reply + 2; i < reply + nbytes; ++i) { if (isspace (*i)) continue; switch (toupper (*i)) { case 'T': float_reply (dev, &i, RR_NOZZLE_TEMP); break; case 'B': float_reply (dev, &i, RR_BED_TEMP); break; case 'C': break; case 'X': float_reply (dev, &i, RR_X_POS); break; case 'Y': float_reply (dev, &i, RR_Y_POS); break; case 'Z': float_reply (dev, &i, RR_Z_POS); break; case 'E': float_reply (dev, &i, RR_E_POS); break; default: break; //return rr_dev_emit_error (dev, RR_E_UNKNOWN_REPLY, reply, nbytes); } } return 0; } else if (!strncasecmp ("rs", reply, 2) || !strncasecmp ("resend", reply, 6)) { /* check where the line number starts */ size_t n_start = strcspn (reply, "123456789"); if (n_start) { long long lineno = strtoll (reply + n_start, NULL, 10); if (dev->sendsize[RR_PRIO_SENTCACHE] + dev->sendsize[RR_PRIO_RESEND] <= 1) { /* * oh dear - most likely we re-connected to a device that * had problems mid-flow, now we need to re-send the * line-number reset as if it was this line-no it is asking * for a re-send of, or there will be no peace */ rr_dev_log (dev, RR_DEBUG_ALWAYS, "resetting confused firmware with synthetic resend of line %d\n", lineno); rr_dev_enqueue_internal (dev, RR_PRIO_HIGH, "M110", 4, lineno); /* re-start the print */ rr_dev_resend (dev, 0, "synthetic restart", 16); return rr_dev_emit_error (dev, RR_E_UNSENT_RESEND, reply, nbytes); } return rr_dev_resend (dev, lineno, reply, nbytes); } else return rr_dev_emit_error (dev, RR_E_MALFORMED_RESEND_REQUEST, reply, nbytes); } else if (!strncmp("!!", reply, 2)) { return rr_dev_emit_error (dev, RR_E_HARDWARE_FAULT, reply, nbytes); } else if (!strncasecmp ("start", reply, 5)) { return rr_dev_handle_start (dev); } else return rr_dev_emit_error (dev, RR_E_UNKNOWN_REPLY, reply, nbytes); }
int rr_dev_handle_writable (rr_dev dev) { ssize_t result; if (dev->sendbuf_fill == 0) { if (dev->init_send_count <= 0 && !dev->send_next) { rr_dev_log (dev, RR_DEBUG_MEDIUM, "writeable - init count is %d, no send_next queue %d resend %d\n", dev->init_send_count, dev->dev_cmdqueue_size, dev->sendsize[RR_PRIO_RESEND]); /* wait until there is space in the device buffer and/or an ok */ dev->wait_wr_cb (dev, 0, dev->wait_wr_cl); return 0; } /* Last block is gone; prepare to send a new block */ int prio; blocknode *node = NULL; for (prio = RR_PRIO_COUNT - 1; prio >= 0; --prio) { if (dev->paused[prio]) continue; node = dev->sendhead[prio]; if (node) { /* We have a block to send! Get it ready. */ dev->bytes_sent = 0; result = fmtblock (dev, node); if (result < 0) { /* FIXME: This will confuse code expecting errno to be set */ return result; } if (result == 0) rr_dev_log (dev, RR_DEBUG_ALWAYS, "unusual error - nothing in block to write\n"); dev->send_next = 0; dev->sendbuf_fill = result; dev->sending_prio = prio; break; } } if (!node) { /* No data to write */ dev->wait_wr_cb (dev, 0, dev->wait_wr_cl); return 0; } } /* Perform write */ do { result = write (dev->fd, dev->sendbuf + dev->bytes_sent, dev->sendbuf_fill - dev->bytes_sent); } while (result < 0 && errno == EINTR); if (result < 0) return result; if (dev->opt_log_cb) dev->opt_log_cb (dev, RR_LOG_SEND, dev->sendbuf + dev->bytes_sent, dev->sendbuf_fill - dev->bytes_sent, dev->opt_log_cl); dev->bytes_sent += result; if (dev->bytes_sent == dev->sendbuf_fill) { /* We've sent the complete block. */ blocknode *node = rr_dev_pop_from_queue (dev, dev->sending_prio); if (node->line < 0) { node->line = dev->lineno; ++(dev->lineno); } /* Update sent cache */ assert (node->block != NULL); rr_dev_prepend_to_queue (dev, RR_PRIO_SENTCACHE, node); if (dev->sendsize[RR_PRIO_SENTCACHE] > (dev->sentcachesize * 3 / 2)) shrink_sentcache (dev); /* Indicate that we're ready for the next. */ dev->sendbuf_fill = 0; } return result; }
int rr_dev_handle_readable (rr_dev dev) { int i; /* Grow receive buffer if it's full */ if (dev->recvbuf_fill >= dev->recvbufsize) { dev->recvbufsize *= 2; dev->recvbuf = realloc (dev->recvbuf, dev->recvbufsize + 1); } ssize_t result; do { result = read (dev->fd, dev->recvbuf + dev->recvbuf_fill, dev->recvbufsize - dev->recvbuf_fill); } while (result < 0 && errno == EINTR); if (result < 0) return result; dev->recvbuf_fill += result; /* string terminate */ dev->recvbuf[dev->recvbuf_fill] = '\0'; /* validate the stream is ascii and of the correct length */ for (i = 0; i < dev->recvbuf_fill; i++) { if (dev->recvbuf[i] == '\0' || !isascii (dev->recvbuf[i])) { rr_dev_log (dev, RR_DEBUG_ALWAYS, "invalid char in recvbuf at char %d (0x%2x) full " "msg: '%s', truncating here", i, dev->recvbuf[i], dev->recvbuf); dev->recvbuf_fill = i; dev->recvbuf[dev->recvbuf_fill] = '\0'; break; } } /* spot control characters */ if (dev->recvbuf[0] == 1) { rr_dev_log (dev, RR_DEBUG_ALWAYS, "unusual - control character 0x%2x 0x%2x\n", dev->recvbuf[0], dev->recvbuf[1]); memmove (dev->recvbuf, dev->recvbuf + 2, dev->recvbuf_fill - 2); dev->recvbuf_fill -= 2; } /* Scan the buffer and shift it down if we detect a full command */ size_t reply_span, term_span; while (1) { /* How many non terminator chars and how many terminator chars after them */ reply_span = strcspn (dev->recvbuf, REPLY_TERMINATOR); term_span = strspn (dev->recvbuf + reply_span, REPLY_TERMINATOR); if (term_span > 0) { if (reply_span > 0) handle_reply (dev, dev->recvbuf, reply_span, term_span); /* else - perhaps a prepended \n having sent in reaction to \r previously */ size_t len = reply_span + term_span; assert (dev->recvbuf_fill >= len); dev->recvbuf_fill -= len; memmove (dev->recvbuf, dev->recvbuf + len, dev->recvbuf_fill + 1); continue; } break; } return 0; }
rr_dev rr_dev_create (rr_proto proto, /* how many commands can we push */ size_t dev_cmdqueue_size, /* callbacks cf. above */ rr_reply_fn reply_cb, void *reply_cl, rr_more_fn more_cb, void *more_cl, rr_error_fn error_cb, void *error_cl, /* notify when socket is writeable */ rr_wait_wr_fn wait_wr_cb, void *wait_wr_cl, /* optional (or NULL, NULL) */ rr_log_fn opt_log_cb, void *opt_log_cl) { unsigned int i; rr_dev dev; /* Required callbacks */ if (!reply_cb || !more_cb || !error_cb || !wait_wr_cb) return NULL; dev = malloc (sizeof(struct rr_dev_t)); if (!dev) return NULL; dev->proto = proto; dev->dev_cmdqueue_size = dev_cmdqueue_size; dev->reply_cb = reply_cb; dev->reply_cl = reply_cl; dev->more_cb = more_cb; dev->more_cl = more_cl; dev->error_cb = error_cb; dev->error_cl = error_cl; dev->wait_wr_cb = wait_wr_cb; dev->wait_wr_cl = wait_wr_cl; dev->opt_log_cb = opt_log_cb; dev->opt_log_cl = opt_log_cl; dev->lineno = 0; dev->fd = SERIAL_INVALID_INIT; dev->send_next = 0; dev->init_send_count = dev->dev_cmdqueue_size; dev->debug_output = 0; if (getenv("RR_DEBUG")) dev->debug_output = atoi (getenv("RR_DEBUG")); dev->sentcachesize = dev_cmdqueue_size * 4 + 64; for (i = 0; i < RR_PRIO_ALL_QUEUES; ++i) { dev->sendsize[i] = 0; dev->sendhead[i] = NULL; dev->sendtail[i] = NULL; dev->paused[i] = 0; } dev->sendbuf_fill = 0; dev->bytes_sent = 0; dev->recvbufsize = INITIAL_RECVBUFSIZE; dev->recvbuf = calloc (dev->recvbufsize + 1, sizeof (char)); dev->recvbuf_fill = 0; rr_dev_log (dev, RR_DEBUG_ALWAYS, "Connecting with libreprap\n"); return dev; }