162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci   BlueZ - Bluetooth protocol stack for Linux
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci   Copyright (C) 2015  Intel Corporation
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci   This program is free software; you can redistribute it and/or modify
762306a36Sopenharmony_ci   it under the terms of the GNU General Public License version 2 as
862306a36Sopenharmony_ci   published by the Free Software Foundation;
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1162306a36Sopenharmony_ci   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1262306a36Sopenharmony_ci   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
1362306a36Sopenharmony_ci   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
1462306a36Sopenharmony_ci   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
1562306a36Sopenharmony_ci   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1662306a36Sopenharmony_ci   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1762306a36Sopenharmony_ci   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
2062306a36Sopenharmony_ci   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
2162306a36Sopenharmony_ci   SOFTWARE IS DISCLAIMED.
2262306a36Sopenharmony_ci*/
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/unaligned.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <net/bluetooth/bluetooth.h>
2762306a36Sopenharmony_ci#include <net/bluetooth/hci_core.h>
2862306a36Sopenharmony_ci#include <net/bluetooth/hci_mon.h>
2962306a36Sopenharmony_ci#include <net/bluetooth/mgmt.h>
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "mgmt_util.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct sk_buff *create_monitor_ctrl_event(__le16 index, u32 cookie,
3462306a36Sopenharmony_ci						 u16 opcode, u16 len, void *buf)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct hci_mon_hdr *hdr;
3762306a36Sopenharmony_ci	struct sk_buff *skb;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	skb = bt_skb_alloc(6 + len, GFP_ATOMIC);
4062306a36Sopenharmony_ci	if (!skb)
4162306a36Sopenharmony_ci		return NULL;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	put_unaligned_le32(cookie, skb_put(skb, 4));
4462306a36Sopenharmony_ci	put_unaligned_le16(opcode, skb_put(skb, 2));
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (buf)
4762306a36Sopenharmony_ci		skb_put_data(skb, buf, len);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	__net_timestamp(skb);
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	hdr = skb_push(skb, HCI_MON_HDR_SIZE);
5262306a36Sopenharmony_ci	hdr->opcode = cpu_to_le16(HCI_MON_CTRL_EVENT);
5362306a36Sopenharmony_ci	hdr->index = index;
5462306a36Sopenharmony_ci	hdr->len = cpu_to_le16(skb->len - HCI_MON_HDR_SIZE);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return skb;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct sk_buff *mgmt_alloc_skb(struct hci_dev *hdev, u16 opcode,
6062306a36Sopenharmony_ci			       unsigned int size)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	struct sk_buff *skb;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	skb = alloc_skb(sizeof(struct mgmt_hdr) + size, GFP_KERNEL);
6562306a36Sopenharmony_ci	if (!skb)
6662306a36Sopenharmony_ci		return skb;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	skb_reserve(skb, sizeof(struct mgmt_hdr));
6962306a36Sopenharmony_ci	bt_cb(skb)->mgmt.hdev = hdev;
7062306a36Sopenharmony_ci	bt_cb(skb)->mgmt.opcode = opcode;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return skb;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ciint mgmt_send_event_skb(unsigned short channel, struct sk_buff *skb, int flag,
7662306a36Sopenharmony_ci			struct sock *skip_sk)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct hci_dev *hdev;
7962306a36Sopenharmony_ci	struct mgmt_hdr *hdr;
8062306a36Sopenharmony_ci	int len;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (!skb)
8362306a36Sopenharmony_ci		return -EINVAL;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	len = skb->len;
8662306a36Sopenharmony_ci	hdev = bt_cb(skb)->mgmt.hdev;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Time stamp */
8962306a36Sopenharmony_ci	__net_timestamp(skb);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* Send just the data, without headers, to the monitor */
9262306a36Sopenharmony_ci	if (channel == HCI_CHANNEL_CONTROL)
9362306a36Sopenharmony_ci		hci_send_monitor_ctrl_event(hdev, bt_cb(skb)->mgmt.opcode,
9462306a36Sopenharmony_ci					    skb->data, skb->len,
9562306a36Sopenharmony_ci					    skb_get_ktime(skb), flag, skip_sk);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	hdr = skb_push(skb, sizeof(*hdr));
9862306a36Sopenharmony_ci	hdr->opcode = cpu_to_le16(bt_cb(skb)->mgmt.opcode);
9962306a36Sopenharmony_ci	if (hdev)
10062306a36Sopenharmony_ci		hdr->index = cpu_to_le16(hdev->id);
10162306a36Sopenharmony_ci	else
10262306a36Sopenharmony_ci		hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
10362306a36Sopenharmony_ci	hdr->len = cpu_to_le16(len);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	hci_send_to_channel(channel, skb, flag, skip_sk);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	kfree_skb(skb);
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ciint mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
11262306a36Sopenharmony_ci		    void *data, u16 data_len, int flag, struct sock *skip_sk)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	struct sk_buff *skb;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	skb = mgmt_alloc_skb(hdev, event, data_len);
11762306a36Sopenharmony_ci	if (!skb)
11862306a36Sopenharmony_ci		return -ENOMEM;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (data)
12162306a36Sopenharmony_ci		skb_put_data(skb, data, data_len);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return mgmt_send_event_skb(channel, skb, flag, skip_sk);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint mgmt_cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct sk_buff *skb, *mskb;
12962306a36Sopenharmony_ci	struct mgmt_hdr *hdr;
13062306a36Sopenharmony_ci	struct mgmt_ev_cmd_status *ev;
13162306a36Sopenharmony_ci	int err;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_KERNEL);
13662306a36Sopenharmony_ci	if (!skb)
13762306a36Sopenharmony_ci		return -ENOMEM;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	hdr = skb_put(skb, sizeof(*hdr));
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
14262306a36Sopenharmony_ci	hdr->index = cpu_to_le16(index);
14362306a36Sopenharmony_ci	hdr->len = cpu_to_le16(sizeof(*ev));
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	ev = skb_put(skb, sizeof(*ev));
14662306a36Sopenharmony_ci	ev->status = status;
14762306a36Sopenharmony_ci	ev->opcode = cpu_to_le16(cmd);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
15062306a36Sopenharmony_ci					 MGMT_EV_CMD_STATUS, sizeof(*ev), ev);
15162306a36Sopenharmony_ci	if (mskb)
15262306a36Sopenharmony_ci		skb->tstamp = mskb->tstamp;
15362306a36Sopenharmony_ci	else
15462306a36Sopenharmony_ci		__net_timestamp(skb);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	err = sock_queue_rcv_skb(sk, skb);
15762306a36Sopenharmony_ci	if (err < 0)
15862306a36Sopenharmony_ci		kfree_skb(skb);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	if (mskb) {
16162306a36Sopenharmony_ci		hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
16262306a36Sopenharmony_ci				    HCI_SOCK_TRUSTED, NULL);
16362306a36Sopenharmony_ci		kfree_skb(mskb);
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return err;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ciint mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
17062306a36Sopenharmony_ci		      void *rp, size_t rp_len)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct sk_buff *skb, *mskb;
17362306a36Sopenharmony_ci	struct mgmt_hdr *hdr;
17462306a36Sopenharmony_ci	struct mgmt_ev_cmd_complete *ev;
17562306a36Sopenharmony_ci	int err;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	BT_DBG("sock %p", sk);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
18062306a36Sopenharmony_ci	if (!skb)
18162306a36Sopenharmony_ci		return -ENOMEM;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	hdr = skb_put(skb, sizeof(*hdr));
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
18662306a36Sopenharmony_ci	hdr->index = cpu_to_le16(index);
18762306a36Sopenharmony_ci	hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	ev = skb_put(skb, sizeof(*ev) + rp_len);
19062306a36Sopenharmony_ci	ev->opcode = cpu_to_le16(cmd);
19162306a36Sopenharmony_ci	ev->status = status;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (rp)
19462306a36Sopenharmony_ci		memcpy(ev->data, rp, rp_len);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
19762306a36Sopenharmony_ci					 MGMT_EV_CMD_COMPLETE,
19862306a36Sopenharmony_ci					 sizeof(*ev) + rp_len, ev);
19962306a36Sopenharmony_ci	if (mskb)
20062306a36Sopenharmony_ci		skb->tstamp = mskb->tstamp;
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		__net_timestamp(skb);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	err = sock_queue_rcv_skb(sk, skb);
20562306a36Sopenharmony_ci	if (err < 0)
20662306a36Sopenharmony_ci		kfree_skb(skb);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	if (mskb) {
20962306a36Sopenharmony_ci		hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
21062306a36Sopenharmony_ci				    HCI_SOCK_TRUSTED, NULL);
21162306a36Sopenharmony_ci		kfree_skb(mskb);
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return err;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_find(unsigned short channel, u16 opcode,
21862306a36Sopenharmony_ci					   struct hci_dev *hdev)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	struct mgmt_pending_cmd *cmd;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
22362306a36Sopenharmony_ci		if (hci_sock_get_channel(cmd->sk) != channel)
22462306a36Sopenharmony_ci			continue;
22562306a36Sopenharmony_ci		if (cmd->opcode == opcode)
22662306a36Sopenharmony_ci			return cmd;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return NULL;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_find_data(unsigned short channel,
23362306a36Sopenharmony_ci						u16 opcode,
23462306a36Sopenharmony_ci						struct hci_dev *hdev,
23562306a36Sopenharmony_ci						const void *data)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	struct mgmt_pending_cmd *cmd;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	list_for_each_entry(cmd, &hdev->mgmt_pending, list) {
24062306a36Sopenharmony_ci		if (cmd->user_data != data)
24162306a36Sopenharmony_ci			continue;
24262306a36Sopenharmony_ci		if (cmd->opcode == opcode)
24362306a36Sopenharmony_ci			return cmd;
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return NULL;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_civoid mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev,
25062306a36Sopenharmony_ci			  void (*cb)(struct mgmt_pending_cmd *cmd, void *data),
25162306a36Sopenharmony_ci			  void *data)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct mgmt_pending_cmd *cmd, *tmp;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, tmp, &hdev->mgmt_pending, list) {
25662306a36Sopenharmony_ci		if (opcode > 0 && cmd->opcode != opcode)
25762306a36Sopenharmony_ci			continue;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		cb(cmd, data);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_new(struct sock *sk, u16 opcode,
26462306a36Sopenharmony_ci					  struct hci_dev *hdev,
26562306a36Sopenharmony_ci					  void *data, u16 len)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct mgmt_pending_cmd *cmd;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
27062306a36Sopenharmony_ci	if (!cmd)
27162306a36Sopenharmony_ci		return NULL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	cmd->opcode = opcode;
27462306a36Sopenharmony_ci	cmd->index = hdev->id;
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	cmd->param = kmemdup(data, len, GFP_KERNEL);
27762306a36Sopenharmony_ci	if (!cmd->param) {
27862306a36Sopenharmony_ci		kfree(cmd);
27962306a36Sopenharmony_ci		return NULL;
28062306a36Sopenharmony_ci	}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	cmd->param_len = len;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	cmd->sk = sk;
28562306a36Sopenharmony_ci	sock_hold(sk);
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	return cmd;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistruct mgmt_pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode,
29162306a36Sopenharmony_ci					  struct hci_dev *hdev,
29262306a36Sopenharmony_ci					  void *data, u16 len)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct mgmt_pending_cmd *cmd;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	cmd = mgmt_pending_new(sk, opcode, hdev, data, len);
29762306a36Sopenharmony_ci	if (!cmd)
29862306a36Sopenharmony_ci		return NULL;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	list_add_tail(&cmd->list, &hdev->mgmt_pending);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return cmd;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_civoid mgmt_pending_free(struct mgmt_pending_cmd *cmd)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	sock_put(cmd->sk);
30862306a36Sopenharmony_ci	kfree(cmd->param);
30962306a36Sopenharmony_ci	kfree(cmd);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_civoid mgmt_pending_remove(struct mgmt_pending_cmd *cmd)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	list_del(&cmd->list);
31562306a36Sopenharmony_ci	mgmt_pending_free(cmd);
31662306a36Sopenharmony_ci}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_civoid mgmt_mesh_foreach(struct hci_dev *hdev,
31962306a36Sopenharmony_ci		       void (*cb)(struct mgmt_mesh_tx *mesh_tx, void *data),
32062306a36Sopenharmony_ci		       void *data, struct sock *sk)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct mgmt_mesh_tx *mesh_tx, *tmp;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	list_for_each_entry_safe(mesh_tx, tmp, &hdev->mgmt_pending, list) {
32562306a36Sopenharmony_ci		if (!sk || mesh_tx->sk == sk)
32662306a36Sopenharmony_ci			cb(mesh_tx, data);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistruct mgmt_mesh_tx *mgmt_mesh_next(struct hci_dev *hdev, struct sock *sk)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct mgmt_mesh_tx *mesh_tx;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (list_empty(&hdev->mesh_pending))
33562306a36Sopenharmony_ci		return NULL;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
33862306a36Sopenharmony_ci		if (!sk || mesh_tx->sk == sk)
33962306a36Sopenharmony_ci			return mesh_tx;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	return NULL;
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistruct mgmt_mesh_tx *mgmt_mesh_find(struct hci_dev *hdev, u8 handle)
34662306a36Sopenharmony_ci{
34762306a36Sopenharmony_ci	struct mgmt_mesh_tx *mesh_tx;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	if (list_empty(&hdev->mesh_pending))
35062306a36Sopenharmony_ci		return NULL;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	list_for_each_entry(mesh_tx, &hdev->mesh_pending, list) {
35362306a36Sopenharmony_ci		if (mesh_tx->handle == handle)
35462306a36Sopenharmony_ci			return mesh_tx;
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return NULL;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistruct mgmt_mesh_tx *mgmt_mesh_add(struct sock *sk, struct hci_dev *hdev,
36162306a36Sopenharmony_ci				   void *data, u16 len)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct mgmt_mesh_tx *mesh_tx;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	mesh_tx = kzalloc(sizeof(*mesh_tx), GFP_KERNEL);
36662306a36Sopenharmony_ci	if (!mesh_tx)
36762306a36Sopenharmony_ci		return NULL;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	hdev->mesh_send_ref++;
37062306a36Sopenharmony_ci	if (!hdev->mesh_send_ref)
37162306a36Sopenharmony_ci		hdev->mesh_send_ref++;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	mesh_tx->handle = hdev->mesh_send_ref;
37462306a36Sopenharmony_ci	mesh_tx->index = hdev->id;
37562306a36Sopenharmony_ci	memcpy(mesh_tx->param, data, len);
37662306a36Sopenharmony_ci	mesh_tx->param_len = len;
37762306a36Sopenharmony_ci	mesh_tx->sk = sk;
37862306a36Sopenharmony_ci	sock_hold(sk);
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	list_add_tail(&mesh_tx->list, &hdev->mesh_pending);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return mesh_tx;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_civoid mgmt_mesh_remove(struct mgmt_mesh_tx *mesh_tx)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	list_del(&mesh_tx->list);
38862306a36Sopenharmony_ci	sock_put(mesh_tx->sk);
38962306a36Sopenharmony_ci	kfree(mesh_tx);
39062306a36Sopenharmony_ci}
391