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