mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.3 KiB
138 lines
3.3 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* |
|
* Copyright (C) 2012 Intel Corporation. All rights reserved. |
|
*/ |
|
|
|
#define pr_fmt(fmt) "hci: %s: " fmt, __func__ |
|
|
|
#include <linux/init.h> |
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
|
|
#include <net/nfc/hci.h> |
|
|
|
#include "hci.h" |
|
|
|
/* |
|
* Payload is the HCP message data only. Instruction will be prepended. |
|
* Guarantees that cb will be called upon completion or timeout delay |
|
* counted from the moment the cmd is sent to the transport. |
|
*/ |
|
int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, |
|
u8 type, u8 instruction, |
|
const u8 *payload, size_t payload_len, |
|
data_exchange_cb_t cb, void *cb_context, |
|
unsigned long completion_delay) |
|
{ |
|
struct nfc_dev *ndev = hdev->ndev; |
|
struct hci_msg *cmd; |
|
const u8 *ptr = payload; |
|
int hci_len, err; |
|
bool firstfrag = true; |
|
|
|
cmd = kzalloc(sizeof(struct hci_msg), GFP_KERNEL); |
|
if (cmd == NULL) |
|
return -ENOMEM; |
|
|
|
INIT_LIST_HEAD(&cmd->msg_l); |
|
skb_queue_head_init(&cmd->msg_frags); |
|
cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false; |
|
cmd->cb = cb; |
|
cmd->cb_context = cb_context; |
|
cmd->completion_delay = completion_delay; |
|
|
|
hci_len = payload_len + 1; |
|
while (hci_len > 0) { |
|
struct sk_buff *skb; |
|
int skb_len, data_link_len; |
|
struct hcp_packet *packet; |
|
|
|
if (NFC_HCI_HCP_PACKET_HEADER_LEN + hci_len <= |
|
hdev->max_data_link_payload) |
|
data_link_len = hci_len; |
|
else |
|
data_link_len = hdev->max_data_link_payload - |
|
NFC_HCI_HCP_PACKET_HEADER_LEN; |
|
|
|
skb_len = ndev->tx_headroom + NFC_HCI_HCP_PACKET_HEADER_LEN + |
|
data_link_len + ndev->tx_tailroom; |
|
hci_len -= data_link_len; |
|
|
|
skb = alloc_skb(skb_len, GFP_KERNEL); |
|
if (skb == NULL) { |
|
err = -ENOMEM; |
|
goto out_skb_err; |
|
} |
|
skb_reserve(skb, ndev->tx_headroom); |
|
|
|
skb_put(skb, NFC_HCI_HCP_PACKET_HEADER_LEN + data_link_len); |
|
|
|
/* Only the last fragment will have the cb bit set to 1 */ |
|
packet = (struct hcp_packet *)skb->data; |
|
packet->header = pipe; |
|
if (firstfrag) { |
|
firstfrag = false; |
|
packet->message.header = HCP_HEADER(type, instruction); |
|
if (ptr) { |
|
memcpy(packet->message.data, ptr, |
|
data_link_len - 1); |
|
ptr += data_link_len - 1; |
|
} |
|
} else { |
|
memcpy(&packet->message, ptr, data_link_len); |
|
ptr += data_link_len; |
|
} |
|
|
|
/* This is the last fragment, set the cb bit */ |
|
if (hci_len == 0) |
|
packet->header |= ~NFC_HCI_FRAGMENT; |
|
|
|
skb_queue_tail(&cmd->msg_frags, skb); |
|
} |
|
|
|
mutex_lock(&hdev->msg_tx_mutex); |
|
|
|
if (hdev->shutting_down) { |
|
err = -ESHUTDOWN; |
|
mutex_unlock(&hdev->msg_tx_mutex); |
|
goto out_skb_err; |
|
} |
|
|
|
list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue); |
|
mutex_unlock(&hdev->msg_tx_mutex); |
|
|
|
schedule_work(&hdev->msg_tx_work); |
|
|
|
return 0; |
|
|
|
out_skb_err: |
|
skb_queue_purge(&cmd->msg_frags); |
|
kfree(cmd); |
|
|
|
return err; |
|
} |
|
|
|
/* |
|
* Receive hcp message for pipe, with type and cmd. |
|
* skb contains optional message data only. |
|
*/ |
|
void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type, |
|
u8 instruction, struct sk_buff *skb) |
|
{ |
|
switch (type) { |
|
case NFC_HCI_HCP_RESPONSE: |
|
nfc_hci_resp_received(hdev, instruction, skb); |
|
break; |
|
case NFC_HCI_HCP_COMMAND: |
|
nfc_hci_cmd_received(hdev, pipe, instruction, skb); |
|
break; |
|
case NFC_HCI_HCP_EVENT: |
|
nfc_hci_event_received(hdev, pipe, instruction, skb); |
|
break; |
|
default: |
|
pr_err("UNKNOWN MSG Type %d, instruction=%d\n", |
|
type, instruction); |
|
kfree_skb(skb); |
|
break; |
|
} |
|
}
|
|
|