1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2012 Intel Corporation. All rights reserved. |
4 | */ |
5 | |
6 | #define pr_fmt(fmt) "hci: %s: " fmt, __func__ |
7 | |
8 | #include <linux/init.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/sched.h> |
11 | #include <linux/module.h> |
12 | |
13 | #include <net/nfc/hci.h> |
14 | |
15 | #include "hci.h" |
16 | |
17 | #define MAX_FWI 4949 |
18 | |
19 | static int nfc_hci_execute_cmd_async(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, |
20 | const u8 *param, size_t param_len, |
21 | data_exchange_cb_t cb, void *cb_context) |
22 | { |
23 | pr_debug("exec cmd async through pipe=%d, cmd=%d, plen=%zd\n" , pipe, |
24 | cmd, param_len); |
25 | |
26 | /* TODO: Define hci cmd execution delay. Should it be the same |
27 | * for all commands? |
28 | */ |
29 | return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_COMMAND, instruction: cmd, |
30 | payload: param, payload_len: param_len, cb, cb_context, MAX_FWI); |
31 | } |
32 | |
33 | /* |
34 | * HCI command execution completion callback. |
35 | * err will be a standard linux error (may be converted from HCI response) |
36 | * skb contains the response data and must be disposed, or may be NULL if |
37 | * an error occurred |
38 | */ |
39 | static void nfc_hci_execute_cb(void *context, struct sk_buff *skb, int err) |
40 | { |
41 | struct hcp_exec_waiter *hcp_ew = (struct hcp_exec_waiter *)context; |
42 | |
43 | pr_debug("HCI Cmd completed with result=%d\n" , err); |
44 | |
45 | hcp_ew->exec_result = err; |
46 | if (hcp_ew->exec_result == 0) |
47 | hcp_ew->result_skb = skb; |
48 | else |
49 | kfree_skb(skb); |
50 | hcp_ew->exec_complete = true; |
51 | |
52 | wake_up(hcp_ew->wq); |
53 | } |
54 | |
55 | static int nfc_hci_execute_cmd(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, |
56 | const u8 *param, size_t param_len, |
57 | struct sk_buff **skb) |
58 | { |
59 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(ew_wq); |
60 | struct hcp_exec_waiter hcp_ew; |
61 | hcp_ew.wq = &ew_wq; |
62 | hcp_ew.exec_complete = false; |
63 | hcp_ew.result_skb = NULL; |
64 | |
65 | pr_debug("exec cmd sync through pipe=%d, cmd=%d, plen=%zd\n" , pipe, |
66 | cmd, param_len); |
67 | |
68 | /* TODO: Define hci cmd execution delay. Should it be the same |
69 | * for all commands? |
70 | */ |
71 | hcp_ew.exec_result = nfc_hci_hcp_message_tx(hdev, pipe, |
72 | NFC_HCI_HCP_COMMAND, instruction: cmd, |
73 | payload: param, payload_len: param_len, |
74 | cb: nfc_hci_execute_cb, cb_context: &hcp_ew, |
75 | MAX_FWI); |
76 | if (hcp_ew.exec_result < 0) |
77 | return hcp_ew.exec_result; |
78 | |
79 | wait_event(ew_wq, hcp_ew.exec_complete == true); |
80 | |
81 | if (hcp_ew.exec_result == 0) { |
82 | if (skb) |
83 | *skb = hcp_ew.result_skb; |
84 | else |
85 | kfree_skb(skb: hcp_ew.result_skb); |
86 | } |
87 | |
88 | return hcp_ew.exec_result; |
89 | } |
90 | |
91 | int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event, |
92 | const u8 *param, size_t param_len) |
93 | { |
94 | u8 pipe; |
95 | |
96 | pr_debug("%d to gate %d\n" , event, gate); |
97 | |
98 | pipe = hdev->gate2pipe[gate]; |
99 | if (pipe == NFC_HCI_INVALID_PIPE) |
100 | return -EADDRNOTAVAIL; |
101 | |
102 | return nfc_hci_hcp_message_tx(hdev, pipe, NFC_HCI_HCP_EVENT, instruction: event, |
103 | payload: param, payload_len: param_len, NULL, NULL, completion_delay: 0); |
104 | } |
105 | EXPORT_SYMBOL(nfc_hci_send_event); |
106 | |
107 | /* |
108 | * Execute an hci command sent to gate. |
109 | * skb will contain response data if success. skb can be NULL if you are not |
110 | * interested by the response. |
111 | */ |
112 | int nfc_hci_send_cmd(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, |
113 | const u8 *param, size_t param_len, struct sk_buff **skb) |
114 | { |
115 | u8 pipe; |
116 | |
117 | pipe = hdev->gate2pipe[gate]; |
118 | if (pipe == NFC_HCI_INVALID_PIPE) |
119 | return -EADDRNOTAVAIL; |
120 | |
121 | return nfc_hci_execute_cmd(hdev, pipe, cmd, param, param_len, skb); |
122 | } |
123 | EXPORT_SYMBOL(nfc_hci_send_cmd); |
124 | |
125 | int nfc_hci_send_cmd_async(struct nfc_hci_dev *hdev, u8 gate, u8 cmd, |
126 | const u8 *param, size_t param_len, |
127 | data_exchange_cb_t cb, void *cb_context) |
128 | { |
129 | u8 pipe; |
130 | |
131 | pipe = hdev->gate2pipe[gate]; |
132 | if (pipe == NFC_HCI_INVALID_PIPE) |
133 | return -EADDRNOTAVAIL; |
134 | |
135 | return nfc_hci_execute_cmd_async(hdev, pipe, cmd, param, param_len, |
136 | cb, cb_context); |
137 | } |
138 | EXPORT_SYMBOL(nfc_hci_send_cmd_async); |
139 | |
140 | int nfc_hci_set_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, |
141 | const u8 *param, size_t param_len) |
142 | { |
143 | int r; |
144 | u8 *tmp; |
145 | |
146 | /* TODO ELa: reg idx must be inserted before param, but we don't want |
147 | * to ask the caller to do it to keep a simpler API. |
148 | * For now, just create a new temporary param buffer. This is far from |
149 | * optimal though, and the plan is to modify APIs to pass idx down to |
150 | * nfc_hci_hcp_message_tx where the frame is actually built, thereby |
151 | * eliminating the need for the temp allocation-copy here. |
152 | */ |
153 | |
154 | pr_debug("idx=%d to gate %d\n" , idx, gate); |
155 | |
156 | tmp = kmalloc(size: 1 + param_len, GFP_KERNEL); |
157 | if (tmp == NULL) |
158 | return -ENOMEM; |
159 | |
160 | *tmp = idx; |
161 | memcpy(tmp + 1, param, param_len); |
162 | |
163 | r = nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_SET_PARAMETER, |
164 | tmp, param_len + 1, NULL); |
165 | |
166 | kfree(objp: tmp); |
167 | |
168 | return r; |
169 | } |
170 | EXPORT_SYMBOL(nfc_hci_set_param); |
171 | |
172 | int nfc_hci_get_param(struct nfc_hci_dev *hdev, u8 gate, u8 idx, |
173 | struct sk_buff **skb) |
174 | { |
175 | pr_debug("gate=%d regidx=%d\n" , gate, idx); |
176 | |
177 | return nfc_hci_send_cmd(hdev, gate, NFC_HCI_ANY_GET_PARAMETER, |
178 | &idx, 1, skb); |
179 | } |
180 | EXPORT_SYMBOL(nfc_hci_get_param); |
181 | |
182 | static int nfc_hci_open_pipe(struct nfc_hci_dev *hdev, u8 pipe) |
183 | { |
184 | struct sk_buff *skb; |
185 | int r; |
186 | |
187 | pr_debug("pipe=%d\n" , pipe); |
188 | |
189 | r = nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_OPEN_PIPE, |
190 | NULL, param_len: 0, skb: &skb); |
191 | if (r == 0) { |
192 | /* dest host other than host controller will send |
193 | * number of pipes already open on this gate before |
194 | * execution. The number can be found in skb->data[0] |
195 | */ |
196 | kfree_skb(skb); |
197 | } |
198 | |
199 | return r; |
200 | } |
201 | |
202 | static int nfc_hci_close_pipe(struct nfc_hci_dev *hdev, u8 pipe) |
203 | { |
204 | return nfc_hci_execute_cmd(hdev, pipe, NFC_HCI_ANY_CLOSE_PIPE, |
205 | NULL, param_len: 0, NULL); |
206 | } |
207 | |
208 | static u8 nfc_hci_create_pipe(struct nfc_hci_dev *hdev, u8 dest_host, |
209 | u8 dest_gate, int *result) |
210 | { |
211 | struct sk_buff *skb; |
212 | struct hci_create_pipe_params params; |
213 | struct hci_create_pipe_resp *resp; |
214 | u8 pipe; |
215 | |
216 | pr_debug("gate=%d\n" , dest_gate); |
217 | |
218 | params.src_gate = NFC_HCI_ADMIN_GATE; |
219 | params.dest_host = dest_host; |
220 | params.dest_gate = dest_gate; |
221 | |
222 | *result = nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, |
223 | NFC_HCI_ADM_CREATE_PIPE, |
224 | param: (u8 *) ¶ms, param_len: sizeof(params), skb: &skb); |
225 | if (*result < 0) |
226 | return NFC_HCI_INVALID_PIPE; |
227 | |
228 | resp = (struct hci_create_pipe_resp *)skb->data; |
229 | pipe = resp->pipe; |
230 | kfree_skb(skb); |
231 | |
232 | pr_debug("pipe created=%d\n" , pipe); |
233 | |
234 | return pipe; |
235 | } |
236 | |
237 | static int nfc_hci_delete_pipe(struct nfc_hci_dev *hdev, u8 pipe) |
238 | { |
239 | return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, |
240 | NFC_HCI_ADM_DELETE_PIPE, param: &pipe, param_len: 1, NULL); |
241 | } |
242 | |
243 | static int nfc_hci_clear_all_pipes(struct nfc_hci_dev *hdev) |
244 | { |
245 | u8 param[2]; |
246 | size_t param_len = 2; |
247 | |
248 | /* TODO: Find out what the identity reference data is |
249 | * and fill param with it. HCI spec 6.1.3.5 */ |
250 | |
251 | if (test_bit(NFC_HCI_QUIRK_SHORT_CLEAR, &hdev->quirks)) |
252 | param_len = 0; |
253 | |
254 | return nfc_hci_execute_cmd(hdev, NFC_HCI_ADMIN_PIPE, |
255 | NFC_HCI_ADM_CLEAR_ALL_PIPE, param, param_len, |
256 | NULL); |
257 | } |
258 | |
259 | int nfc_hci_disconnect_gate(struct nfc_hci_dev *hdev, u8 gate) |
260 | { |
261 | int r; |
262 | u8 pipe = hdev->gate2pipe[gate]; |
263 | |
264 | if (pipe == NFC_HCI_INVALID_PIPE) |
265 | return -EADDRNOTAVAIL; |
266 | |
267 | r = nfc_hci_close_pipe(hdev, pipe); |
268 | if (r < 0) |
269 | return r; |
270 | |
271 | if (pipe != NFC_HCI_LINK_MGMT_PIPE && pipe != NFC_HCI_ADMIN_PIPE) { |
272 | r = nfc_hci_delete_pipe(hdev, pipe); |
273 | if (r < 0) |
274 | return r; |
275 | } |
276 | |
277 | hdev->gate2pipe[gate] = NFC_HCI_INVALID_PIPE; |
278 | |
279 | return 0; |
280 | } |
281 | EXPORT_SYMBOL(nfc_hci_disconnect_gate); |
282 | |
283 | int nfc_hci_disconnect_all_gates(struct nfc_hci_dev *hdev) |
284 | { |
285 | int r; |
286 | |
287 | r = nfc_hci_clear_all_pipes(hdev); |
288 | if (r < 0) |
289 | return r; |
290 | |
291 | nfc_hci_reset_pipes(dev: hdev); |
292 | |
293 | return 0; |
294 | } |
295 | EXPORT_SYMBOL(nfc_hci_disconnect_all_gates); |
296 | |
297 | int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, |
298 | u8 pipe) |
299 | { |
300 | bool pipe_created = false; |
301 | int r; |
302 | |
303 | if (pipe == NFC_HCI_DO_NOT_CREATE_PIPE) |
304 | return 0; |
305 | |
306 | if (hdev->gate2pipe[dest_gate] != NFC_HCI_INVALID_PIPE) |
307 | return -EADDRINUSE; |
308 | |
309 | if (pipe != NFC_HCI_INVALID_PIPE) |
310 | goto open_pipe; |
311 | |
312 | switch (dest_gate) { |
313 | case NFC_HCI_LINK_MGMT_GATE: |
314 | pipe = NFC_HCI_LINK_MGMT_PIPE; |
315 | break; |
316 | case NFC_HCI_ADMIN_GATE: |
317 | pipe = NFC_HCI_ADMIN_PIPE; |
318 | break; |
319 | default: |
320 | pipe = nfc_hci_create_pipe(hdev, dest_host, dest_gate, result: &r); |
321 | if (pipe == NFC_HCI_INVALID_PIPE) |
322 | return r; |
323 | pipe_created = true; |
324 | break; |
325 | } |
326 | |
327 | open_pipe: |
328 | r = nfc_hci_open_pipe(hdev, pipe); |
329 | if (r < 0) { |
330 | if (pipe_created) |
331 | if (nfc_hci_delete_pipe(hdev, pipe) < 0) { |
332 | /* TODO: Cannot clean by deleting pipe... |
333 | * -> inconsistent state */ |
334 | } |
335 | return r; |
336 | } |
337 | |
338 | hdev->pipes[pipe].gate = dest_gate; |
339 | hdev->pipes[pipe].dest_host = dest_host; |
340 | hdev->gate2pipe[dest_gate] = pipe; |
341 | |
342 | return 0; |
343 | } |
344 | EXPORT_SYMBOL(nfc_hci_connect_gate); |
345 | |