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 <lyle.zhu@nxp.com>
This commit is contained in:
Lyle Zhu 2025-01-23 17:28:46 +08:00 committed by Carles Cufí
parent 51bfb50b66
commit c0f7aefe55
6 changed files with 190 additions and 18 deletions

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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,
"<value: on, off> [length: 1-48] [mode: limited]",
cmd_discovery, 2, 2),
SHELL_CMD_ARG(iscan, NULL, "<value: on, off>", cmd_discoverable, 2, 0),
SHELL_CMD_ARG(iscan, NULL, "<value: on, off> [mode: limited]",
cmd_discoverable, 2, 1),
SHELL_CMD_ARG(l2cap-register, NULL, "<psm>", cmd_l2cap_register, 2, 0),
SHELL_CMD_ARG(oob, NULL, NULL, cmd_oob, 1, 0),
SHELL_CMD_ARG(pscan, NULL, "<value: on, off>", cmd_connectable, 2, 0),

View File

@ -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 */