xref: /openbmc/linux/net/bluetooth/mgmt_util.c (revision b338d917)
1a380b6cfSJohan Hedberg /*
2a380b6cfSJohan Hedberg    BlueZ - Bluetooth protocol stack for Linux
3a380b6cfSJohan Hedberg 
4a380b6cfSJohan Hedberg    Copyright (C) 2015  Intel Corporation
5a380b6cfSJohan Hedberg 
6a380b6cfSJohan Hedberg    This program is free software; you can redistribute it and/or modify
7a380b6cfSJohan Hedberg    it under the terms of the GNU General Public License version 2 as
8a380b6cfSJohan Hedberg    published by the Free Software Foundation;
9a380b6cfSJohan Hedberg 
10a380b6cfSJohan Hedberg    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
11a380b6cfSJohan Hedberg    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
12a380b6cfSJohan Hedberg    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
13a380b6cfSJohan Hedberg    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
14a380b6cfSJohan Hedberg    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
15a380b6cfSJohan Hedberg    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16a380b6cfSJohan Hedberg    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17a380b6cfSJohan Hedberg    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18a380b6cfSJohan Hedberg 
19a380b6cfSJohan Hedberg    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
20a380b6cfSJohan Hedberg    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
21a380b6cfSJohan Hedberg    SOFTWARE IS DISCLAIMED.
22a380b6cfSJohan Hedberg */
23a380b6cfSJohan Hedberg 
2438ceaa00SMarcel Holtmann #include <asm/unaligned.h>
2538ceaa00SMarcel Holtmann 
26a380b6cfSJohan Hedberg #include <net/bluetooth/bluetooth.h>
27a380b6cfSJohan Hedberg #include <net/bluetooth/hci_core.h>
2838ceaa00SMarcel Holtmann #include <net/bluetooth/hci_mon.h>
29a380b6cfSJohan Hedberg #include <net/bluetooth/mgmt.h>
30a380b6cfSJohan Hedberg 
31a380b6cfSJohan Hedberg #include "mgmt_util.h"
32a380b6cfSJohan Hedberg 
create_monitor_ctrl_event(__le16 index,u32 cookie,u16 opcode,u16 len,void * buf)3338ceaa00SMarcel Holtmann static struct sk_buff *create_monitor_ctrl_event(__le16 index, u32 cookie,
3438ceaa00SMarcel Holtmann 						 u16 opcode, u16 len, void *buf)
3538ceaa00SMarcel Holtmann {
3638ceaa00SMarcel Holtmann 	struct hci_mon_hdr *hdr;
3738ceaa00SMarcel Holtmann 	struct sk_buff *skb;
3838ceaa00SMarcel Holtmann 
3938ceaa00SMarcel Holtmann 	skb = bt_skb_alloc(6 + len, GFP_ATOMIC);
4038ceaa00SMarcel Holtmann 	if (!skb)
4138ceaa00SMarcel Holtmann 		return NULL;
4238ceaa00SMarcel Holtmann 
4338ceaa00SMarcel Holtmann 	put_unaligned_le32(cookie, skb_put(skb, 4));
4438ceaa00SMarcel Holtmann 	put_unaligned_le16(opcode, skb_put(skb, 2));
4538ceaa00SMarcel Holtmann 
4638ceaa00SMarcel Holtmann 	if (buf)
4759ae1d12SJohannes Berg 		skb_put_data(skb, buf, len);
4838ceaa00SMarcel Holtmann 
4938ceaa00SMarcel Holtmann 	__net_timestamp(skb);
5038ceaa00SMarcel Holtmann 
51d58ff351SJohannes Berg 	hdr = skb_push(skb, HCI_MON_HDR_SIZE);
5238ceaa00SMarcel Holtmann 	hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT);
5338ceaa00SMarcel Holtmann 	hdr->index = index;
5438ceaa00SMarcel Holtmann 	hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
5538ceaa00SMarcel Holtmann 
5638ceaa00SMarcel Holtmann 	return skb;
5738ceaa00SMarcel Holtmann }
5838ceaa00SMarcel Holtmann 
mgmt_alloc_skb(struct hci_dev * hdev,u16 opcode,unsigned int size)598aca46f9SLuiz Augusto von Dentz struct sk_buff *mgmt_alloc_skb(struct hci_dev *hdev, u16 opcode,
608aca46f9SLuiz Augusto von Dentz 			       unsigned int size)
61a380b6cfSJohan Hedberg {
62a380b6cfSJohan Hedberg 	struct sk_buff *skb;
63a380b6cfSJohan Hedberg 
648aca46f9SLuiz Augusto von Dentz 	skb = alloc_skb(sizeof(struct mgmt_hdr) + size, GFP_KERNEL);
65a380b6cfSJohan Hedberg 	if (!skb)
668aca46f9SLuiz Augusto von Dentz 		return skb;
67a380b6cfSJohan Hedberg 
688aca46f9SLuiz Augusto von Dentz 	skb_reserve(skb, sizeof(struct mgmt_hdr));
698aca46f9SLuiz Augusto von Dentz 	bt_cb(skb)->mgmt.hdev = hdev;
708aca46f9SLuiz Augusto von Dentz 	bt_cb(skb)->mgmt.opcode = opcode;
71a380b6cfSJohan Hedberg 
728aca46f9SLuiz Augusto von Dentz 	return skb;
738aca46f9SLuiz Augusto von Dentz }
748aca46f9SLuiz Augusto von Dentz 
mgmt_send_event_skb(unsigned short channel,struct sk_buff * skb,int flag,struct sock * skip_sk)758aca46f9SLuiz Augusto von Dentz int mgmt_send_event_skb(unsigned short channel, struct sk_buff *skb, int flag,
768aca46f9SLuiz Augusto von Dentz 			struct sock *skip_sk)
778aca46f9SLuiz Augusto von Dentz {
788aca46f9SLuiz Augusto von Dentz 	struct hci_dev *hdev;
798aca46f9SLuiz Augusto von Dentz 	struct mgmt_hdr *hdr;
802e8ecb4bSWang Qing 	int len;
818aca46f9SLuiz Augusto von Dentz 
828aca46f9SLuiz Augusto von Dentz 	if (!skb)
838aca46f9SLuiz Augusto von Dentz 		return -EINVAL;
848aca46f9SLuiz Augusto von Dentz 
852e8ecb4bSWang Qing 	len = skb->len;
868aca46f9SLuiz Augusto von Dentz 	hdev = bt_cb(skb)->mgmt.hdev;
87a380b6cfSJohan Hedberg 
88a380b6cfSJohan Hedberg 	/* Time stamp */
89a380b6cfSJohan Hedberg 	__net_timestamp(skb);
90a380b6cfSJohan Hedberg 
918aca46f9SLuiz Augusto von Dentz 	/* Send just the data, without headers, to the monitor */
9238ceaa00SMarcel Holtmann 	if (channel == HCI_CHANNEL_CONTROL)
938aca46f9SLuiz Augusto von Dentz 		hci_send_monitor_ctrl_event(hdev, bt_cb(skb)->mgmt.opcode,
948aca46f9SLuiz Augusto von Dentz 					    skb->data, skb->len,
9538ceaa00SMarcel Holtmann 					    skb_get_ktime(skb), flag, skip_sk);
9638ceaa00SMarcel Holtmann 
978aca46f9SLuiz Augusto von Dentz 	hdr = skb_push(skb, sizeof(*hdr));
988aca46f9SLuiz Augusto von Dentz 	hdr->opcode = cpu_to_le16(bt_cb(skb)->mgmt.opcode);
998aca46f9SLuiz Augusto von Dentz 	if (hdev)
1008aca46f9SLuiz Augusto von Dentz 		hdr->index = cpu_to_le16(hdev->id);
1018aca46f9SLuiz Augusto von Dentz 	else
1028aca46f9SLuiz Augusto von Dentz 		hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
1038aca46f9SLuiz Augusto von Dentz 	hdr->len = cpu_to_le16(len);
1048aca46f9SLuiz Augusto von Dentz 
1058aca46f9SLuiz Augusto von Dentz 	hci_send_to_channel(channel, skb, flag, skip_sk);
1068aca46f9SLuiz Augusto von Dentz 
10738ceaa00SMarcel Holtmann 	kfree_skb(skb);
108a380b6cfSJohan Hedberg 	return 0;
109a380b6cfSJohan Hedberg }
110a380b6cfSJohan Hedberg 
mgmt_send_event(u16 event,struct hci_dev * hdev,unsigned short channel,void * data,u16 data_len,int flag,struct sock * skip_sk)1118aca46f9SLuiz Augusto von Dentz int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
1128aca46f9SLuiz Augusto von Dentz 		    void *data, u16 data_len, int flag, struct sock *skip_sk)
1138aca46f9SLuiz Augusto von Dentz {
1148aca46f9SLuiz Augusto von Dentz 	struct sk_buff *skb;
1158aca46f9SLuiz Augusto von Dentz 
1168aca46f9SLuiz Augusto von Dentz 	skb = mgmt_alloc_skb(hdev, event, data_len);
1178aca46f9SLuiz Augusto von Dentz 	if (!skb)
1188aca46f9SLuiz Augusto von Dentz 		return -ENOMEM;
1198aca46f9SLuiz Augusto von Dentz 
1208aca46f9SLuiz Augusto von Dentz 	if (data)
1218aca46f9SLuiz Augusto von Dentz 		skb_put_data(skb, data, data_len);
1228aca46f9SLuiz Augusto von Dentz 
1238aca46f9SLuiz Augusto von Dentz 	return mgmt_send_event_skb(channel, skb, flag, skip_sk);
1248aca46f9SLuiz Augusto von Dentz }
1258aca46f9SLuiz Augusto von Dentz 
mgmt_cmd_status(struct sock * sk,u16 index,u16 cmd,u8 status)126a380b6cfSJohan Hedberg int mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
127a380b6cfSJohan Hedberg {
12838ceaa00SMarcel Holtmann 	struct sk_buff *skb, *mskb;
129a380b6cfSJohan Hedberg 	struct mgmt_hdr *hdr;
130a380b6cfSJohan Hedberg 	struct mgmt_ev_cmd_status *ev;
131a380b6cfSJohan Hedberg 	int err;
132a380b6cfSJohan Hedberg 
133a380b6cfSJohan Hedberg 	BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
134a380b6cfSJohan Hedberg 
135a380b6cfSJohan Hedberg 	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
136a380b6cfSJohan Hedberg 	if (!skb)
137a380b6cfSJohan Hedberg 		return -ENOMEM;
138a380b6cfSJohan Hedberg 
1394df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(*hdr));
140a380b6cfSJohan Hedberg 
141a380b6cfSJohan Hedberg 	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
142a380b6cfSJohan Hedberg 	hdr->index = cpu_to_le16(index);
143a380b6cfSJohan Hedberg 	hdr->len = cpu_to_le16(sizeof(*ev));
144a380b6cfSJohan Hedberg 
1454df864c1SJohannes Berg 	ev = skb_put(skb, sizeof(*ev));
146a380b6cfSJohan Hedberg 	ev->status = status;
147a380b6cfSJohan Hedberg 	ev->opcode = cpu_to_le16(cmd);
148a380b6cfSJohan Hedberg 
14938ceaa00SMarcel Holtmann 	mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
15038ceaa00SMarcel Holtmann 					 MGMT_EV_CMD_STATUS, sizeof(*ev), ev);
15138ceaa00SMarcel Holtmann 	if (mskb)
15238ceaa00SMarcel Holtmann 		skb->tstamp = mskb->tstamp;
15338ceaa00SMarcel Holtmann 	else
15438ceaa00SMarcel Holtmann 		__net_timestamp(skb);
15538ceaa00SMarcel Holtmann 
156a380b6cfSJohan Hedberg 	err = sock_queue_rcv_skb(sk, skb);
157a380b6cfSJohan Hedberg 	if (err < 0)
158a380b6cfSJohan Hedberg 		kfree_skb(skb);
159a380b6cfSJohan Hedberg 
16038ceaa00SMarcel Holtmann 	if (mskb) {
16138ceaa00SMarcel Holtmann 		hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
16238ceaa00SMarcel Holtmann 				    HCI_SOCK_TRUSTED, NULL);
16338ceaa00SMarcel Holtmann 		kfree_skb(mskb);
16438ceaa00SMarcel Holtmann 	}
16538ceaa00SMarcel Holtmann 
166a380b6cfSJohan Hedberg 	return err;
167a380b6cfSJohan Hedberg }
168a380b6cfSJohan Hedberg 
mgmt_cmd_complete(struct sock * sk,u16 index,u16 cmd,u8 status,void * rp,size_t rp_len)169a380b6cfSJohan Hedberg int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
170a380b6cfSJohan Hedberg 		      void *rp, size_t rp_len)
171a380b6cfSJohan Hedberg {
17238ceaa00SMarcel Holtmann 	struct sk_buff *skb, *mskb;
173a380b6cfSJohan Hedberg 	struct mgmt_hdr *hdr;
174a380b6cfSJohan Hedberg 	struct mgmt_ev_cmd_complete *ev;
175a380b6cfSJohan Hedberg 	int err;
176a380b6cfSJohan Hedberg 
177a380b6cfSJohan Hedberg 	BT_DBG("sock %p", sk);
178a380b6cfSJohan Hedberg 
179a380b6cfSJohan Hedberg 	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
180a380b6cfSJohan Hedberg 	if (!skb)
181a380b6cfSJohan Hedberg 		return -ENOMEM;
182a380b6cfSJohan Hedberg 
1834df864c1SJohannes Berg 	hdr = skb_put(skb, sizeof(*hdr));
184a380b6cfSJohan Hedberg 
185a380b6cfSJohan Hedberg 	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
186a380b6cfSJohan Hedberg 	hdr->index = cpu_to_le16(index);
187a380b6cfSJohan Hedberg 	hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
188a380b6cfSJohan Hedberg 
1894df864c1SJohannes Berg 	ev = skb_put(skb, sizeof(*ev) + rp_len);
190a380b6cfSJohan Hedberg 	ev->opcode = cpu_to_le16(cmd);
191a380b6cfSJohan Hedberg 	ev->status = status;
192a380b6cfSJohan Hedberg 
193a380b6cfSJohan Hedberg 	if (rp)
194a380b6cfSJohan Hedberg 		memcpy(ev->data, rp, rp_len);
195a380b6cfSJohan Hedberg 
19638ceaa00SMarcel Holtmann 	mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
19738ceaa00SMarcel Holtmann 					 MGMT_EV_CMD_COMPLETE,
19838ceaa00SMarcel Holtmann 					 sizeof(*ev) + rp_len, ev);
19938ceaa00SMarcel Holtmann 	if (mskb)
20038ceaa00SMarcel Holtmann 		skb->tstamp = mskb->tstamp;
20138ceaa00SMarcel Holtmann 	else
20238ceaa00SMarcel Holtmann 		__net_timestamp(skb);
20338ceaa00SMarcel Holtmann 
204a380b6cfSJohan Hedberg 	err = sock_queue_rcv_skb(sk, skb);
205a380b6cfSJohan Hedberg 	if (err < 0)
206a380b6cfSJohan Hedberg 		kfree_skb(skb);
207a380b6cfSJohan Hedberg 
20838ceaa00SMarcel Holtmann 	if (mskb) {
20938ceaa00SMarcel Holtmann 		hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
21038ceaa00SMarcel Holtmann 				    HCI_SOCK_TRUSTED, NULL);
21138ceaa00SMarcel Holtmann 		kfree_skb(mskb);
21238ceaa00SMarcel Holtmann 	}
21338ceaa00SMarcel Holtmann 
214a380b6cfSJohan Hedberg 	return err;
215a380b6cfSJohan Hedberg }
216a380b6cfSJohan Hedberg 
mgmt_pending_find(unsigned short channel,u16 opcode,struct hci_dev * hdev)217a380b6cfSJohan Hedberg struct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
218a380b6cfSJohan Hedberg 					   struct hci_dev *hdev)
219a380b6cfSJohan Hedberg {
220a380b6cfSJohan Hedberg 	struct mgmt_pending_cmd *cmd;
221a380b6cfSJohan Hedberg 
222a380b6cfSJohan Hedberg 	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
223a380b6cfSJohan Hedberg 		if (hci_sock_get_channel(cmd->sk) != channel)
224a380b6cfSJohan Hedberg 			continue;
225a380b6cfSJohan Hedberg 		if (cmd->opcode == opcode)
226a380b6cfSJohan Hedberg 			return cmd;
227a380b6cfSJohan Hedberg 	}
228a380b6cfSJohan Hedberg 
229a380b6cfSJohan Hedberg 	return NULL;
230a380b6cfSJohan Hedberg }
231a380b6cfSJohan Hedberg 
mgmt_pending_find_data(unsigned short channel,u16 opcode,struct hci_dev * hdev,const void * data)232a380b6cfSJohan Hedberg struct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
233a380b6cfSJohan Hedberg 						u16 opcode,
234a380b6cfSJohan Hedberg 						struct hci_dev *hdev,
235a380b6cfSJohan Hedberg 						const void *data)
236a380b6cfSJohan Hedberg {
237a380b6cfSJohan Hedberg 	struct mgmt_pending_cmd *cmd;
238a380b6cfSJohan Hedberg 
239a380b6cfSJohan Hedberg 	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
240a380b6cfSJohan Hedberg 		if (cmd->user_data != data)
241a380b6cfSJohan Hedberg 			continue;
242a380b6cfSJohan Hedberg 		if (cmd->opcode == opcode)
243a380b6cfSJohan Hedberg 			return cmd;
244a380b6cfSJohan Hedberg 	}
245a380b6cfSJohan Hedberg 
246a380b6cfSJohan Hedberg 	return NULL;
247a380b6cfSJohan Hedberg }
248a380b6cfSJohan Hedberg 
mgmt_pending_foreach(u16 opcode,struct hci_dev * hdev,void (* cb)(struct mgmt_pending_cmd * cmd,void * data),void * data)249a380b6cfSJohan Hedberg void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
250a380b6cfSJohan Hedberg 			  void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
251a380b6cfSJohan Hedberg 			  void *data)
252a380b6cfSJohan Hedberg {
253a380b6cfSJohan Hedberg 	struct mgmt_pending_cmd *cmd, *tmp;
254a380b6cfSJohan Hedberg 
255a380b6cfSJohan Hedberg 	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
256a380b6cfSJohan Hedberg 		if (opcode > 0 && cmd->opcode != opcode)
257a380b6cfSJohan Hedberg 			continue;
258a380b6cfSJohan Hedberg 
259a380b6cfSJohan Hedberg 		cb(cmd, data);
260a380b6cfSJohan Hedberg 	}
261a380b6cfSJohan Hedberg }
262a380b6cfSJohan Hedberg 
mgmt_pending_new(struct sock * sk,u16 opcode,struct hci_dev * hdev,void * data,u16 len)263161510ccSLuiz Augusto von Dentz struct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
264a380b6cfSJohan Hedberg 					  struct hci_dev *hdev,
265a380b6cfSJohan Hedberg 					  void *data, u16 len)
266a380b6cfSJohan Hedberg {
267a380b6cfSJohan Hedberg 	struct mgmt_pending_cmd *cmd;
268a380b6cfSJohan Hedberg 
269a380b6cfSJohan Hedberg 	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
270a380b6cfSJohan Hedberg 	if (!cmd)
271a380b6cfSJohan Hedberg 		return NULL;
272a380b6cfSJohan Hedberg 
273a380b6cfSJohan Hedberg 	cmd->opcode = opcode;
274a380b6cfSJohan Hedberg 	cmd->index = hdev->id;
275a380b6cfSJohan Hedberg 
276a380b6cfSJohan Hedberg 	cmd->param = kmemdup(data, len, GFP_KERNEL);
277a380b6cfSJohan Hedberg 	if (!cmd->param) {
278a380b6cfSJohan Hedberg 		kfree(cmd);
279a380b6cfSJohan Hedberg 		return NULL;
280a380b6cfSJohan Hedberg 	}
281a380b6cfSJohan Hedberg 
282a380b6cfSJohan Hedberg 	cmd->param_len = len;
283a380b6cfSJohan Hedberg 
284a380b6cfSJohan Hedberg 	cmd->sk = sk;
285a380b6cfSJohan Hedberg 	sock_hold(sk);
286a380b6cfSJohan Hedberg 
287161510ccSLuiz Augusto von Dentz 	return cmd;
288161510ccSLuiz Augusto von Dentz }
289161510ccSLuiz Augusto von Dentz 
mgmt_pending_add(struct sock * sk,u16 opcode,struct hci_dev * hdev,void * data,u16 len)290161510ccSLuiz Augusto von Dentz struct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
291161510ccSLuiz Augusto von Dentz 					  struct hci_dev *hdev,
292161510ccSLuiz Augusto von Dentz 					  void *data, u16 len)
293161510ccSLuiz Augusto von Dentz {
294161510ccSLuiz Augusto von Dentz 	struct mgmt_pending_cmd *cmd;
295161510ccSLuiz Augusto von Dentz 
296161510ccSLuiz Augusto von Dentz 	cmd = mgmt_pending_new(sk, opcode, hdev, data, len);
297161510ccSLuiz Augusto von Dentz 	if (!cmd)
298161510ccSLuiz Augusto von Dentz 		return NULL;
299161510ccSLuiz Augusto von Dentz 
30031396dd5SBrian Gix 	list_add_tail(&cmd->list, &hdev->mgmt_pending);
301a380b6cfSJohan Hedberg 
302a380b6cfSJohan Hedberg 	return cmd;
303a380b6cfSJohan Hedberg }
304a380b6cfSJohan Hedberg 
mgmt_pending_free(struct mgmt_pending_cmd * cmd)305a380b6cfSJohan Hedberg void mgmt_pending_free(struct mgmt_pending_cmd *cmd)
306a380b6cfSJohan Hedberg {
307a380b6cfSJohan Hedberg 	sock_put(cmd->sk);
308a380b6cfSJohan Hedberg 	kfree(cmd->param);
309a380b6cfSJohan Hedberg 	kfree(cmd);
310a380b6cfSJohan Hedberg }
311a380b6cfSJohan Hedberg 
mgmt_pending_remove(struct mgmt_pending_cmd * cmd)312a380b6cfSJohan Hedberg void mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
313a380b6cfSJohan Hedberg {
314a380b6cfSJohan Hedberg 	list_del(&cmd->list);
315a380b6cfSJohan Hedberg 	mgmt_pending_free(cmd);
316a380b6cfSJohan Hedberg }
317*b338d917SBrian Gix 
mgmt_mesh_foreach(struct hci_dev * hdev,void (* cb)(struct mgmt_mesh_tx * mesh_tx,void * data),void * data,struct sock * sk)318*b338d917SBrian Gix void mgmt_mesh_foreach(struct hci_dev *hdev,
319*b338d917SBrian Gix 		       void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
320*b338d917SBrian Gix 		       void *data, struct sock *sk)
321*b338d917SBrian Gix {
322*b338d917SBrian Gix 	struct mgmt_mesh_tx *mesh_tx, *tmp;
323*b338d917SBrian Gix 
324*b338d917SBrian Gix 	list_for_each_entry_safe(mesh_tx, tmp, &hdev->mgmt_pending, list) {
325*b338d917SBrian Gix 		if (!sk || mesh_tx->sk == sk)
326*b338d917SBrian Gix 			cb(mesh_tx, data);
327*b338d917SBrian Gix 	}
328*b338d917SBrian Gix }
329*b338d917SBrian Gix 
mgmt_mesh_next(struct hci_dev * hdev,struct sock * sk)330*b338d917SBrian Gix struct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk)
331*b338d917SBrian Gix {
332*b338d917SBrian Gix 	struct mgmt_mesh_tx *mesh_tx;
333*b338d917SBrian Gix 
334*b338d917SBrian Gix 	if (list_empty(&hdev->mesh_pending))
335*b338d917SBrian Gix 		return NULL;
336*b338d917SBrian Gix 
337*b338d917SBrian Gix 	list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
338*b338d917SBrian Gix 		if (!sk || mesh_tx->sk == sk)
339*b338d917SBrian Gix 			return mesh_tx;
340*b338d917SBrian Gix 	}
341*b338d917SBrian Gix 
342*b338d917SBrian Gix 	return NULL;
343*b338d917SBrian Gix }
344*b338d917SBrian Gix 
mgmt_mesh_find(struct hci_dev * hdev,u8 handle)345*b338d917SBrian Gix struct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle)
346*b338d917SBrian Gix {
347*b338d917SBrian Gix 	struct mgmt_mesh_tx *mesh_tx;
348*b338d917SBrian Gix 
349*b338d917SBrian Gix 	if (list_empty(&hdev->mesh_pending))
350*b338d917SBrian Gix 		return NULL;
351*b338d917SBrian Gix 
352*b338d917SBrian Gix 	list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
353*b338d917SBrian Gix 		if (mesh_tx->handle == handle)
354*b338d917SBrian Gix 			return mesh_tx;
355*b338d917SBrian Gix 	}
356*b338d917SBrian Gix 
357*b338d917SBrian Gix 	return NULL;
358*b338d917SBrian Gix }
359*b338d917SBrian Gix 
mgmt_mesh_add(struct sock * sk,struct hci_dev * hdev,void * data,u16 len)360*b338d917SBrian Gix struct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
361*b338d917SBrian Gix 				   void *data, u16 len)
362*b338d917SBrian Gix {
363*b338d917SBrian Gix 	struct mgmt_mesh_tx *mesh_tx;
364*b338d917SBrian Gix 
365*b338d917SBrian Gix 	mesh_tx = kzalloc(sizeof(*mesh_tx), GFP_KERNEL);
366*b338d917SBrian Gix 	if (!mesh_tx)
367*b338d917SBrian Gix 		return NULL;
368*b338d917SBrian Gix 
369*b338d917SBrian Gix 	hdev->mesh_send_ref++;
370*b338d917SBrian Gix 	if (!hdev->mesh_send_ref)
371*b338d917SBrian Gix 		hdev->mesh_send_ref++;
372*b338d917SBrian Gix 
373*b338d917SBrian Gix 	mesh_tx->handle = hdev->mesh_send_ref;
374*b338d917SBrian Gix 	mesh_tx->index = hdev->id;
375*b338d917SBrian Gix 	memcpy(mesh_tx->param, data, len);
376*b338d917SBrian Gix 	mesh_tx->param_len = len;
377*b338d917SBrian Gix 	mesh_tx->sk = sk;
378*b338d917SBrian Gix 	sock_hold(sk);
379*b338d917SBrian Gix 
380*b338d917SBrian Gix 	list_add_tail(&mesh_tx->list, &hdev->mesh_pending);
381*b338d917SBrian Gix 
382*b338d917SBrian Gix 	return mesh_tx;
383*b338d917SBrian Gix }
384*b338d917SBrian Gix 
mgmt_mesh_remove(struct mgmt_mesh_tx * mesh_tx)385*b338d917SBrian Gix void mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx)
386*b338d917SBrian Gix {
387*b338d917SBrian Gix 	list_del(&mesh_tx->list);
388*b338d917SBrian Gix 	sock_put(mesh_tx->sk);
389*b338d917SBrian Gix 	kfree(mesh_tx);
390*b338d917SBrian Gix }
391