X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fserial.c;h=12026a8965d6b9228efc245cecf9d87b4d3231f3;hb=d47872480164724f348ceedc8af2f39fc2e9fce4;hp=dc6e0126eefa164b67f57061d83e420c35f67df5;hpb=a7b8692ed061c5baff6620c0839db85465cd24c9;p=libsigrok.git diff --git a/src/serial.c b/src/serial.c index dc6e0126..12026a89 100644 --- a/src/serial.c +++ b/src/serial.c @@ -5,6 +5,7 @@ * Copyright (C) 2010-2012 Uwe Hermann * Copyright (C) 2012 Alexandru Gagniuc * Copyright (C) 2014 Uffe Jakobsen + * Copyright (C) 2017-2019 Gerhard Sittig * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +26,9 @@ #include #include #include +#ifdef HAVE_LIBSERIALPORT #include +#endif #include #include "libsigrok-internal.h" #ifdef _WIN32 @@ -50,6 +53,8 @@ * @{ */ +#ifdef HAVE_SERIAL_COMM + /* See if a (assumed opened) serial port is of any supported type. */ static int dev_is_supported(struct sr_serial_dev_inst *serial) { @@ -87,11 +92,28 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) sr_spew("Opening serial port '%s' (flags %d).", serial->port, flags); - /* Default to the libserialport transport layer. */ - serial->lib_funcs = ser_lib_funcs_libsp; + /* + * Determine which serial transport library to use. Derive the + * variant from the serial port's name. Default to libserialport + * for backwards compatibility. + */ + if (ser_name_is_hid(serial)) + serial->lib_funcs = ser_lib_funcs_hid; + else if (ser_name_is_bt(serial)) + serial->lib_funcs = ser_lib_funcs_bt; + else + serial->lib_funcs = ser_lib_funcs_libsp; if (!serial->lib_funcs) return SR_ERR_NA; + /* + * Note that use of the 'rcv_buffer' is optional, and the buffer's + * size heavily depends on the specific transport. That's why the + * buffer's content gets accessed and the buffer is released here in + * common code, but the buffer gets allocated in libraries' open() + * routines. + */ + /* * Run the transport's open routine. Setup the bitrate and the * UART frame format. @@ -120,6 +142,8 @@ SR_PRIV int serial_open(struct sr_serial_dev_inst *serial, int flags) */ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) { + int rc; + if (!serial) { sr_dbg("Invalid serial port."); return SR_ERR; @@ -130,7 +154,13 @@ SR_PRIV int serial_close(struct sr_serial_dev_inst *serial) if (!serial->lib_funcs || !serial->lib_funcs->close) return SR_ERR_NA; - return serial->lib_funcs->close(serial); + rc = serial->lib_funcs->close(serial); + if (rc == SR_OK && serial->rcv_buffer) { + g_string_free(serial->rcv_buffer, TRUE); + serial->rcv_buffer = NULL; + } + + return rc; } /** @@ -152,6 +182,8 @@ SR_PRIV int serial_flush(struct sr_serial_dev_inst *serial) sr_spew("Flushing serial port %s.", serial->port); + sr_ser_discard_queued_data(serial); + if (!serial->lib_funcs || !serial->lib_funcs->flush) return SR_ERR_NA; @@ -183,6 +215,147 @@ SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial) return serial->lib_funcs->drain(serial); } +/* + * Provide an internal RX data buffer for the serial port. This is not + * supposed to be used directly by applications. Instead optional and + * alternative transports for serial communication can use this buffer + * if their progress is driven from background activity, and is not + * (directly) driven by external API calls. + * + * BEWARE! This implementation assumes that data which gets communicated + * 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, + * coordination between common and transport specific support code. + * + * @param[in] serial Previously opened serial port instance. + * + * @internal + */ +SR_PRIV void sr_ser_discard_queued_data(struct sr_serial_dev_inst *serial) +{ + if (!serial) + return; + if (!serial->rcv_buffer) + return; + + g_string_truncate(serial->rcv_buffer, 0); +} + +/** + * Get amount of queued RX data. Internal to the serial subsystem, + * coordination between common and transport specific support code. + * + * @param[in] serial Previously opened serial port instance. + * + * @internal + */ +SR_PRIV size_t sr_ser_has_queued_data(struct sr_serial_dev_inst *serial) +{ + if (!serial) + return 0; + if (!serial->rcv_buffer) + return 0; + + return serial->rcv_buffer->len; +} + +/** + * Queue received data. Internal to the serial subsystem, coordination + * between common and transport specific support code. + * + * @param[in] serial Previously opened serial port instance. + * @param[in] data Pointer to data bytes to queue. + * @param[in] len Number of data bytes to queue. + * + * @internal + */ +SR_PRIV void sr_ser_queue_rx_data(struct sr_serial_dev_inst *serial, + const uint8_t *data, size_t len) +{ + if (!serial) + return; + if (!data || !len) + return; + + 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); +} + +/** + * Retrieve previously queued RX data. Internal to the serial subsystem, + * coordination between common and transport specific support code. + * + * @param[in] serial Previously opened serial port instance. + * @param[out] data Pointer to store retrieved data bytes into. + * @param[in] len Number of data bytes to retrieve. + * + * @internal + */ +SR_PRIV size_t sr_ser_unqueue_rx_data(struct sr_serial_dev_inst *serial, + uint8_t *data, size_t len) +{ + size_t qlen; + GString *buf; + + if (!serial) + return 0; + if (!data || !len) + return 0; + + qlen = sr_ser_has_queued_data(serial); + if (!qlen) + return 0; + + buf = serial->rcv_buffer; + if (len > buf->len) + len = buf->len; + if (len) { + memcpy(data, buf->str, len); + g_string_erase(buf, 0, len); + } + + return len; +} + /** * Check for available receive data. * @@ -195,7 +368,7 @@ SR_PRIV int serial_drain(struct sr_serial_dev_inst *serial) */ SR_PRIV size_t serial_has_receive_data(struct sr_serial_dev_inst *serial) { - size_t lib_count; + size_t lib_count, buf_count; if (!serial) return 0; @@ -204,7 +377,9 @@ SR_PRIV size_t serial_has_receive_data(struct sr_serial_dev_inst *serial) if (serial->lib_funcs && serial->lib_funcs->get_rx_avail) lib_count = serial->lib_funcs->get_rx_avail(serial); - return lib_count; + buf_count = sr_ser_has_queued_data(serial); + + return lib_count + buf_count; } static int _serial_write(struct sr_serial_dev_inst *serial, @@ -799,6 +974,14 @@ SR_API GSList *sr_serial_list(const struct sr_dev_driver *driver) list_func = ser_lib_funcs_libsp->list; tty_devs = list_func(tty_devs, append_port_list); } + if (ser_lib_funcs_hid && ser_lib_funcs_hid->list) { + list_func = ser_lib_funcs_hid->list; + tty_devs = list_func(tty_devs, append_port_list); + } + if (ser_lib_funcs_bt && ser_lib_funcs_bt->list) { + list_func = ser_lib_funcs_bt->list; + tty_devs = list_func(tty_devs, append_port_list); + } return tty_devs; } @@ -835,6 +1018,11 @@ SR_PRIV GSList *sr_serial_find_usb(uint16_t vendor_id, uint16_t product_id) tty_devs = find_func(tty_devs, append_port_find, vendor_id, product_id); } + if (ser_lib_funcs_hid && ser_lib_funcs_hid->find_usb) { + find_func = ser_lib_funcs_hid->find_usb; + tty_devs = find_func(tty_devs, append_port_find, + vendor_id, product_id); + } return tty_devs; } @@ -870,4 +1058,10 @@ SR_PRIV int serial_timeout(struct sr_serial_dev_inst *port, int num_bytes) return timeout_ms; } +#else + +/* TODO Put fallback.c content here? */ + +#endif + /** @} */