blob: 8c30c7eb8bef58ea1471f646a39c8739fa0e82c0 [file] [log] [blame]
Johan Hedberga380b6c2015-03-17 13:48:48 +02001/*
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}