tekpower-dmm: Improve serial detection.

Serial detection was using serial_readline, which stripped carriage
return from the packets. This made for a very unreliable detection
mechanism.

Switch to a timeout-based detection mechanism that parses the data
as it comes in. This also allows us to stop parsing once we found
our first valid packet.

Signed-off-by: Alexandru Gagniuc <mr.nuke.me@gmail.com>
This commit is contained in:
Alexandru Gagniuc 2012-11-02 10:53:06 -05:00 committed by Uwe Hermann
parent bda8fc98b1
commit 2546b05c80
1 changed files with 113 additions and 54 deletions

View File

@ -91,6 +91,78 @@ static int hw_init(void)
return SR_OK;
}
typedef gboolean (*packet_valid_t)(const uint8_t *buf);
/**
* Try to find a valid packet in a serial data stream
*
* @param fd File descriptor of the serial port.
* @param buf Buffer containing the bytes to write.
* @param count Size of the buffer.
* @param packet_size Size, in bytes, of a valid packet
* @param is_valid callback that assesses whether the packet is valid or not
* @param timeout_ms the timeout after which, if no packet is detected, to abort
* scanning.
* @param baudrate the baudrate of the serial port. This parameter is not
* critical, but it helps fine tune the serial port polling
* delay
*
* @return SR_OK if a valid packet is found within he given timeout,
* SR_ERR upon failure.
*/
static int serial_stream_detect(struct sr_serial_dev_inst *serial,
uint8_t *buf, size_t *buflen,
const size_t packet_size,
packet_valid_t is_valid,
uint64_t timeout_ms, int baudrate)
{
uint64_t start;
uint64_t time;
uint64_t byte_delay_us;
size_t ibuf, i;
int len;
const size_t maxlen = *buflen;
if(maxlen < (packet_size << 1) ) {
sr_err("Buffer size must be at least twice the packet size");
return SR_ERR;
}
timeout_ms *= 1000;
/* Assume 8n1 transmission. That is 10 bits for every byte */
byte_delay_us = 10000000 / baudrate;
start = g_get_monotonic_time();
i = ibuf = len = 0;
while (ibuf < maxlen) {
len = serial_read(serial, &buf[ibuf], 1);
if (len > 0)
ibuf+= len;
if ((ibuf - i) >= packet_size) {
/* We have at least a packet's worth of data */
if (is_valid(&buf[i])) {
time = g_get_monotonic_time()-start;
time /= 1000;
sr_spew("Serial detection took %li ms", time);
*buflen = ibuf;
return SR_OK;
}
/* Not a valid packet; continue searching */
i++;
}
if (g_get_monotonic_time() - start > timeout_ms) {
/* Timeout */
sr_warn("Serial detection timeout");
break;
}
g_usleep(byte_delay_us);
}
*buflen = ibuf;
return SR_ERR;
}
static GSList *lcd14_scan(const char *conn, const char *serialcomm)
{
struct sr_dev_inst *sdi;
@ -99,8 +171,9 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
struct sr_probe *probe;
struct sr_serial_dev_inst *serial;
GSList *devices;
int i, len, retry, good_packets = 0, dropped;
uint8_t buf[128], *b;
int dropped, ret;
size_t len;
uint8_t buf[128];
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
return NULL;
@ -111,8 +184,6 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
sr_info("Probing port %s readonly.", conn);
drvc = di->priv;
b = buf;
retry = 0;
devices = NULL;
serial_flush(serial);
@ -121,63 +192,51 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
* periodically, so the best we can do is check if the packets match
* the expected format.
*/
while (!devices && retry < 3) {
retry++;
/* Let's get a bit of data and see if we can find a packet. */
len = sizeof(buf);
serial_readline(serial, (char **)&b, &len, 500);
if ((len == 0) || (len < FS9721_PACKET_SIZE)) {
/* Not enough data received, is the DMM connected? */
continue;
}
/* Let's get a bit of data and see if we can find a packet. */
len = sizeof(buf);
/* Let's treat our buffer like a stream, and find any
* valid packets */
for (i = 0; i < len - FS9721_PACKET_SIZE + 1;) {
if (!sr_fs9721_packet_valid(&buf[i])) {
i++;
continue;
}
good_packets++;
i += FS9721_PACKET_SIZE;
}
ret = serial_stream_detect(serial, buf, &len, FS9721_PACKET_SIZE,
sr_fs9721_packet_valid, 1000, 2400);
if (ret != SR_OK)
goto scan_cleanup;
/*
* If we dropped more than two packets worth of data,
* something is wrong.
*/
dropped = len - (good_packets * FS9721_PACKET_SIZE);
if (dropped > 2 * FS9721_PACKET_SIZE)
continue;
/*
* If we dropped more than two packets worth of data, something is
* wrong. We shouldn't quit however, since the dropped bytes might be
* just zeroes at the beginning of the stream. Those can occur as a
* combination of the nonstandard cable that ships with this device and
* the serial port or USB to serial adapter.
*/
dropped = len - FS9721_PACKET_SIZE;
if (dropped > 2 * FS9721_PACKET_SIZE) {
/* Let's see if we have anything good. */
if (good_packets == 0)
continue;
sr_info("Found device on port %s.", conn);
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "TekPower",
"TP4000ZC", "")))
return NULL;
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
sr_err("Device context malloc failed.");
return NULL;
}
devc->serial = serial;
sdi->priv = devc;
sdi->driver = di;
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
return NULL;
sdi->probes = g_slist_append(sdi->probes, probe);
drvc->instances = g_slist_append(drvc->instances, sdi);
devices = g_slist_append(devices, sdi);
break;
sr_warn("Had to drop too much data");
}
sr_info("Found device on port %s.", conn);
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "TekPower",
"TP4000ZC", "")))
goto scan_cleanup;
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
sr_err("Device context malloc failed.");
goto scan_cleanup;
}
devc->serial = serial;
sdi->priv = devc;
sdi->driver = di;
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
goto scan_cleanup;
sdi->probes = g_slist_append(sdi->probes, probe);
drvc->instances = g_slist_append(drvc->instances, sdi);
devices = g_slist_append(devices, sdi);
scan_cleanup:
serial_close(serial);
return devices;
}