|
@@ -1107,6 +1107,43 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)
|
|
|
hci_dev_unlock(hdev);
|
|
|
}
|
|
|
|
|
|
+static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status)
|
|
|
+{
|
|
|
+ struct hci_cp_le_create_conn *cp;
|
|
|
+ struct hci_conn *conn;
|
|
|
+
|
|
|
+ BT_DBG("%s status 0x%x", hdev->name, status);
|
|
|
+
|
|
|
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN);
|
|
|
+ if (!cp)
|
|
|
+ return;
|
|
|
+
|
|
|
+ hci_dev_lock(hdev);
|
|
|
+
|
|
|
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr);
|
|
|
+
|
|
|
+ BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr),
|
|
|
+ conn);
|
|
|
+
|
|
|
+ if (status) {
|
|
|
+ if (conn && conn->state == BT_CONNECT) {
|
|
|
+ conn->state = BT_CLOSED;
|
|
|
+ hci_proto_connect_cfm(conn, status);
|
|
|
+ hci_conn_del(conn);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (!conn) {
|
|
|
+ conn = hci_conn_add(hdev, LE_LINK, &cp->peer_addr);
|
|
|
+ if (conn)
|
|
|
+ conn->out = 1;
|
|
|
+ else
|
|
|
+ BT_ERR("No memory for new connection");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ hci_dev_unlock(hdev);
|
|
|
+}
|
|
|
+
|
|
|
static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
{
|
|
|
__u8 status = *((__u8 *) skb->data);
|
|
@@ -1738,6 +1775,10 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
mgmt_disconnect_failed(hdev->id);
|
|
|
break;
|
|
|
|
|
|
+ case HCI_OP_LE_CREATE_CONN:
|
|
|
+ hci_cs_le_create_conn(hdev, ev->status);
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
BT_DBG("%s opcode 0x%x", hdev->name, opcode);
|
|
|
break;
|
|
@@ -2321,6 +2362,54 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
|
|
|
hci_dev_unlock(hdev);
|
|
|
}
|
|
|
|
|
|
+static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct hci_ev_le_conn_complete *ev = (void *) skb->data;
|
|
|
+ struct hci_conn *conn;
|
|
|
+
|
|
|
+ BT_DBG("%s status %d", hdev->name, ev->status);
|
|
|
+
|
|
|
+ hci_dev_lock(hdev);
|
|
|
+
|
|
|
+ conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr);
|
|
|
+ if (!conn)
|
|
|
+ goto unlock;
|
|
|
+
|
|
|
+ if (ev->status) {
|
|
|
+ hci_proto_connect_cfm(conn, ev->status);
|
|
|
+ conn->state = BT_CLOSED;
|
|
|
+ hci_conn_del(conn);
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ conn->handle = __le16_to_cpu(ev->handle);
|
|
|
+ conn->state = BT_CONNECTED;
|
|
|
+
|
|
|
+ hci_conn_hold_device(conn);
|
|
|
+ hci_conn_add_sysfs(conn);
|
|
|
+
|
|
|
+ hci_proto_connect_cfm(conn, ev->status);
|
|
|
+
|
|
|
+unlock:
|
|
|
+ hci_dev_unlock(hdev);
|
|
|
+}
|
|
|
+
|
|
|
+static inline void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
+{
|
|
|
+ struct hci_ev_le_meta *le_ev = (void *) skb->data;
|
|
|
+
|
|
|
+ skb_pull(skb, sizeof(*le_ev));
|
|
|
+
|
|
|
+ switch (le_ev->subevent) {
|
|
|
+ case HCI_EV_LE_CONN_COMPLETE:
|
|
|
+ hci_le_conn_complete_evt(hdev, skb);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
{
|
|
|
struct hci_event_hdr *hdr = (void *) skb->data;
|
|
@@ -2461,6 +2550,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
hci_remote_host_features_evt(hdev, skb);
|
|
|
break;
|
|
|
|
|
|
+ case HCI_EV_LE_META:
|
|
|
+ hci_le_meta_evt(hdev, skb);
|
|
|
+ break;
|
|
|
+
|
|
|
default:
|
|
|
BT_DBG("%s event 0x%x", hdev->name, event);
|
|
|
break;
|