| // 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); |
| } else { |
| packet->message.header = *ptr++; |
| } |
| if (ptr) { |
| memcpy(packet->message.data, ptr, data_link_len - 1); |
| ptr += data_link_len - 1; |
| } |
| |
| /* 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; |
| } |
| } |