Bluetooth: Add add/remove_remote_oob_data management commands
authorSzymon Janc <szymon.janc@tieto.com>
Tue, 22 Mar 2011 12:12:22 +0000 (13:12 +0100)
committerGustavo F. Padovan <padovan@profusion.mobi>
Thu, 31 Mar 2011 17:22:57 +0000 (14:22 -0300)
This patch adds commands to add and remove remote OOB data to the managment
interface. Remote data is stored in kernel and can be used by corresponding
HCI commands and events when needed.

Signed-off-by: Szymon Janc <szymon.janc@tieto.com>
Signed-off-by: Gustavo F. Padovan <padovan@profusion.mobi>
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/mgmt.h
net/bluetooth/hci_core.c
net/bluetooth/hci_event.c
net/bluetooth/mgmt.c

index ac4de1afe046921ffbd625d3a77ce745569f02c6..b989a8c3e01a387fdf33d52721fa4d1966696396 100644 (file)
@@ -426,6 +426,18 @@ struct hci_rp_user_confirm_reply {
 
 #define HCI_OP_USER_CONFIRM_NEG_REPLY  0x042d
 
+#define HCI_OP_REMOTE_OOB_DATA_REPLY   0x0430
+struct hci_cp_remote_oob_data_reply {
+       bdaddr_t bdaddr;
+       __u8     hash[16];
+       __u8     randomizer[16];
+} __packed;
+
+#define HCI_OP_REMOTE_OOB_DATA_NEG_REPLY       0x0433
+struct hci_cp_remote_oob_data_neg_reply {
+       bdaddr_t bdaddr;
+} __packed;
+
 #define HCI_OP_IO_CAPABILITY_NEG_REPLY 0x0434
 struct hci_cp_io_capability_neg_reply {
        bdaddr_t bdaddr;
@@ -962,6 +974,11 @@ struct hci_ev_user_confirm_req {
        __le32          passkey;
 } __packed;
 
+#define HCI_EV_REMOTE_OOB_DATA_REQUEST 0x35
+struct hci_ev_remote_oob_data_request {
+       bdaddr_t bdaddr;
+} __packed;
+
 #define HCI_EV_SIMPLE_PAIR_COMPLETE    0x36
 struct hci_ev_simple_pair_complete {
        __u8     status;
index fd9b8a31e5b0cc011cf9de7e74ec97d25f36d1be..87bff518b54bda9fddad2060a112886a62e410fb 100644 (file)
@@ -82,6 +82,13 @@ struct link_key {
        u8 pin_len;
 };
 
+struct oob_data {
+       struct list_head list;
+       bdaddr_t bdaddr;
+       u8 hash[16];
+       u8 randomizer[16];
+};
+
 #define NUM_REASSEMBLY 4
 struct hci_dev {
        struct list_head list;
@@ -169,6 +176,8 @@ struct hci_dev {
 
        struct list_head        link_keys;
 
+       struct list_head        remote_oob_data;
+
        struct hci_dev_stats    stat;
 
        struct sk_buff_head     driver_init;
@@ -505,6 +514,13 @@ int hci_add_link_key(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr,
                                                u8 *key, u8 type, u8 pin_len);
 int hci_remove_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr);
 
+int hci_remote_oob_data_clear(struct hci_dev *hdev);
+struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
+                                                       bdaddr_t *bdaddr);
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
+                                                               u8 *randomizer);
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr);
+
 void hci_del_off_timer(struct hci_dev *hdev);
 
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
index 6ebb1265c36ef9ec9086a83c897fa8055014fc16..1a6283f9fee8a9cac394b8224e6b408c9ca4ab5e 100644 (file)
@@ -183,6 +183,18 @@ struct mgmt_rp_read_local_oob_data {
        __u8 randomizer[16];
 } __packed;
 
+#define MGMT_OP_ADD_REMOTE_OOB_DATA    0x0019
+struct mgmt_cp_add_remote_oob_data {
+       bdaddr_t bdaddr;
+       __u8 hash[16];
+       __u8 randomizer[16];
+} __packed;
+
+#define MGMT_OP_REMOVE_REMOTE_OOB_DATA 0x001A
+struct mgmt_cp_remove_remote_oob_data {
+       bdaddr_t bdaddr;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE           0x0001
 struct mgmt_ev_cmd_complete {
        __le16 opcode;
index c20cbe5ff6db7cff65365d4e786d21d96706055b..675f0a1832eedf849853369f58c1b339ca51a01f 100644 (file)
@@ -1076,6 +1076,70 @@ static void hci_cmd_timer(unsigned long arg)
        tasklet_schedule(&hdev->cmd_task);
 }
 
+struct oob_data *hci_find_remote_oob_data(struct hci_dev *hdev,
+                                                       bdaddr_t *bdaddr)
+{
+       struct oob_data *data;
+
+       list_for_each_entry(data, &hdev->remote_oob_data, list)
+               if (bacmp(bdaddr, &data->bdaddr) == 0)
+                       return data;
+
+       return NULL;
+}
+
+int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr)
+{
+       struct oob_data *data;
+
+       data = hci_find_remote_oob_data(hdev, bdaddr);
+       if (!data)
+               return -ENOENT;
+
+       BT_DBG("%s removing %s", hdev->name, batostr(bdaddr));
+
+       list_del(&data->list);
+       kfree(data);
+
+       return 0;
+}
+
+int hci_remote_oob_data_clear(struct hci_dev *hdev)
+{
+       struct oob_data *data, *n;
+
+       list_for_each_entry_safe(data, n, &hdev->remote_oob_data, list) {
+               list_del(&data->list);
+               kfree(data);
+       }
+
+       return 0;
+}
+
+int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash,
+                                                               u8 *randomizer)
+{
+       struct oob_data *data;
+
+       data = hci_find_remote_oob_data(hdev, bdaddr);
+
+       if (!data) {
+               data = kmalloc(sizeof(*data), GFP_ATOMIC);
+               if (!data)
+                       return -ENOMEM;
+
+               bacpy(&data->bdaddr, bdaddr);
+               list_add(&data->list, &hdev->remote_oob_data);
+       }
+
+       memcpy(data->hash, hash, sizeof(data->hash));
+       memcpy(data->randomizer, randomizer, sizeof(data->randomizer));
+
+       BT_DBG("%s for %s", hdev->name, batostr(bdaddr));
+
+       return 0;
+}
+
 /* Register HCI device */
 int hci_register_dev(struct hci_dev *hdev)
 {
@@ -1140,6 +1204,8 @@ int hci_register_dev(struct hci_dev *hdev)
 
        INIT_LIST_HEAD(&hdev->link_keys);
 
+       INIT_LIST_HEAD(&hdev->remote_oob_data);
+
        INIT_WORK(&hdev->power_on, hci_power_on);
        INIT_WORK(&hdev->power_off, hci_power_off);
        setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev);
@@ -1219,6 +1285,7 @@ int hci_unregister_dev(struct hci_dev *hdev)
        hci_blacklist_clear(hdev);
        hci_uuids_clear(hdev);
        hci_link_keys_clear(hdev);
+       hci_remote_oob_data_clear(hdev);
        hci_dev_unlock_bh(hdev);
 
        __hci_dev_put(hdev);
index 582ef60a8bc0d60328c4fa12aa7fbdf97ec51e42..e0aaf30536670fe991ca6ee04804fe873ca21063 100644 (file)
@@ -2471,6 +2471,37 @@ static inline void hci_remote_host_features_evt(struct hci_dev *hdev, struct sk_
        hci_dev_unlock(hdev);
 }
 
+static inline void hci_remote_oob_data_request_evt(struct hci_dev *hdev,
+                                                       struct sk_buff *skb)
+{
+       struct hci_ev_remote_oob_data_request *ev = (void *) skb->data;
+       struct oob_data *data;
+
+       BT_DBG("%s", hdev->name);
+
+       hci_dev_lock(hdev);
+
+       data = hci_find_remote_oob_data(hdev, &ev->bdaddr);
+       if (data) {
+               struct hci_cp_remote_oob_data_reply cp;
+
+               bacpy(&cp.bdaddr, &ev->bdaddr);
+               memcpy(cp.hash, data->hash, sizeof(cp.hash));
+               memcpy(cp.randomizer, data->randomizer, sizeof(cp.randomizer));
+
+               hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_REPLY, sizeof(cp),
+                                                                       &cp);
+       } else {
+               struct hci_cp_remote_oob_data_neg_reply cp;
+
+               bacpy(&cp.bdaddr, &ev->bdaddr);
+               hci_send_cmd(hdev, HCI_OP_REMOTE_OOB_DATA_NEG_REPLY, sizeof(cp),
+                                                                       &cp);
+       }
+
+       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;
@@ -2673,6 +2704,10 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
                hci_le_meta_evt(hdev, skb);
                break;
 
+       case HCI_EV_REMOTE_OOB_DATA_REQUEST:
+               hci_remote_oob_data_request_evt(hdev, skb);
+               break;
+
        default:
                BT_DBG("%s event 0x%x", hdev->name, event);
                break;
index 33b1f7400dab0ac2fc4d374df11de9c656a5634b..a42dc8ca0a6f6b36afd92d57bf9d44c77b5b192f 100644 (file)
@@ -1345,6 +1345,74 @@ unlock:
        return err;
 }
 
+static int add_remote_oob_data(struct sock *sk, u16 index, unsigned char *data,
+                                                                       u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_add_remote_oob_data *cp = (void *) data;
+       int err;
+
+       BT_DBG("hci%u ", index);
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                                                       EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
+                                                                       ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
+                                                               cp->randomizer);
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
+                                                                       0);
+
+       hci_dev_unlock_bh(hdev);
+       hci_dev_put(hdev);
+
+       return err;
+}
+
+static int remove_remote_oob_data(struct sock *sk, u16 index,
+                                               unsigned char *data, u16 len)
+{
+       struct hci_dev *hdev;
+       struct mgmt_cp_remove_remote_oob_data *cp = (void *) data;
+       int err;
+
+       BT_DBG("hci%u ", index);
+
+       if (len != sizeof(*cp))
+               return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       EINVAL);
+
+       hdev = hci_dev_get(index);
+       if (!hdev)
+               return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       ENODEV);
+
+       hci_dev_lock_bh(hdev);
+
+       err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
+       if (err < 0)
+               err = cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                                       -err);
+       else
+               err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+                                                               NULL, 0);
+
+       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;
@@ -1446,6 +1514,13 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
        case MGMT_OP_READ_LOCAL_OOB_DATA:
                err = read_local_oob_data(sk, index);
                break;
+       case MGMT_OP_ADD_REMOTE_OOB_DATA:
+               err = add_remote_oob_data(sk, index, buf + sizeof(*hdr), len);
+               break;
+       case MGMT_OP_REMOVE_REMOTE_OOB_DATA:
+               err = remove_remote_oob_data(sk, index, buf + sizeof(*hdr),
+                                                                       len);
+               break;
 
        default:
                BT_DBG("Unknown op %u", opcode);