diff options
Diffstat (limited to 'net/nfc/hci/hcp.c')
-rw-r--r-- | net/nfc/hci/hcp.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/net/nfc/hci/hcp.c b/net/nfc/hci/hcp.c new file mode 100644 index 000000000000..7212cf2c5785 --- /dev/null +++ b/net/nfc/hci/hcp.c | |||
@@ -0,0 +1,156 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License as published by | ||
6 | * the Free Software Foundation; either version 2 of the License, or | ||
7 | * (at your option) any later version. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the | ||
16 | * Free Software Foundation, Inc., | ||
17 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
18 | */ | ||
19 | |||
20 | #define pr_fmt(fmt) "hci: %s: " fmt, __func__ | ||
21 | |||
22 | #include <linux/init.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | |||
26 | #include <net/nfc/hci.h> | ||
27 | |||
28 | #include "hci.h" | ||
29 | |||
30 | /* | ||
31 | * Payload is the HCP message data only. Instruction will be prepended. | ||
32 | * Guarantees that cb will be called upon completion or timeout delay | ||
33 | * counted from the moment the cmd is sent to the transport. | ||
34 | */ | ||
35 | int nfc_hci_hcp_message_tx(struct nfc_hci_dev *hdev, u8 pipe, | ||
36 | u8 type, u8 instruction, | ||
37 | const u8 *payload, size_t payload_len, | ||
38 | hci_cmd_cb_t cb, void *cb_data, | ||
39 | unsigned long completion_delay) | ||
40 | { | ||
41 | struct nfc_dev *ndev = hdev->ndev; | ||
42 | struct hci_msg *cmd; | ||
43 | const u8 *ptr = payload; | ||
44 | int hci_len, err; | ||
45 | bool firstfrag = true; | ||
46 | |||
47 | cmd = kzalloc(sizeof(struct hci_msg), GFP_KERNEL); | ||
48 | if (cmd == NULL) | ||
49 | return -ENOMEM; | ||
50 | |||
51 | INIT_LIST_HEAD(&cmd->msg_l); | ||
52 | skb_queue_head_init(&cmd->msg_frags); | ||
53 | cmd->wait_response = (type == NFC_HCI_HCP_COMMAND) ? true : false; | ||
54 | cmd->cb = cb; | ||
55 | cmd->cb_context = cb_data; | ||
56 | cmd->completion_delay = completion_delay; | ||
57 | |||
58 | hci_len = payload_len + 1; | ||
59 | while (hci_len > 0) { | ||
60 | struct sk_buff *skb; | ||
61 | int skb_len, data_link_len; | ||
62 | struct hcp_packet *packet; | ||
63 | |||
64 | if (NFC_HCI_HCP_PACKET_HEADER_LEN + hci_len <= | ||
65 | hdev->max_data_link_payload) | ||
66 | data_link_len = hci_len; | ||
67 | else | ||
68 | data_link_len = hdev->max_data_link_payload - | ||
69 | NFC_HCI_HCP_PACKET_HEADER_LEN; | ||
70 | |||
71 | skb_len = ndev->tx_headroom + NFC_HCI_HCP_PACKET_HEADER_LEN + | ||
72 | data_link_len + ndev->tx_tailroom; | ||
73 | hci_len -= data_link_len; | ||
74 | |||
75 | skb = alloc_skb(skb_len, GFP_KERNEL); | ||
76 | if (skb == NULL) { | ||
77 | err = -ENOMEM; | ||
78 | goto out_skb_err; | ||
79 | } | ||
80 | skb_reserve(skb, ndev->tx_headroom); | ||
81 | |||
82 | skb_put(skb, NFC_HCI_HCP_PACKET_HEADER_LEN + data_link_len); | ||
83 | |||
84 | /* Only the last fragment will have the cb bit set to 1 */ | ||
85 | packet = (struct hcp_packet *)skb->data; | ||
86 | packet->header = pipe; | ||
87 | if (firstfrag) { | ||
88 | firstfrag = false; | ||
89 | packet->message.header = HCP_HEADER(type, instruction); | ||
90 | if (ptr) { | ||
91 | memcpy(packet->message.data, ptr, | ||
92 | data_link_len - 1); | ||
93 | ptr += data_link_len - 1; | ||
94 | } | ||
95 | } else { | ||
96 | memcpy(&packet->message, ptr, data_link_len); | ||
97 | ptr += data_link_len; | ||
98 | } | ||
99 | |||
100 | /* This is the last fragment, set the cb bit */ | ||
101 | if (hci_len == 0) | ||
102 | packet->header |= ~NFC_HCI_FRAGMENT; | ||
103 | |||
104 | skb_queue_tail(&cmd->msg_frags, skb); | ||
105 | } | ||
106 | |||
107 | mutex_lock(&hdev->msg_tx_mutex); | ||
108 | list_add_tail(&hdev->msg_tx_queue, &cmd->msg_l); | ||
109 | mutex_unlock(&hdev->msg_tx_mutex); | ||
110 | |||
111 | queue_work(hdev->msg_tx_wq, &hdev->msg_tx_work); | ||
112 | |||
113 | return 0; | ||
114 | |||
115 | out_skb_err: | ||
116 | skb_queue_purge(&cmd->msg_frags); | ||
117 | kfree(cmd); | ||
118 | |||
119 | return err; | ||
120 | } | ||
121 | |||
122 | u8 nfc_hci_pipe2gate(struct nfc_hci_dev *hdev, u8 pipe) | ||
123 | { | ||
124 | int gate; | ||
125 | |||
126 | for (gate = 0; gate < NFC_HCI_MAX_GATES; gate++) | ||
127 | if (hdev->gate2pipe[gate] == pipe) | ||
128 | return gate; | ||
129 | |||
130 | return 0xff; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Receive hcp message for pipe, with type and cmd. | ||
135 | * skb contains optional message data only. | ||
136 | */ | ||
137 | void nfc_hci_hcp_message_rx(struct nfc_hci_dev *hdev, u8 pipe, u8 type, | ||
138 | u8 instruction, struct sk_buff *skb) | ||
139 | { | ||
140 | switch (type) { | ||
141 | case NFC_HCI_HCP_RESPONSE: | ||
142 | nfc_hci_resp_received(hdev, instruction, skb); | ||
143 | break; | ||
144 | case NFC_HCI_HCP_COMMAND: | ||
145 | nfc_hci_cmd_received(hdev, pipe, instruction, skb); | ||
146 | break; | ||
147 | case NFC_HCI_HCP_EVENT: | ||
148 | nfc_hci_event_received(hdev, pipe, instruction, skb); | ||
149 | break; | ||
150 | default: | ||
151 | pr_err("UNKNOWN MSG Type %d, instruction=%d\n", | ||
152 | type, instruction); | ||
153 | kfree_skb(skb); | ||
154 | break; | ||
155 | } | ||
156 | } | ||