1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Low Level Transport (NDLC) Driver for STMicroelectronics NFC Chip |
4 | * |
5 | * Copyright (C) 2014-2015 STMicroelectronics SAS. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/sched.h> |
9 | #include <net/nfc/nci_core.h> |
10 | |
11 | #include "st-nci.h" |
12 | |
13 | #define NDLC_TIMER_T1 100 |
14 | #define NDLC_TIMER_T1_WAIT 400 |
15 | #define NDLC_TIMER_T2 1200 |
16 | |
17 | #define PCB_TYPE_DATAFRAME 0x80 |
18 | #define PCB_TYPE_SUPERVISOR 0xc0 |
19 | #define PCB_TYPE_MASK PCB_TYPE_SUPERVISOR |
20 | |
21 | #define PCB_SYNC_ACK 0x20 |
22 | #define PCB_SYNC_NACK 0x10 |
23 | #define PCB_SYNC_WAIT 0x30 |
24 | #define PCB_SYNC_NOINFO 0x00 |
25 | #define PCB_SYNC_MASK PCB_SYNC_WAIT |
26 | |
27 | #define PCB_DATAFRAME_RETRANSMIT_YES 0x00 |
28 | #define PCB_DATAFRAME_RETRANSMIT_NO 0x04 |
29 | #define PCB_DATAFRAME_RETRANSMIT_MASK PCB_DATAFRAME_RETRANSMIT_NO |
30 | |
31 | #define PCB_SUPERVISOR_RETRANSMIT_YES 0x00 |
32 | #define PCB_SUPERVISOR_RETRANSMIT_NO 0x02 |
33 | #define PCB_SUPERVISOR_RETRANSMIT_MASK PCB_SUPERVISOR_RETRANSMIT_NO |
34 | |
35 | #define PCB_FRAME_CRC_INFO_PRESENT 0x08 |
36 | #define PCB_FRAME_CRC_INFO_NOTPRESENT 0x00 |
37 | #define PCB_FRAME_CRC_INFO_MASK PCB_FRAME_CRC_INFO_PRESENT |
38 | |
39 | #define NDLC_DUMP_SKB(info, skb) \ |
40 | do { \ |
41 | pr_debug("%s:\n", info); \ |
42 | print_hex_dump(KERN_DEBUG, "ndlc: ", DUMP_PREFIX_OFFSET, \ |
43 | 16, 1, skb->data, skb->len, 0); \ |
44 | } while (0) |
45 | |
46 | int ndlc_open(struct llt_ndlc *ndlc) |
47 | { |
48 | /* toggle reset pin */ |
49 | ndlc->ops->enable(ndlc->phy_id); |
50 | ndlc->powered = 1; |
51 | return 0; |
52 | } |
53 | EXPORT_SYMBOL(ndlc_open); |
54 | |
55 | void ndlc_close(struct llt_ndlc *ndlc) |
56 | { |
57 | struct nci_mode_set_cmd cmd; |
58 | |
59 | cmd.cmd_type = ST_NCI_SET_NFC_MODE; |
60 | cmd.mode = 0; |
61 | |
62 | /* toggle reset pin */ |
63 | ndlc->ops->enable(ndlc->phy_id); |
64 | |
65 | nci_prop_cmd(ndev: ndlc->ndev, ST_NCI_CORE_PROP, |
66 | len: sizeof(struct nci_mode_set_cmd), payload: (__u8 *)&cmd); |
67 | |
68 | ndlc->powered = 0; |
69 | ndlc->ops->disable(ndlc->phy_id); |
70 | } |
71 | EXPORT_SYMBOL(ndlc_close); |
72 | |
73 | int ndlc_send(struct llt_ndlc *ndlc, struct sk_buff *skb) |
74 | { |
75 | /* add ndlc header */ |
76 | u8 pcb = PCB_TYPE_DATAFRAME | PCB_DATAFRAME_RETRANSMIT_NO | |
77 | PCB_FRAME_CRC_INFO_NOTPRESENT; |
78 | |
79 | *(u8 *)skb_push(skb, len: 1) = pcb; |
80 | skb_queue_tail(list: &ndlc->send_q, newsk: skb); |
81 | |
82 | schedule_work(work: &ndlc->sm_work); |
83 | |
84 | return 0; |
85 | } |
86 | EXPORT_SYMBOL(ndlc_send); |
87 | |
88 | static void llt_ndlc_send_queue(struct llt_ndlc *ndlc) |
89 | { |
90 | struct sk_buff *skb; |
91 | int r; |
92 | unsigned long time_sent; |
93 | |
94 | if (ndlc->send_q.qlen) |
95 | pr_debug("sendQlen=%d unackQlen=%d\n" , |
96 | ndlc->send_q.qlen, ndlc->ack_pending_q.qlen); |
97 | |
98 | while (ndlc->send_q.qlen) { |
99 | skb = skb_dequeue(list: &ndlc->send_q); |
100 | NDLC_DUMP_SKB("ndlc frame written" , skb); |
101 | r = ndlc->ops->write(ndlc->phy_id, skb); |
102 | if (r < 0) { |
103 | ndlc->hard_fault = r; |
104 | break; |
105 | } |
106 | time_sent = jiffies; |
107 | *(unsigned long *)skb->cb = time_sent; |
108 | |
109 | skb_queue_tail(list: &ndlc->ack_pending_q, newsk: skb); |
110 | |
111 | /* start timer t1 for ndlc aknowledge */ |
112 | ndlc->t1_active = true; |
113 | mod_timer(timer: &ndlc->t1_timer, expires: time_sent + |
114 | msecs_to_jiffies(NDLC_TIMER_T1)); |
115 | /* start timer t2 for chip availability */ |
116 | ndlc->t2_active = true; |
117 | mod_timer(timer: &ndlc->t2_timer, expires: time_sent + |
118 | msecs_to_jiffies(NDLC_TIMER_T2)); |
119 | } |
120 | } |
121 | |
122 | static void llt_ndlc_requeue_data_pending(struct llt_ndlc *ndlc) |
123 | { |
124 | struct sk_buff *skb; |
125 | u8 pcb; |
126 | |
127 | while ((skb = skb_dequeue_tail(list: &ndlc->ack_pending_q))) { |
128 | pcb = skb->data[0]; |
129 | switch (pcb & PCB_TYPE_MASK) { |
130 | case PCB_TYPE_SUPERVISOR: |
131 | skb->data[0] = (pcb & ~PCB_SUPERVISOR_RETRANSMIT_MASK) | |
132 | PCB_SUPERVISOR_RETRANSMIT_YES; |
133 | break; |
134 | case PCB_TYPE_DATAFRAME: |
135 | skb->data[0] = (pcb & ~PCB_DATAFRAME_RETRANSMIT_MASK) | |
136 | PCB_DATAFRAME_RETRANSMIT_YES; |
137 | break; |
138 | default: |
139 | pr_err("UNKNOWN Packet Control Byte=%d\n" , pcb); |
140 | kfree_skb(skb); |
141 | continue; |
142 | } |
143 | skb_queue_head(list: &ndlc->send_q, newsk: skb); |
144 | } |
145 | } |
146 | |
147 | static void llt_ndlc_rcv_queue(struct llt_ndlc *ndlc) |
148 | { |
149 | struct sk_buff *skb; |
150 | u8 pcb; |
151 | unsigned long time_sent; |
152 | |
153 | if (ndlc->rcv_q.qlen) |
154 | pr_debug("rcvQlen=%d\n" , ndlc->rcv_q.qlen); |
155 | |
156 | while ((skb = skb_dequeue(list: &ndlc->rcv_q)) != NULL) { |
157 | pcb = skb->data[0]; |
158 | skb_pull(skb, len: 1); |
159 | if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_SUPERVISOR) { |
160 | switch (pcb & PCB_SYNC_MASK) { |
161 | case PCB_SYNC_ACK: |
162 | skb = skb_dequeue(list: &ndlc->ack_pending_q); |
163 | kfree_skb(skb); |
164 | del_timer_sync(timer: &ndlc->t1_timer); |
165 | del_timer_sync(timer: &ndlc->t2_timer); |
166 | ndlc->t2_active = false; |
167 | ndlc->t1_active = false; |
168 | break; |
169 | case PCB_SYNC_NACK: |
170 | llt_ndlc_requeue_data_pending(ndlc); |
171 | llt_ndlc_send_queue(ndlc); |
172 | /* start timer t1 for ndlc aknowledge */ |
173 | time_sent = jiffies; |
174 | ndlc->t1_active = true; |
175 | mod_timer(timer: &ndlc->t1_timer, expires: time_sent + |
176 | msecs_to_jiffies(NDLC_TIMER_T1)); |
177 | break; |
178 | case PCB_SYNC_WAIT: |
179 | time_sent = jiffies; |
180 | ndlc->t1_active = true; |
181 | mod_timer(timer: &ndlc->t1_timer, expires: time_sent + |
182 | msecs_to_jiffies(NDLC_TIMER_T1_WAIT)); |
183 | break; |
184 | default: |
185 | kfree_skb(skb); |
186 | break; |
187 | } |
188 | } else if ((pcb & PCB_TYPE_MASK) == PCB_TYPE_DATAFRAME) { |
189 | nci_recv_frame(ndev: ndlc->ndev, skb); |
190 | } else { |
191 | kfree_skb(skb); |
192 | } |
193 | } |
194 | } |
195 | |
196 | static void llt_ndlc_sm_work(struct work_struct *work) |
197 | { |
198 | struct llt_ndlc *ndlc = container_of(work, struct llt_ndlc, sm_work); |
199 | |
200 | llt_ndlc_send_queue(ndlc); |
201 | llt_ndlc_rcv_queue(ndlc); |
202 | |
203 | if (ndlc->t1_active && timer_pending(timer: &ndlc->t1_timer) == 0) { |
204 | pr_debug |
205 | ("Handle T1(recv SUPERVISOR) elapsed (T1 now inactive)\n" ); |
206 | ndlc->t1_active = false; |
207 | |
208 | llt_ndlc_requeue_data_pending(ndlc); |
209 | llt_ndlc_send_queue(ndlc); |
210 | } |
211 | |
212 | if (ndlc->t2_active && timer_pending(timer: &ndlc->t2_timer) == 0) { |
213 | pr_debug("Handle T2(recv DATA) elapsed (T2 now inactive)\n" ); |
214 | ndlc->t2_active = false; |
215 | ndlc->t1_active = false; |
216 | del_timer_sync(timer: &ndlc->t1_timer); |
217 | del_timer_sync(timer: &ndlc->t2_timer); |
218 | ndlc_close(ndlc); |
219 | ndlc->hard_fault = -EREMOTEIO; |
220 | } |
221 | } |
222 | |
223 | void ndlc_recv(struct llt_ndlc *ndlc, struct sk_buff *skb) |
224 | { |
225 | if (skb == NULL) { |
226 | pr_err("NULL Frame -> link is dead\n" ); |
227 | ndlc->hard_fault = -EREMOTEIO; |
228 | ndlc_close(ndlc); |
229 | } else { |
230 | NDLC_DUMP_SKB("incoming frame" , skb); |
231 | skb_queue_tail(list: &ndlc->rcv_q, newsk: skb); |
232 | } |
233 | |
234 | schedule_work(work: &ndlc->sm_work); |
235 | } |
236 | EXPORT_SYMBOL(ndlc_recv); |
237 | |
238 | static void ndlc_t1_timeout(struct timer_list *t) |
239 | { |
240 | struct llt_ndlc *ndlc = from_timer(ndlc, t, t1_timer); |
241 | |
242 | schedule_work(work: &ndlc->sm_work); |
243 | } |
244 | |
245 | static void ndlc_t2_timeout(struct timer_list *t) |
246 | { |
247 | struct llt_ndlc *ndlc = from_timer(ndlc, t, t2_timer); |
248 | |
249 | schedule_work(work: &ndlc->sm_work); |
250 | } |
251 | |
252 | int ndlc_probe(void *phy_id, const struct nfc_phy_ops *phy_ops, |
253 | struct device *dev, int phy_headroom, int phy_tailroom, |
254 | struct llt_ndlc **ndlc_id, struct st_nci_se_status *se_status) |
255 | { |
256 | struct llt_ndlc *ndlc; |
257 | |
258 | ndlc = devm_kzalloc(dev, size: sizeof(struct llt_ndlc), GFP_KERNEL); |
259 | if (!ndlc) |
260 | return -ENOMEM; |
261 | |
262 | ndlc->ops = phy_ops; |
263 | ndlc->phy_id = phy_id; |
264 | ndlc->dev = dev; |
265 | ndlc->powered = 0; |
266 | |
267 | *ndlc_id = ndlc; |
268 | |
269 | /* initialize timers */ |
270 | timer_setup(&ndlc->t1_timer, ndlc_t1_timeout, 0); |
271 | timer_setup(&ndlc->t2_timer, ndlc_t2_timeout, 0); |
272 | |
273 | skb_queue_head_init(list: &ndlc->rcv_q); |
274 | skb_queue_head_init(list: &ndlc->send_q); |
275 | skb_queue_head_init(list: &ndlc->ack_pending_q); |
276 | |
277 | INIT_WORK(&ndlc->sm_work, llt_ndlc_sm_work); |
278 | |
279 | return st_nci_probe(ndlc, phy_headroom, phy_tailroom, se_status); |
280 | } |
281 | EXPORT_SYMBOL(ndlc_probe); |
282 | |
283 | void ndlc_remove(struct llt_ndlc *ndlc) |
284 | { |
285 | /* cancel timers */ |
286 | del_timer_sync(timer: &ndlc->t1_timer); |
287 | del_timer_sync(timer: &ndlc->t2_timer); |
288 | ndlc->t2_active = false; |
289 | ndlc->t1_active = false; |
290 | /* cancel work */ |
291 | cancel_work_sync(work: &ndlc->sm_work); |
292 | |
293 | st_nci_remove(ndev: ndlc->ndev); |
294 | |
295 | skb_queue_purge(list: &ndlc->rcv_q); |
296 | skb_queue_purge(list: &ndlc->send_q); |
297 | } |
298 | EXPORT_SYMBOL(ndlc_remove); |
299 | |