diff options
Diffstat (limited to 'net/bluetooth/hci_event.c')
-rw-r--r-- | net/bluetooth/hci_event.c | 190 |
1 files changed, 130 insertions, 60 deletions
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 715d7e33fba0..2022b43c7353 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -29,6 +29,7 @@ #include <net/bluetooth/bluetooth.h> #include <net/bluetooth/hci_core.h> +#include <net/bluetooth/mgmt.h> /* Handle HCI Event packets */ @@ -303,7 +304,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (status != 0) { + if (status) { mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; goto done; @@ -513,7 +514,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (hdev->features[3] & LMP_RSSI_INQ) events[4] |= 0x02; /* Inquiry Result with RSSI */ - if (hdev->features[5] & LMP_SNIFF_SUBR) + if (lmp_sniffsubr_capable(hdev)) events[5] |= 0x20; /* Sniff Subrating */ if (hdev->features[5] & LMP_PAUSE_ENC) @@ -522,13 +523,13 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (hdev->features[6] & LMP_EXT_INQ) events[5] |= 0x40; /* Extended Inquiry Result */ - if (hdev->features[6] & LMP_NO_FLUSH) + if (lmp_no_flush_capable(hdev)) events[7] |= 0x01; /* Enhanced Flush Complete */ if (hdev->features[7] & LMP_LSTO) events[6] |= 0x80; /* Link Supervision Timeout Changed */ - if (hdev->features[6] & LMP_SIMPLE_PAIR) { + if (lmp_ssp_capable(hdev)) { events[6] |= 0x01; /* IO Capability Request */ events[6] |= 0x02; /* IO Capability Response */ events[6] |= 0x04; /* User Confirmation Request */ @@ -541,7 +542,7 @@ static void hci_setup_event_mask(struct hci_dev *hdev) * Features Notification */ } - if (hdev->features[4] & LMP_LE) + if (lmp_le_capable(hdev)) events[7] |= 0x20; /* LE Meta-Event */ hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); @@ -623,11 +624,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev) struct hci_cp_write_def_link_policy cp; u16 link_policy = 0; - if (hdev->features[0] & LMP_RSWITCH) + if (lmp_rswitch_capable(hdev)) link_policy |= HCI_LP_RSWITCH; if (hdev->features[0] & LMP_HOLD) link_policy |= HCI_LP_HOLD; - if (hdev->features[0] & LMP_SNIFF) + if (lmp_sniff_capable(hdev)) link_policy |= HCI_LP_SNIFF; if (hdev->features[1] & LMP_PARK) link_policy |= HCI_LP_PARK; @@ -686,7 +687,7 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, hdev->esco_type |= (ESCO_HV3); } - if (hdev->features[3] & LMP_ESCO) + if (lmp_esco_capable(hdev)) hdev->esco_type |= (ESCO_EV3); if (hdev->features[4] & LMP_EV4) @@ -746,7 +747,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, break; } - if (test_bit(HCI_INIT, &hdev->flags) && hdev->features[4] & LMP_LE) + if (test_bit(HCI_INIT, &hdev->flags) && lmp_le_capable(hdev)) hci_set_le_support(hdev); done: @@ -925,7 +926,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); - if (rp->status != 0) + if (rp->status) goto unlock; cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); @@ -1625,43 +1626,30 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status) 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%2.2x", hdev->name, status); - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_CREATE_CONN); - if (!cp) - return; + if (status) { + hci_dev_lock(hdev); - hci_dev_lock(hdev); + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (!conn) { + hci_dev_unlock(hdev); + return; + } - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->peer_addr); + BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&conn->dst), + conn); - BT_DBG("%s bdaddr %s conn %p", hdev->name, batostr(&cp->peer_addr), - conn); + conn->state = BT_CLOSED; + mgmt_connect_failed(hdev, &conn->dst, conn->type, + conn->dst_type, status); + hci_proto_connect_cfm(conn, status); + hci_conn_del(conn); - if (status) { - if (conn && conn->state == BT_CONNECT) { - conn->state = BT_CLOSED; - mgmt_connect_failed(hdev, &cp->peer_addr, conn->type, - conn->dst_type, status); - 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->dst_type = cp->peer_addr_type; - conn->out = true; - } else { - BT_ERR("No memory for new connection"); - } - } + hci_dev_unlock(hdev); } - - hci_dev_unlock(hdev); } static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) @@ -1904,6 +1892,22 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) } } +static u8 hci_to_mgmt_reason(u8 err) +{ + switch (err) { + case HCI_ERROR_CONNECTION_TIMEOUT: + return MGMT_DEV_DISCONN_TIMEOUT; + case HCI_ERROR_REMOTE_USER_TERM: + case HCI_ERROR_REMOTE_LOW_RESOURCES: + case HCI_ERROR_REMOTE_POWER_OFF: + return MGMT_DEV_DISCONN_REMOTE; + case HCI_ERROR_LOCAL_HOST_TERM: + return MGMT_DEV_DISCONN_LOCAL_HOST; + default: + return MGMT_DEV_DISCONN_UNKNOWN; + } +} + static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_disconn_complete *ev = (void *) skb->data; @@ -1922,12 +1926,15 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) && (conn->type == ACL_LINK || conn->type == LE_LINK)) { - if (ev->status != 0) + if (ev->status) { mgmt_disconnect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); - else + } else { + u8 reason = hci_to_mgmt_reason(ev->reason); + mgmt_device_disconnected(hdev, &conn->dst, conn->type, - conn->dst_type); + conn->dst_type, reason); + } } if (ev->status == 0) { @@ -3268,12 +3275,67 @@ static void hci_user_passkey_request_evt(struct hci_dev *hdev, BT_DBG("%s", hdev->name); - hci_dev_lock(hdev); - if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0); +} - hci_dev_unlock(hdev); +static void hci_user_passkey_notify_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_user_passkey_notify *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s", hdev->name); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) + return; + + conn->passkey_notify = __le32_to_cpu(ev->passkey); + conn->passkey_entered = 0; + + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, + conn->dst_type, conn->passkey_notify, + conn->passkey_entered); +} + +static void hci_keypress_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_keypress_notify *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s", hdev->name); + + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); + if (!conn) + return; + + switch (ev->type) { + case HCI_KEYPRESS_STARTED: + conn->passkey_entered = 0; + return; + + case HCI_KEYPRESS_ENTERED: + conn->passkey_entered++; + break; + + case HCI_KEYPRESS_ERASED: + conn->passkey_entered--; + break; + + case HCI_KEYPRESS_CLEARED: + conn->passkey_entered = 0; + break; + + case HCI_KEYPRESS_COMPLETED: + return; + } + + if (test_bit(HCI_MGMT, &hdev->dev_flags)) + mgmt_user_passkey_notify(hdev, &conn->dst, conn->type, + conn->dst_type, conn->passkey_notify, + conn->passkey_entered); } static void hci_simple_pair_complete_evt(struct hci_dev *hdev, @@ -3295,7 +3357,7 @@ static void hci_simple_pair_complete_evt(struct hci_dev *hdev, * initiated the authentication. A traditional auth_complete * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status != 0) + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && ev->status) mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); @@ -3366,11 +3428,23 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); - if (ev->status) { - conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); - if (!conn) + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); + if (!conn) { + conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); + if (!conn) { + BT_ERR("No memory for new connection"); goto unlock; + } + + conn->dst_type = ev->bdaddr_type; + if (ev->role == LE_CONN_ROLE_MASTER) { + conn->out = true; + conn->link_mode |= HCI_LM_MASTER; + } + } + + if (ev->status) { mgmt_connect_failed(hdev, &conn->dst, conn->type, conn->dst_type, ev->status); hci_proto_connect_cfm(conn, ev->status); @@ -3379,18 +3453,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) goto unlock; } - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } - - conn->dst_type = ev->bdaddr_type; - } - if (!test_and_set_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags)) mgmt_device_connected(hdev, &ev->bdaddr, conn->type, conn->dst_type, 0, NULL, 0, NULL); @@ -3640,6 +3702,14 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_user_passkey_request_evt(hdev, skb); break; + case HCI_EV_USER_PASSKEY_NOTIFY: + hci_user_passkey_notify_evt(hdev, skb); + break; + + case HCI_EV_KEYPRESS_NOTIFY: + hci_keypress_notify_evt(hdev, skb); + break; + case HCI_EV_SIMPLE_PAIR_COMPLETE: hci_simple_pair_complete_evt(hdev, skb); break; |