in wireless/bluetooth/bt_ioctl.c [471:966]
int btnet_ioctl(FAR struct net_driver_s *netdev, int cmd, unsigned long arg)
{
FAR struct btreq_s *btreq = (FAR struct btreq_s *)((uintptr_t)arg);
int ret;
wlinfo("cmd=%04x arg=%lu\n", cmd, arg);
DEBUGASSERT(netdev != NULL && netdev->d_private != NULL);
if (btreq == NULL)
{
return -EINVAL;
}
wlinfo("ifname: %s\n", btreq->btr_name);
/* Process the command */
switch (cmd)
{
case SIOCBTCONNECT:
{
FAR struct bt_conn_s *conn;
conn = bt_conn_create_le(&btreq->btr_rmtpeer);
if (!conn)
{
wlerr("Connection failed\n");
ret = -ENOTCONN;
}
else
{
wlinfo("Connection pending\n");
ret = OK;
}
}
break;
case SIOCBTDISCONNECT:
{
FAR struct bt_conn_s *conn;
conn = bt_conn_lookup_addr_le(&btreq->btr_rmtpeer);
if (!conn)
{
wlerr("Peer not connected\n");
ret = -ENOTCONN;
}
else
{
ret = bt_conn_disconnect(conn,
BT_HCI_ERR_REMOTE_USER_TERM_CONN);
if (ret == -ENOTCONN)
{
wlerr("Already disconnected\n");
}
else
{
/* Release reference taken in bt_conn_create_le */
bt_conn_release(conn);
ret = OK;
}
/* Release reference taken in bt_conn_lookup_addr_le */
bt_conn_release(conn);
}
}
break;
/* SIOCGBTINFO: Get Bluetooth device Info. Given the device name,
* fill in the btreq_s structure.
*
* REVISIT: Little more than a stub at present. It does return the
* device address associated with the device name which in itself is
* important.
*/
case SIOCGBTINFO:
{
memset(&btreq->btru.btri, 0, sizeof(btreq->btru.btri));
BLUETOOTH_ADDRCOPY(btreq->btr_bdaddr.val, g_btdev.bdaddr.val);
btreq->btr_num_cmd = CONFIG_BLUETOOTH_BUFFER_PREALLOC;
btreq->btr_num_acl = CONFIG_BLUETOOTH_BUFFER_PREALLOC;
btreq->btr_acl_mtu = BLUETOOTH_MAX_MTU;
btreq->btr_sco_mtu = BLUETOOTH_MAX_MTU;
btreq->btr_max_acl = CONFIG_IOB_NBUFFERS;
ret = OK;
}
break;
/* SIOCGBTFEAT
* Get Bluetooth BR/BDR device Features. This returns the cached
* basic (page 0) and extended (page 1 & 2) features. Only page 0
* is valid.
* SIOCGBTLEFEAT
* Get Bluetooth LE device Features. This returns the cached page
* 0-2 features. Only page 0 is value.
*/
case SIOCGBTFEAT:
case SIOCGBTLEFEAT:
{
FAR const uint8_t *src;
memset(&btreq->btru.btrf, 0, sizeof(btreq->btru.btrf));
if (cmd == SIOCGBTFEAT)
{
src = g_btdev.features;
}
else
{
src = g_btdev.le_features;
}
memcpy(btreq->btr_features0, src, 8);
ret = OK;
}
break;
/* SIOCBTADVSTART: Set advertisement data, scan response data,
* advertisement parameters and start advertising.
*/
case SIOCBTADVSTART:
{
ret = bt_start_advertising(btreq->btr_advtype,
btreq->btr_advad,
btreq->btr_advsd);
wlinfo("Start advertising: %d\n", ret);
}
break;
/* SIOCBTADVSTOP: Stop advertising. */
case SIOCBTADVSTOP:
{
wlinfo("Stop advertising\n");
bt_stop_advertising();
ret = OK;
}
break;
/* SIOCBTSCANSTART: Start LE scanning. Buffered scan results may be
* obtained via SIOCBTSCANGET
*/
case SIOCBTSCANSTART:
{
/* Are we already scanning? */
if (g_scanstate.bs_scanning)
{
wlwarn("WARNING: Already scanning\n");
ret = -EBUSY;
}
else
{
/* Initialize scan state */
g_scanstate.bs_scanning = true;
g_scanstate.bs_head = 0;
g_scanstate.bs_tail = 0;
g_scanstate.msgcount = 0;
ret = bt_start_scanning(btreq->btr_dupenable,
btnet_scan_callback);
wlinfo("Start scan: %d\n", ret);
if (ret < 0)
{
g_scanstate.bs_scanning = false;
}
}
}
break;
/* SIOCBTSCANGET: Return scan results buffered since the call time
* that the SIOCBTSCANGET command was invoked.
*/
case SIOCBTSCANGET:
{
ret = btnet_scan_result(btreq->btr_rsp, btreq->btr_nrsp);
wlinfo("Get scan results: %d\n", ret);
if (ret >= 0)
{
btreq->btr_nrsp = ret;
ret = OK;
}
}
break;
/* SIOCBTSCANSTOP:
* Stop LE scanning and discard any buffered results.
*/
case SIOCBTSCANSTOP:
{
/* Stop scanning */
ret = bt_stop_scanning();
wlinfo("Stop scanning: %d\n", ret);
g_scanstate.bs_scanning = false;
}
break;
/* SIOCBTSECURITY: Enable security for a connection. */
case SIOCBTSECURITY:
{
FAR struct bt_conn_s *conn;
/* Get the connection associated with the provided LE address */
conn = bt_conn_lookup_addr_le(&btreq->btr_secaddr);
if (conn == NULL)
{
wlwarn("WARNING: Peer not connected\n");
ret = -ENOTCONN;
}
else
{
ret = bt_conn_security(conn, btreq->btr_seclevel);
if (ret < 0)
{
wlerr("ERROR: Security setting failed: %d\n", ret);
}
bt_conn_release(conn);
}
}
break;
/* SIOCBTEXCHANGE: Exchange MTUs */
case SIOCBTEXCHANGE:
{
FAR struct bt_conn_s *conn;
/* Get the connection associated with the provided LE address */
conn = bt_conn_lookup_addr_le(&btreq->btr_expeer);
if (conn == NULL)
{
wlwarn("WARNING: Peer not connected\n");
ret = -ENOTCONN;
}
else
{
struct btnet_wrstate_s wrstate;
memset(&wrstate, 0, sizeof(wrstate));
wrstate.wr_btreq = btreq;
conn->p_iostate = &wrstate;
nxsem_init(&wrstate.wr_donesem, 0, 0);
btreq->btr_wrresult = EBUSY;
ret = bt_gatt_exchange_mtu(conn, btnet_exchange_rsp);
if (ret < 0)
{
wlerr("ERROR: Exchange operation failed: %d\n", ret);
}
else
{
/* Wait for callback to complete the exchange */
ret = nxsem_wait(&wrstate.wr_donesem);
}
nxsem_destroy(&wrstate.wr_donesem);
bt_conn_release(conn);
}
}
break;
/* SIOCBTDISCOVER: Starts GATT discovery */
case SIOCBTDISCOVER:
{
FAR struct bt_conn_s *conn;
/* Get the connection associated with the provided LE address */
conn = bt_conn_lookup_addr_le(&btreq->btr_dpeer);
if (conn == NULL)
{
wlwarn("WARNING: Peer not connected\n");
ret = -ENOTCONN;
}
else
{
struct btnet_discoverstate_s dstate;
FAR struct bt_gatt_discover_params_s *params;
memset(&dstate, 0, sizeof(dstate));
/* Set up the query */
dstate.bd_uuid.type = BT_UUID_16;
dstate.bd_uuid.u.u16 = btreq->btr_duuid16;
params = &dstate.bd_params;
params->func = btnet_discover_func;
params->destroy = btnet_discover_destroy;
params->start_handle = btreq->btr_dstart;
params->end_handle = btreq->btr_dend;
params->p_data = (FAR void *)arg;
btreq->btr_indx = 0;
if (btreq->btr_duuid16 == 0)
{
params->uuid = NULL;
}
else
{
params->uuid = &dstate.bd_uuid;
}
nxsem_init(&dstate.bd_donesem, 0, 0);
/* Start the query */
switch (btreq->btr_dtype)
{
case GATT_DISCOVER:
ret = bt_gatt_discover(conn, params);
break;
case GATT_DISCOVER_DESC:
ret = bt_gatt_discover_descriptor(conn, params);
break;
case GATT_DISCOVER_CHAR:
ret = bt_gatt_discover_characteristic(conn, params);
break;
default:
wlerr("ERROR: Unrecognized GATT discover type: %u\n",
btreq->btr_dtype);
ret = -EINVAL;
}
if (ret < 0)
{
wlerr("ERROR: Failed to start discovery: %d\n", ret);
}
else
{
ret = nxsem_wait(&dstate.bd_donesem);
if (ret == 0)
{
btreq->btr_gnrsp = btreq->btr_indx;
}
}
nxsem_destroy(&dstate.bd_donesem);
bt_conn_release(conn);
}
}
break;
/* SIOCBTGATTRD: Read GATT data */
case SIOCBTGATTRD:
{
FAR struct bt_conn_s *conn;
/* Get the connection associated with the provided LE address */
conn = bt_conn_lookup_addr_le(&btreq->btr_rdpeer);
if (conn == NULL)
{
wlwarn("WARNING: Peer not connected\n");
ret = -ENOTCONN;
}
else
{
/* Set up for the read */
struct btnet_rdstate_s rdstate;
memset(&rdstate, 0, sizeof(rdstate));
rdstate.rd_btreq = btreq;
conn->p_iostate = &rdstate;
nxsem_init(&rdstate.rd_donesem, 0, 0);
/* Initiate read.. single or multiple? */
if (btreq->btr_rdnhandles == 1)
{
/* Read single */
DEBUGASSERT(conn->p_iostate != NULL);
ret = bt_gatt_read(conn, btreq->btr_rdhandles[0],
btreq->btr_rdoffset,
btnet_read_callback);
}
else if (btreq->btr_rdnhandles < HCI_GATT_MAXHANDLES)
{
/* Read multiple */
DEBUGASSERT(conn->p_iostate != NULL);
DEBUGASSERT(btreq->btr_rdnhandles > 0);
ret = bt_gatt_read_multiple(conn, btreq->btr_rdhandles,
btreq->btr_rdnhandles,
btnet_read_callback);
}
else
{
ret = -ENFILE;
}
if (ret < 0)
{
wlerr("ERROR: Read operation failed: %d\n", ret);
}
else
{
/* Wait for callback to complete the transfer */
ret = nxsem_wait(&rdstate.rd_donesem);
}
nxsem_destroy(&rdstate.rd_donesem);
bt_conn_release(conn);
}
}
break;
/* SIOCBTGATTWR: Write GATT data */
case SIOCBTGATTWR:
{
DEBUGASSERT(btreq->btr_wrdata != NULL);
FAR struct bt_conn_s *conn;
/* Get the connection associated with the provided LE address */
conn = bt_conn_lookup_addr_le(&btreq->btr_wrpeer);
if (conn == NULL)
{
wlwarn("WARNING: Peer not connected\n");
ret = -ENOTCONN;
}
else
{
/* Set up for the write */
struct btnet_wrstate_s wrstate;
memset(&wrstate, 0, sizeof(wrstate));
wrstate.wr_btreq = btreq;
conn->p_iostate = &wrstate;
nxsem_init(&wrstate.wr_donesem, 0, 0);
btreq->btr_wrresult = EBUSY;
/* Initiate write */
ret = bt_gatt_write(conn, btreq->btr_wrhandle,
btreq->btr_wrdata,
btreq->btr_wrnbytes,
bnet_write_callback);
if (ret < 0)
{
wlerr("ERROR: Write operation failed: %d\n", ret);
}
else
{
/* Wait for callback to complete the transfer */
ret = nxsem_wait(&wrstate.wr_donesem);
}
nxsem_destroy(&wrstate.wr_donesem);
bt_conn_release(conn);
}
}
break;
default:
wlwarn("WARNING: Unrecognized IOCTL command: %02x\n", cmd);
ret = -ENOTTY;
break;
}
return ret;
}