aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Lapuyade <eric.lapuyade@linux.intel.com>2012-09-13 11:11:37 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2012-09-24 18:17:25 -0400
commit4a61cd6687fc6348d08724676d34e38160d6cf9b (patch)
tree9bfcd0af9ad2e7c085c0542f2565467659823789
parent8af00d48dc929442644bf68e9cd3d951d9697296 (diff)
NFC: Add an shdlc llc module to llc core
This is used by HCI drivers such as the one for the pn544 which require communications between HCI and the chip to use shdlc. Signed-off-by: Eric Lapuyade <eric.lapuyade@intel.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
-rw-r--r--include/net/nfc/llc.h1
-rw-r--r--net/nfc/hci/Makefile2
-rw-r--r--net/nfc/hci/llc.c16
-rw-r--r--net/nfc/hci/llc.h1
-rw-r--r--net/nfc/hci/llc_shdlc.c834
5 files changed, 852 insertions, 2 deletions
diff --git a/include/net/nfc/llc.h b/include/net/nfc/llc.h
index 146e053f91e7..400ab7ae749d 100644
--- a/include/net/nfc/llc.h
+++ b/include/net/nfc/llc.h
@@ -25,6 +25,7 @@
25#include <linux/skbuff.h> 25#include <linux/skbuff.h>
26 26
27#define LLC_NOP_NAME "nop" 27#define LLC_NOP_NAME "nop"
28#define LLC_SHDLC_NAME "shdlc"
28 29
29typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); 30typedef void (*rcv_to_hci_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
30typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb); 31typedef int (*xmit_to_drv_t) (struct nfc_hci_dev *hdev, struct sk_buff *skb);
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 @@
5obj-$(CONFIG_NFC_HCI) += hci.o 5obj-$(CONFIG_NFC_HCI) += hci.o
6 6
7hci-y := core.o hcp.o command.o llc.o llc_nop.o 7hci-y := core.o hcp.o command.o llc.o llc_nop.o
8hci-$(CONFIG_NFC_SHDLC) += shdlc.o 8hci-$(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
27int nfc_llc_init(void) 27int 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
43exit:
44 nfc_llc_exit();
45 return r;
32} 46}
33EXPORT_SYMBOL(nfc_llc_init); 47EXPORT_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);
56void nfc_llc_unregister(const char *name); 56void nfc_llc_unregister(const char *name);
57 57
58int nfc_llc_nop_register(void); 58int nfc_llc_nop_register(void);
59int 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
32enum shdlc_state {
33 SHDLC_DISCONNECTED = 0,
34 SHDLC_CONNECTING = 1,
35 SHDLC_NEGOCIATING = 2,
36 SHDLC_CONNECTED = 3
37};
38
39struct 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
98enum sframe_type {
99 S_FRAME_RR = 0x00,
100 S_FRAME_REJ = 0x01,
101 S_FRAME_RNR = 0x02,
102 S_FRAME_SREJ = 0x03
103};
104
105enum 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) \
115do { \
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 */
122static 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 */
131static 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
139static 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. */
153static 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 */
175static 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 */
196static 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 */
236static 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
272exit:
273 kfree_skb(skb);
274}
275
276static 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
286static 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
299static 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 */
324static 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
356static 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
377static 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
393static 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
406static 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
455static 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
495static 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 */
508static 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
562static 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
571static 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
580static 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
589static 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 */
675static 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
697static 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 */
715static 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
728static 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
776static 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
787static 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
794static 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
803static 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
810static 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
821static 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
830int nfc_llc_shdlc_register()
831{
832 return nfc_llc_register(LLC_SHDLC_NAME, &llc_shdlc_ops);
833}
834EXPORT_SYMBOL(nfc_llc_shdlc_register);