linux/net/bluetooth/mgmt_util.c
<<
>>
Prefs
   1/*
   2   BlueZ - Bluetooth protocol stack for Linux
   3
   4   Copyright (C) 2015  Intel Corporation
   5
   6   This program is free software; you can redistribute it and/or modify
   7   it under the terms of the GNU General Public License version 2 as
   8   published by the Free Software Foundation;
   9
  10   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  11   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  12   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
  13   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
  14   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
  15   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  16   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  17   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  18
  19   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
  20   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
  21   SOFTWARE IS DISCLAIMED.
  22*/
  23
  24#include <asm/unaligned.h>
  25
  26#include <net/bluetooth/bluetooth.h>
  27#include <net/bluetooth/hci_core.h>
  28#include <net/bluetooth/hci_mon.h>
  29#include <net/bluetooth/mgmt.h>
  30
  31#include "mgmt_util.h"
  32
  33static struct sk_buff *create_monitor_ctrl_event(__le16 index, u32 cookie,
  34                                                 u16 opcode, u16 len, void *buf)
  35{
  36        struct hci_mon_hdr *hdr;
  37        struct sk_buff *skb;
  38
  39        skb = bt_skb_alloc(6 + len, GFP_ATOMIC);
  40        if (!skb)
  41                return NULL;
  42
  43        put_unaligned_le32(cookie, skb_put(skb, 4));
  44        put_unaligned_le16(opcode, skb_put(skb, 2));
  45
  46        if (buf)
  47                skb_put_data(skb, buf, len);
  48
  49        __net_timestamp(skb);
  50
  51        hdr = skb_push(skb, HCI_MON_HDR_SIZE);
  52        hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT);
  53        hdr->index = index;
  54        hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
  55
  56        return skb;
  57}
  58
  59int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
  60                    void *data, u16 data_len, int flag, struct sock *skip_sk)
  61{
  62        struct sk_buff *skb;
  63        struct mgmt_hdr *hdr;
  64
  65        skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
  66        if (!skb)
  67                return -ENOMEM;
  68
  69        hdr = skb_put(skb, sizeof(*hdr));
  70        hdr->opcode = cpu_to_le16(event);
  71        if (hdev)
  72                hdr->index = cpu_to_le16(hdev->id);
  73        else
  74                hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
  75        hdr->len = cpu_to_le16(data_len);
  76
  77        if (data)
  78                skb_put_data(skb, data, data_len);
  79
  80        /* Time stamp */
  81        __net_timestamp(skb);
  82
  83        hci_send_to_channel(channel, skb, flag, skip_sk);
  84
  85        if (channel == HCI_CHANNEL_CONTROL)
  86                hci_send_monitor_ctrl_event(hdev, event, data, data_len,
  87                                            skb_get_ktime(skb), flag, skip_sk);
  88
  89        kfree_skb(skb);
  90        return 0;
  91}
  92
  93int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
  94{
  95        struct sk_buff *skb, *mskb;
  96        struct mgmt_hdr *hdr;
  97        struct mgmt_ev_cmd_status *ev;
  98        int err;
  99
 100        BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
 101
 102        skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
 103        if (!skb)
 104                return -ENOMEM;
 105
 106        hdr = skb_put(skb, sizeof(*hdr));
 107
 108        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
 109        hdr->index = cpu_to_le16(index);
 110        hdr->len = cpu_to_le16(sizeof(*ev));
 111
 112        ev = skb_put(skb, sizeof(*ev));
 113        ev->status = status;
 114        ev->opcode = cpu_to_le16(cmd);
 115
 116        mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
 117                                         MGMT_EV_CMD_STATUS, sizeof(*ev), ev);
 118        if (mskb)
 119                skb->tstamp = mskb->tstamp;
 120        else
 121                __net_timestamp(skb);
 122
 123        err = sock_queue_rcv_skb(sk, skb);
 124        if (err < 0)
 125                kfree_skb(skb);
 126
 127        if (mskb) {
 128                hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
 129                                    HCI_SOCK_TRUSTED, NULL);
 130                kfree_skb(mskb);
 131        }
 132
 133        return err;
 134}
 135
 136int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
 137                      void *rp, size_t rp_len)
 138{
 139        struct sk_buff *skb, *mskb;
 140        struct mgmt_hdr *hdr;
 141        struct mgmt_ev_cmd_complete *ev;
 142        int err;
 143
 144        BT_DBG("sock %p", sk);
 145
 146        skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
 147        if (!skb)
 148                return -ENOMEM;
 149
 150        hdr = skb_put(skb, sizeof(*hdr));
 151
 152        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
 153        hdr->index = cpu_to_le16(index);
 154        hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
 155
 156        ev = skb_put(skb, sizeof(*ev) + rp_len);
 157        ev->opcode = cpu_to_le16(cmd);
 158        ev->status = status;
 159
 160        if (rp)
 161                memcpy(ev->data, rp, rp_len);
 162
 163        mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
 164                                         MGMT_EV_CMD_COMPLETE,
 165                                         sizeof(*ev) + rp_len, ev);
 166        if (mskb)
 167                skb->tstamp = mskb->tstamp;
 168        else
 169                __net_timestamp(skb);
 170
 171        err = sock_queue_rcv_skb(sk, skb);
 172        if (err < 0)
 173                kfree_skb(skb);
 174
 175        if (mskb) {
 176                hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
 177                                    HCI_SOCK_TRUSTED, NULL);
 178                kfree_skb(mskb);
 179        }
 180
 181        return err;
 182}
 183
 184struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
 185                                           struct hci_dev *hdev)
 186{
 187        struct mgmt_pending_cmd *cmd;
 188
 189        list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
 190                if (hci_sock_get_channel(cmd->sk) != channel)
 191                        continue;
 192                if (cmd->opcode == opcode)
 193                        return cmd;
 194        }
 195
 196        return NULL;
 197}
 198
 199struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
 200                                                u16 opcode,
 201                                                struct hci_dev *hdev,
 202                                                const void *data)
 203{
 204        struct mgmt_pending_cmd *cmd;
 205
 206        list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
 207                if (cmd->user_data != data)
 208                        continue;
 209                if (cmd->opcode == opcode)
 210                        return cmd;
 211        }
 212
 213        return NULL;
 214}
 215
 216void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
 217                          void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
 218                          void *data)
 219{
 220        struct mgmt_pending_cmd *cmd, *tmp;
 221
 222        list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
 223                if (opcode > 0 && cmd->opcode != opcode)
 224                        continue;
 225
 226                cb(cmd, data);
 227        }
 228}
 229
 230struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
 231                                          struct hci_dev *hdev,
 232                                          void *data, u16 len)
 233{
 234        struct mgmt_pending_cmd *cmd;
 235
 236        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 237        if (!cmd)
 238                return NULL;
 239
 240        cmd->opcode = opcode;
 241        cmd->index = hdev->id;
 242
 243        cmd->param = kmemdup(data, len, GFP_KERNEL);
 244        if (!cmd->param) {
 245                kfree(cmd);
 246                return NULL;
 247        }
 248
 249        cmd->param_len = len;
 250
 251        cmd->sk = sk;
 252        sock_hold(sk);
 253
 254        list_add(&cmd->list, &hdev->mgmt_pending);
 255
 256        return cmd;
 257}
 258
 259void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
 260{
 261        sock_put(cmd->sk);
 262        kfree(cmd->param);
 263        kfree(cmd);
 264}
 265
 266void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
 267{
 268        list_del(&cmd->list);
 269        mgmt_pending_free(cmd);
 270}
 271