- struct dev_context *devc = sdi->priv;
- const struct rdtech_um_profile *p = devc->profile;
- int len;
-
- /* Serial data arrived. */
- while (devc->buflen < UM_POLL_LEN) {
- len = serial_read_nonblocking(serial, devc->buf + devc->buflen, 1);
- if (len < 1)
- return;
-
- devc->buflen++;
-
- /* Check if the poll model ID matches the profile. */
- if (devc->buflen == 2 && RB16(devc->buf) != p->model_id) {
- sr_warn("Illegal model ID in poll response (0x%.4" PRIx16 "),"
- " skipping 1 byte.",
- RB16(devc->buf));
- devc->buflen--;
- memmove(devc->buf, devc->buf + 1, devc->buflen);
- }
+ struct dev_context *devc;
+ const struct rdtech_um_profile *p;
+ uint8_t *rdptr;
+ size_t space, rcvd, rdlen;
+ int ret;
+ gboolean do_sync_check;
+ size_t sync_len, sync_idx;
+
+ /*
+ * Receive data became available. Drain the serial transport.
+ * Grab incoming data in as large a chunk as possible. Also
+ * copes with zero receive data length, as some transports may
+ * trigger periodically without data really being available.
+ */
+ devc = sdi->priv;
+ p = devc->profile;
+ rdptr = &devc->buf[devc->buflen];
+ space = sizeof(devc->buf) - devc->buflen;
+ do_sync_check = FALSE;
+ sync_len = sizeof(uint16_t);
+ while (space) {
+ ret = serial_read_nonblocking(serial, rdptr, space);
+ if (ret < 0)
+ return SR_ERR_IO;
+ rcvd = (size_t)ret;
+ if (rcvd == 0)
+ break;
+ if (rcvd > space)
+ return SR_ERR_BUG;
+ if (devc->buflen < sync_len)
+ do_sync_check = TRUE;
+ devc->buflen += rcvd;
+ if (devc->buflen < sync_len)
+ do_sync_check = FALSE;
+ space -= rcvd;
+ rdptr += rcvd;
+ }
+
+ /*
+ * Synchronize to the packetized input stream. Check the model
+ * ID at the start of receive data. Which is a weak condition,
+ * but going out of sync should be rare, and repeated attempts
+ * to synchronize should eventually succeed. Try to rate limit
+ * the emission of diagnostics messages. (Re-)run this logic
+ * at the first reception which makes enough data available,
+ * but not during subsequent accumulation of more data.
+ *
+ * Reducing redundancy in the implementation at the same time as
+ * increasing robustness would involve the creation of a checker
+ * routine, which just gets called for every byte position until
+ * it succeeds. Similar to what a previous implementation of the
+ * read loop did, which was expensive on the serial transport.
+ */
+ sync_idx = 0;
+ if (do_sync_check && read_u16be(&devc->buf[sync_idx]) != p->model_id)
+ sr_warn("Unexpected response data, trying to synchronize.");
+ while (do_sync_check) {
+ if (sync_idx + sync_len >= devc->buflen)
+ break;
+ if (read_u16be(&devc->buf[sync_idx]) == p->model_id)
+ break;
+ sync_idx++;
+ }
+ if (do_sync_check && sync_idx) {
+ sr_dbg("Skipping %zu bytes in attempt to sync.", sync_idx);
+ sync_len = devc->buflen - sync_idx;
+ if (sync_len)
+ memmove(&devc->buf[0], &devc->buf[sync_idx], sync_len);
+ devc->buflen -= sync_idx;