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
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user