From: Gerhard Sittig Date: Wed, 3 Apr 2019 05:52:00 +0000 (+0200) Subject: serial: add support for optional "RX chunk" callback X-Git-Url: https://sigrok.org/gitaction?a=commitdiff_plain;h=d47872480164724f348ceedc8af2f39fc2e9fce4;p=libsigrok.git serial: add support for optional "RX chunk" callback 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. --- diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h index fcb0527a..9a34ba16 100644 --- a/src/libsigrok-internal.h +++ b/src/libsigrok-internal.h @@ -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, diff --git a/src/serial.c b/src/serial.c index 5c364831..12026a89 100644 --- a/src/serial.c +++ b/src/serial.c @@ -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); }