/* recv_fsm * * Attempt to process some data from the buffer located at 'data'. * * If a command is succcessfully read, then this returns the number of bytes * consumed from the buffer. If a partial command is present in the buffer, * but not enough to process yet, then this will return 0; the invoking * function should call it later when more data is available. If an error * occurs such that no further data can be handled from the connection, then * this will call close_conn on the connection and return -1. */ static int recv_fsm(struct tcp_pcb *pcb, uint8_t * data, int len) { uint8_t cmd = *data; int npoints; switch (ps_state) { case MAIN: switch (cmd) { case 'p': /* Prepare stream. */ if (dac_prepare() < 0) { return send_resp(pcb, RESP_NAK_INVL, cmd, 1); } else { return send_resp(pcb, RESP_ACK, cmd, 1); } case 'b': /* Make sure we have all of this packet... */ if (len < sizeof(struct begin_command)) return 0; struct begin_command *bc = (struct begin_command *)data; if (bc->point_rate > DAC_MAX_POINT_RATE) return send_resp(pcb, RESP_NAK_INVL, cmd, 1); // XXX set_low_water_mark(bc->low_water_mark); dac_set_rate(bc->point_rate); dac_start(); return send_resp(pcb, RESP_ACK, cmd, sizeof(struct begin_command)); case 'u': /* Update and Begin use the same packet format */ if (len < sizeof(struct begin_command)) return 0; struct begin_command *uc = (struct begin_command *)data; if (uc->point_rate > DAC_MAX_POINT_RATE) return send_resp(pcb, RESP_NAK_INVL, cmd, 1); dac_set_rate(uc->point_rate); // XXX set_low_water_mark(uc->low_water_mark); return send_resp(pcb, RESP_ACK, cmd, sizeof(struct begin_command)); case 'q': if (len < sizeof(struct queue_command)) return 0; struct queue_command *qc = (struct queue_command *)data; if (qc->point_rate > DAC_MAX_POINT_RATE) return send_resp(pcb, RESP_NAK_INVL, cmd, 1); dac_rate_queue(qc->point_rate); return send_resp(pcb, RESP_ACK, cmd, sizeof(struct queue_command)); case 'd': /* Data: switch into the DATA state to start reading * points into the buffer. */ if (len < sizeof(struct data_command)) return 0; struct data_command *h = (struct data_command *) data; if (h->npoints) { ps_state = DATA; ps_pointsleft = h->npoints; /* We'll send a response once we've read all * of the data. */ return sizeof(struct data_command); } else { /* 0-length data packets are legit. */ return send_resp(pcb, RESP_ACK, cmd, sizeof(struct data_command)); } case 's': /* Stop */ if (dac_get_state() == DAC_IDLE) { return send_resp(pcb, RESP_NAK_INVL, cmd, 1); } else { dac_stop(0); return send_resp(pcb, RESP_ACK, cmd, 1); } case 0: case 0xFF: /* Emergency-stop. */ le_estop(ESTOP_PACKET); return send_resp(pcb, RESP_ACK, cmd, 1); case 'c': /* Clear e-stop. */ le_estop_clear(ESTOP_CLEAR_ALL); if (le_get_state() == LIGHTENGINE_READY) return send_resp(pcb, RESP_ACK, cmd, 1); else return send_resp(pcb, RESP_NAK_ESTOP, cmd, 1); case '?': /* Ping */ return send_resp(pcb, RESP_ACK, cmd, 1); default: outputf("unknown cmd 0x%02x", cmd); return close_conn(pcb, CONNCLOSED_UNKNOWNCMD, -1); } return -1; case DATA: ASSERT_NOT_EQUAL(ps_pointsleft, 0); /* We can only write a complete point at a time. */ if (len < sizeof(struct dac_point)) return 0; /* How many bytes of data is it our business to write? */ npoints = len / sizeof(struct dac_point); if (npoints > ps_pointsleft) npoints = ps_pointsleft; /* How much do we have room for now? Note that dac_prepare * is a ring buffer, so it's OK if it returns less than the * number of points we have ready for it. We'll just have * the FSM invoke us again. */ int nready = dac_request(); packed_point_t *addr = dac_request_addr(); /* On the other hand, if the DAC isn't ready for *any* data, * then ignore the rest of this write command and NAK when * it's over. The FSM will take care of us... */ if (nready <= 0) { if (nready == 0) { outputf("overflow: wanted to write %d", npoints); } else { outputf("underflow: pl %d np %d r %d", ps_pointsleft, npoints, nready); } ps_state = DATA_ABORTING; /* Danger: goto. This could probably be structured * better... */ goto handle_aborted_data; } if (npoints > nready) npoints = nready; dac_point_t *pdata = (dac_point_t *)data; int i; for (i = 0; i < npoints; i++) { dac_pack_point(addr + i, pdata + i); } /* Let the DAC know we've given it more data */ dac_advance(npoints); ps_pointsleft -= npoints; if (!ps_pointsleft) { ps_state = MAIN; return send_resp(pcb, RESP_ACK, 'd', npoints * sizeof(struct dac_point)); } else { return (npoints * sizeof(struct dac_point)); } case DATA_ABORTING: ASSERT_NOT_EQUAL(ps_pointsleft, 0); /* We can only consume a complete point at a time. */ if (len < sizeof(struct dac_point)) return 0; /* How many points do we have? */ npoints = len / sizeof(struct dac_point); if (npoints > ps_pointsleft) npoints = ps_pointsleft; handle_aborted_data: ps_pointsleft -= npoints; if (!ps_pointsleft) { ps_state = MAIN; return send_resp(pcb, RESP_NAK_INVL, 'd', npoints * sizeof(struct dac_point)); } else { return (npoints * sizeof(struct dac_point)); } default: break; } panic("invalid state in recv_dfa"); return -1; }
void playback_refill() { dac_point_t *ptr = 0; int i; int dlen = dac_request(&ptr); /* Have we underflowed? */ if (dlen < 0) { if (le_get_state() != LIGHTENGINE_READY) return; outputf("*U*"); dac_prepare(); return; } /* If we don't have any more room... */ if (dlen == 0) { if (dac_get_state() == DAC_PREPARED) dac_start(); return; } switch (playback_src) { case SRC_ILDAPLAYER: if (!(playback_source_flags & ILDA_PLAYER_PLAYING)) break; outputf("%d", dlen); i = ilda_read_points(dlen, ptr); if (i < 0) { outputf("err: %d", i); playback_source_flags &= ~ILDA_PLAYER_PLAYING; } else if (i == 0) { ilda_reset_file(); if (playback_source_flags & ILDA_PLAYER_REPEAT) { outputf("rep"); } else { outputf("done"); /* If the whole file didn't fit in the * buffer, we may have to start it now. */ dlen = 0; playback_source_flags &= ~ILDA_PLAYER_PLAYING; } } else { dac_advance(i); } break; default: panic("bad playback source"); } /* If the buffer is nearly full, start it up */ if (dlen < 100 && dac_get_state() == DAC_PREPARED) dac_start(); }