diff options
Diffstat (limited to 'net/nfc')
-rw-r--r-- | net/nfc/hci/Makefile | 2 | ||||
-rw-r--r-- | net/nfc/hci/llc.c | 16 | ||||
-rw-r--r-- | net/nfc/hci/llc.h | 1 | ||||
-rw-r--r-- | net/nfc/hci/llc_shdlc.c | 834 |
4 files changed, 851 insertions, 2 deletions
diff --git a/net/nfc/hci/Makefile b/net/nfc/hci/Makefile index 2ec4e5876f6b..c4d65479629b 100644 --- a/net/nfc/hci/Makefile +++ b/net/nfc/hci/Makefile | |||
@@ -5,4 +5,4 @@ | |||
5 | obj-$(CONFIG_NFC_HCI) += hci.o | 5 | obj-$(CONFIG_NFC_HCI) += hci.o |
6 | 6 | ||
7 | hci-y := core.o hcp.o command.o llc.o llc_nop.o | 7 | hci-y := core.o hcp.o command.o llc.o llc_nop.o |
8 | hci-$(CONFIG_NFC_SHDLC) += shdlc.o | 8 | hci-$(CONFIG_NFC_SHDLC) += shdlc.o llc_shdlc.o |
diff --git a/net/nfc/hci/llc.c b/net/nfc/hci/llc.c index 32002e5339c0..bd11b0f7658a 100644 --- a/net/nfc/hci/llc.c +++ b/net/nfc/hci/llc.c | |||
@@ -26,9 +26,23 @@ static struct list_head llc_engines; | |||
26 | 26 | ||
27 | int nfc_llc_init(void) | 27 | int nfc_llc_init(void) |
28 | { | 28 | { |
29 | int r; | ||
30 | |||
29 | INIT_LIST_HEAD(&llc_engines); | 31 | INIT_LIST_HEAD(&llc_engines); |
30 | 32 | ||
31 | return nfc_llc_nop_register(); | 33 | r = nfc_llc_nop_register(); |
34 | if (r) | ||
35 | goto exit; | ||
36 | |||
37 | r = nfc_llc_shdlc_register(); | ||
38 | if (r) | ||
39 | goto exit; | ||
40 | |||
41 | return 0; | ||
42 | |||
43 | exit: | ||
44 | nfc_llc_exit(); | ||
45 | return r; | ||
32 | } | 46 | } |
33 | EXPORT_SYMBOL(nfc_llc_init); | 47 | EXPORT_SYMBOL(nfc_llc_init); |
34 | 48 | ||
diff --git a/net/nfc/hci/llc.h b/net/nfc/hci/llc.h index acdd8d1bbae5..c7014fdfc8c9 100644 --- a/net/nfc/hci/llc.h +++ b/net/nfc/hci/llc.h | |||
@@ -56,5 +56,6 @@ int nfc_llc_register(const char *name, struct nfc_llc_ops *ops); | |||
56 | void nfc_llc_unregister(const char *name); | 56 | void nfc_llc_unregister(const char *name); |
57 | 57 | ||
58 | int nfc_llc_nop_register(void); | 58 | int nfc_llc_nop_register(void); |
59 | int nfc_llc_shdlc_register(void); | ||
59 | 60 | ||
60 | #endif /* __LOCAL_LLC_H_ */ | 61 | #endif /* __LOCAL_LLC_H_ */ |
diff --git a/net/nfc/hci/llc_shdlc.c b/net/nfc/hci/llc_shdlc.c new file mode 100644 index 000000000000..bb191100ee96 --- /dev/null +++ b/net/nfc/hci/llc_shdlc.c | |||
@@ -0,0 +1,834 @@ | |||
1 | /* | ||
2 | * shdlc Link Layer Control | ||
3 | * | ||
4 | * Copyright (C) 2012 Intel Corporation. All rights reserved. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the | ||
17 | * Free Software Foundation, Inc., | ||
18 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | #define pr_fmt(fmt) "shdlc: %s: " fmt, __func__ | ||
22 | |||
23 | #include <linux/types.h> | ||
24 | #include <linux/sched.h> | ||
25 | #include <linux/export.h> | ||
26 | #include <linux/wait.h> | ||
27 | #include <linux/slab.h> | ||
28 | #include <linux/skbuff.h> | ||
29 | |||
30 | #include "llc.h" | ||
31 | |||
32 | enum shdlc_state { | ||
33 | SHDLC_DISCONNECTED = 0, | ||
34 | SHDLC_CONNECTING = 1, | ||
35 | SHDLC_NEGOCIATING = 2, | ||
36 | SHDLC_CONNECTED = 3 | ||
37 | }; | ||
38 | |||
39 | struct llc_shdlc { | ||
40 | struct nfc_hci_dev *hdev; | ||
41 | xmit_to_drv_t xmit_to_drv; | ||
42 | rcv_to_hci_t rcv_to_hci; | ||
43 | |||
44 | struct mutex state_mutex; | ||
45 | enum shdlc_state state; | ||
46 | int hard_fault; | ||
47 | |||
48 | wait_queue_head_t *connect_wq; | ||
49 | int connect_tries; | ||
50 | int connect_result; | ||
51 | struct timer_list connect_timer;/* aka T3 in spec 10.6.1 */ | ||
52 | |||
53 | u8 w; /* window size */ | ||
54 | bool srej_support; | ||
55 | |||
56 | struct timer_list t1_timer; /* send ack timeout */ | ||
57 | bool t1_active; | ||
58 | |||
59 | struct timer_list t2_timer; /* guard/retransmit timeout */ | ||
60 | bool t2_active; | ||
61 | |||
62 | int ns; /* next seq num for send */ | ||
63 | int nr; /* next expected seq num for receive */ | ||
64 | int dnr; /* oldest sent unacked seq num */ | ||
65 | |||
66 | struct sk_buff_head rcv_q; | ||
67 | |||
68 | struct sk_buff_head send_q; | ||
69 | bool rnr; /* other side is not ready to receive */ | ||
70 | |||
71 | struct sk_buff_head ack_pending_q; | ||
72 | |||
73 | struct work_struct sm_work; | ||
74 | |||
75 | int tx_headroom; | ||
76 | int tx_tailroom; | ||
77 | |||
78 | llc_failure_t llc_failure; | ||
79 | }; | ||
80 | |||
81 | #define SHDLC_LLC_HEAD_ROOM 2 | ||
82 | |||
83 | #define SHDLC_MAX_WINDOW 4 | ||
84 | #define SHDLC_SREJ_SUPPORT false | ||
85 | |||
86 | #define SHDLC_CONTROL_HEAD_MASK 0xe0 | ||
87 | #define SHDLC_CONTROL_HEAD_I 0x80 | ||
88 | #define SHDLC_CONTROL_HEAD_I2 0xa0 | ||
89 | #define SHDLC_CONTROL_HEAD_S 0xc0 | ||
90 | #define SHDLC_CONTROL_HEAD_U 0xe0 | ||
91 | |||
92 | #define SHDLC_CONTROL_NS_MASK 0x38 | ||
93 | #define SHDLC_CONTROL_NR_MASK 0x07 | ||
94 | #define SHDLC_CONTROL_TYPE_MASK 0x18 | ||
95 | |||
96 | #define SHDLC_CONTROL_M_MASK 0x1f | ||
97 | |||
98 | enum sframe_type { | ||
99 | S_FRAME_RR = 0x00, | ||
100 | S_FRAME_REJ = 0x01, | ||
101 | S_FRAME_RNR = 0x02, | ||
102 | S_FRAME_SREJ = 0x03 | ||
103 | }; | ||
104 | |||
105 | enum uframe_modifier { | ||
106 | U_FRAME_UA = 0x06, | ||
107 | U_FRAME_RSET = 0x19 | ||
108 | }; | ||
109 | |||
110 | #define SHDLC_CONNECT_VALUE_MS 5 | ||
111 | #define SHDLC_T1_VALUE_MS(w) ((5 * w) / 4) | ||
112 | #define SHDLC_T2_VALUE_MS 300 | ||
113 | |||
114 | #define SHDLC_DUMP_SKB(info, skb) \ | ||
115 | do { \ | ||
116 | pr_debug("%s:\n", info); \ | ||
117 | print_hex_dump(KERN_DEBUG, "shdlc: ", DUMP_PREFIX_OFFSET, \ | ||
118 | 16, 1, skb->data, skb->len, 0); \ | ||
119 | } while (0) | ||
120 | |||
121 | /* checks x < y <= z modulo 8 */ | ||
122 | static bool llc_shdlc_x_lt_y_lteq_z(int x, int y, int z) | ||
123 | { | ||
124 | if (x < z) | ||
125 | return ((x < y) && (y <= z)) ? true : false; | ||
126 | else | ||
127 | return ((y > x) || (y <= z)) ? true : false; | ||
128 | } | ||
129 | |||
130 | /* checks x <= y < z modulo 8 */ | ||
131 | static bool llc_shdlc_x_lteq_y_lt_z(int x, int y, int z) | ||
132 | { | ||
133 | if (x <= z) | ||
134 | return ((x <= y) && (y < z)) ? true : false; | ||
135 | else /* x > z -> z+8 > x */ | ||
136 | return ((y >= x) || (y < z)) ? true : false; | ||
137 | } | ||
138 | |||
139 | static struct sk_buff *llc_shdlc_alloc_skb(struct llc_shdlc *shdlc, | ||
140 | int payload_len) | ||
141 | { | ||
142 | struct sk_buff *skb; | ||
143 | |||
144 | skb = alloc_skb(shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM + | ||
145 | shdlc->tx_tailroom + payload_len, GFP_KERNEL); | ||
146 | if (skb) | ||
147 | skb_reserve(skb, shdlc->tx_headroom + SHDLC_LLC_HEAD_ROOM); | ||
148 | |||
149 | return skb; | ||
150 | } | ||
151 | |||
152 | /* immediately sends an S frame. */ | ||
153 | static int llc_shdlc_send_s_frame(struct llc_shdlc *shdlc, | ||
154 | enum sframe_type sframe_type, int nr) | ||
155 | { | ||
156 | int r; | ||
157 | struct sk_buff *skb; | ||
158 | |||
159 | pr_debug("sframe_type=%d nr=%d\n", sframe_type, nr); | ||
160 | |||
161 | skb = llc_shdlc_alloc_skb(shdlc, 0); | ||
162 | if (skb == NULL) | ||
163 | return -ENOMEM; | ||
164 | |||
165 | *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_S | (sframe_type << 3) | nr; | ||
166 | |||
167 | r = shdlc->xmit_to_drv(shdlc->hdev, skb); | ||
168 | |||
169 | kfree_skb(skb); | ||
170 | |||
171 | return r; | ||
172 | } | ||
173 | |||
174 | /* immediately sends an U frame. skb may contain optional payload */ | ||
175 | static int llc_shdlc_send_u_frame(struct llc_shdlc *shdlc, | ||
176 | struct sk_buff *skb, | ||
177 | enum uframe_modifier uframe_modifier) | ||
178 | { | ||
179 | int r; | ||
180 | |||
181 | pr_debug("uframe_modifier=%d\n", uframe_modifier); | ||
182 | |||
183 | *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_U | uframe_modifier; | ||
184 | |||
185 | r = shdlc->xmit_to_drv(shdlc->hdev, skb); | ||
186 | |||
187 | kfree_skb(skb); | ||
188 | |||
189 | return r; | ||
190 | } | ||
191 | |||
192 | /* | ||
193 | * Free ack_pending frames until y_nr - 1, and reset t2 according to | ||
194 | * the remaining oldest ack_pending frame sent time | ||
195 | */ | ||
196 | static void llc_shdlc_reset_t2(struct llc_shdlc *shdlc, int y_nr) | ||
197 | { | ||
198 | struct sk_buff *skb; | ||
199 | int dnr = shdlc->dnr; /* MUST initially be < y_nr */ | ||
200 | |||
201 | pr_debug("release ack pending up to frame %d excluded\n", y_nr); | ||
202 | |||
203 | while (dnr != y_nr) { | ||
204 | pr_debug("release ack pending frame %d\n", dnr); | ||
205 | |||
206 | skb = skb_dequeue(&shdlc->ack_pending_q); | ||
207 | kfree_skb(skb); | ||
208 | |||
209 | dnr = (dnr + 1) % 8; | ||
210 | } | ||
211 | |||
212 | if (skb_queue_empty(&shdlc->ack_pending_q)) { | ||
213 | if (shdlc->t2_active) { | ||
214 | del_timer_sync(&shdlc->t2_timer); | ||
215 | shdlc->t2_active = false; | ||
216 | |||
217 | pr_debug | ||
218 | ("All sent frames acked. Stopped T2(retransmit)\n"); | ||
219 | } | ||
220 | } else { | ||
221 | skb = skb_peek(&shdlc->ack_pending_q); | ||
222 | |||
223 | mod_timer(&shdlc->t2_timer, *(unsigned long *)skb->cb + | ||
224 | msecs_to_jiffies(SHDLC_T2_VALUE_MS)); | ||
225 | shdlc->t2_active = true; | ||
226 | |||
227 | pr_debug | ||
228 | ("Start T2(retransmit) for remaining unacked sent frames\n"); | ||
229 | } | ||
230 | } | ||
231 | |||
232 | /* | ||
233 | * Receive validated frames from lower layer. skb contains HCI payload only. | ||
234 | * Handle according to algorithm at spec:10.8.2 | ||
235 | */ | ||
236 | static void llc_shdlc_rcv_i_frame(struct llc_shdlc *shdlc, | ||
237 | struct sk_buff *skb, int ns, int nr) | ||
238 | { | ||
239 | int x_ns = ns; | ||
240 | int y_nr = nr; | ||
241 | |||
242 | pr_debug("recvd I-frame %d, remote waiting frame %d\n", ns, nr); | ||
243 | |||
244 | if (shdlc->state != SHDLC_CONNECTED) | ||
245 | goto exit; | ||
246 | |||
247 | if (x_ns != shdlc->nr) { | ||
248 | llc_shdlc_send_s_frame(shdlc, S_FRAME_REJ, shdlc->nr); | ||
249 | goto exit; | ||
250 | } | ||
251 | |||
252 | if (shdlc->t1_active == false) { | ||
253 | shdlc->t1_active = true; | ||
254 | mod_timer(&shdlc->t1_timer, jiffies + | ||
255 | msecs_to_jiffies(SHDLC_T1_VALUE_MS(shdlc->w))); | ||
256 | pr_debug("(re)Start T1(send ack)\n"); | ||
257 | } | ||
258 | |||
259 | if (skb->len) { | ||
260 | shdlc->rcv_to_hci(shdlc->hdev, skb); | ||
261 | skb = NULL; | ||
262 | } | ||
263 | |||
264 | shdlc->nr = (shdlc->nr + 1) % 8; | ||
265 | |||
266 | if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { | ||
267 | llc_shdlc_reset_t2(shdlc, y_nr); | ||
268 | |||
269 | shdlc->dnr = y_nr; | ||
270 | } | ||
271 | |||
272 | exit: | ||
273 | kfree_skb(skb); | ||
274 | } | ||
275 | |||
276 | static void llc_shdlc_rcv_ack(struct llc_shdlc *shdlc, int y_nr) | ||
277 | { | ||
278 | pr_debug("remote acked up to frame %d excluded\n", y_nr); | ||
279 | |||
280 | if (llc_shdlc_x_lt_y_lteq_z(shdlc->dnr, y_nr, shdlc->ns)) { | ||
281 | llc_shdlc_reset_t2(shdlc, y_nr); | ||
282 | shdlc->dnr = y_nr; | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static void llc_shdlc_requeue_ack_pending(struct llc_shdlc *shdlc) | ||
287 | { | ||
288 | struct sk_buff *skb; | ||
289 | |||
290 | pr_debug("ns reset to %d\n", shdlc->dnr); | ||
291 | |||
292 | while ((skb = skb_dequeue_tail(&shdlc->ack_pending_q))) { | ||
293 | skb_pull(skb, 1); /* remove control field */ | ||
294 | skb_queue_head(&shdlc->send_q, skb); | ||
295 | } | ||
296 | shdlc->ns = shdlc->dnr; | ||
297 | } | ||
298 | |||
299 | static void llc_shdlc_rcv_rej(struct llc_shdlc *shdlc, int y_nr) | ||
300 | { | ||
301 | struct sk_buff *skb; | ||
302 | |||
303 | pr_debug("remote asks retransmition from frame %d\n", y_nr); | ||
304 | |||
305 | if (llc_shdlc_x_lteq_y_lt_z(shdlc->dnr, y_nr, shdlc->ns)) { | ||
306 | if (shdlc->t2_active) { | ||
307 | del_timer_sync(&shdlc->t2_timer); | ||
308 | shdlc->t2_active = false; | ||
309 | pr_debug("Stopped T2(retransmit)\n"); | ||
310 | } | ||
311 | |||
312 | if (shdlc->dnr != y_nr) { | ||
313 | while ((shdlc->dnr = ((shdlc->dnr + 1) % 8)) != y_nr) { | ||
314 | skb = skb_dequeue(&shdlc->ack_pending_q); | ||
315 | kfree_skb(skb); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | llc_shdlc_requeue_ack_pending(shdlc); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | /* See spec RR:10.8.3 REJ:10.8.4 */ | ||
324 | static void llc_shdlc_rcv_s_frame(struct llc_shdlc *shdlc, | ||
325 | enum sframe_type s_frame_type, int nr) | ||
326 | { | ||
327 | struct sk_buff *skb; | ||
328 | |||
329 | if (shdlc->state != SHDLC_CONNECTED) | ||
330 | return; | ||
331 | |||
332 | switch (s_frame_type) { | ||
333 | case S_FRAME_RR: | ||
334 | llc_shdlc_rcv_ack(shdlc, nr); | ||
335 | if (shdlc->rnr == true) { /* see SHDLC 10.7.7 */ | ||
336 | shdlc->rnr = false; | ||
337 | if (shdlc->send_q.qlen == 0) { | ||
338 | skb = llc_shdlc_alloc_skb(shdlc, 0); | ||
339 | if (skb) | ||
340 | skb_queue_tail(&shdlc->send_q, skb); | ||
341 | } | ||
342 | } | ||
343 | break; | ||
344 | case S_FRAME_REJ: | ||
345 | llc_shdlc_rcv_rej(shdlc, nr); | ||
346 | break; | ||
347 | case S_FRAME_RNR: | ||
348 | llc_shdlc_rcv_ack(shdlc, nr); | ||
349 | shdlc->rnr = true; | ||
350 | break; | ||
351 | default: | ||
352 | break; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | static void llc_shdlc_connect_complete(struct llc_shdlc *shdlc, int r) | ||
357 | { | ||
358 | pr_debug("result=%d\n", r); | ||
359 | |||
360 | del_timer_sync(&shdlc->connect_timer); | ||
361 | |||
362 | if (r == 0) { | ||
363 | shdlc->ns = 0; | ||
364 | shdlc->nr = 0; | ||
365 | shdlc->dnr = 0; | ||
366 | |||
367 | shdlc->state = SHDLC_CONNECTED; | ||
368 | } else { | ||
369 | shdlc->state = SHDLC_DISCONNECTED; | ||
370 | } | ||
371 | |||
372 | shdlc->connect_result = r; | ||
373 | |||
374 | wake_up(shdlc->connect_wq); | ||
375 | } | ||
376 | |||
377 | static int llc_shdlc_connect_initiate(struct llc_shdlc *shdlc) | ||
378 | { | ||
379 | struct sk_buff *skb; | ||
380 | |||
381 | pr_debug("\n"); | ||
382 | |||
383 | skb = llc_shdlc_alloc_skb(shdlc, 2); | ||
384 | if (skb == NULL) | ||
385 | return -ENOMEM; | ||
386 | |||
387 | *skb_put(skb, 1) = SHDLC_MAX_WINDOW; | ||
388 | *skb_put(skb, 1) = SHDLC_SREJ_SUPPORT ? 1 : 0; | ||
389 | |||
390 | return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_RSET); | ||
391 | } | ||
392 | |||
393 | static int llc_shdlc_connect_send_ua(struct llc_shdlc *shdlc) | ||
394 | { | ||
395 | struct sk_buff *skb; | ||
396 | |||
397 | pr_debug("\n"); | ||
398 | |||
399 | skb = llc_shdlc_alloc_skb(shdlc, 0); | ||
400 | if (skb == NULL) | ||
401 | return -ENOMEM; | ||
402 | |||
403 | return llc_shdlc_send_u_frame(shdlc, skb, U_FRAME_UA); | ||
404 | } | ||
405 | |||
406 | static void llc_shdlc_rcv_u_frame(struct llc_shdlc *shdlc, | ||
407 | struct sk_buff *skb, | ||
408 | enum uframe_modifier u_frame_modifier) | ||
409 | { | ||
410 | u8 w = SHDLC_MAX_WINDOW; | ||
411 | bool srej_support = SHDLC_SREJ_SUPPORT; | ||
412 | int r; | ||
413 | |||
414 | pr_debug("u_frame_modifier=%d\n", u_frame_modifier); | ||
415 | |||
416 | switch (u_frame_modifier) { | ||
417 | case U_FRAME_RSET: | ||
418 | if (shdlc->state == SHDLC_NEGOCIATING) { | ||
419 | /* we sent RSET, but chip wants to negociate */ | ||
420 | if (skb->len > 0) | ||
421 | w = skb->data[0]; | ||
422 | |||
423 | if (skb->len > 1) | ||
424 | srej_support = skb->data[1] & 0x01 ? true : | ||
425 | false; | ||
426 | |||
427 | if ((w <= SHDLC_MAX_WINDOW) && | ||
428 | (SHDLC_SREJ_SUPPORT || (srej_support == false))) { | ||
429 | shdlc->w = w; | ||
430 | shdlc->srej_support = srej_support; | ||
431 | r = llc_shdlc_connect_send_ua(shdlc); | ||
432 | llc_shdlc_connect_complete(shdlc, r); | ||
433 | } | ||
434 | } else if (shdlc->state == SHDLC_CONNECTED) { | ||
435 | /* | ||
436 | * Chip wants to reset link. This is unexpected and | ||
437 | * unsupported. | ||
438 | */ | ||
439 | shdlc->hard_fault = -ECONNRESET; | ||
440 | } | ||
441 | break; | ||
442 | case U_FRAME_UA: | ||
443 | if ((shdlc->state == SHDLC_CONNECTING && | ||
444 | shdlc->connect_tries > 0) || | ||
445 | (shdlc->state == SHDLC_NEGOCIATING)) | ||
446 | llc_shdlc_connect_complete(shdlc, 0); | ||
447 | break; | ||
448 | default: | ||
449 | break; | ||
450 | } | ||
451 | |||
452 | kfree_skb(skb); | ||
453 | } | ||
454 | |||
455 | static void llc_shdlc_handle_rcv_queue(struct llc_shdlc *shdlc) | ||
456 | { | ||
457 | struct sk_buff *skb; | ||
458 | u8 control; | ||
459 | int nr; | ||
460 | int ns; | ||
461 | enum sframe_type s_frame_type; | ||
462 | enum uframe_modifier u_frame_modifier; | ||
463 | |||
464 | if (shdlc->rcv_q.qlen) | ||
465 | pr_debug("rcvQlen=%d\n", shdlc->rcv_q.qlen); | ||
466 | |||
467 | while ((skb = skb_dequeue(&shdlc->rcv_q)) != NULL) { | ||
468 | control = skb->data[0]; | ||
469 | skb_pull(skb, 1); | ||
470 | switch (control & SHDLC_CONTROL_HEAD_MASK) { | ||
471 | case SHDLC_CONTROL_HEAD_I: | ||
472 | case SHDLC_CONTROL_HEAD_I2: | ||
473 | ns = (control & SHDLC_CONTROL_NS_MASK) >> 3; | ||
474 | nr = control & SHDLC_CONTROL_NR_MASK; | ||
475 | llc_shdlc_rcv_i_frame(shdlc, skb, ns, nr); | ||
476 | break; | ||
477 | case SHDLC_CONTROL_HEAD_S: | ||
478 | s_frame_type = (control & SHDLC_CONTROL_TYPE_MASK) >> 3; | ||
479 | nr = control & SHDLC_CONTROL_NR_MASK; | ||
480 | llc_shdlc_rcv_s_frame(shdlc, s_frame_type, nr); | ||
481 | kfree_skb(skb); | ||
482 | break; | ||
483 | case SHDLC_CONTROL_HEAD_U: | ||
484 | u_frame_modifier = control & SHDLC_CONTROL_M_MASK; | ||
485 | llc_shdlc_rcv_u_frame(shdlc, skb, u_frame_modifier); | ||
486 | break; | ||
487 | default: | ||
488 | pr_err("UNKNOWN Control=%d\n", control); | ||
489 | kfree_skb(skb); | ||
490 | break; | ||
491 | } | ||
492 | } | ||
493 | } | ||
494 | |||
495 | static int llc_shdlc_w_used(int ns, int dnr) | ||
496 | { | ||
497 | int unack_count; | ||
498 | |||
499 | if (dnr <= ns) | ||
500 | unack_count = ns - dnr; | ||
501 | else | ||
502 | unack_count = 8 - dnr + ns; | ||
503 | |||
504 | return unack_count; | ||
505 | } | ||
506 | |||
507 | /* Send frames according to algorithm at spec:10.8.1 */ | ||
508 | static void llc_shdlc_handle_send_queue(struct llc_shdlc *shdlc) | ||
509 | { | ||
510 | struct sk_buff *skb; | ||
511 | int r; | ||
512 | unsigned long time_sent; | ||
513 | |||
514 | if (shdlc->send_q.qlen) | ||
515 | pr_debug | ||
516 | ("sendQlen=%d ns=%d dnr=%d rnr=%s w_room=%d unackQlen=%d\n", | ||
517 | shdlc->send_q.qlen, shdlc->ns, shdlc->dnr, | ||
518 | shdlc->rnr == false ? "false" : "true", | ||
519 | shdlc->w - llc_shdlc_w_used(shdlc->ns, shdlc->dnr), | ||
520 | shdlc->ack_pending_q.qlen); | ||
521 | |||
522 | while (shdlc->send_q.qlen && shdlc->ack_pending_q.qlen < shdlc->w && | ||
523 | (shdlc->rnr == false)) { | ||
524 | |||
525 | if (shdlc->t1_active) { | ||
526 | del_timer_sync(&shdlc->t1_timer); | ||
527 | shdlc->t1_active = false; | ||
528 | pr_debug("Stopped T1(send ack)\n"); | ||
529 | } | ||
530 | |||
531 | skb = skb_dequeue(&shdlc->send_q); | ||
532 | |||
533 | *skb_push(skb, 1) = SHDLC_CONTROL_HEAD_I | (shdlc->ns << 3) | | ||
534 | shdlc->nr; | ||
535 | |||
536 | pr_debug("Sending I-Frame %d, waiting to rcv %d\n", shdlc->ns, | ||
537 | shdlc->nr); | ||
538 | /* SHDLC_DUMP_SKB("shdlc frame written", skb); */ | ||
539 | |||
540 | r = shdlc->xmit_to_drv(shdlc->hdev, skb); | ||
541 | if (r < 0) { | ||
542 | shdlc->hard_fault = r; | ||
543 | break; | ||
544 | } | ||
545 | |||
546 | shdlc->ns = (shdlc->ns + 1) % 8; | ||
547 | |||
548 | time_sent = jiffies; | ||
549 | *(unsigned long *)skb->cb = time_sent; | ||
550 | |||
551 | skb_queue_tail(&shdlc->ack_pending_q, skb); | ||
552 | |||
553 | if (shdlc->t2_active == false) { | ||
554 | shdlc->t2_active = true; | ||
555 | mod_timer(&shdlc->t2_timer, time_sent + | ||
556 | msecs_to_jiffies(SHDLC_T2_VALUE_MS)); | ||
557 | pr_debug("Started T2 (retransmit)\n"); | ||
558 | } | ||
559 | } | ||
560 | } | ||
561 | |||
562 | static void llc_shdlc_connect_timeout(unsigned long data) | ||
563 | { | ||
564 | struct llc_shdlc *shdlc = (struct llc_shdlc *)data; | ||
565 | |||
566 | pr_debug("\n"); | ||
567 | |||
568 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
569 | } | ||
570 | |||
571 | static void llc_shdlc_t1_timeout(unsigned long data) | ||
572 | { | ||
573 | struct llc_shdlc *shdlc = (struct llc_shdlc *)data; | ||
574 | |||
575 | pr_debug("SoftIRQ: need to send ack\n"); | ||
576 | |||
577 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
578 | } | ||
579 | |||
580 | static void llc_shdlc_t2_timeout(unsigned long data) | ||
581 | { | ||
582 | struct llc_shdlc *shdlc = (struct llc_shdlc *)data; | ||
583 | |||
584 | pr_debug("SoftIRQ: need to retransmit\n"); | ||
585 | |||
586 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
587 | } | ||
588 | |||
589 | static void llc_shdlc_sm_work(struct work_struct *work) | ||
590 | { | ||
591 | struct llc_shdlc *shdlc = container_of(work, struct llc_shdlc, sm_work); | ||
592 | int r; | ||
593 | |||
594 | pr_debug("\n"); | ||
595 | |||
596 | mutex_lock(&shdlc->state_mutex); | ||
597 | |||
598 | switch (shdlc->state) { | ||
599 | case SHDLC_DISCONNECTED: | ||
600 | skb_queue_purge(&shdlc->rcv_q); | ||
601 | skb_queue_purge(&shdlc->send_q); | ||
602 | skb_queue_purge(&shdlc->ack_pending_q); | ||
603 | break; | ||
604 | case SHDLC_CONNECTING: | ||
605 | if (shdlc->hard_fault) { | ||
606 | llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); | ||
607 | break; | ||
608 | } | ||
609 | |||
610 | if (shdlc->connect_tries++ < 5) | ||
611 | r = llc_shdlc_connect_initiate(shdlc); | ||
612 | else | ||
613 | r = -ETIME; | ||
614 | if (r < 0) | ||
615 | llc_shdlc_connect_complete(shdlc, r); | ||
616 | else { | ||
617 | mod_timer(&shdlc->connect_timer, jiffies + | ||
618 | msecs_to_jiffies(SHDLC_CONNECT_VALUE_MS)); | ||
619 | |||
620 | shdlc->state = SHDLC_NEGOCIATING; | ||
621 | } | ||
622 | break; | ||
623 | case SHDLC_NEGOCIATING: | ||
624 | if (timer_pending(&shdlc->connect_timer) == 0) { | ||
625 | shdlc->state = SHDLC_CONNECTING; | ||
626 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
627 | } | ||
628 | |||
629 | llc_shdlc_handle_rcv_queue(shdlc); | ||
630 | |||
631 | if (shdlc->hard_fault) { | ||
632 | llc_shdlc_connect_complete(shdlc, shdlc->hard_fault); | ||
633 | break; | ||
634 | } | ||
635 | break; | ||
636 | case SHDLC_CONNECTED: | ||
637 | llc_shdlc_handle_rcv_queue(shdlc); | ||
638 | llc_shdlc_handle_send_queue(shdlc); | ||
639 | |||
640 | if (shdlc->t1_active && timer_pending(&shdlc->t1_timer) == 0) { | ||
641 | pr_debug | ||
642 | ("Handle T1(send ack) elapsed (T1 now inactive)\n"); | ||
643 | |||
644 | shdlc->t1_active = false; | ||
645 | r = llc_shdlc_send_s_frame(shdlc, S_FRAME_RR, | ||
646 | shdlc->nr); | ||
647 | if (r < 0) | ||
648 | shdlc->hard_fault = r; | ||
649 | } | ||
650 | |||
651 | if (shdlc->t2_active && timer_pending(&shdlc->t2_timer) == 0) { | ||
652 | pr_debug | ||
653 | ("Handle T2(retransmit) elapsed (T2 inactive)\n"); | ||
654 | |||
655 | shdlc->t2_active = false; | ||
656 | |||
657 | llc_shdlc_requeue_ack_pending(shdlc); | ||
658 | llc_shdlc_handle_send_queue(shdlc); | ||
659 | } | ||
660 | |||
661 | if (shdlc->hard_fault) { | ||
662 | shdlc->llc_failure(shdlc->hdev, shdlc->hard_fault); | ||
663 | } | ||
664 | break; | ||
665 | default: | ||
666 | break; | ||
667 | } | ||
668 | mutex_unlock(&shdlc->state_mutex); | ||
669 | } | ||
670 | |||
671 | /* | ||
672 | * Called from syscall context to establish shdlc link. Sleeps until | ||
673 | * link is ready or failure. | ||
674 | */ | ||
675 | static int llc_shdlc_connect(struct llc_shdlc *shdlc) | ||
676 | { | ||
677 | DECLARE_WAIT_QUEUE_HEAD_ONSTACK(connect_wq); | ||
678 | |||
679 | pr_debug("\n"); | ||
680 | |||
681 | mutex_lock(&shdlc->state_mutex); | ||
682 | |||
683 | shdlc->state = SHDLC_CONNECTING; | ||
684 | shdlc->connect_wq = &connect_wq; | ||
685 | shdlc->connect_tries = 0; | ||
686 | shdlc->connect_result = 1; | ||
687 | |||
688 | mutex_unlock(&shdlc->state_mutex); | ||
689 | |||
690 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
691 | |||
692 | wait_event(connect_wq, shdlc->connect_result != 1); | ||
693 | |||
694 | return shdlc->connect_result; | ||
695 | } | ||
696 | |||
697 | static void llc_shdlc_disconnect(struct llc_shdlc *shdlc) | ||
698 | { | ||
699 | pr_debug("\n"); | ||
700 | |||
701 | mutex_lock(&shdlc->state_mutex); | ||
702 | |||
703 | shdlc->state = SHDLC_DISCONNECTED; | ||
704 | |||
705 | mutex_unlock(&shdlc->state_mutex); | ||
706 | |||
707 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
708 | } | ||
709 | |||
710 | /* | ||
711 | * Receive an incoming shdlc frame. Frame has already been crc-validated. | ||
712 | * skb contains only LLC header and payload. | ||
713 | * If skb == NULL, it is a notification that the link below is dead. | ||
714 | */ | ||
715 | static void llc_shdlc_recv_frame(struct llc_shdlc *shdlc, struct sk_buff *skb) | ||
716 | { | ||
717 | if (skb == NULL) { | ||
718 | pr_err("NULL Frame -> link is dead\n"); | ||
719 | shdlc->hard_fault = -EREMOTEIO; | ||
720 | } else { | ||
721 | SHDLC_DUMP_SKB("incoming frame", skb); | ||
722 | skb_queue_tail(&shdlc->rcv_q, skb); | ||
723 | } | ||
724 | |||
725 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
726 | } | ||
727 | |||
728 | static void *llc_shdlc_init(struct nfc_hci_dev *hdev, xmit_to_drv_t xmit_to_drv, | ||
729 | rcv_to_hci_t rcv_to_hci, int tx_headroom, | ||
730 | int tx_tailroom, int *rx_headroom, int *rx_tailroom, | ||
731 | llc_failure_t llc_failure) | ||
732 | { | ||
733 | struct llc_shdlc *shdlc; | ||
734 | |||
735 | *rx_headroom = SHDLC_LLC_HEAD_ROOM; | ||
736 | *rx_tailroom = 0; | ||
737 | |||
738 | shdlc = kzalloc(sizeof(struct llc_shdlc), GFP_KERNEL); | ||
739 | if (shdlc == NULL) | ||
740 | return NULL; | ||
741 | |||
742 | mutex_init(&shdlc->state_mutex); | ||
743 | shdlc->state = SHDLC_DISCONNECTED; | ||
744 | |||
745 | init_timer(&shdlc->connect_timer); | ||
746 | shdlc->connect_timer.data = (unsigned long)shdlc; | ||
747 | shdlc->connect_timer.function = llc_shdlc_connect_timeout; | ||
748 | |||
749 | init_timer(&shdlc->t1_timer); | ||
750 | shdlc->t1_timer.data = (unsigned long)shdlc; | ||
751 | shdlc->t1_timer.function = llc_shdlc_t1_timeout; | ||
752 | |||
753 | init_timer(&shdlc->t2_timer); | ||
754 | shdlc->t2_timer.data = (unsigned long)shdlc; | ||
755 | shdlc->t2_timer.function = llc_shdlc_t2_timeout; | ||
756 | |||
757 | shdlc->w = SHDLC_MAX_WINDOW; | ||
758 | shdlc->srej_support = SHDLC_SREJ_SUPPORT; | ||
759 | |||
760 | skb_queue_head_init(&shdlc->rcv_q); | ||
761 | skb_queue_head_init(&shdlc->send_q); | ||
762 | skb_queue_head_init(&shdlc->ack_pending_q); | ||
763 | |||
764 | INIT_WORK(&shdlc->sm_work, llc_shdlc_sm_work); | ||
765 | |||
766 | shdlc->hdev = hdev; | ||
767 | shdlc->xmit_to_drv = xmit_to_drv; | ||
768 | shdlc->rcv_to_hci = rcv_to_hci; | ||
769 | shdlc->tx_headroom = tx_headroom; | ||
770 | shdlc->tx_tailroom = tx_tailroom; | ||
771 | shdlc->llc_failure = llc_failure; | ||
772 | |||
773 | return shdlc; | ||
774 | } | ||
775 | |||
776 | static void llc_shdlc_deinit(struct nfc_llc *llc) | ||
777 | { | ||
778 | struct llc_shdlc *shdlc = nfc_llc_get_data(llc); | ||
779 | |||
780 | skb_queue_purge(&shdlc->rcv_q); | ||
781 | skb_queue_purge(&shdlc->send_q); | ||
782 | skb_queue_purge(&shdlc->ack_pending_q); | ||
783 | |||
784 | kfree(shdlc); | ||
785 | } | ||
786 | |||
787 | static int llc_shdlc_start(struct nfc_llc *llc) | ||
788 | { | ||
789 | struct llc_shdlc *shdlc = nfc_llc_get_data(llc); | ||
790 | |||
791 | return llc_shdlc_connect(shdlc); | ||
792 | } | ||
793 | |||
794 | static int llc_shdlc_stop(struct nfc_llc *llc) | ||
795 | { | ||
796 | struct llc_shdlc *shdlc = nfc_llc_get_data(llc); | ||
797 | |||
798 | llc_shdlc_disconnect(shdlc); | ||
799 | |||
800 | return 0; | ||
801 | } | ||
802 | |||
803 | static void llc_shdlc_rcv_from_drv(struct nfc_llc *llc, struct sk_buff *skb) | ||
804 | { | ||
805 | struct llc_shdlc *shdlc = nfc_llc_get_data(llc); | ||
806 | |||
807 | llc_shdlc_recv_frame(shdlc, skb); | ||
808 | } | ||
809 | |||
810 | static int llc_shdlc_xmit_from_hci(struct nfc_llc *llc, struct sk_buff *skb) | ||
811 | { | ||
812 | struct llc_shdlc *shdlc = nfc_llc_get_data(llc); | ||
813 | |||
814 | skb_queue_tail(&shdlc->send_q, skb); | ||
815 | |||
816 | queue_work(system_nrt_wq, &shdlc->sm_work); | ||
817 | |||
818 | return 0; | ||
819 | } | ||
820 | |||
821 | static struct nfc_llc_ops llc_shdlc_ops = { | ||
822 | .init = llc_shdlc_init, | ||
823 | .deinit = llc_shdlc_deinit, | ||
824 | .start = llc_shdlc_start, | ||
825 | .stop = llc_shdlc_stop, | ||
826 | .rcv_from_drv = llc_shdlc_rcv_from_drv, | ||
827 | .xmit_from_hci = llc_shdlc_xmit_from_hci, | ||
828 | }; | ||
829 | |||
830 | int nfc_llc_shdlc_register() | ||
831 | { | ||
832 | return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops); | ||
833 | } | ||
834 | EXPORT_SYMBOL(nfc_llc_shdlc_register); | ||