xref: /kernel/linux/linux-5.10/net/nfc/hci/hcp.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2012  Intel Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "hci: %s: " fmt, __func__
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <net/nfc/hci.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "hci.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/*
178c2ecf20Sopenharmony_ci * Payload is the HCP message data only. Instruction will be prepended.
188c2ecf20Sopenharmony_ci * Guarantees that cb will be called upon completion or timeout delay
198c2ecf20Sopenharmony_ci * counted from the moment the cmd is sent to the transport.
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ciint nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe,
228c2ecf20Sopenharmony_ci			   u8 type, u8 instruction,
238c2ecf20Sopenharmony_ci			   const u8 *payload, size_t payload_len,
248c2ecf20Sopenharmony_ci			   data_exchange_cb_t cb, void *cb_context,
258c2ecf20Sopenharmony_ci			   unsigned long completion_delay)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct nfc_dev *ndev = hdev->ndev;
288c2ecf20Sopenharmony_ci	struct hci_msg *cmd;
298c2ecf20Sopenharmony_ci	const u8 *ptr = payload;
308c2ecf20Sopenharmony_ci	int hci_len, err;
318c2ecf20Sopenharmony_ci	bool firstfrag = true;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	cmd = kzalloc(sizeof(struct hci_msg), GFP_KERNEL);
348c2ecf20Sopenharmony_ci	if (cmd == NULL)
358c2ecf20Sopenharmony_ci		return -ENOMEM;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cmd->msg_l);
388c2ecf20Sopenharmony_ci	skb_queue_head_init(&cmd->msg_frags);
398c2ecf20Sopenharmony_ci	cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false;
408c2ecf20Sopenharmony_ci	cmd->cb = cb;
418c2ecf20Sopenharmony_ci	cmd->cb_context = cb_context;
428c2ecf20Sopenharmony_ci	cmd->completion_delay = completion_delay;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	hci_len = payload_len + 1;
458c2ecf20Sopenharmony_ci	while (hci_len > 0) {
468c2ecf20Sopenharmony_ci		struct sk_buff *skb;
478c2ecf20Sopenharmony_ci		int skb_len, data_link_len;
488c2ecf20Sopenharmony_ci		struct hcp_packet *packet;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		if (NFC_HCI_HCP_PACKET_HEADER_LEN + hci_len <=
518c2ecf20Sopenharmony_ci		    hdev->max_data_link_payload)
528c2ecf20Sopenharmony_ci			data_link_len = hci_len;
538c2ecf20Sopenharmony_ci		else
548c2ecf20Sopenharmony_ci			data_link_len = hdev->max_data_link_payload -
558c2ecf20Sopenharmony_ci					NFC_HCI_HCP_PACKET_HEADER_LEN;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		skb_len = ndev->tx_headroom + NFC_HCI_HCP_PACKET_HEADER_LEN +
588c2ecf20Sopenharmony_ci			  data_link_len + ndev->tx_tailroom;
598c2ecf20Sopenharmony_ci		hci_len -= data_link_len;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci		skb = alloc_skb(skb_len, GFP_KERNEL);
628c2ecf20Sopenharmony_ci		if (skb == NULL) {
638c2ecf20Sopenharmony_ci			err = -ENOMEM;
648c2ecf20Sopenharmony_ci			goto out_skb_err;
658c2ecf20Sopenharmony_ci		}
668c2ecf20Sopenharmony_ci		skb_reserve(skb, ndev->tx_headroom);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		skb_put(skb, NFC_HCI_HCP_PACKET_HEADER_LEN + data_link_len);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		/* Only the last fragment will have the cb bit set to 1 */
718c2ecf20Sopenharmony_ci		packet = (struct hcp_packet *)skb->data;
728c2ecf20Sopenharmony_ci		packet->header = pipe;
738c2ecf20Sopenharmony_ci		if (firstfrag) {
748c2ecf20Sopenharmony_ci			firstfrag = false;
758c2ecf20Sopenharmony_ci			packet->message.header = HCP_HEADER(type, instruction);
768c2ecf20Sopenharmony_ci			if (ptr) {
778c2ecf20Sopenharmony_ci				memcpy(packet->message.data, ptr,
788c2ecf20Sopenharmony_ci				       data_link_len - 1);
798c2ecf20Sopenharmony_ci				ptr += data_link_len - 1;
808c2ecf20Sopenharmony_ci			}
818c2ecf20Sopenharmony_ci		} else {
828c2ecf20Sopenharmony_ci			memcpy(&packet->message, ptr, data_link_len);
838c2ecf20Sopenharmony_ci			ptr += data_link_len;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		/* This is the last fragment, set the cb bit */
878c2ecf20Sopenharmony_ci		if (hci_len == 0)
888c2ecf20Sopenharmony_ci			packet->header |= ~NFC_HCI_FRAGMENT;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		skb_queue_tail(&cmd->msg_frags, skb);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	mutex_lock(&hdev->msg_tx_mutex);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (hdev->shutting_down) {
968c2ecf20Sopenharmony_ci		err = -ESHUTDOWN;
978c2ecf20Sopenharmony_ci		mutex_unlock(&hdev->msg_tx_mutex);
988c2ecf20Sopenharmony_ci		goto out_skb_err;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	list_add_tail(&cmd->msg_l, &hdev->msg_tx_queue);
1028c2ecf20Sopenharmony_ci	mutex_unlock(&hdev->msg_tx_mutex);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	schedule_work(&hdev->msg_tx_work);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	return 0;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciout_skb_err:
1098c2ecf20Sopenharmony_ci	skb_queue_purge(&cmd->msg_frags);
1108c2ecf20Sopenharmony_ci	kfree(cmd);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return err;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/*
1168c2ecf20Sopenharmony_ci * Receive hcp message for pipe, with type and cmd.
1178c2ecf20Sopenharmony_ci * skb contains optional message data only.
1188c2ecf20Sopenharmony_ci */
1198c2ecf20Sopenharmony_civoid nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type,
1208c2ecf20Sopenharmony_ci			    u8 instruction, struct sk_buff *skb)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	switch (type) {
1238c2ecf20Sopenharmony_ci	case NFC_HCI_HCP_RESPONSE:
1248c2ecf20Sopenharmony_ci		nfc_hci_resp_received(hdev, instruction, skb);
1258c2ecf20Sopenharmony_ci		break;
1268c2ecf20Sopenharmony_ci	case NFC_HCI_HCP_COMMAND:
1278c2ecf20Sopenharmony_ci		nfc_hci_cmd_received(hdev, pipe, instruction, skb);
1288c2ecf20Sopenharmony_ci		break;
1298c2ecf20Sopenharmony_ci	case NFC_HCI_HCP_EVENT:
1308c2ecf20Sopenharmony_ci		nfc_hci_event_received(hdev, pipe, instruction, skb);
1318c2ecf20Sopenharmony_ci		break;
1328c2ecf20Sopenharmony_ci	default:
1338c2ecf20Sopenharmony_ci		pr_err("UNKNOWN MSG Type %d, instruction=%d\n",
1348c2ecf20Sopenharmony_ci		       type, instruction);
1358c2ecf20Sopenharmony_ci		kfree_skb(skb);
1368c2ecf20Sopenharmony_ci		break;
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
139