From 16fc1b47b9b7dfe830b1dfa83a80d336e71e57c9 Mon Sep 17 00:00:00 2001 From: Lwazi Dube Date: Wed, 3 May 2023 13:05:45 -0400 Subject: [PATCH] wireless/bluetooth: Support removable bluetooth modules. This bluetooth stack remains in an inconsistent state when the bluetooth HCI module is removed. This change adds a bt_netdev_unregister function that can be used to clean up after a module is removed. Some global variables are also set to their default values. --- include/nuttx/wireless/bluetooth/bt_driver.h | 17 +++ wireless/bluetooth/bt_att.c | 2 + wireless/bluetooth/bt_conn.c | 19 +++ wireless/bluetooth/bt_conn.h | 2 + wireless/bluetooth/bt_hcicore.c | 126 ++++++++++++++++++- wireless/bluetooth/bt_hcicore.h | 17 +++ wireless/bluetooth/bt_l2cap.c | 4 + wireless/bluetooth/bt_netdev.c | 56 ++++++++- wireless/bluetooth/bt_smp.c | 2 + 9 files changed, 238 insertions(+), 7 deletions(-) diff --git a/include/nuttx/wireless/bluetooth/bt_driver.h b/include/nuttx/wireless/bluetooth/bt_driver.h index 66d9f38c51..9e85e1b5f5 100644 --- a/include/nuttx/wireless/bluetooth/bt_driver.h +++ b/include/nuttx/wireless/bluetooth/bt_driver.h @@ -119,4 +119,21 @@ struct bt_driver_s int bt_netdev_register(FAR struct bt_driver_s *btdev); +/**************************************************************************** + * Name: bt_netdev_unregister + * + * Description: + * Unregister a network driver registered by bt_netdev_register. + * + * Input Parameters: + * btdev - An instance of the low-level driver interface structure. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int bt_netdev_unregister(FAR struct bt_driver_s *btdev); + #endif /* __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_DRIVER_H */ diff --git a/wireless/bluetooth/bt_att.c b/wireless/bluetooth/bt_att.c index bb5a19e8fd..9eb6868997 100644 --- a/wireless/bluetooth/bt_att.c +++ b/wireless/bluetooth/bt_att.c @@ -1846,6 +1846,8 @@ void bt_att_initialize(void) .disconnected = bt_att_disconnected, }; + memset(g_bt_att_pool, 0, sizeof(g_bt_att_pool)); + bt_l2cap_chan_register(&chan); } diff --git a/wireless/bluetooth/bt_conn.c b/wireless/bluetooth/bt_conn.c index c69658a685..ec1669490d 100644 --- a/wireless/bluetooth/bt_conn.c +++ b/wireless/bluetooth/bt_conn.c @@ -1078,3 +1078,22 @@ int bt_conn_le_conn_update(FAR struct bt_conn_s *conn, uint16_t min, return bt_hci_cmd_send(BT_HCI_OP_LE_CONN_UPDATE, buf); } + +/**************************************************************************** + * Name: bt_conn_initialize + * + * Description: + * Initialize this module's private data. + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +void bt_conn_initialize(void) +{ + memset(g_conns, 0, sizeof(g_conns)); +} diff --git a/wireless/bluetooth/bt_conn.h b/wireless/bluetooth/bt_conn.h index 3a29997b11..9973a70d74 100644 --- a/wireless/bluetooth/bt_conn.h +++ b/wireless/bluetooth/bt_conn.h @@ -416,4 +416,6 @@ int bt_conn_le_conn_update(FAR struct bt_conn_s *conn, uint16_t min, uint16_t max, uint16_t latency, uint16_t timeout); +void bt_conn_initialize(void); + #endif /* __WIRELESS_BLUETOOTH_BT_CONN_H */ diff --git a/wireless/bluetooth/bt_hcicore.c b/wireless/bluetooth/bt_hcicore.c index 1f1b5f1d98..9b8595787d 100644 --- a/wireless/bluetooth/bt_hcicore.c +++ b/wireless/bluetooth/bt_hcicore.c @@ -985,7 +985,7 @@ static int hci_tx_kthread(int argc, FAR char *argv[]) wlinfo("started\n"); - for (; ; ) + for (; g_btdev.tx_status == OK; ) { FAR struct bt_buf_s *buf; @@ -1016,16 +1016,25 @@ static int hci_tx_kthread(int argc, FAR char *argv[]) g_btdev.sent_cmd = NULL; } - g_btdev.sent_cmd = bt_buf_addref(buf); + /* Allow transmission if module is connected. */ - wlinfo("Sending command %04x buf %p to driver\n", - buf->u.hci.opcode, buf); + if (g_btdev.tx_status == OK) + { + g_btdev.sent_cmd = bt_buf_addref(buf); + + wlinfo("Sending command %04x buf %p to driver\n", + buf->u.hci.opcode, buf); + + bt_send(btdev, buf); + } - bt_send(btdev, buf); bt_buf_release(buf); } - return EXIT_SUCCESS; /* Can't get here */ + /* Acknowledge the termination request. */ + + g_btdev.tx_status = ESHUTDOWN; + return EXIT_SUCCESS; } /**************************************************************************** @@ -1425,6 +1434,64 @@ static int hci_initialize(void) nxsem_init(&g_btdev.le_pkts_sem, 0, g_btdev.le_pkts); return 0; } + +/**************************************************************************** + * Name: cmd_queue_deinit + * + * Description: + * Threads, fifos and semaphores deinitialization + * + * Input Parameters: + * none + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void cmd_queue_deinit(void) +{ + int ret; + FAR struct bt_buf_s *buf; + + /* Tell the tx thread that the module is disconnected. + */ + + g_btdev.tx_status = ENOTCONN; + + /* Make sure the thread is not blocked by the semaphore. + */ + + nxsem_post(&g_btdev.ncmd_sem); + + /* We create and push a packet into the tx queue to unblock the thread. + * Any packet will do. + */ + + buf = bt_hci_cmd_create(BT_HCI_OP_RESET, 0); + if (buf) + { + ret = bt_queue_send(&g_btdev.tx_queue, buf, BT_NORMAL_PRIO); + if (ret < 0) + { + wlerr("ERROR: bt_queue_send() failed: %d\n", ret); + } + } + + /* Wait for the tx thread to exit gracefully. */ + + while (g_btdev.tx_status == ENOTCONN) + { + nxsig_usleep(1000); + } + + /* Deinitialization */ + + nxsem_destroy(&g_btdev.ncmd_sem); + file_mq_close(&g_btdev.tx_queue); + work_cancel(HPWORK, &g_hp_work); + work_cancel(LPWORK, &g_lp_work); +} #endif /* threads, fifos and semaphores initialization */ @@ -1444,6 +1511,7 @@ static void cmd_queue_init(void) nxsem_init(&g_btdev.ncmd_sem, 0, 1); g_btdev.ncmd = 1; + g_btdev.tx_status = OK; ret = kthread_create("BT HCI Tx", CONFIG_BLUETOOTH_TXCMD_PRIORITY, CONFIG_BLUETOOTH_TXCMD_STACKSIZE, hci_tx_kthread, NULL); @@ -1497,6 +1565,14 @@ int bt_initialize(void) wlinfo("btdev %p\n", btdev); +#ifdef CONFIG_WIRELESS_BLUETOOTH_HOST + g_callback_list = NULL; + g_scan_dev_found_cb = NULL; +#endif + + memset(&g_lp_rxlist, 0, sizeof(g_lp_rxlist)); + memset(&g_hp_rxlist, 0, sizeof(g_hp_rxlist)); + DEBUGASSERT(btdev != NULL); bt_buf_initialize(); @@ -1505,6 +1581,9 @@ int bt_initialize(void) ret = btdev->open(btdev); if (ret < 0) { +#ifdef CONFIG_WIRELESS_BLUETOOTH_HOST + cmd_queue_deinit(); +#endif wlerr("ERROR: HCI driver open failed (%d)\n", ret); return ret; } @@ -1513,6 +1592,7 @@ int bt_initialize(void) ret = hci_initialize(); if (ret < 0) { + cmd_queue_deinit(); wlerr("ERROR: hci_initialize failed: %d\n", ret); return ret; } @@ -1523,6 +1603,38 @@ int bt_initialize(void) return ret; } +/**************************************************************************** + * Name: bt_deinitialize + * + * Description: + * Deinitialize Bluetooth. + * + * Returned Value: + * Zero on success or (negative) error code otherwise. + * + ****************************************************************************/ + +int bt_deinitialize(void) +{ +#ifdef CONFIG_WIRELESS_BLUETOOTH_HOST + FAR struct bt_driver_s *btdev = g_btdev.btdev; + + cmd_queue_deinit(); + + /* Call the bluetooth HCI driver's close function if available. */ + + if (btdev) + { + if (btdev->close) + { + btdev->close(btdev); + } + } +#endif + + return OK; +} + /**************************************************************************** * Name: bt_driver_register * @@ -1552,6 +1664,8 @@ int bt_driver_register(FAR struct bt_driver_s *btdev) return -EALREADY; } + memset(&g_btdev, 0, sizeof(g_btdev)); + g_btdev.btdev = btdev; return 0; } diff --git a/wireless/bluetooth/bt_hcicore.h b/wireless/bluetooth/bt_hcicore.h index 8bc2047fb8..e4c29cbea4 100644 --- a/wireless/bluetooth/bt_hcicore.h +++ b/wireless/bluetooth/bt_hcicore.h @@ -100,6 +100,10 @@ struct bt_dev_s sem_t le_pkts_sem; #endif + /* TX thread status */ + + int tx_status; + /* Number of commands controller can accept */ uint8_t ncmd; @@ -255,6 +259,19 @@ struct bt_eir_s; /* Forward reference */ int bt_initialize(void); +/**************************************************************************** + * Name: bt_deinitialize + * + * Description: + * Deinitialize Bluetooth. + * + * Returned Value: + * Zero on success or (negative) error code otherwise. + * + ****************************************************************************/ + +int bt_deinitialize(void); + /**************************************************************************** * Name: bt_driver_register * diff --git a/wireless/bluetooth/bt_l2cap.c b/wireless/bluetooth/bt_l2cap.c index a7e4f011a1..43b71b5ce6 100644 --- a/wireless/bluetooth/bt_l2cap.c +++ b/wireless/bluetooth/bt_l2cap.c @@ -454,6 +454,10 @@ int bt_l2cap_init(void) .receive = le_sig, }; + g_channels = NULL; + g_default = NULL; + + bt_conn_initialize(); bt_att_initialize(); ret = bt_smp_initialize(); diff --git a/wireless/bluetooth/bt_netdev.c b/wireless/bluetooth/bt_netdev.c index 9d829a0be7..a2b5d446eb 100644 --- a/wireless/bluetooth/bt_netdev.c +++ b/wireless/bluetooth/bt_netdev.c @@ -1315,9 +1315,63 @@ int bt_netdev_register(FAR struct bt_driver_s *btdev) errout: + btnet_ifdown(netdev); + bt_driver_unregister(btdev); + /* Free memory and return the error */ - kmm_free(priv); + kmm_free(btdev->bt_net); + btdev->bt_net = NULL; + return ret; +} + +/**************************************************************************** + * Name: bt_netdev_unregister + * + * Description: + * Unregister a network a driver registered by bt_netdev_register. + * + * Input Parameters: + * btdev - An instance of the low-level driver interface structure. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the failure. + * + ****************************************************************************/ + +int bt_netdev_unregister(FAR struct bt_driver_s *btdev) +{ + int ret; + FAR struct btnet_driver_s *priv; + + if (!btdev) + { + return -EINVAL; + } + + priv = (FAR struct btnet_driver_s *)btdev->bt_net; + if (!priv) + { + nerr("ERROR: bt_driver_s is probably not registered\n"); + return -EINVAL; + } + + btnet_ifdown(&priv->bd_dev.r_dev); + + ret = netdev_unregister(&priv->bd_dev.r_dev); + if (ret < 0) + { + nerr("ERROR: netdev_unregister bfailed: %d\n", ret); + } + + bt_deinitialize(); + + bt_driver_unregister(btdev); + + kmm_free(btdev->bt_net); + btdev->bt_net = NULL; + return ret; } diff --git a/wireless/bluetooth/bt_smp.c b/wireless/bluetooth/bt_smp.c index f3977bdf25..72e75871f9 100644 --- a/wireless/bluetooth/bt_smp.c +++ b/wireless/bluetooth/bt_smp.c @@ -1584,6 +1584,8 @@ int bt_smp_initialize(void) .encrypt_change = bt_smp_encrypt_change, }; + memset(g_smp_pool, 0, sizeof(g_smp_pool)); + bt_l2cap_chan_register(&chan); return smp_self_test();