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:
parent
bda8fc98b1
commit
2546b05c80
|
@ -91,6 +91,78 @@ static int hw_init(void)
|
||||||
return SR_OK;
|
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)
|
static GSList *lcd14_scan(const char *conn, const char *serialcomm)
|
||||||
{
|
{
|
||||||
struct sr_dev_inst *sdi;
|
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_probe *probe;
|
||||||
struct sr_serial_dev_inst *serial;
|
struct sr_serial_dev_inst *serial;
|
||||||
GSList *devices;
|
GSList *devices;
|
||||||
int i, len, retry, good_packets = 0, dropped;
|
int dropped, ret;
|
||||||
uint8_t buf[128], *b;
|
size_t len;
|
||||||
|
uint8_t buf[128];
|
||||||
|
|
||||||
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
if (!(serial = sr_serial_dev_inst_new(conn, serialcomm)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -111,8 +184,6 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
|
||||||
sr_info("Probing port %s readonly.", conn);
|
sr_info("Probing port %s readonly.", conn);
|
||||||
|
|
||||||
drvc = di->priv;
|
drvc = di->priv;
|
||||||
b = buf;
|
|
||||||
retry = 0;
|
|
||||||
devices = NULL;
|
devices = NULL;
|
||||||
serial_flush(serial);
|
serial_flush(serial);
|
||||||
|
|
||||||
|
@ -121,48 +192,36 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
|
||||||
* periodically, so the best we can do is check if the packets match
|
* periodically, so the best we can do is check if the packets match
|
||||||
* the expected format.
|
* the expected format.
|
||||||
*/
|
*/
|
||||||
while (!devices && retry < 3) {
|
|
||||||
retry++;
|
|
||||||
|
|
||||||
/* Let's get a bit of data and see if we can find a packet. */
|
/* Let's get a bit of data and see if we can find a packet. */
|
||||||
len = sizeof(buf);
|
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 treat our buffer like a stream, and find any
|
ret = serial_stream_detect(serial, buf, &len, FS9721_PACKET_SIZE,
|
||||||
* valid packets */
|
sr_fs9721_packet_valid, 1000, 2400);
|
||||||
for (i = 0; i < len - FS9721_PACKET_SIZE + 1;) {
|
if (ret != SR_OK)
|
||||||
if (!sr_fs9721_packet_valid(&buf[i])) {
|
goto scan_cleanup;
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
good_packets++;
|
|
||||||
i += FS9721_PACKET_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we dropped more than two packets worth of data,
|
* If we dropped more than two packets worth of data, something is
|
||||||
* something is wrong.
|
* 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 - (good_packets * FS9721_PACKET_SIZE);
|
dropped = len - FS9721_PACKET_SIZE;
|
||||||
if (dropped > 2 * FS9721_PACKET_SIZE)
|
if (dropped > 2 * FS9721_PACKET_SIZE) {
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Let's see if we have anything good. */
|
sr_warn("Had to drop too much data");
|
||||||
if (good_packets == 0)
|
}
|
||||||
continue;
|
|
||||||
|
|
||||||
sr_info("Found device on port %s.", conn);
|
sr_info("Found device on port %s.", conn);
|
||||||
|
|
||||||
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "TekPower",
|
if (!(sdi = sr_dev_inst_new(0, SR_ST_INACTIVE, "TekPower",
|
||||||
"TP4000ZC", "")))
|
"TP4000ZC", "")))
|
||||||
return NULL;
|
goto scan_cleanup;
|
||||||
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
if (!(devc = g_try_malloc0(sizeof(struct dev_context)))) {
|
||||||
sr_err("Device context malloc failed.");
|
sr_err("Device context malloc failed.");
|
||||||
return NULL;
|
goto scan_cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
devc->serial = serial;
|
devc->serial = serial;
|
||||||
|
@ -170,14 +229,14 @@ static GSList *lcd14_scan(const char *conn, const char *serialcomm)
|
||||||
sdi->priv = devc;
|
sdi->priv = devc;
|
||||||
sdi->driver = di;
|
sdi->driver = di;
|
||||||
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
if (!(probe = sr_probe_new(0, SR_PROBE_ANALOG, TRUE, "P1")))
|
||||||
return NULL;
|
goto scan_cleanup;
|
||||||
sdi->probes = g_slist_append(sdi->probes, probe);
|
sdi->probes = g_slist_append(sdi->probes, probe);
|
||||||
drvc->instances = g_slist_append(drvc->instances, sdi);
|
drvc->instances = g_slist_append(drvc->instances, sdi);
|
||||||
devices = g_slist_append(devices, sdi);
|
devices = g_slist_append(devices, sdi);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
scan_cleanup:
|
||||||
serial_close(serial);
|
serial_close(serial);
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue