Bluetooth: Set le data length command and event
[platform/kernel/linux-starfive.git] / net / bluetooth / hci_conn.c
index a6c1286..473d44a 100644 (file)
@@ -49,6 +49,16 @@ struct conn_handle_t {
        __u16 handle;
 };
 
+#ifdef TIZEN_BT
+static const struct sco_param esco_param_cvsd[] = {
+       { (EDR_ESCO_MASK & ~ESCO_2EV3) | SCO_ESCO_MASK | ESCO_EV3,
+                                     0x000a,   0x01 }, /* S3 */
+       { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007,   0x01 }, /* S2 */
+       { EDR_ESCO_MASK | ESCO_EV3,   0x0007,   0x01 }, /* S1 */
+       { EDR_ESCO_MASK | ESCO_HV3,   0xffff,   0x01 }, /* D1 */
+       { EDR_ESCO_MASK | ESCO_HV1,   0xffff,   0x01 }, /* D0 */
+};
+#else
 static const struct sco_param esco_param_cvsd[] = {
        { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000a,   0x01 }, /* S3 */
        { EDR_ESCO_MASK & ~ESCO_2EV3, 0x0007,   0x01 }, /* S2 */
@@ -56,19 +66,28 @@ static const struct sco_param esco_param_cvsd[] = {
        { EDR_ESCO_MASK | ESCO_HV3,   0xffff,   0x01 }, /* D1 */
        { EDR_ESCO_MASK | ESCO_HV1,   0xffff,   0x01 }, /* D0 */
 };
+#endif
 
 static const struct sco_param sco_param_cvsd[] = {
        { EDR_ESCO_MASK | ESCO_HV3,   0xffff,   0xff }, /* D1 */
        { EDR_ESCO_MASK | ESCO_HV1,   0xffff,   0xff }, /* D0 */
 };
 
+#ifdef TIZEN_BT
+static const struct sco_param esco_param_msbc[] = {
+       { (EDR_ESCO_MASK & ~ESCO_2EV3) | ESCO_EV3,
+                                     0x000d,   0x02 }, /* T2 */
+       { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d,   0x02 }, /* T2 */
+};
+#else
 static const struct sco_param esco_param_msbc[] = {
        { EDR_ESCO_MASK & ~ESCO_2EV3, 0x000d,   0x02 }, /* T2 */
        { EDR_ESCO_MASK | ESCO_EV3,   0x0008,   0x02 }, /* T1 */
 };
+#endif
 
 /* This function requires the caller holds hdev->lock */
-static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
+static void hci_connect_le_scan_cleanup(struct hci_conn *conn, u8 status)
 {
        struct hci_conn_params *params;
        struct hci_dev *hdev = conn->hdev;
@@ -88,9 +107,28 @@ static void hci_connect_le_scan_cleanup(struct hci_conn *conn)
 
        params = hci_pend_le_action_lookup(&hdev->pend_le_conns, bdaddr,
                                           bdaddr_type);
-       if (!params || !params->explicit_connect)
+       if (!params)
+               return;
+
+       if (params->conn) {
+               hci_conn_drop(params->conn);
+               hci_conn_put(params->conn);
+               params->conn = NULL;
+       }
+
+       if (!params->explicit_connect)
                return;
 
+       /* If the status indicates successful cancellation of
+        * the attempt (i.e. Unknown Connection Id) there's no point of
+        * notifying failure since we'll go back to keep trying to
+        * connect. The only exception is explicit connect requests
+        * where a timeout + cancel does indicate an actual failure.
+        */
+       if (status && status != HCI_ERROR_UNKNOWN_CONN_ID)
+               mgmt_connect_failed(hdev, &conn->dst, conn->type,
+                                   conn->dst_type, status);
+
        /* The connection attempt was doing scan for new RPA, and is
         * in scan phase. If params are not associated with any other
         * autoconnect action, remove them completely. If they are, just unmark
@@ -178,7 +216,7 @@ static void le_scan_cleanup(struct work_struct *work)
        rcu_read_unlock();
 
        if (c == conn) {
-               hci_connect_le_scan_cleanup(conn);
+               hci_connect_le_scan_cleanup(conn, 0x00);
                hci_conn_cleanup(conn);
        }
 
@@ -751,6 +789,11 @@ static void le_conn_timeout(struct work_struct *work)
        hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
 }
 
+struct iso_cig_params {
+       struct hci_cp_le_set_cig_params cp;
+       struct hci_cis_params cis[0x1f];
+};
+
 struct iso_list_data {
        union {
                u8  cig;
@@ -762,10 +805,7 @@ struct iso_list_data {
                u16 sync_handle;
        };
        int count;
-       struct {
-               struct hci_cp_le_set_cig_params cp;
-               struct hci_cis_params cis[0x11];
-       } pdu;
+       struct iso_cig_params pdu;
 };
 
 static void bis_list(struct hci_conn *conn, void *data)
@@ -821,6 +861,7 @@ static void terminate_big_destroy(struct hci_dev *hdev, void *data, int err)
 static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis)
 {
        struct iso_list_data *d;
+       int ret;
 
        bt_dev_dbg(hdev, "big 0x%2.2x bis 0x%2.2x", big, bis);
 
@@ -832,8 +873,12 @@ static int hci_le_terminate_big(struct hci_dev *hdev, u8 big, u8 bis)
        d->big = big;
        d->bis = bis;
 
-       return hci_cmd_sync_queue(hdev, terminate_big_sync, d,
-                                 terminate_big_destroy);
+       ret = hci_cmd_sync_queue(hdev, terminate_big_sync, d,
+                                terminate_big_destroy);
+       if (ret)
+               kfree(d);
+
+       return ret;
 }
 
 static int big_terminate_sync(struct hci_dev *hdev, void *data)
@@ -858,6 +903,7 @@ static int big_terminate_sync(struct hci_dev *hdev, void *data)
 static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
 {
        struct iso_list_data *d;
+       int ret;
 
        bt_dev_dbg(hdev, "big 0x%2.2x sync_handle 0x%4.4x", big, sync_handle);
 
@@ -869,8 +915,12 @@ static int hci_le_big_terminate(struct hci_dev *hdev, u8 big, u16 sync_handle)
        d->big = big;
        d->sync_handle = sync_handle;
 
-       return hci_cmd_sync_queue(hdev, big_terminate_sync, d,
-                                 terminate_big_destroy);
+       ret = hci_cmd_sync_queue(hdev, big_terminate_sync, d,
+                                terminate_big_destroy);
+       if (ret)
+               kfree(d);
+
+       return ret;
 }
 
 /* Cleanup BIS connection
@@ -937,6 +987,8 @@ static void cis_cleanup(struct hci_conn *conn)
        /* Check if ISO connection is a CIS and remove CIG if there are
         * no other connections using it.
         */
+       hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_BOUND, &d);
+       hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECT, &d);
        hci_conn_hash_list_state(hdev, find_cis, ISO_LINK, BT_CONNECTED, &d);
        if (d.count)
                return;
@@ -971,6 +1023,11 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        conn->tx_power = HCI_TX_POWER_INVALID;
        conn->max_tx_power = HCI_TX_POWER_INVALID;
 
+#ifdef TIZEN_BT
+       /* enable sniff mode for incoming connection */
+       conn->link_policy = hdev->link_policy;
+#endif
+
        set_bit(HCI_CONN_POWER_SAVE, &conn->flags);
        conn->disc_timeout = HCI_DISCONN_TIMEOUT;
 
@@ -1041,6 +1098,17 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst,
        return conn;
 }
 
+static bool hci_conn_unlink(struct hci_conn *conn)
+{
+       if (!conn->link)
+               return false;
+
+       conn->link->link = NULL;
+       conn->link = NULL;
+
+       return true;
+}
+
 int hci_conn_del(struct hci_conn *conn)
 {
        struct hci_dev *hdev = conn->hdev;
@@ -1052,9 +1120,17 @@ int hci_conn_del(struct hci_conn *conn)
        cancel_delayed_work_sync(&conn->idle_work);
 
        if (conn->type == ACL_LINK) {
-               struct hci_conn *sco = conn->link;
-               if (sco)
-                       sco->link = NULL;
+               struct hci_conn *link = conn->link;
+
+               if (link) {
+                       hci_conn_unlink(conn);
+                       /* Due to race, SCO connection might be not established
+                        * yet at this point. Delete it now, otherwise it is
+                        * possible for it to be stuck and can't be deleted.
+                        */
+                       if (link->handle == HCI_CONN_HANDLE_UNSET)
+                               hci_conn_del(link);
+               }
 
                /* Unacked frames */
                hdev->acl_cnt += conn->sent;
@@ -1069,7 +1145,7 @@ int hci_conn_del(struct hci_conn *conn)
                struct hci_conn *acl = conn->link;
 
                if (acl) {
-                       acl->link = NULL;
+                       hci_conn_unlink(conn);
                        hci_conn_drop(acl);
                }
 
@@ -1164,31 +1240,8 @@ EXPORT_SYMBOL(hci_get_route);
 static void hci_le_conn_failed(struct hci_conn *conn, u8 status)
 {
        struct hci_dev *hdev = conn->hdev;
-       struct hci_conn_params *params;
 
-       params = hci_pend_le_action_lookup(&hdev->pend_le_conns, &conn->dst,
-                                          conn->dst_type);
-       if (params && params->conn) {
-               hci_conn_drop(params->conn);
-               hci_conn_put(params->conn);
-               params->conn = NULL;
-       }
-
-       /* If the status indicates successful cancellation of
-        * the attempt (i.e. Unknown Connection Id) there's no point of
-        * notifying failure since we'll go back to keep trying to
-        * connect. The only exception is explicit connect requests
-        * where a timeout + cancel does indicate an actual failure.
-        */
-       if (status != HCI_ERROR_UNKNOWN_CONN_ID ||
-           (params && params->explicit_connect))
-               mgmt_connect_failed(hdev, &conn->dst, conn->type,
-                                   conn->dst_type, status);
-
-       /* Since we may have temporarily stopped the background scanning in
-        * favor of connection establishment, we should restart it.
-        */
-       hci_update_passive_scan(hdev);
+       hci_connect_le_scan_cleanup(conn, status);
 
        /* Enable advertising in case this was a failed connection
         * attempt as a peripheral.
@@ -1222,15 +1275,15 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
 {
        struct hci_conn *conn = data;
 
+       bt_dev_dbg(hdev, "err %d", err);
+
        hci_dev_lock(hdev);
 
        if (!err) {
-               hci_connect_le_scan_cleanup(conn);
+               hci_connect_le_scan_cleanup(conn, 0x00);
                goto done;
        }
 
-       bt_dev_err(hdev, "request failed to create LE connection: err %d", err);
-
        /* Check if connection is still pending */
        if (conn != hci_lookup_le_connect(hdev))
                goto done;
@@ -1678,10 +1731,33 @@ static int hci_le_create_big(struct hci_conn *conn, struct bt_iso_qos *qos)
        return hci_send_cmd(hdev, HCI_OP_LE_CREATE_BIG, sizeof(cp), &cp);
 }
 
+static void set_cig_params_complete(struct hci_dev *hdev, void *data, int err)
+{
+       struct iso_cig_params *pdu = data;
+
+       bt_dev_dbg(hdev, "");
+
+       if (err)
+               bt_dev_err(hdev, "Unable to set CIG parameters: %d", err);
+
+       kfree(pdu);
+}
+
+static int set_cig_params_sync(struct hci_dev *hdev, void *data)
+{
+       struct iso_cig_params *pdu = data;
+       u32 plen;
+
+       plen = sizeof(pdu->cp) + pdu->cp.num_cis * sizeof(pdu->cis[0]);
+       return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_CIG_PARAMS, plen, pdu,
+                                    HCI_CMD_TIMEOUT);
+}
+
 static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
 {
        struct hci_dev *hdev = conn->hdev;
        struct iso_list_data data;
+       struct iso_cig_params *pdu;
 
        memset(&data, 0, sizeof(data));
 
@@ -1752,12 +1828,18 @@ static bool hci_le_set_cig_params(struct hci_conn *conn, struct bt_iso_qos *qos)
        if (qos->cis == BT_ISO_QOS_CIS_UNSET || !data.pdu.cp.num_cis)
                return false;
 
-       if (hci_send_cmd(hdev, HCI_OP_LE_SET_CIG_PARAMS,
-                        sizeof(data.pdu.cp) +
-                        (data.pdu.cp.num_cis * sizeof(*data.pdu.cis)),
-                        &data.pdu) < 0)
+       pdu = kzalloc(sizeof(*pdu), GFP_KERNEL);
+       if (!pdu)
                return false;
 
+       memcpy(pdu, &data.pdu, sizeof(*pdu));
+
+       if (hci_cmd_sync_queue(hdev, set_cig_params_sync, pdu,
+                              set_cig_params_complete) < 0) {
+               kfree(pdu);
+               return false;
+       }
+
        return true;
 }
 
@@ -1881,7 +1963,7 @@ static int hci_create_cis_sync(struct hci_dev *hdev, void *data)
                        continue;
 
                /* Check if all CIS(s) belonging to a CIG are ready */
-               if (conn->link->state != BT_CONNECTED ||
+               if (!conn->link || conn->link->state != BT_CONNECTED ||
                    conn->state != BT_CONNECT) {
                        cmd.cp.num_cis = 0;
                        break;
@@ -1973,16 +2055,14 @@ static void hci_iso_qos_setup(struct hci_dev *hdev, struct hci_conn *conn,
                qos->latency = conn->le_conn_latency;
 }
 
-static struct hci_conn *hci_bind_bis(struct hci_conn *conn,
-                                    struct bt_iso_qos *qos)
+static void hci_bind_bis(struct hci_conn *conn,
+                        struct bt_iso_qos *qos)
 {
        /* Update LINK PHYs according to QoS preference */
        conn->le_tx_phy = qos->out.phy;
        conn->le_tx_phy = qos->out.phy;
        conn->iso_qos = *qos;
        conn->state = BT_BOUND;
-
-       return conn;
 }
 
 static int create_big_sync(struct hci_dev *hdev, void *data)
@@ -2118,11 +2198,7 @@ struct hci_conn *hci_connect_bis(struct hci_dev *hdev, bdaddr_t *dst,
        if (IS_ERR(conn))
                return conn;
 
-       conn = hci_bind_bis(conn, qos);
-       if (!conn) {
-               hci_conn_drop(conn);
-               return ERR_PTR(-ENOMEM);
-       }
+       hci_bind_bis(conn, qos);
 
        /* Add Basic Announcement into Peridic Adv Data if BASE is set */
        if (base_len && base) {
@@ -2397,6 +2473,58 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
 }
 EXPORT_SYMBOL(hci_conn_switch_role);
 
+#ifdef TIZEN_BT
+int hci_conn_change_supervision_timeout(struct hci_conn *conn, __u16 timeout)
+{
+       struct hci_cp_write_link_supervision_timeout cp;
+
+       if (!((get_link_mode(conn)) & HCI_LM_MASTER))
+               return 1;
+
+       if (conn->handle == 0)
+               return 1;
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle  = cpu_to_le16(conn->handle);
+       cp.timeout = cpu_to_le16(timeout);
+
+       if (hci_send_cmd(conn->hdev, HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT,
+                        sizeof(cp), &cp) < 0)
+               BT_ERR("HCI_OP_WRITE_LINK_SUPERVISION_TIMEOUT is failed");
+
+       return 0;
+}
+
+int hci_le_set_data_length(struct hci_conn *conn, u16 tx_octets, u16 tx_time)
+{
+       struct hci_dev *hdev = conn->hdev;
+       struct hci_conn_params *params;
+       struct hci_cp_le_set_data_len cp;
+
+       hci_dev_lock(hdev);
+
+       params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
+       if (params) {
+               params->max_tx_octets = tx_octets;
+               params->max_tx_time = tx_time;
+       }
+
+       hci_dev_unlock(hdev);
+
+       memset(&cp, 0, sizeof(cp));
+       cp.handle = cpu_to_le16(conn->handle);
+       cp.tx_len = cpu_to_le16(tx_octets);
+       cp.tx_time = cpu_to_le16(tx_time);
+
+       hci_send_cmd(hdev, HCI_OP_LE_SET_DATA_LEN, sizeof(cp), &cp);
+
+       if (params)
+               return 0x01;
+
+       return 0x00;
+}
+#endif
+
 /* Enter active mode */
 void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
 {
@@ -2417,9 +2545,18 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active)
        }
 
 timer:
+#ifdef TIZEN_BT
+       if (hdev->idle_timeout > 0) {
+               /* Sniff timer cancel */
+               cancel_delayed_work(&conn->idle_work);
+               queue_delayed_work(hdev->workqueue, &conn->idle_work,
+                                  msecs_to_jiffies(hdev->idle_timeout));
+       }
+#else
        if (hdev->idle_timeout > 0)
                queue_delayed_work(hdev->workqueue, &conn->idle_work,
                                   msecs_to_jiffies(hdev->idle_timeout));
+#endif
 }
 
 /* Drop all connection on the device */
@@ -2434,6 +2571,12 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
                c->state = BT_CLOSED;
 
                hci_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM);
+
+               /* Unlink before deleting otherwise it is possible that
+                * hci_conn_del removes the link which may cause the list to
+                * contain items already freed.
+                */
+               hci_conn_unlink(c);
                hci_conn_del(c);
        }
 }
@@ -2454,7 +2597,11 @@ void hci_conn_check_pending(struct hci_dev *hdev)
        hci_dev_unlock(hdev);
 }
 
+#ifndef TIZEN_BT
 static u32 get_link_mode(struct hci_conn *conn)
+#else
+u32 get_link_mode(struct hci_conn *conn)
+#endif
 {
        u32 link_mode = 0;
 
@@ -2771,6 +2918,9 @@ int hci_abort_conn(struct hci_conn *conn, u8 reason)
 {
        int r = 0;
 
+       if (test_and_set_bit(HCI_CONN_CANCEL, &conn->flags))
+               return 0;
+
        switch (conn->state) {
        case BT_CONNECTED:
        case BT_CONFIG: