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.
This commit is contained in:
Lwazi Dube 2023-05-03 13:05:45 -04:00 committed by Xiang Xiao
parent af559a8311
commit 16fc1b47b9
9 changed files with 238 additions and 7 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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