|
@@ -481,6 +481,29 @@ failed:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct mgmt_hdr *hdr;
|
|
|
+
|
|
|
+ skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
|
|
|
+ if (!skb)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
|
|
|
+
|
|
|
+ hdr = (void *) skb_put(skb, sizeof(*hdr));
|
|
|
+ hdr->opcode = cpu_to_le16(event);
|
|
|
+ hdr->len = cpu_to_le16(data_len);
|
|
|
+
|
|
|
+ memcpy(skb_put(skb, data_len), data, data_len);
|
|
|
+
|
|
|
+ hci_send_to_sock(NULL, skb, skip_sk);
|
|
|
+ kfree_skb(skb);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
|
|
|
{
|
|
|
struct mgmt_hdr *hdr;
|
|
@@ -509,6 +532,45 @@ static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int set_pairable(struct sock *sk, unsigned char *data, u16 len)
|
|
|
+{
|
|
|
+ struct mgmt_mode *cp, ev;
|
|
|
+ struct hci_dev *hdev;
|
|
|
+ u16 dev_id;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ cp = (void *) data;
|
|
|
+ dev_id = get_unaligned_le16(&cp->index);
|
|
|
+
|
|
|
+ BT_DBG("request for hci%u", dev_id);
|
|
|
+
|
|
|
+ hdev = hci_dev_get(dev_id);
|
|
|
+ if (!hdev)
|
|
|
+ return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV);
|
|
|
+
|
|
|
+ hci_dev_lock_bh(hdev);
|
|
|
+
|
|
|
+ if (cp->val)
|
|
|
+ set_bit(HCI_PAIRABLE, &hdev->flags);
|
|
|
+ else
|
|
|
+ clear_bit(HCI_PAIRABLE, &hdev->flags);
|
|
|
+
|
|
|
+ err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val);
|
|
|
+ if (err < 0)
|
|
|
+ goto failed;
|
|
|
+
|
|
|
+ put_unaligned_le16(dev_id, &ev.index);
|
|
|
+ ev.val = cp->val;
|
|
|
+
|
|
|
+ err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk);
|
|
|
+
|
|
|
+failed:
|
|
|
+ hci_dev_unlock_bh(hdev);
|
|
|
+ hci_dev_put(hdev);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|
|
{
|
|
|
unsigned char *buf;
|
|
@@ -558,6 +620,9 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
|
|
|
case MGMT_OP_SET_CONNECTABLE:
|
|
|
err = set_connectable(sk, buf + sizeof(*hdr), len);
|
|
|
break;
|
|
|
+ case MGMT_OP_SET_PAIRABLE:
|
|
|
+ err = set_pairable(sk, buf + sizeof(*hdr), len);
|
|
|
+ break;
|
|
|
default:
|
|
|
BT_DBG("Unknown op %u", opcode);
|
|
|
err = cmd_status(sk, opcode, 0x01);
|
|
@@ -574,29 +639,6 @@ done:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk)
|
|
|
-{
|
|
|
- struct sk_buff *skb;
|
|
|
- struct mgmt_hdr *hdr;
|
|
|
-
|
|
|
- skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
|
|
|
- if (!skb)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
|
|
|
-
|
|
|
- hdr = (void *) skb_put(skb, sizeof(*hdr));
|
|
|
- hdr->opcode = cpu_to_le16(event);
|
|
|
- hdr->len = cpu_to_le16(data_len);
|
|
|
-
|
|
|
- memcpy(skb_put(skb, data_len), data, data_len);
|
|
|
-
|
|
|
- hci_send_to_sock(NULL, skb, skip_sk);
|
|
|
- kfree_skb(skb);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
int mgmt_index_added(u16 index)
|
|
|
{
|
|
|
struct mgmt_ev_index_added ev;
|