]> sigrok.org Git - libsigrok.git/commitdiff
serial: add support for optional "RX chunk" callback
authorGerhard Sittig <redacted>
Wed, 3 Apr 2019 05:52:00 +0000 (07:52 +0200)
committerUwe Hermann <redacted>
Tue, 4 Jun 2019 16:59:04 +0000 (18:59 +0200)
The previous implementation provided a raw input stream of RX data from
read() calls to device drivers. This works great with genuine COM ports,
as well as with most setups which involve simple "cable expanders".

Recent additions of alternative transports (serial over HID and BLE)
added more protocol layers to the setup, and some device drivers are
reported to depend on the very framing of these transports: Mooshimeter
cares about individual BLE notification "frames", and the information
cannot get derived from the payload bytes. Some HID based cables which
obscure the DMM chips' serial protocol, or some HID based setups which
the serial layer does not abstract away as "a cable" may suffer from
similar requirements (do some drivers require access to individual HID
reports? Ikalogic? Victor DMM?).

Add support for an optional "RX chunk callback" which takes precedence
over "mere payload byte streams". Instead of returning payload bytes
from read() calls, the serial layer can call an application defined
routine and pass data bytes in the very framing which the physical
transport happens to use.

It's still up to the implementation of the specific transport whether
the callback approach is supported, and whether the wire's framing is
obeyed or whether payload data keeps getting provided as one raw stream.
It's also implementation dependent whether data reception transparently
occurs in background, or whether callers need to periodically "stimulate"
data reception by calling read or check routines which happen to call
back into the caller should RX data become available.

The approach that got implemented here is not universally applicable,
but serves those specific environments that were identified so far.

src/libsigrok-internal.h
src/serial.c

index fcb0527a22f43f5473594052d1a79cf44b7d88b5..9a34ba169a84863e0dffb382d2c0688c27ec724a 100644 (file)
@@ -732,6 +732,8 @@ struct sr_serial_dev_inst;
 struct ser_lib_functions;
 struct ser_hid_chip_functions;
 struct sr_bt_desc;
+typedef void (*serial_rx_chunk_callback)(struct sr_serial_dev_inst *serial,
+       void *cb_data, const void *buf, size_t count);
 struct sr_serial_dev_inst {
        /** Port name, e.g. '/dev/tty42'. */
        char *port;
@@ -745,6 +747,8 @@ struct sr_serial_dev_inst {
                int stop_bits;
        } comm_params;
        GString *rcv_buffer;
+       serial_rx_chunk_callback rx_chunk_cb_func;
+       void *rx_chunk_cb_data;
 #ifdef HAVE_LIBSERIALPORT
        /** libserialport port handle */
        struct sp_port *sp_data;
@@ -1159,6 +1163,8 @@ SR_PRIV int serial_read_blocking(struct sr_serial_dev_inst *serial, void *buf,
                size_t count, unsigned int timeout_ms);
 SR_PRIV int serial_read_nonblocking(struct sr_serial_dev_inst *serial, void *buf,
                size_t count);
+SR_PRIV int serial_set_read_chunk_cb(struct sr_serial_dev_inst *serial,
+               serial_rx_chunk_callback cb, void *cb_data);
 SR_PRIV int serial_set_params(struct sr_serial_dev_inst *serial, int baudrate,
                int bits, int parity, int stopbits, int flowcontrol, int rts, int dtr);
 SR_PRIV int serial_set_paramstr(struct sr_serial_dev_inst *serial,
index 5c3648317298af1d9232fc6e4bea69ed0443f2c1..12026a8965d6b9228efc245cecf9d87b4d3231f3 100644 (file)
@@ -226,7 +226,39 @@ SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial)
  * via UART can get stored in a GString (which is a char array). Since
  * the API hides this detail, we can address this issue later when needed.
  * Callers use the API which communicates bytes.
+ *
+ * Applications optionally can register a "per RX chunk" callback, when
+ * they depend on the frame boundaries of the respective physical layer.
+ * Most callers just want the stream of RX data, and can use the buffer.
+ *
+ * The availability of RX chunks to callbacks, as well as the capability
+ * to pass on exact frames as chunks or potential re-assembly of chunks
+ * to a single data block, depend on each transport's implementation.
+ */
+
+/**
+ * Register application callback for RX data chunks.
+ *
+ * @param[in] serial Previously initialized serial port instance.
+ * @param[in] cb Routine to call as RX data becomes available.
+ * @param[in] cb_data User data to pass to the callback in addition to RX data.
+ *
+ * @retval SR_ERR_ARG Invalid parameters.
+ * @retval SR_OK Successful registration.
+ *
+ * Callbacks get unregistered by specifying #NULL for the 'cb' parameter.
  */
+SR_PRIV int serial_set_read_chunk_cb(struct sr_serial_dev_inst *serial,
+       serial_rx_chunk_callback cb, void *cb_data)
+{
+       if (!serial)
+               return SR_ERR_ARG;
+
+       serial->rx_chunk_cb_func = cb;
+       serial->rx_chunk_cb_data = cb_data;
+
+       return SR_OK;
+}
 
 /**
  * Discard previously queued RX data. Internal to the serial subsystem,
@@ -282,7 +314,9 @@ SR_PRIV void sr_ser_queue_rx_data(struct sr_serial_dev_inst *serial,
        if (!data || !len)
                return;
 
-       if (serial->rcv_buffer)
+       if (serial->rx_chunk_cb_func)
+               serial->rx_chunk_cb_func(serial, serial->rx_chunk_cb_data, data, len);
+       else if (serial->rcv_buffer)
                g_string_append_len(serial->rcv_buffer, (const gchar *)data, len);
 }