From c0f7aefe55518e37bf10db4c1e00a2b69df46f41 Mon Sep 17 00:00:00 2001 From: Lyle Zhu Date: Thu, 23 Jan 2025 17:28:46 +0800 Subject: [PATCH] Bluetooth: BR: Support limited discoverable mode Add a Kconfig BT_LIMITED_DISCOVERABLE_DURATION to set the timeout for limited discoverable mode. Add a argument `limited` to function `bt_br_set_discoverable()` to support the limited discoverable mode. When enabling discoverable mode with `limited` is true, both write LIAC and GIAC to controller and set the bit 13 of COD in function `bt_br_set_discoverable()`. And start a delay worker with the timeout CONFIG_BT_LIMITED_DISCOVERABLE_DURATION to disable the discoverable mode. When disabling discoverable mode, only set GIAC to controller and clear bit 13 of COD. Signed-off-by: Lyle Zhu --- include/zephyr/bluetooth/classic/classic.h | 7 +- samples/bluetooth/handsfree/src/main.c | 2 +- subsys/bluetooth/host/classic/Kconfig | 8 + subsys/bluetooth/host/classic/br.c | 168 +++++++++++++++++++- subsys/bluetooth/host/classic/shell/bredr.c | 22 +-- subsys/bluetooth/host/hci_core.h | 1 + 6 files changed, 190 insertions(+), 18 deletions(-) diff --git a/include/zephyr/bluetooth/classic/classic.h b/include/zephyr/bluetooth/classic/classic.h index be7cf8dc404..835d46c188a 100644 --- a/include/zephyr/bluetooth/classic/classic.h +++ b/include/zephyr/bluetooth/classic/classic.h @@ -175,12 +175,17 @@ int bt_br_oob_get_local(struct bt_br_oob *oob); * to devices making general inquiry. To enable this state it's mandatory * to first be in connectable state. * + * If the device enters limited discoverable mode, the controller will leave from discoverable + * mode after the duration of @kconfig{BT_LIMITED_DISCOVERABLE_DURATION} seconds in the limited + * discoverable mode. + * * @param enable Value allowing/disallowing controller to become discoverable. + * @param limited Value allowing/disallowing controller to enter limited discoverable mode. * * @return Negative if fail set to requested state or requested state has been * already set. Zero if done successfully. */ -int bt_br_set_discoverable(bool enable); +int bt_br_set_discoverable(bool enable, bool limited); /** * @brief Enable/disable set controller in connectable state. diff --git a/samples/bluetooth/handsfree/src/main.c b/samples/bluetooth/handsfree/src/main.c index 6aeb7219f0a..cbad46596a6 100644 --- a/samples/bluetooth/handsfree/src/main.c +++ b/samples/bluetooth/handsfree/src/main.c @@ -100,7 +100,7 @@ static void bt_ready(int err) printk("BR/EDR set/rest connectable failed (err %d)\n", err); return; } - err = bt_br_set_discoverable(true); + err = bt_br_set_discoverable(true, false); if (err) { printk("BR/EDR set discoverable failed (err %d)\n", err); return; diff --git a/subsys/bluetooth/host/classic/Kconfig b/subsys/bluetooth/host/classic/Kconfig index a967cfa65ae..3422c2fda99 100644 --- a/subsys/bluetooth/host/classic/Kconfig +++ b/subsys/bluetooth/host/classic/Kconfig @@ -18,6 +18,14 @@ config BT_CLASSIC This option enables Bluetooth BR/EDR support if BT_CLASSIC +config BT_LIMITED_DISCOVERABLE_DURATION + int "Maximum duration that a device is in limited discoverable mode in seconds" + default 60 + range 31 60 + help + This option sets the maximum duration that a device is in limited discoverable mode in + seconds. + config BT_BR_MIN_ENC_KEY_SIZE int prompt "Minimum encryption key size accepted in octets" if !BT_SMP_SC_ONLY diff --git a/subsys/bluetooth/host/classic/br.c b/subsys/bluetooth/host/classic/br.c index 082720baf9d..4267faf65c7 100644 --- a/subsys/bluetooth/host/classic/br.c +++ b/subsys/bluetooth/host/classic/br.c @@ -1057,8 +1057,128 @@ int bt_br_set_connectable(bool enable) } } -int bt_br_set_discoverable(bool enable) +#define BT_LIAC 0x9e8b00 +#define BT_GIAC 0x9e8b33 + +static int bt_br_write_current_iac_lap(bool limited) { + struct bt_hci_cp_write_current_iac_lap *iac_lap; + struct net_buf *buf; + uint8_t param_len; + uint8_t num_current_iac = limited ? 2 : 1; + + LOG_DBG("limited discoverable mode? %s", limited ? "Yes" : "No"); + + param_len = sizeof(*iac_lap) + (num_current_iac * sizeof(struct bt_hci_iac_lap)); + + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_CURRENT_IAC_LAP, param_len); + if (!buf) { + return -ENOBUFS; + } + + iac_lap = net_buf_add(buf, param_len); + iac_lap->num_current_iac = num_current_iac; + sys_put_le24(BT_GIAC, iac_lap->lap[0].iac); + if (num_current_iac > 1) { + sys_put_le24(BT_LIAC, iac_lap->lap[1].iac); + } + + return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_CURRENT_IAC_LAP, buf, NULL); +} + +#define BT_COD_MAJOR_SVC_CLASS_LIMITED_DISCOVER BIT(13) + +static int bt_br_read_cod(uint32_t *cod) +{ + struct net_buf *rsp; + struct bt_hci_rp_read_class_of_device *rp; + int err; + + LOG_DBG("Read COD"); + + /* Read Class of device */ + err = bt_hci_cmd_send_sync(BT_HCI_OP_READ_CLASS_OF_DEVICE, NULL, &rsp); + if (err) { + LOG_WRN("Fail to read COD"); + return err; + } + + if (!rsp || (rsp->len < sizeof(*rp))) { + LOG_WRN("Invalid response"); + return -EIO; + } + + rp = (void *)rsp->data; + *cod = sys_get_le24(rp->class_of_device); + + LOG_DBG("Current COD %06x", *cod); + + net_buf_unref(rsp); + + return 0; +} + +static int bt_br_write_cod(uint32_t cod) +{ + struct net_buf *buf; + + /* Set Class of device */ + buf = bt_hci_cmd_create(BT_HCI_OP_WRITE_CLASS_OF_DEVICE, + sizeof(struct bt_hci_cp_write_class_of_device)); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_le24(buf, cod); + + return bt_hci_cmd_send_sync(BT_HCI_OP_WRITE_CLASS_OF_DEVICE, buf, NULL); +} + +static int bt_br_update_cod(bool limited) +{ + int err; + uint32_t cod; + + LOG_DBG("Update COD"); + + err = bt_br_read_cod(&cod); + if (err) { + return err; + } + + if (limited) { + cod |= BT_COD_MAJOR_SVC_CLASS_LIMITED_DISCOVER; + } else { + cod &= ~BT_COD_MAJOR_SVC_CLASS_LIMITED_DISCOVER; + } + + err = bt_br_write_cod(cod); + return err; +} + +static void bt_br_limited_discoverable_timeout_handler(struct k_work *work) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_LIMITED_DISCOVERABLE_MODE)) { + LOG_INF("Limited discoverable mode has been disabled"); + return; + } + + err = bt_br_set_discoverable(false, false); + if (err) { + LOG_WRN("Disable discoverable failure (err %d)", err); + } +} + +/* Work used for limited discoverable mode time span */ +static K_WORK_DELAYABLE_DEFINE(bt_br_limited_discoverable_timeout, + bt_br_limited_discoverable_timeout_handler); + +int bt_br_set_discoverable(bool enable, bool limited) +{ + int err; + if (enable) { if (atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { return -EALREADY; @@ -1068,12 +1188,48 @@ int bt_br_set_discoverable(bool enable) return -EPERM; } - return write_scan_enable(BT_BREDR_SCAN_INQUIRY | BT_BREDR_SCAN_PAGE); - } else { - if (!atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { - return -EALREADY; + err = bt_br_write_current_iac_lap(limited); + if (err) { + return err; } - return write_scan_enable(BT_BREDR_SCAN_PAGE); + err = bt_br_update_cod(limited); + if (err) { + return err; + } + + err = write_scan_enable(BT_BREDR_SCAN_INQUIRY | BT_BREDR_SCAN_PAGE); + if (!err && (limited == true)) { + atomic_set_bit(bt_dev.flags, BT_DEV_LIMITED_DISCOVERABLE_MODE); + k_work_reschedule(&bt_br_limited_discoverable_timeout, + K_SECONDS(CONFIG_BT_LIMITED_DISCOVERABLE_DURATION)); + } + return err; } + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_ISCAN)) { + return -EALREADY; + } + + err = write_scan_enable(BT_BREDR_SCAN_PAGE); + if (err) { + return err; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_LIMITED_DISCOVERABLE_MODE)) { + err = bt_br_write_current_iac_lap(false); + if (err) { + return err; + } + + err = bt_br_update_cod(false); + if (err) { + return err; + } + + atomic_clear_bit(bt_dev.flags, BT_DEV_LIMITED_DISCOVERABLE_MODE); + k_work_cancel_delayable(&bt_br_limited_discoverable_timeout); + } + + return 0; } diff --git a/subsys/bluetooth/host/classic/shell/bredr.c b/subsys/bluetooth/host/classic/shell/bredr.c index 267d4eee9a7..23d5319c1f3 100644 --- a/subsys/bluetooth/host/classic/shell/bredr.c +++ b/subsys/bluetooth/host/classic/shell/bredr.c @@ -297,20 +297,21 @@ static int cmd_l2cap_register(const struct shell *sh, static int cmd_discoverable(const struct shell *sh, size_t argc, char *argv[]) { - int err; - const char *action; + int err = 0; + bool enable; + bool limited = false; - action = argv[1]; - - if (!strcmp(action, "on")) { - err = bt_br_set_discoverable(true); - } else if (!strcmp(action, "off")) { - err = bt_br_set_discoverable(false); - } else { + enable = shell_strtobool(argv[1], 10, &err); + if (err) { shell_help(sh); return SHELL_CMD_HELP_PRINTED; } + if (argc > 2 && !strcmp(argv[2], "limited")) { + limited = true; + } + + err = bt_br_set_discoverable(enable, limited); if (err) { shell_print(sh, "BR/EDR set/reset discoverable failed " "(err %d)", err); @@ -544,7 +545,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(br_cmds, SHELL_CMD_ARG(discovery, NULL, " [length: 1-48] [mode: limited]", cmd_discovery, 2, 2), - SHELL_CMD_ARG(iscan, NULL, "", cmd_discoverable, 2, 0), + SHELL_CMD_ARG(iscan, NULL, " [mode: limited]", + cmd_discoverable, 2, 1), SHELL_CMD_ARG(l2cap-register, NULL, "", cmd_l2cap_register, 2, 0), SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0), SHELL_CMD_ARG(pscan, NULL, "", cmd_connectable, 2, 0), diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index e501ae1d5fe..95e2fa75c98 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -74,6 +74,7 @@ enum { BT_DEV_ISCAN, BT_DEV_PSCAN, BT_DEV_INQUIRY, + BT_DEV_LIMITED_DISCOVERABLE_MODE, #endif /* CONFIG_BT_CLASSIC */ /* Total number of flags - must be at the end of the enum */