mirror of
https://github.com/apache/nuttx.git
synced 2025-01-13 07:28:38 +08:00
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:
parent
af559a8311
commit
16fc1b47b9
9 changed files with 238 additions and 7 deletions
|
@ -119,4 +119,21 @@ struct bt_driver_s
|
||||||
|
|
||||||
int bt_netdev_register(FAR struct bt_driver_s *btdev);
|
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 */
|
#endif /* __INCLUDE_NUTTX_WIRELESS_BLUETOOTH_BT_DRIVER_H */
|
||||||
|
|
|
@ -1846,6 +1846,8 @@ void bt_att_initialize(void)
|
||||||
.disconnected = bt_att_disconnected,
|
.disconnected = bt_att_disconnected,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
memset(g_bt_att_pool, 0, sizeof(g_bt_att_pool));
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
bt_l2cap_chan_register(&chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
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));
|
||||||
|
}
|
||||||
|
|
|
@ -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 max, uint16_t latency,
|
||||||
uint16_t timeout);
|
uint16_t timeout);
|
||||||
|
|
||||||
|
void bt_conn_initialize(void);
|
||||||
|
|
||||||
#endif /* __WIRELESS_BLUETOOTH_BT_CONN_H */
|
#endif /* __WIRELESS_BLUETOOTH_BT_CONN_H */
|
||||||
|
|
|
@ -985,7 +985,7 @@ static int hci_tx_kthread(int argc, FAR char *argv[])
|
||||||
|
|
||||||
wlinfo("started\n");
|
wlinfo("started\n");
|
||||||
|
|
||||||
for (; ; )
|
for (; g_btdev.tx_status == OK; )
|
||||||
{
|
{
|
||||||
FAR struct bt_buf_s *buf;
|
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 = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_btdev.sent_cmd = bt_buf_addref(buf);
|
/* Allow transmission if module is connected. */
|
||||||
|
|
||||||
wlinfo("Sending command %04x buf %p to driver\n",
|
if (g_btdev.tx_status == OK)
|
||||||
buf->u.hci.opcode, buf);
|
{
|
||||||
|
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);
|
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);
|
nxsem_init(&g_btdev.le_pkts_sem, 0, g_btdev.le_pkts);
|
||||||
return 0;
|
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
|
#endif
|
||||||
|
|
||||||
/* threads, fifos and semaphores initialization */
|
/* threads, fifos and semaphores initialization */
|
||||||
|
@ -1444,6 +1511,7 @@ static void cmd_queue_init(void)
|
||||||
nxsem_init(&g_btdev.ncmd_sem, 0, 1);
|
nxsem_init(&g_btdev.ncmd_sem, 0, 1);
|
||||||
|
|
||||||
g_btdev.ncmd = 1;
|
g_btdev.ncmd = 1;
|
||||||
|
g_btdev.tx_status = OK;
|
||||||
ret = kthread_create("BT HCI Tx", CONFIG_BLUETOOTH_TXCMD_PRIORITY,
|
ret = kthread_create("BT HCI Tx", CONFIG_BLUETOOTH_TXCMD_PRIORITY,
|
||||||
CONFIG_BLUETOOTH_TXCMD_STACKSIZE,
|
CONFIG_BLUETOOTH_TXCMD_STACKSIZE,
|
||||||
hci_tx_kthread, NULL);
|
hci_tx_kthread, NULL);
|
||||||
|
@ -1497,6 +1565,14 @@ int bt_initialize(void)
|
||||||
|
|
||||||
wlinfo("btdev %p\n", btdev);
|
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);
|
DEBUGASSERT(btdev != NULL);
|
||||||
bt_buf_initialize();
|
bt_buf_initialize();
|
||||||
|
|
||||||
|
@ -1505,6 +1581,9 @@ int bt_initialize(void)
|
||||||
ret = btdev->open(btdev);
|
ret = btdev->open(btdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_WIRELESS_BLUETOOTH_HOST
|
||||||
|
cmd_queue_deinit();
|
||||||
|
#endif
|
||||||
wlerr("ERROR: HCI driver open failed (%d)\n", ret);
|
wlerr("ERROR: HCI driver open failed (%d)\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1513,6 +1592,7 @@ int bt_initialize(void)
|
||||||
ret = hci_initialize();
|
ret = hci_initialize();
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
|
cmd_queue_deinit();
|
||||||
wlerr("ERROR: hci_initialize failed: %d\n", ret);
|
wlerr("ERROR: hci_initialize failed: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1523,6 +1603,38 @@ int bt_initialize(void)
|
||||||
return ret;
|
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
|
* Name: bt_driver_register
|
||||||
*
|
*
|
||||||
|
@ -1552,6 +1664,8 @@ int bt_driver_register(FAR struct bt_driver_s *btdev)
|
||||||
return -EALREADY;
|
return -EALREADY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(&g_btdev, 0, sizeof(g_btdev));
|
||||||
|
|
||||||
g_btdev.btdev = btdev;
|
g_btdev.btdev = btdev;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,10 @@ struct bt_dev_s
|
||||||
sem_t le_pkts_sem;
|
sem_t le_pkts_sem;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* TX thread status */
|
||||||
|
|
||||||
|
int tx_status;
|
||||||
|
|
||||||
/* Number of commands controller can accept */
|
/* Number of commands controller can accept */
|
||||||
|
|
||||||
uint8_t ncmd;
|
uint8_t ncmd;
|
||||||
|
@ -255,6 +259,19 @@ struct bt_eir_s; /* Forward reference */
|
||||||
|
|
||||||
int bt_initialize(void);
|
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
|
* Name: bt_driver_register
|
||||||
*
|
*
|
||||||
|
|
|
@ -454,6 +454,10 @@ int bt_l2cap_init(void)
|
||||||
.receive = le_sig,
|
.receive = le_sig,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
g_channels = NULL;
|
||||||
|
g_default = NULL;
|
||||||
|
|
||||||
|
bt_conn_initialize();
|
||||||
bt_att_initialize();
|
bt_att_initialize();
|
||||||
|
|
||||||
ret = bt_smp_initialize();
|
ret = bt_smp_initialize();
|
||||||
|
|
|
@ -1315,9 +1315,63 @@ int bt_netdev_register(FAR struct bt_driver_s *btdev)
|
||||||
|
|
||||||
errout:
|
errout:
|
||||||
|
|
||||||
|
btnet_ifdown(netdev);
|
||||||
|
bt_driver_unregister(btdev);
|
||||||
|
|
||||||
/* Free memory and return the error */
|
/* 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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1584,6 +1584,8 @@ int bt_smp_initialize(void)
|
||||||
.encrypt_change = bt_smp_encrypt_change,
|
.encrypt_change = bt_smp_encrypt_change,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
memset(g_smp_pool, 0, sizeof(g_smp_pool));
|
||||||
|
|
||||||
bt_l2cap_chan_register(&chan);
|
bt_l2cap_chan_register(&chan);
|
||||||
|
|
||||||
return smp_self_test();
|
return smp_self_test();
|
||||||
|
|
Loading…
Reference in a new issue