18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci BlueZ - Bluetooth protocol stack for Linux 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright (C) 2015 Intel Corporation 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci This program is free software; you can redistribute it and/or modify 78c2ecf20Sopenharmony_ci it under the terms of the GNU General Public License version 2 as 88c2ecf20Sopenharmony_ci published by the Free Software Foundation; 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 118c2ecf20Sopenharmony_ci OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 128c2ecf20Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 138c2ecf20Sopenharmony_ci IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 148c2ecf20Sopenharmony_ci CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 158c2ecf20Sopenharmony_ci WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 168c2ecf20Sopenharmony_ci ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 178c2ecf20Sopenharmony_ci OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 208c2ecf20Sopenharmony_ci COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 218c2ecf20Sopenharmony_ci SOFTWARE IS DISCLAIMED. 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 278c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h> 288c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_mon.h> 298c2ecf20Sopenharmony_ci#include <net/bluetooth/mgmt.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "mgmt_util.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct sk_buff *create_monitor_ctrl_event(__le16 index, u32 cookie, 348c2ecf20Sopenharmony_ci u16 opcode, u16 len, void *buf) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct hci_mon_hdr *hdr; 378c2ecf20Sopenharmony_ci struct sk_buff *skb; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci skb = bt_skb_alloc(6 + len, GFP_ATOMIC); 408c2ecf20Sopenharmony_ci if (!skb) 418c2ecf20Sopenharmony_ci return NULL; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci put_unaligned_le32(cookie, skb_put(skb, 4)); 448c2ecf20Sopenharmony_ci put_unaligned_le16(opcode, skb_put(skb, 2)); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (buf) 478c2ecf20Sopenharmony_ci skb_put_data(skb, buf, len); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci __net_timestamp(skb); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci hdr = skb_push(skb, HCI_MON_HDR_SIZE); 528c2ecf20Sopenharmony_ci hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT); 538c2ecf20Sopenharmony_ci hdr->index = index; 548c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return skb; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciint mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel, 608c2ecf20Sopenharmony_ci void *data, u16 data_len, int flag, struct sock *skip_sk) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct sk_buff *skb; 638c2ecf20Sopenharmony_ci struct mgmt_hdr *hdr; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL); 668c2ecf20Sopenharmony_ci if (!skb) 678c2ecf20Sopenharmony_ci return -ENOMEM; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 708c2ecf20Sopenharmony_ci hdr->opcode = cpu_to_le16(event); 718c2ecf20Sopenharmony_ci if (hdev) 728c2ecf20Sopenharmony_ci hdr->index = cpu_to_le16(hdev->id); 738c2ecf20Sopenharmony_ci else 748c2ecf20Sopenharmony_ci hdr->index = cpu_to_le16(MGMT_INDEX_NONE); 758c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(data_len); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci if (data) 788c2ecf20Sopenharmony_ci skb_put_data(skb, data, data_len); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Time stamp */ 818c2ecf20Sopenharmony_ci __net_timestamp(skb); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci hci_send_to_channel(channel, skb, flag, skip_sk); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (channel == HCI_CHANNEL_CONTROL) 868c2ecf20Sopenharmony_ci hci_send_monitor_ctrl_event(hdev, event, data, data_len, 878c2ecf20Sopenharmony_ci skb_get_ktime(skb), flag, skip_sk); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci kfree_skb(skb); 908c2ecf20Sopenharmony_ci return 0; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ciint mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct sk_buff *skb, *mskb; 968c2ecf20Sopenharmony_ci struct mgmt_hdr *hdr; 978c2ecf20Sopenharmony_ci struct mgmt_ev_cmd_status *ev; 988c2ecf20Sopenharmony_ci int err; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL); 1038c2ecf20Sopenharmony_ci if (!skb) 1048c2ecf20Sopenharmony_ci return -ENOMEM; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); 1098c2ecf20Sopenharmony_ci hdr->index = cpu_to_le16(index); 1108c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(sizeof(*ev)); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ev = skb_put(skb, sizeof(*ev)); 1138c2ecf20Sopenharmony_ci ev->status = status; 1148c2ecf20Sopenharmony_ci ev->opcode = cpu_to_le16(cmd); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk), 1178c2ecf20Sopenharmony_ci MGMT_EV_CMD_STATUS, sizeof(*ev), ev); 1188c2ecf20Sopenharmony_ci if (mskb) 1198c2ecf20Sopenharmony_ci skb->tstamp = mskb->tstamp; 1208c2ecf20Sopenharmony_ci else 1218c2ecf20Sopenharmony_ci __net_timestamp(skb); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci err = sock_queue_rcv_skb(sk, skb); 1248c2ecf20Sopenharmony_ci if (err < 0) 1258c2ecf20Sopenharmony_ci kfree_skb(skb); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (mskb) { 1288c2ecf20Sopenharmony_ci hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb, 1298c2ecf20Sopenharmony_ci HCI_SOCK_TRUSTED, NULL); 1308c2ecf20Sopenharmony_ci kfree_skb(mskb); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return err; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, 1378c2ecf20Sopenharmony_ci void *rp, size_t rp_len) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct sk_buff *skb, *mskb; 1408c2ecf20Sopenharmony_ci struct mgmt_hdr *hdr; 1418c2ecf20Sopenharmony_ci struct mgmt_ev_cmd_complete *ev; 1428c2ecf20Sopenharmony_ci int err; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci BT_DBG("sock %p", sk); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL); 1478c2ecf20Sopenharmony_ci if (!skb) 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci hdr = skb_put(skb, sizeof(*hdr)); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); 1538c2ecf20Sopenharmony_ci hdr->index = cpu_to_le16(index); 1548c2ecf20Sopenharmony_ci hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ev = skb_put(skb, sizeof(*ev) + rp_len); 1578c2ecf20Sopenharmony_ci ev->opcode = cpu_to_le16(cmd); 1588c2ecf20Sopenharmony_ci ev->status = status; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (rp) 1618c2ecf20Sopenharmony_ci memcpy(ev->data, rp, rp_len); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk), 1648c2ecf20Sopenharmony_ci MGMT_EV_CMD_COMPLETE, 1658c2ecf20Sopenharmony_ci sizeof(*ev) + rp_len, ev); 1668c2ecf20Sopenharmony_ci if (mskb) 1678c2ecf20Sopenharmony_ci skb->tstamp = mskb->tstamp; 1688c2ecf20Sopenharmony_ci else 1698c2ecf20Sopenharmony_ci __net_timestamp(skb); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci err = sock_queue_rcv_skb(sk, skb); 1728c2ecf20Sopenharmony_ci if (err < 0) 1738c2ecf20Sopenharmony_ci kfree_skb(skb); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (mskb) { 1768c2ecf20Sopenharmony_ci hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb, 1778c2ecf20Sopenharmony_ci HCI_SOCK_TRUSTED, NULL); 1788c2ecf20Sopenharmony_ci kfree_skb(mskb); 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode, 1858c2ecf20Sopenharmony_ci struct hci_dev *hdev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 1908c2ecf20Sopenharmony_ci if (hci_sock_get_channel(cmd->sk) != channel) 1918c2ecf20Sopenharmony_ci continue; 1928c2ecf20Sopenharmony_ci if (cmd->opcode == opcode) 1938c2ecf20Sopenharmony_ci return cmd; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return NULL; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel, 2008c2ecf20Sopenharmony_ci u16 opcode, 2018c2ecf20Sopenharmony_ci struct hci_dev *hdev, 2028c2ecf20Sopenharmony_ci const void *data) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 2078c2ecf20Sopenharmony_ci if (cmd->user_data != data) 2088c2ecf20Sopenharmony_ci continue; 2098c2ecf20Sopenharmony_ci if (cmd->opcode == opcode) 2108c2ecf20Sopenharmony_ci return cmd; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci return NULL; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, 2178c2ecf20Sopenharmony_ci void (*cb)(struct mgmt_pending_cmd *cmd, void *data), 2188c2ecf20Sopenharmony_ci void *data) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd, *tmp; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) { 2238c2ecf20Sopenharmony_ci if (opcode > 0 && cmd->opcode != opcode) 2248c2ecf20Sopenharmony_ci continue; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci cb(cmd, data); 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, 2318c2ecf20Sopenharmony_ci struct hci_dev *hdev, 2328c2ecf20Sopenharmony_ci void *data, u16 len) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 2378c2ecf20Sopenharmony_ci if (!cmd) 2388c2ecf20Sopenharmony_ci return NULL; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci cmd->opcode = opcode; 2418c2ecf20Sopenharmony_ci cmd->index = hdev->id; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci cmd->param = kmemdup(data, len, GFP_KERNEL); 2448c2ecf20Sopenharmony_ci if (!cmd->param) { 2458c2ecf20Sopenharmony_ci kfree(cmd); 2468c2ecf20Sopenharmony_ci return NULL; 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci cmd->param_len = len; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci cmd->sk = sk; 2528c2ecf20Sopenharmony_ci sock_hold(sk); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci list_add(&cmd->list, &hdev->mgmt_pending); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return cmd; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_civoid mgmt_pending_free(struct mgmt_pending_cmd *cmd) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci sock_put(cmd->sk); 2628c2ecf20Sopenharmony_ci kfree(cmd->param); 2638c2ecf20Sopenharmony_ci kfree(cmd); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_civoid mgmt_pending_remove(struct mgmt_pending_cmd *cmd) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci list_del(&cmd->list); 2698c2ecf20Sopenharmony_ci mgmt_pending_free(cmd); 2708c2ecf20Sopenharmony_ci} 271