root/net/nfc/hci/hcp.c
// 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_obj(struct hci_msg);
        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;
        }
}