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 <net/bluetooth/bluetooth.h> 25#include <net/bluetooth/hci_core.h> 26#include <net/bluetooth/mgmt.h> 27 28#include "mgmt_util.h" 29 30int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, 31 void *data, u16 data_len, int flag, struct sock *skip_sk) 32{ 33 struct sk_buff *skb; 34 struct mgmt_hdr *hdr; 35 36 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); 37 if (!skb) 38 return -ENOMEM; 39 40 hdr = (void *) skb_put(skb, sizeof(*hdr)); 41 hdr->opcode = cpu_to_le16(event); 42 if (hdev) 43 hdr->index = cpu_to_le16(hdev->id); 44 else 45 hdr->index = cpu_to_le16(MGMT_INDEX_NONE); 46 hdr->len = cpu_to_le16(data_len); 47 48 if (data) 49 memcpy(skb_put(skb, data_len), data, data_len); 50 51 /* Time stamp */ 52 __net_timestamp(skb); 53 54 hci_send_to_channel(channel, skb, flag, skip_sk); 55 kfree_skb(skb); 56 57 return 0; 58} 59 60int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) 61{ 62 struct sk_buff *skb; 63 struct mgmt_hdr *hdr; 64 struct mgmt_ev_cmd_status *ev; 65 int err; 66 67 BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); 68 69 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); 70 if (!skb) 71 return -ENOMEM; 72 73 hdr = (void *) skb_put(skb, sizeof(*hdr)); 74 75 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); 76 hdr->index = cpu_to_le16(index); 77 hdr->len = cpu_to_le16(sizeof(*ev)); 78 79 ev = (void *) skb_put(skb, sizeof(*ev)); 80 ev->status = status; 81 ev->opcode = cpu_to_le16(cmd); 82 83 err = sock_queue_rcv_skb(sk, skb); 84 if (err < 0) 85 kfree_skb(skb); 86 87 return err; 88} 89 90int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, 91 void *rp, size_t rp_len) 92{ 93 struct sk_buff *skb; 94 struct mgmt_hdr *hdr; 95 struct mgmt_ev_cmd_complete *ev; 96 int err; 97 98 BT_DBG("sock %p", sk); 99 100 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); 101 if (!skb) 102 return -ENOMEM; 103 104 hdr = (void *) skb_put(skb, sizeof(*hdr)); 105 106 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); 107 hdr->index = cpu_to_le16(index); 108 hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); 109 110 ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); 111 ev->opcode = cpu_to_le16(cmd); 112 ev->status = status; 113 114 if (rp) 115 memcpy(ev->data, rp, rp_len); 116 117 err = sock_queue_rcv_skb(sk, skb); 118 if (err < 0) 119 kfree_skb(skb); 120 121 return err; 122} 123 124struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode, 125 struct hci_dev *hdev) 126{ 127 struct mgmt_pending_cmd *cmd; 128 129 list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 130 if (hci_sock_get_channel(cmd->sk) != channel) 131 continue; 132 if (cmd->opcode == opcode) 133 return cmd; 134 } 135 136 return NULL; 137} 138 139struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, 140 u16 opcode, 141 struct hci_dev *hdev, 142 const void *data) 143{ 144 struct mgmt_pending_cmd *cmd; 145 146 list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 147 if (cmd->user_data != data) 148 continue; 149 if (cmd->opcode == opcode) 150 return cmd; 151 } 152 153 return NULL; 154} 155 156void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, 157 void (*cb)(struct mgmt_pending_cmd *cmd, void *data), 158 void *data) 159{ 160 struct mgmt_pending_cmd *cmd, *tmp; 161 162 list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) { 163 if (opcode > 0 && cmd->opcode != opcode) 164 continue; 165 166 cb(cmd, data); 167 } 168} 169 170struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, 171 struct hci_dev *hdev, 172 void *data, u16 len) 173{ 174 struct mgmt_pending_cmd *cmd; 175 176 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 177 if (!cmd) 178 return NULL; 179 180 cmd->opcode = opcode; 181 cmd->index = hdev->id; 182 183 cmd->param = kmemdup(data, len, GFP_KERNEL); 184 if (!cmd->param) { 185 kfree(cmd); 186 return NULL; 187 } 188 189 cmd->param_len = len; 190 191 cmd->sk = sk; 192 sock_hold(sk); 193 194 list_add(&cmd->list, &hdev->mgmt_pending); 195 196 return cmd; 197} 198 199void mgmt_pending_free(struct mgmt_pending_cmd *cmd) 200{ 201 sock_put(cmd->sk); 202 kfree(cmd->param); 203 kfree(cmd); 204} 205 206void mgmt_pending_remove(struct mgmt_pending_cmd *cmd) 207{ 208 list_del(&cmd->list); 209 mgmt_pending_free(cmd); 210} 211