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