#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/interrupt.h>
-#include <linux/notifier.h>
#include <net/sock.h>
#include <asm/system.h>
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-static bool enable_le;
-
/* Handle HCI Event packets */
static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb)
hci_req_complete(hdev, HCI_OP_RESET, status);
- /* Reset all flags, except persistent ones */
- hdev->dev_flags &= BIT(HCI_MGMT) | BIT(HCI_SETUP) | BIT(HCI_AUTO_OFF);
+ /* Reset all non-persistent flags */
+ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN));
}
static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb)
hci_dev_lock(hdev);
- if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_set_local_name_complete(hdev, sent, status);
-
if (status == 0)
memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_set_local_name_complete(hdev, sent, status);
+
hci_dev_unlock(hdev);
}
clear_bit(HCI_AUTH, &hdev->flags);
}
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_auth_enable_complete(hdev, status);
+
hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status);
}
hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status);
}
-static void hci_cc_read_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
-{
- struct hci_rp_read_ssp_mode *rp = (void *) skb->data;
-
- BT_DBG("%s status 0x%x", hdev->name, rp->status);
-
- if (rp->status)
- return;
-
- if (rp->mode)
- set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
- else
- clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
-}
-
static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb)
{
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_SSP_MODE);
if (!sent)
return;
- if (*((u8 *) sent))
- set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
- else
- clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ if (test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status);
+ else if (!status) {
+ if (*((u8 *) sent))
+ set_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ else
+ clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags);
+ }
}
static u8 hci_get_inquiry_mode(struct hci_dev *hdev)
memset(&cp, 0, sizeof(cp));
- if (enable_le) {
+ if (enable_le && test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
cp.le = 1;
cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
}
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
if (hdev->features[6] & LMP_SIMPLE_PAIR) {
- u8 mode = 0x01;
- hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode);
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ u8 mode = 0x01;
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE,
+ sizeof(mode), &mode);
+ } else {
+ struct hci_cp_write_eir cp;
+
+ memset(hdev->eir, 0, sizeof(hdev->eir));
+ memset(&cp, 0, sizeof(cp));
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
+ }
}
if (hdev->features[3] & LMP_RSSI_INQ)
sizeof(cp), &cp);
}
+ if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) {
+ u8 enable = 1;
+ hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE,
+ sizeof(enable), &enable);
+ }
+
if (hdev->features[4] & LMP_LE)
hci_set_le_support(hdev);
}
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
+ 0, rp->status);
hci_dev_unlock(hdev);
}
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr,
+ ACL_LINK, 0,
rp->status);
hci_dev_unlock(hdev);
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr,
- rp->status);
+ mgmt_user_passkey_reply_complete(hdev, &rp->bdaddr, ACL_LINK,
+ 0, rp->status);
hci_dev_unlock(hdev);
}
if (test_bit(HCI_MGMT, &hdev->dev_flags))
mgmt_user_passkey_neg_reply_complete(hdev, &rp->bdaddr,
+ ACL_LINK, 0,
rp->status);
hci_dev_unlock(hdev);
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
+
+ hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status);
+
+ if (status) {
+ hci_dev_lock(hdev);
+ mgmt_start_discovery_failed(hdev, status);
+ hci_dev_unlock(hdev);
+ return;
+ }
}
static void hci_cc_le_set_scan_enable(struct hci_dev *hdev,
BT_DBG("%s status 0x%x", hdev->name, status);
- if (status)
- return;
-
cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE);
if (!cp)
return;
switch (cp->enable) {
case LE_SCANNING_ENABLED:
+ hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status);
+
+ if (status) {
+ hci_dev_lock(hdev);
+ mgmt_start_discovery_failed(hdev, status);
+ hci_dev_unlock(hdev);
+ return;
+ }
+
set_bit(HCI_LE_SCAN, &hdev->dev_flags);
cancel_delayed_work_sync(&hdev->adv_work);
hci_dev_lock(hdev);
hci_adv_entries_clear(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
hci_dev_unlock(hdev);
break;
case LE_SCANNING_DISABLED:
+ if (status)
+ return;
+
clear_bit(HCI_LE_SCAN, &hdev->dev_flags);
schedule_delayed_work(&hdev->adv_work, ADV_CLEAR_TIMEOUT);
+
+ if (hdev->discovery.type == DISCOV_TYPE_INTERLEAVED) {
+ mgmt_interleaved_discovery(hdev);
+ } else {
+ hci_dev_lock(hdev);
+ hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
+ hci_dev_unlock(hdev);
+ }
+
break;
default:
struct sk_buff *skb)
{
struct hci_cp_read_local_ext_features cp;
+ struct hci_cp_write_le_host_supported *sent;
__u8 status = *((__u8 *) skb->data);
BT_DBG("%s status 0x%x", hdev->name, status);
+ sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED);
+ if (sent && test_bit(HCI_MGMT, &hdev->dev_flags))
+ mgmt_le_enable_complete(hdev, sent->le, status);
+
if (status)
return;
set_bit(HCI_INQUIRY, &hdev->flags);
hci_dev_lock(hdev);
- hci_discovery_set_state(hdev, DISCOVERY_INQUIRY);
+ hci_discovery_set_state(hdev, DISCOVERY_FINDING);
hci_dev_unlock(hdev);
}
hci_dev_unlock(hdev);
}
+static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
+{
+ struct hci_cp_disconnect *cp;
+ struct hci_conn *conn;
+
+ if (!status)
+ return;
+
+ cp = hci_sent_cmd_data(hdev, HCI_OP_DISCONNECT);
+ if (!cp)
+ return;
+
+ hci_dev_lock(hdev);
+
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
+ if (conn)
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, 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;
hci_dev_lock(hdev);
- if (discov->state != DISCOVERY_INQUIRY)
+ if (discov->state != DISCOVERY_FINDING)
goto unlock;
if (list_empty(&discov->resolve)) {
if (test_and_clear_bit(HCI_CONN_MGMT_CONNECTED, &conn->flags) &&
(conn->type == ACL_LINK || conn->type == LE_LINK)) {
if (ev->status != 0)
- mgmt_disconnect_failed(hdev, &conn->dst, ev->status);
+ mgmt_disconnect_failed(hdev, &conn->dst, conn->type,
+ conn->dst_type, ev->status);
else
mgmt_device_disconnected(hdev, &conn->dst, conn->type,
conn->dst_type);
conn->sec_level = conn->pending_sec_level;
}
} else {
- mgmt_auth_failed(hdev, &conn->dst, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ ev->status);
}
clear_bit(HCI_CONN_AUTH_PEND, &conn->flags);
hci_cc_host_buffer_size(hdev, skb);
break;
- case HCI_OP_READ_SSP_MODE:
- hci_cc_read_ssp_mode(hdev, skb);
- break;
-
case HCI_OP_WRITE_SSP_MODE:
hci_cc_write_ssp_mode(hdev, skb);
break;
break;
case HCI_OP_DISCONNECT:
- if (ev->status != 0)
- mgmt_disconnect_failed(hdev, NULL, ev->status);
+ hci_cs_disconnect(hdev, ev->status);
break;
case HCI_OP_LE_CREATE_CONN:
struct hci_cp_io_capability_reply cp;
bacpy(&cp.bdaddr, &ev->bdaddr);
- cp.capability = conn->io_capability;
+ /* Change the IO capability from KeyboardDisplay
+ * to DisplayYesNo as it is not supported by BT spec. */
+ cp.capability = (conn->io_capability == 0x04) ?
+ 0x01 : conn->io_capability;
conn->auth_type = hci_get_auth_req(conn);
cp.authentication = conn->auth_type;
}
confirm:
- mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey,
+ mgmt_user_confirm_request(hdev, &ev->bdaddr, ACL_LINK, 0, ev->passkey,
confirm_hint);
unlock:
hci_dev_lock(hdev);
if (test_bit(HCI_MGMT, &hdev->dev_flags))
- mgmt_user_passkey_request(hdev, &ev->bdaddr);
+ mgmt_user_passkey_request(hdev, &ev->bdaddr, ACL_LINK, 0);
hci_dev_unlock(hdev);
}
* 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)
- mgmt_auth_failed(hdev, &conn->dst, ev->status);
+ mgmt_auth_failed(hdev, &conn->dst, conn->type, conn->dst_type,
+ ev->status);
hci_conn_put(conn);
struct hci_cp_le_ltk_reply cp;
struct hci_cp_le_ltk_neg_reply neg;
struct hci_conn *conn;
- struct link_key *ltk;
+ struct smp_ltk *ltk;
BT_DBG("%s handle %d", hdev->name, cpu_to_le16(ev->handle));
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
cp.handle = cpu_to_le16(conn->handle);
- conn->pin_length = ltk->pin_len;
+
+ if (ltk->authenticated)
+ conn->sec_level = BT_SECURITY_HIGH;
hci_send_cmd(hdev, HCI_OP_LE_LTK_REPLY, sizeof(cp), &cp);
+ if (ltk->type & HCI_SMP_STK) {
+ list_del(<k->list);
+ kfree(ltk);
+ }
+
hci_dev_unlock(hdev);
return;
kfree_skb(skb);
hdev->stat.evt_rx++;
}
-
-/* Generate internal stack event */
-void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
-{
- struct hci_event_hdr *hdr;
- struct hci_ev_stack_internal *ev;
- struct sk_buff *skb;
-
- skb = bt_skb_alloc(HCI_EVENT_HDR_SIZE + sizeof(*ev) + dlen, GFP_ATOMIC);
- if (!skb)
- return;
-
- hdr = (void *) skb_put(skb, HCI_EVENT_HDR_SIZE);
- hdr->evt = HCI_EV_STACK_INTERNAL;
- hdr->plen = sizeof(*ev) + dlen;
-
- ev = (void *) skb_put(skb, sizeof(*ev) + dlen);
- ev->type = type;
- memcpy(ev->data, data, dlen);
-
- bt_cb(skb)->incoming = 1;
- __net_timestamp(skb);
-
- bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
- skb->dev = (void *) hdev;
- hci_send_to_sock(hdev, skb, NULL);
- kfree_skb(skb);
-}
-
-module_param(enable_le, bool, 0644);
-MODULE_PARM_DESC(enable_le, "Enable LE support");