int btnet_ioctl()

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