]> sigrok.org Git - libsigrok.git/blame - src/serial_bt.c
serial_bt, bluez: rework diag in BLE reception, accept zero length data
[libsigrok.git] / src / serial_bt.c
CommitLineData
b79c3422
GS
1/*
2 * This file is part of the libsigrok project.
3 *
4 * Copyright (C) 2018-2019 Gerhard Sittig <gerhard.sittig@gmx.net>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <config.h>
21#include <glib.h>
22#include <libsigrok/libsigrok.h>
23#include "libsigrok-internal.h"
24#include <string.h>
25#include <memory.h>
26
b79c3422 27#define LOG_PREFIX "serial-bt"
b79c3422
GS
28
29#ifdef HAVE_SERIAL_COMM
30#ifdef HAVE_BLUETOOTH
31
32#define SER_BT_CONN_PREFIX "bt"
33#define SER_BT_CHUNK_SIZE 1200
34
0e4a85a9
GS
35#define SER_BT_PARAM_PREFIX_CHANNEL "channel="
36#define SER_BT_PARAM_PREFIX_HDL_RX "handle_rx="
37#define SER_BT_PARAM_PREFIX_HDL_TX "handle_tx="
38#define SER_BT_PARAM_PREFIX_HDL_CCCD "handle_cccd="
39#define SER_BT_PARAM_PREFIX_VAL_CCCD "value_cccd="
40
b79c3422
GS
41/**
42 * @file
43 *
44 * Serial port handling, wraps the external BT/BLE dependencies.
45 */
46
47/**
48 * @defgroup grp_serial_bt Serial port handling, BT/BLE group
49 *
50 * Make serial-over-BT communication appear like a regular serial port.
51 *
52 * @{
53 */
54
55/* {{{ support for serial-over-BT channels */
56
ca98f7ae
GS
57/*
58 * This builtin database of known devices (keyed by their names as
59 * provided during BT/BLE scans) can help improve the presentation of
60 * scan results. Ideally users could take the output and pass it to
61 * subsequent program invocations, not having to "come up with" the
62 * conn= spec, or only having to touch it up minimally. GUI dialogs
63 * could present scan results such that users just need to pick an
64 * item to open a connection.
65 *
66 * The current implementation guesses connection types from device
67 * names, and optionally amends them with additional parameters if
68 * experience shows that individual devices need these extra specs.
69 *
70 * This database may have to move to a separate source file should
71 * its size grow to amounts that are considered inappropriate here
72 * in the serial transport's BT dispatcher. For now the item count
73 * is small.
74 */
75
b79c3422
GS
76static const struct scan_supported_item {
77 const char *name;
78 enum ser_bt_conn_t type;
c622c88c 79 const char *add_params;
b79c3422 80} scan_supported_items[] = {
c622c88c
GS
81 { "121GW", SER_BT_CONN_BLE122, NULL, },
82 { "Adafruit Bluefruit LE 8134", SER_BT_CONN_NRF51, NULL, },
d0a73799
GS
83 { "DL24M_BLE", SER_BT_CONN_AC6328, NULL, },
84 { "DL24M_SPP", SER_BT_CONN_RFCOMM, "/channel=2", },
c622c88c 85 { "HC-05", SER_BT_CONN_RFCOMM, NULL, },
d0a73799
GS
86 { "UC96_BLE", SER_BT_CONN_AC6328, NULL, },
87 { "UC96_SPP", SER_BT_CONN_RFCOMM, "/channel=2", },
c622c88c
GS
88 { "UM25C", SER_BT_CONN_RFCOMM, NULL, },
89 { NULL, SER_BT_CONN_UNKNOWN, NULL, },
b79c3422
GS
90};
91
ca98f7ae
GS
92static const struct scan_supported_item *scan_is_supported(const char *name)
93{
94 size_t idx;
95 const struct scan_supported_item *item;
96
97 for (idx = 0; idx < ARRAY_SIZE(scan_supported_items); idx++) {
98 item = &scan_supported_items[idx];
99 if (!item->name)
100 break;
101 if (strcmp(name, item->name) != 0)
102 continue;
103 return item;
104 }
105
106 return NULL;
107}
108
b79c3422
GS
109static const char *ser_bt_conn_names[SER_BT_CONN_MAX] = {
110 [SER_BT_CONN_UNKNOWN] = "<type>",
111 [SER_BT_CONN_RFCOMM] = "rfcomm",
112 [SER_BT_CONN_BLE122] = "ble122",
113 [SER_BT_CONN_NRF51] = "nrf51",
114 [SER_BT_CONN_CC254x] = "cc254x",
d0a73799 115 [SER_BT_CONN_AC6328] = "ac6328",
b79c3422
GS
116};
117
118static enum ser_bt_conn_t lookup_conn_name(const char *name)
119{
120 size_t idx;
121 const char *item;
122
123 if (!name || !*name)
124 return SER_BT_CONN_UNKNOWN;
125 idx = ARRAY_SIZE(ser_bt_conn_names);
126 while (idx-- > 0) {
127 item = ser_bt_conn_names[idx];
128 if (strcmp(item, name) == 0)
129 return idx;
130 }
131
132 return SER_BT_CONN_UNKNOWN;
133}
134
135static const char *conn_name_text(enum ser_bt_conn_t type)
136{
137 if (type >= ARRAY_SIZE(ser_bt_conn_names))
138 type = SER_BT_CONN_UNKNOWN;
139
140 return ser_bt_conn_names[type];
141}
142
143/**
144 * Parse conn= specs for serial over Bluetooth communication.
145 *
146 * @param[in] serial The serial port that is about to get opened.
147 * @param[in] spec The caller provided conn= specification.
148 * @param[out] conn_type The type of BT comm (BT RFCOMM, BLE notify).
149 * @param[out] remote_addr The remote device address.
150 * @param[out] rfcomm_channel The RFCOMM channel (if applicable).
151 * @param[out] read_hdl The BLE notify read handle (if applicable).
152 * @param[out] write_hdl The BLE notify write handle (if applicable).
153 * @param[out] cccd_hdl The BLE notify CCCD handle (if applicable).
154 * @param[out] cccd_val The BLE notify CCCD value (if applicable).
155 *
156 * @return 0 upon success, non-zero upon failure.
157 *
b79c3422
GS
158 * Summary of parsing rules as they are implemented:
159 * - Implementor's note: Automatic scan for available devices is not
160 * yet implemented. So strictly speaking some parts of the input
161 * spec are optional, but fallbacks may not take effect ATM.
162 * - Insist on the "bt" prefix. Accept "bt" alone without any other
163 * additional field.
164 * - The first field that follows is the connection type. Supported
165 * types are 'rfcomm', 'ble122', 'cc254x', and potentially others
166 * in a future implementation.
167 * - The next field is the remote device's address, either separated
168 * by colons or dashes or spaces, or not separated at all.
169 * - Other parameters (RFCOMM channel, notify handles and write values)
ee617d62
GS
170 * get derived from the connection type.
171 * - More fields after the remote address are options which override
172 * builtin defaults (RFCOMM channels, BLE handles, etc).
b79c3422
GS
173 *
174 * Supported formats resulting from these rules:
ee617d62 175 * bt/<conn>/<addr>[/<param>]...
b79c3422
GS
176 *
177 * Examples:
178 * bt/rfcomm/11-22-33-44-55-66
ee617d62 179 * bt/rfcomm/11-22-33-44-55-66/channel=2
b79c3422
GS
180 * bt/ble122/88:6b:12:34:56:78
181 * bt/cc254x/0123456789ab
182 *
183 * It's assumed that users easily can create those conn= specs from
184 * available information, or that scan routines will create such specs
185 * that copy'n'paste results (or GUI choices from previous scan results)
186 * can get processed here.
187 */
188static int ser_bt_parse_conn_spec(
189 struct sr_serial_dev_inst *serial, const char *spec,
190 enum ser_bt_conn_t *conn_type, const char **remote_addr,
191 size_t *rfcomm_channel,
192 uint16_t *read_hdl, uint16_t *write_hdl,
193 uint16_t *cccd_hdl, uint16_t *cccd_val)
194{
819277b5 195 char **fields, *field;
b79c3422
GS
196 enum ser_bt_conn_t type;
197 const char *addr;
0e4a85a9
GS
198 int ret_parse, ret;
199 size_t fields_count, field_idx;
200 char *endp;
201 unsigned long parm_val;
b79c3422
GS
202
203 if (conn_type)
204 *conn_type = SER_BT_CONN_UNKNOWN;
205 if (remote_addr)
206 *remote_addr = NULL;
207 if (rfcomm_channel)
208 *rfcomm_channel = 0;
209 if (read_hdl)
210 *read_hdl = 0;
211 if (write_hdl)
212 *write_hdl = 0;
213 if (cccd_hdl)
214 *cccd_hdl = 0;
215 if (cccd_val)
216 *cccd_val = 0;
217
b79c3422
GS
218 if (!serial || !spec || !spec[0])
219 return SR_ERR_ARG;
220
221 /* Evaluate the mandatory first three fields. */
222 fields = g_strsplit_set(spec, "/", 0);
223 if (!fields)
224 return SR_ERR_ARG;
225 if (g_strv_length(fields) < 3) {
226 g_strfreev(fields);
227 return SR_ERR_ARG;
228 }
229 field = fields[0];
230 if (strcmp(field, SER_BT_CONN_PREFIX) != 0) {
231 g_strfreev(fields);
232 return SR_ERR_ARG;
233 }
234 field = fields[1];
235 type = lookup_conn_name(field);
236 if (!type) {
237 g_strfreev(fields);
238 return SR_ERR_ARG;
239 }
240 if (conn_type)
241 *conn_type = type;
242 field = fields[2];
243 if (!field || !*field) {
244 g_strfreev(fields);
245 return SR_ERR_ARG;
246 }
247 addr = g_strdup(field);
248 if (remote_addr)
249 *remote_addr = addr;
250
251 /* Derive default parameters that match the connection type. */
252 /* TODO Lookup defaults from a table? */
253 switch (type) {
254 case SER_BT_CONN_RFCOMM:
255 if (rfcomm_channel)
256 *rfcomm_channel = 1;
257 break;
258 case SER_BT_CONN_BLE122:
259 if (read_hdl)
260 *read_hdl = 8;
261 if (write_hdl)
262 *write_hdl = 0;
263 if (cccd_hdl)
264 *cccd_hdl = 9;
265 if (cccd_val)
266 *cccd_val = 0x0003;
267 break;
268 case SER_BT_CONN_NRF51:
269 /* TODO
270 * Are these values appropriate? Check the learn article at
271 * https://learn.adafruit.com/introducing-the-adafruit-bluefruit-le-uart-friend?view=all
272 */
273 if (read_hdl)
274 *read_hdl = 13;
275 if (write_hdl)
276 *write_hdl = 11;
277 if (cccd_hdl)
278 *cccd_hdl = 14;
279 if (cccd_val)
280 *cccd_val = 0x0001;
281 /* TODO 'random' type, sec-level=high */
282 break;
283 case SER_BT_CONN_CC254x:
284 /* TODO Are these values appropriate? Just guessing here. */
285 if (read_hdl)
286 *read_hdl = 20;
287 if (write_hdl)
288 *write_hdl = 0;
289 if (cccd_hdl)
290 *cccd_hdl = 21;
291 if (cccd_val)
292 *cccd_val = 0x0001;
293 break;
d0a73799
GS
294 case SER_BT_CONN_AC6328:
295 if (read_hdl)
296 *read_hdl = 12;
297 if (write_hdl)
298 *write_hdl = 15;
299 if (cccd_hdl)
300 *cccd_hdl = 13;
301 if (cccd_val)
302 *cccd_val = 0x0001;
303 break;
b79c3422
GS
304 default:
305 return SR_ERR_ARG;
306 }
307
0e4a85a9
GS
308 /*
309 * Preset a successful return value for the conn= parse call.
310 * Scan optional additional fields which specify more params.
311 * Update the defaults which were setup above. Pessimize the
312 * routine's return value in error paths.
313 */
314 ret_parse = SR_OK;
315 fields_count = g_strv_length(fields);
316 for (field_idx = 3; field_idx < fields_count; field_idx++) {
317 field = fields[field_idx];
318 if (!field || !*field)
319 continue;
320 if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_CHANNEL)) {
321 field += strlen(SER_BT_PARAM_PREFIX_CHANNEL);
322 endp = NULL;
323 ret = sr_atoul_base(field, &parm_val, &endp, 0);
324 if (ret != SR_OK || !endp || *endp != '\0') {
325 ret_parse = SR_ERR_ARG;
326 break;
327 }
328 if (rfcomm_channel)
329 *rfcomm_channel = parm_val;
330 continue;
331 }
332 if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_RX)) {
333 field += strlen(SER_BT_PARAM_PREFIX_HDL_RX);
334 endp = NULL;
335 ret = sr_atoul_base(field, &parm_val, &endp, 0);
336 if (ret != SR_OK || !endp || *endp != '\0') {
337 ret_parse = SR_ERR_ARG;
338 break;
339 }
340 if (read_hdl)
341 *read_hdl = parm_val;
342 continue;
343 }
344 if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_TX)) {
345 field += strlen(SER_BT_PARAM_PREFIX_HDL_TX);
346 endp = NULL;
347 ret = sr_atoul_base(field, &parm_val, &endp, 0);
348 if (ret != SR_OK || !endp || *endp != '\0') {
349 ret_parse = SR_ERR_ARG;
350 break;
351 }
352 if (write_hdl)
353 *write_hdl = parm_val;
354 continue;
355 }
356 if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_HDL_CCCD)) {
357 field += strlen(SER_BT_PARAM_PREFIX_HDL_CCCD);
358 endp = NULL;
359 ret = sr_atoul_base(field, &parm_val, &endp, 0);
360 if (ret != SR_OK || !endp || *endp != '\0') {
361 ret_parse = SR_ERR_ARG;
362 break;
363 }
364 if (cccd_hdl)
365 *cccd_hdl = parm_val;
366 continue;
367 }
368 if (g_str_has_prefix(field, SER_BT_PARAM_PREFIX_VAL_CCCD)) {
369 field += strlen(SER_BT_PARAM_PREFIX_VAL_CCCD);
370 endp = NULL;
371 ret = sr_atoul_base(field, &parm_val, &endp, 0);
372 if (ret != SR_OK || !endp || *endp != '\0') {
373 ret_parse = SR_ERR_ARG;
374 break;
375 }
376 if (cccd_val)
377 *cccd_val = parm_val;
378 continue;
379 }
380 return SR_ERR_DATA;
381 }
b79c3422
GS
382
383 g_strfreev(fields);
0e4a85a9 384 return ret_parse;
b79c3422
GS
385}
386
387static void ser_bt_mask_databits(struct sr_serial_dev_inst *serial,
388 uint8_t *data, size_t len)
389{
390 uint32_t mask32;
391 uint8_t mask;
392 size_t idx;
393
394 if ((serial->comm_params.data_bits % 8) == 0)
395 return;
396
397 mask32 = (1UL << serial->comm_params.data_bits) - 1;
398 mask = mask32 & 0xff;
399 for (idx = 0; idx < len; idx++)
400 data[idx] &= mask;
401}
402
403static int ser_bt_data_cb(void *cb_data, uint8_t *data, size_t dlen)
404{
405 struct sr_serial_dev_inst *serial;
406
407 serial = cb_data;
408 if (!serial)
409 return -1;
410
a8b0e6f5
GS
411 if (!data && dlen)
412 return -1;
413 if (!data || !dlen)
414 return 0;
415
b79c3422
GS
416 ser_bt_mask_databits(serial, data, dlen);
417 sr_ser_queue_rx_data(serial, data, dlen);
418
419 return 0;
420}
421
422/* }}} */
423/* {{{ wrap serial-over-BT operations in a common serial.c API */
424
425/* See if a serial port's name refers to a BT type. */
426SR_PRIV int ser_name_is_bt(struct sr_serial_dev_inst *serial)
427{
428 size_t off;
429 char sep;
430
431 if (!serial)
432 return 0;
433 if (!serial->port || !*serial->port)
434 return 0;
435
436 /* Accept either "bt" alone, or "bt/" as a prefix. */
437 if (!g_str_has_prefix(serial->port, SER_BT_CONN_PREFIX))
438 return 0;
439 off = strlen(SER_BT_CONN_PREFIX);
440 sep = serial->port[off];
441 if (sep != '\0' && sep != '/')
442 return 0;
443
444 return 1;
445}
446
447/* The open() wrapper for BT ports. */
448static int ser_bt_open(struct sr_serial_dev_inst *serial, int flags)
449{
450 enum ser_bt_conn_t conn_type;
451 const char *remote_addr;
452 size_t rfcomm_channel;
453 uint16_t read_hdl, write_hdl, cccd_hdl, cccd_val;
454 int rc;
455 struct sr_bt_desc *desc;
456
457 (void)flags;
458
459 /* Derive BT specific parameters from the port spec. */
460 rc = ser_bt_parse_conn_spec(serial, serial->port,
461 &conn_type, &remote_addr,
462 &rfcomm_channel,
463 &read_hdl, &write_hdl,
464 &cccd_hdl, &cccd_val);
465 if (rc != SR_OK)
466 return SR_ERR_ARG;
467
468 if (!conn_type || !remote_addr || !remote_addr[0]) {
469 /* TODO Auto-search for available connections? */
470 return SR_ERR_NA;
471 }
472
473 /* Create the connection. Only store params after successful use. */
474 desc = sr_bt_desc_new();
475 if (!desc)
476 return SR_ERR;
477 serial->bt_desc = desc;
478 rc = sr_bt_config_addr_remote(desc, remote_addr);
479 if (rc < 0)
480 return SR_ERR;
481 serial->bt_addr_remote = g_strdup(remote_addr);
482 switch (conn_type) {
483 case SER_BT_CONN_RFCOMM:
484 rc = sr_bt_config_rfcomm(desc, rfcomm_channel);
485 if (rc < 0)
486 return SR_ERR;
487 serial->bt_rfcomm_channel = rfcomm_channel;
488 break;
489 case SER_BT_CONN_BLE122:
490 case SER_BT_CONN_NRF51:
491 case SER_BT_CONN_CC254x:
d0a73799 492 case SER_BT_CONN_AC6328:
b79c3422
GS
493 rc = sr_bt_config_notify(desc,
494 read_hdl, write_hdl, cccd_hdl, cccd_val);
495 if (rc < 0)
496 return SR_ERR;
497 serial->bt_notify_handle_read = read_hdl;
498 serial->bt_notify_handle_write = write_hdl;
499 serial->bt_notify_handle_cccd = cccd_hdl;
500 serial->bt_notify_value_cccd = cccd_val;
501 break;
502 default:
503 /* Unsupported type, or incomplete implementation. */
504 return SR_ERR_ARG;
505 }
506 serial->bt_conn_type = conn_type;
507
508 /* Make sure the receive buffer can accept input data. */
509 if (!serial->rcv_buffer)
510 serial->rcv_buffer = g_string_sized_new(SER_BT_CHUNK_SIZE);
511 rc = sr_bt_config_cb_data(desc, ser_bt_data_cb, serial);
512 if (rc < 0)
513 return SR_ERR;
514
515 /* Open the connection. */
516 switch (conn_type) {
517 case SER_BT_CONN_RFCOMM:
518 rc = sr_bt_connect_rfcomm(desc);
519 if (rc < 0)
520 return SR_ERR;
521 break;
522 case SER_BT_CONN_BLE122:
523 case SER_BT_CONN_NRF51:
524 case SER_BT_CONN_CC254x:
d0a73799 525 case SER_BT_CONN_AC6328:
b79c3422
GS
526 rc = sr_bt_connect_ble(desc);
527 if (rc < 0)
528 return SR_ERR;
529 rc = sr_bt_start_notify(desc);
530 if (rc < 0)
531 return SR_ERR;
532 break;
533 default:
534 return SR_ERR_ARG;
535 }
536
537 return SR_OK;
538}
539
540static int ser_bt_close(struct sr_serial_dev_inst *serial)
541{
542 if (!serial)
543 return SR_ERR_ARG;
544
545 if (!serial->bt_desc)
546 return SR_OK;
547
548 sr_bt_disconnect(serial->bt_desc);
549 sr_bt_desc_free(serial->bt_desc);
550 serial->bt_desc = NULL;
551
552 g_free(serial->bt_addr_local);
553 serial->bt_addr_local = NULL;
554 g_free(serial->bt_addr_remote);
555 serial->bt_addr_remote = NULL;
556 g_slist_free_full(serial->bt_source_args, g_free);
557 serial->bt_source_args = NULL;
558
559 return SR_OK;
560}
561
562/* Flush, discards pending RX data, empties buffers. */
563static int ser_bt_flush(struct sr_serial_dev_inst *serial)
564{
565 (void)serial;
566 /* EMPTY */
567
568 return SR_OK;
569}
570
571/* Drain, waits for completion of pending TX data. */
572static int ser_bt_drain(struct sr_serial_dev_inst *serial)
573{
574 (void)serial;
575 /* EMPTY */ /* TODO? */
576
577 return SR_ERR_BUG;
578}
579
580static int ser_bt_write(struct sr_serial_dev_inst *serial,
581 const void *buf, size_t count,
582 int nonblocking, unsigned int timeout_ms)
583{
584 ssize_t wrlen;
585
586 /*
587 * TODO Support chunked transmission when callers' requests
588 * exceed the BT channel's capacity? See ser_hid_write().
589 */
590
591 switch (serial->bt_conn_type) {
592 case SER_BT_CONN_RFCOMM:
593 (void)nonblocking;
594 (void)timeout_ms;
595 wrlen = sr_bt_write(serial->bt_desc, buf, count);
596 if (wrlen < 0)
597 return SR_ERR_IO;
598 return wrlen;
599 case SER_BT_CONN_BLE122:
600 case SER_BT_CONN_NRF51:
601 case SER_BT_CONN_CC254x:
d0a73799 602 case SER_BT_CONN_AC6328:
b79c3422
GS
603 /*
604 * Assume that when applications call the serial layer's
605 * write routine, then the BLE chip/module does support
606 * a TX handle. Just call the serial-BT library's write
607 * routine.
608 */
609 (void)nonblocking;
610 (void)timeout_ms;
611 wrlen = sr_bt_write(serial->bt_desc, buf, count);
612 if (wrlen < 0)
613 return SR_ERR_IO;
614 return wrlen;
615 default:
616 return SR_ERR_ARG;
617 }
618 /* UNREACH */
619}
620
621static int ser_bt_read(struct sr_serial_dev_inst *serial,
622 void *buf, size_t count,
623 int nonblocking, unsigned int timeout_ms)
624{
625 gint64 deadline_us, now_us;
626 uint8_t buffer[SER_BT_CHUNK_SIZE];
627 ssize_t rdlen;
628 int rc;
629 size_t dlen;
630
631 /*
632 * Immediately satisfy the caller's request from the RX buffer
633 * if the requested amount of data is available already.
634 */
bf6b9e7b
UH
635 if (sr_ser_has_queued_data(serial) >= count)
636 return sr_ser_unqueue_rx_data(serial, buf, count);
b79c3422
GS
637
638 /*
639 * When a timeout was specified, then determine the deadline
640 * where to stop reception.
641 */
642 deadline_us = 0;
b79c3422
GS
643 if (timeout_ms) {
644 now_us = g_get_monotonic_time();
645 deadline_us = now_us + timeout_ms * 1000;
646 }
647
648 /*
649 * Keep receiving from the port until the caller's requested
650 * amount of data has become available, or the timeout has
651 * expired. In the absence of a timeout, stop reading when an
652 * attempt no longer yields receive data.
653 */
654 while (TRUE) {
655 /* Run another attempt to receive data. */
656 switch (serial->bt_conn_type) {
657 case SER_BT_CONN_RFCOMM:
658 rdlen = sr_bt_read(serial->bt_desc, buffer, sizeof(buffer));
659 if (rdlen <= 0)
660 break;
661 rc = ser_bt_data_cb(serial, buffer, rdlen);
662 if (rc < 0)
663 rdlen = -1;
664 break;
665 case SER_BT_CONN_BLE122:
666 case SER_BT_CONN_NRF51:
667 case SER_BT_CONN_CC254x:
d0a73799 668 case SER_BT_CONN_AC6328:
b79c3422
GS
669 dlen = sr_ser_has_queued_data(serial);
670 rc = sr_bt_check_notify(serial->bt_desc);
671 if (rc < 0)
672 rdlen = -1;
673 else if (sr_ser_has_queued_data(serial) != dlen)
674 rdlen = +1;
675 else
676 rdlen = 0;
677 break;
678 default:
679 rdlen = -1;
680 break;
681 }
682
683 /*
684 * Stop upon receive errors, or timeout expiration. Only
685 * stop upon empty reception in the absence of a timeout.
686 */
687 if (rdlen < 0)
688 break;
689 if (nonblocking && !rdlen)
690 break;
691 if (deadline_us) {
692 now_us = g_get_monotonic_time();
693 if (now_us > deadline_us)
694 break;
695 }
696
697 /* Also stop when sufficient data has become available. */
698 if (sr_ser_has_queued_data(serial) >= count)
699 break;
700 }
701
702 /*
703 * Satisfy the caller's demand for receive data from previously
704 * queued incoming data.
705 */
706 dlen = sr_ser_has_queued_data(serial);
707 if (dlen > count)
708 dlen = count;
709 if (!dlen)
710 return 0;
711
712 return sr_ser_unqueue_rx_data(serial, buf, dlen);
713}
714
b79c3422
GS
715struct bt_source_args_t {
716 /* The application callback. */
717 sr_receive_data_callback cb;
718 void *cb_data;
719 /* The serial device, to store RX data. */
720 struct sr_serial_dev_inst *serial;
721};
722
723/*
724 * Gets periodically invoked by the glib main loop. "Drives" (checks)
725 * progress of BT communication, and invokes the application's callback
726 * which processes RX data (when some has become available), as well as
727 * handles application level timeouts.
728 */
729static int bt_source_cb(int fd, int revents, void *cb_data)
730{
731 struct bt_source_args_t *args;
732 struct sr_serial_dev_inst *serial;
733 uint8_t rx_buf[SER_BT_CHUNK_SIZE];
734 ssize_t rdlen;
735 size_t dlen;
736 int rc;
737
738 args = cb_data;
739 if (!args)
740 return -1;
741 serial = args->serial;
742 if (!serial)
743 return -1;
744 if (!serial->bt_conn_type)
745 return -1;
746
747 /*
748 * Drain receive data which the channel might have pending.
749 * This is "a copy" of the "background part" of ser_bt_read(),
750 * without the timeout support code, and not knowing how much
751 * data the application is expecting.
752 */
753 do {
754 switch (serial->bt_conn_type) {
755 case SER_BT_CONN_RFCOMM:
756 rdlen = sr_bt_read(serial->bt_desc, rx_buf, sizeof(rx_buf));
757 if (rdlen <= 0)
758 break;
759 rc = ser_bt_data_cb(serial, rx_buf, rdlen);
760 if (rc < 0)
761 rdlen = -1;
762 break;
763 case SER_BT_CONN_BLE122:
764 case SER_BT_CONN_NRF51:
765 case SER_BT_CONN_CC254x:
d0a73799 766 case SER_BT_CONN_AC6328:
b79c3422
GS
767 dlen = sr_ser_has_queued_data(serial);
768 rc = sr_bt_check_notify(serial->bt_desc);
769 if (rc < 0)
770 rdlen = -1;
771 else if (sr_ser_has_queued_data(serial) != dlen)
772 rdlen = +1;
773 else
774 rdlen = 0;
775 break;
776 default:
777 rdlen = -1;
778 break;
779 }
780 } while (rdlen > 0);
781
782 /*
783 * When RX data became available (now or earlier), pass this
784 * condition to the application callback. Always periodically
785 * run the application callback, since it handles timeouts and
786 * might carry out other tasks as well like signalling progress.
787 */
788 if (sr_ser_has_queued_data(args->serial))
789 revents |= G_IO_IN;
790 rc = args->cb(fd, revents, args->cb_data);
791
792 return rc;
793}
794
795/* TODO Can we use the Bluetooth socket's file descriptor? Probably not portably. */
796#define WITH_MAXIMUM_TIMEOUT_VALUE 0
797static int ser_bt_setup_source_add(struct sr_session *session,
798 struct sr_serial_dev_inst *serial,
799 int events, int timeout,
800 sr_receive_data_callback cb, void *cb_data)
801{
802 struct bt_source_args_t *args;
803 int rc;
804
805 (void)events;
806
807 /* Optionally enforce a minimum poll period. */
808 if (WITH_MAXIMUM_TIMEOUT_VALUE && timeout > WITH_MAXIMUM_TIMEOUT_VALUE)
809 timeout = WITH_MAXIMUM_TIMEOUT_VALUE;
810
811 /* Allocate status container for background data reception. */
812 args = g_malloc0(sizeof(*args));
813 args->cb = cb;
814 args->cb_data = cb_data;
815 args->serial = serial;
816
817 /*
818 * Have a periodic timer installed. Register the allocated block
819 * with the serial device, since the GSource's finalizer won't
820 * free the memory, and we haven't bothered to create a custom
821 * BT specific GSource.
822 */
823 rc = sr_session_source_add(session, -1, events, timeout, bt_source_cb, args);
824 if (rc != SR_OK) {
825 g_free(args);
826 return rc;
827 }
828 serial->bt_source_args = g_slist_append(serial->bt_source_args, args);
829
830 return SR_OK;
831}
832
833static int ser_bt_setup_source_remove(struct sr_session *session,
834 struct sr_serial_dev_inst *serial)
835{
836 (void)serial;
837
838 (void)sr_session_source_remove(session, -1);
839 /* Release callback args here already? */
840
841 return SR_OK;
842}
843
b79c3422
GS
844struct bt_scan_args_t {
845 GSList *port_list;
846 sr_ser_list_append_t append;
847 GSList *addr_list;
848 const char *bt_type;
849};
850
851static void scan_cb(void *cb_args, const char *addr, const char *name)
852{
853 struct bt_scan_args_t *scan_args;
854 GSList *l;
855 char addr_text[20];
c622c88c 856 const struct scan_supported_item *item;
b79c3422
GS
857 enum ser_bt_conn_t type;
858 char *port_name, *port_desc;
859 char *addr_copy;
860
861 scan_args = cb_args;
862 if (!scan_args)
863 return;
864 sr_info("BT scan, found: %s - %s\n", addr, name);
865
866 /* Check whether the device was seen before. */
867 for (l = scan_args->addr_list; l; l = l->next) {
bf6b9e7b 868 if (strcmp(addr, l->data) == 0)
b79c3422 869 return;
b79c3422
GS
870 }
871
872 /* Substitute colons in the address by dashes. */
873 if (!addr || !*addr)
874 return;
875 snprintf(addr_text, sizeof(addr_text), "%s", addr);
876 g_strcanon(addr_text, "0123456789abcdefABCDEF", '-');
877
878 /* Create a port name, and a description. */
c622c88c
GS
879 item = scan_is_supported(name);
880 type = item ? item->type : SER_BT_CONN_UNKNOWN;
881 port_name = g_strdup_printf("%s/%s/%s%s",
882 SER_BT_CONN_PREFIX, conn_name_text(type), addr_text,
883 (item && item->add_params) ? item->add_params : "");
b79c3422
GS
884 port_desc = g_strdup_printf("%s (%s)", name, scan_args->bt_type);
885
886 scan_args->port_list = scan_args->append(scan_args->port_list, port_name, port_desc);
887 g_free(port_name);
888 g_free(port_desc);
889
890 /* Keep track of the handled address. */
891 addr_copy = g_strdup(addr);
892 scan_args->addr_list = g_slist_append(scan_args->addr_list, addr_copy);
893}
894
895static GSList *ser_bt_list(GSList *list, sr_ser_list_append_t append)
896{
a1b80f9d 897 static const int scan_duration = 3;
b79c3422
GS
898
899 struct bt_scan_args_t scan_args;
900 struct sr_bt_desc *desc;
901
902 /*
903 * Implementor's note: This "list" routine is best-effort. We
904 * assume that registering callbacks always succeeds. Silently
905 * ignore failure to scan for devices. Just return those which
906 * we happen to find.
907 */
908
909 desc = sr_bt_desc_new();
910 if (!desc)
911 return list;
912
913 memset(&scan_args, 0, sizeof(scan_args));
914 scan_args.port_list = list;
915 scan_args.append = append;
916
917 scan_args.addr_list = NULL;
918 scan_args.bt_type = "BT";
919 (void)sr_bt_config_cb_scan(desc, scan_cb, &scan_args);
920 (void)sr_bt_scan_bt(desc, scan_duration);
921 g_slist_free_full(scan_args.addr_list, g_free);
922
923 scan_args.addr_list = NULL;
924 scan_args.bt_type = "BLE";
925 (void)sr_bt_config_cb_scan(desc, scan_cb, &scan_args);
926 (void)sr_bt_scan_le(desc, scan_duration);
927 g_slist_free_full(scan_args.addr_list, g_free);
928
929 sr_bt_desc_free(desc);
930
931 return scan_args.port_list;
932}
933
934static struct ser_lib_functions serlib_bt = {
935 .open = ser_bt_open,
936 .close = ser_bt_close,
937 .flush = ser_bt_flush,
938 .drain = ser_bt_drain,
939 .write = ser_bt_write,
940 .read = ser_bt_read,
c0aa074e
UH
941 /*
942 * Bluetooth communication has no concept of bitrate, so ignore
943 * these arguments silently. Neither need we pass the frame format
944 * down to internal BT comm routines, nor need we keep the values
945 * here, since the caller will cache/register them already.
946 */
947 .set_params = std_dummy_set_params,
3ad30b4e 948 .set_handshake = std_dummy_set_handshake,
b79c3422
GS
949 .setup_source_add = ser_bt_setup_source_add,
950 .setup_source_remove = ser_bt_setup_source_remove,
951 .list = ser_bt_list,
952 .get_frame_format = NULL,
953};
954SR_PRIV struct ser_lib_functions *ser_lib_funcs_bt = &serlib_bt;
955
956/* }}} */
957#else
958
959SR_PRIV int ser_name_is_bt(struct sr_serial_dev_inst *serial)
960{
961 (void)serial;
962
963 return 0;
964}
965
966SR_PRIV struct ser_lib_functions *ser_lib_funcs_bt = NULL;
967
968#endif
969#endif
970
971/** @} */