1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * LAPB release 002 |
4 | * |
5 | * This code REQUIRES 2.1.15 or higher/ NET3.038 |
6 | * |
7 | * History |
8 | * LAPB 001 Jonathan Naylor Started Coding |
9 | * LAPB 002 Jonathan Naylor New timer architecture. |
10 | * 2000-10-29 Henner Eisen lapb_data_indication() return status. |
11 | */ |
12 | |
13 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
14 | |
15 | #include <linux/module.h> |
16 | #include <linux/errno.h> |
17 | #include <linux/types.h> |
18 | #include <linux/socket.h> |
19 | #include <linux/in.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/jiffies.h> |
22 | #include <linux/timer.h> |
23 | #include <linux/string.h> |
24 | #include <linux/sockios.h> |
25 | #include <linux/net.h> |
26 | #include <linux/inet.h> |
27 | #include <linux/if_arp.h> |
28 | #include <linux/skbuff.h> |
29 | #include <linux/slab.h> |
30 | #include <net/sock.h> |
31 | #include <linux/uaccess.h> |
32 | #include <linux/fcntl.h> |
33 | #include <linux/mm.h> |
34 | #include <linux/interrupt.h> |
35 | #include <linux/stat.h> |
36 | #include <linux/init.h> |
37 | #include <net/lapb.h> |
38 | |
39 | static LIST_HEAD(lapb_list); |
40 | static DEFINE_RWLOCK(lapb_list_lock); |
41 | |
42 | /* |
43 | * Free an allocated lapb control block. |
44 | */ |
45 | static void lapb_free_cb(struct lapb_cb *lapb) |
46 | { |
47 | kfree(objp: lapb); |
48 | } |
49 | |
50 | static __inline__ void lapb_hold(struct lapb_cb *lapb) |
51 | { |
52 | refcount_inc(r: &lapb->refcnt); |
53 | } |
54 | |
55 | static __inline__ void lapb_put(struct lapb_cb *lapb) |
56 | { |
57 | if (refcount_dec_and_test(r: &lapb->refcnt)) |
58 | lapb_free_cb(lapb); |
59 | } |
60 | |
61 | /* |
62 | * Socket removal during an interrupt is now safe. |
63 | */ |
64 | static void __lapb_remove_cb(struct lapb_cb *lapb) |
65 | { |
66 | if (lapb->node.next) { |
67 | list_del(entry: &lapb->node); |
68 | lapb_put(lapb); |
69 | } |
70 | } |
71 | |
72 | /* |
73 | * Add a socket to the bound sockets list. |
74 | */ |
75 | static void __lapb_insert_cb(struct lapb_cb *lapb) |
76 | { |
77 | list_add(new: &lapb->node, head: &lapb_list); |
78 | lapb_hold(lapb); |
79 | } |
80 | |
81 | static struct lapb_cb *__lapb_devtostruct(struct net_device *dev) |
82 | { |
83 | struct lapb_cb *lapb, *use = NULL; |
84 | |
85 | list_for_each_entry(lapb, &lapb_list, node) { |
86 | if (lapb->dev == dev) { |
87 | use = lapb; |
88 | break; |
89 | } |
90 | } |
91 | |
92 | if (use) |
93 | lapb_hold(lapb: use); |
94 | |
95 | return use; |
96 | } |
97 | |
98 | static struct lapb_cb *lapb_devtostruct(struct net_device *dev) |
99 | { |
100 | struct lapb_cb *rc; |
101 | |
102 | read_lock_bh(&lapb_list_lock); |
103 | rc = __lapb_devtostruct(dev); |
104 | read_unlock_bh(&lapb_list_lock); |
105 | |
106 | return rc; |
107 | } |
108 | /* |
109 | * Create an empty LAPB control block. |
110 | */ |
111 | static struct lapb_cb *lapb_create_cb(void) |
112 | { |
113 | struct lapb_cb *lapb = kzalloc(size: sizeof(*lapb), GFP_ATOMIC); |
114 | |
115 | if (!lapb) |
116 | goto out; |
117 | |
118 | skb_queue_head_init(list: &lapb->write_queue); |
119 | skb_queue_head_init(list: &lapb->ack_queue); |
120 | |
121 | timer_setup(&lapb->t1timer, NULL, 0); |
122 | timer_setup(&lapb->t2timer, NULL, 0); |
123 | lapb->t1timer_running = false; |
124 | lapb->t2timer_running = false; |
125 | |
126 | lapb->t1 = LAPB_DEFAULT_T1; |
127 | lapb->t2 = LAPB_DEFAULT_T2; |
128 | lapb->n2 = LAPB_DEFAULT_N2; |
129 | lapb->mode = LAPB_DEFAULT_MODE; |
130 | lapb->window = LAPB_DEFAULT_WINDOW; |
131 | lapb->state = LAPB_STATE_0; |
132 | |
133 | spin_lock_init(&lapb->lock); |
134 | refcount_set(r: &lapb->refcnt, n: 1); |
135 | out: |
136 | return lapb; |
137 | } |
138 | |
139 | int lapb_register(struct net_device *dev, |
140 | const struct lapb_register_struct *callbacks) |
141 | { |
142 | struct lapb_cb *lapb; |
143 | int rc = LAPB_BADTOKEN; |
144 | |
145 | write_lock_bh(&lapb_list_lock); |
146 | |
147 | lapb = __lapb_devtostruct(dev); |
148 | if (lapb) { |
149 | lapb_put(lapb); |
150 | goto out; |
151 | } |
152 | |
153 | lapb = lapb_create_cb(); |
154 | rc = LAPB_NOMEM; |
155 | if (!lapb) |
156 | goto out; |
157 | |
158 | lapb->dev = dev; |
159 | lapb->callbacks = callbacks; |
160 | |
161 | __lapb_insert_cb(lapb); |
162 | |
163 | lapb_start_t1timer(lapb); |
164 | |
165 | rc = LAPB_OK; |
166 | out: |
167 | write_unlock_bh(&lapb_list_lock); |
168 | return rc; |
169 | } |
170 | EXPORT_SYMBOL(lapb_register); |
171 | |
172 | int lapb_unregister(struct net_device *dev) |
173 | { |
174 | struct lapb_cb *lapb; |
175 | int rc = LAPB_BADTOKEN; |
176 | |
177 | write_lock_bh(&lapb_list_lock); |
178 | lapb = __lapb_devtostruct(dev); |
179 | if (!lapb) |
180 | goto out; |
181 | lapb_put(lapb); |
182 | |
183 | /* Wait for other refs to "lapb" to drop */ |
184 | while (refcount_read(r: &lapb->refcnt) > 2) |
185 | usleep_range(min: 1, max: 10); |
186 | |
187 | spin_lock_bh(lock: &lapb->lock); |
188 | |
189 | lapb_stop_t1timer(lapb); |
190 | lapb_stop_t2timer(lapb); |
191 | |
192 | lapb_clear_queues(lapb); |
193 | |
194 | spin_unlock_bh(lock: &lapb->lock); |
195 | |
196 | /* Wait for running timers to stop */ |
197 | del_timer_sync(timer: &lapb->t1timer); |
198 | del_timer_sync(timer: &lapb->t2timer); |
199 | |
200 | __lapb_remove_cb(lapb); |
201 | |
202 | lapb_put(lapb); |
203 | rc = LAPB_OK; |
204 | out: |
205 | write_unlock_bh(&lapb_list_lock); |
206 | return rc; |
207 | } |
208 | EXPORT_SYMBOL(lapb_unregister); |
209 | |
210 | int lapb_getparms(struct net_device *dev, struct lapb_parms_struct *parms) |
211 | { |
212 | int rc = LAPB_BADTOKEN; |
213 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
214 | |
215 | if (!lapb) |
216 | goto out; |
217 | |
218 | spin_lock_bh(lock: &lapb->lock); |
219 | |
220 | parms->t1 = lapb->t1 / HZ; |
221 | parms->t2 = lapb->t2 / HZ; |
222 | parms->n2 = lapb->n2; |
223 | parms->n2count = lapb->n2count; |
224 | parms->state = lapb->state; |
225 | parms->window = lapb->window; |
226 | parms->mode = lapb->mode; |
227 | |
228 | if (!timer_pending(timer: &lapb->t1timer)) |
229 | parms->t1timer = 0; |
230 | else |
231 | parms->t1timer = (lapb->t1timer.expires - jiffies) / HZ; |
232 | |
233 | if (!timer_pending(timer: &lapb->t2timer)) |
234 | parms->t2timer = 0; |
235 | else |
236 | parms->t2timer = (lapb->t2timer.expires - jiffies) / HZ; |
237 | |
238 | spin_unlock_bh(lock: &lapb->lock); |
239 | lapb_put(lapb); |
240 | rc = LAPB_OK; |
241 | out: |
242 | return rc; |
243 | } |
244 | EXPORT_SYMBOL(lapb_getparms); |
245 | |
246 | int lapb_setparms(struct net_device *dev, struct lapb_parms_struct *parms) |
247 | { |
248 | int rc = LAPB_BADTOKEN; |
249 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
250 | |
251 | if (!lapb) |
252 | goto out; |
253 | |
254 | spin_lock_bh(lock: &lapb->lock); |
255 | |
256 | rc = LAPB_INVALUE; |
257 | if (parms->t1 < 1 || parms->t2 < 1 || parms->n2 < 1) |
258 | goto out_put; |
259 | |
260 | if (lapb->state == LAPB_STATE_0) { |
261 | if (parms->mode & LAPB_EXTENDED) { |
262 | if (parms->window < 1 || parms->window > 127) |
263 | goto out_put; |
264 | } else { |
265 | if (parms->window < 1 || parms->window > 7) |
266 | goto out_put; |
267 | } |
268 | lapb->mode = parms->mode; |
269 | lapb->window = parms->window; |
270 | } |
271 | |
272 | lapb->t1 = parms->t1 * HZ; |
273 | lapb->t2 = parms->t2 * HZ; |
274 | lapb->n2 = parms->n2; |
275 | |
276 | rc = LAPB_OK; |
277 | out_put: |
278 | spin_unlock_bh(lock: &lapb->lock); |
279 | lapb_put(lapb); |
280 | out: |
281 | return rc; |
282 | } |
283 | EXPORT_SYMBOL(lapb_setparms); |
284 | |
285 | int lapb_connect_request(struct net_device *dev) |
286 | { |
287 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
288 | int rc = LAPB_BADTOKEN; |
289 | |
290 | if (!lapb) |
291 | goto out; |
292 | |
293 | spin_lock_bh(lock: &lapb->lock); |
294 | |
295 | rc = LAPB_OK; |
296 | if (lapb->state == LAPB_STATE_1) |
297 | goto out_put; |
298 | |
299 | rc = LAPB_CONNECTED; |
300 | if (lapb->state == LAPB_STATE_3 || lapb->state == LAPB_STATE_4) |
301 | goto out_put; |
302 | |
303 | lapb_establish_data_link(lapb); |
304 | |
305 | lapb_dbg(0, "(%p) S0 -> S1\n" , lapb->dev); |
306 | lapb->state = LAPB_STATE_1; |
307 | |
308 | rc = LAPB_OK; |
309 | out_put: |
310 | spin_unlock_bh(lock: &lapb->lock); |
311 | lapb_put(lapb); |
312 | out: |
313 | return rc; |
314 | } |
315 | EXPORT_SYMBOL(lapb_connect_request); |
316 | |
317 | static int __lapb_disconnect_request(struct lapb_cb *lapb) |
318 | { |
319 | switch (lapb->state) { |
320 | case LAPB_STATE_0: |
321 | return LAPB_NOTCONNECTED; |
322 | |
323 | case LAPB_STATE_1: |
324 | lapb_dbg(1, "(%p) S1 TX DISC(1)\n" , lapb->dev); |
325 | lapb_dbg(0, "(%p) S1 -> S0\n" , lapb->dev); |
326 | lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); |
327 | lapb->state = LAPB_STATE_0; |
328 | lapb_start_t1timer(lapb); |
329 | return LAPB_NOTCONNECTED; |
330 | |
331 | case LAPB_STATE_2: |
332 | return LAPB_OK; |
333 | } |
334 | |
335 | lapb_clear_queues(lapb); |
336 | lapb->n2count = 0; |
337 | lapb_send_control(lapb, LAPB_DISC, LAPB_POLLON, LAPB_COMMAND); |
338 | lapb_start_t1timer(lapb); |
339 | lapb_stop_t2timer(lapb); |
340 | lapb->state = LAPB_STATE_2; |
341 | |
342 | lapb_dbg(1, "(%p) S3 DISC(1)\n" , lapb->dev); |
343 | lapb_dbg(0, "(%p) S3 -> S2\n" , lapb->dev); |
344 | |
345 | return LAPB_OK; |
346 | } |
347 | |
348 | int lapb_disconnect_request(struct net_device *dev) |
349 | { |
350 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
351 | int rc = LAPB_BADTOKEN; |
352 | |
353 | if (!lapb) |
354 | goto out; |
355 | |
356 | spin_lock_bh(lock: &lapb->lock); |
357 | |
358 | rc = __lapb_disconnect_request(lapb); |
359 | |
360 | spin_unlock_bh(lock: &lapb->lock); |
361 | lapb_put(lapb); |
362 | out: |
363 | return rc; |
364 | } |
365 | EXPORT_SYMBOL(lapb_disconnect_request); |
366 | |
367 | int lapb_data_request(struct net_device *dev, struct sk_buff *skb) |
368 | { |
369 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
370 | int rc = LAPB_BADTOKEN; |
371 | |
372 | if (!lapb) |
373 | goto out; |
374 | |
375 | spin_lock_bh(lock: &lapb->lock); |
376 | |
377 | rc = LAPB_NOTCONNECTED; |
378 | if (lapb->state != LAPB_STATE_3 && lapb->state != LAPB_STATE_4) |
379 | goto out_put; |
380 | |
381 | skb_queue_tail(list: &lapb->write_queue, newsk: skb); |
382 | lapb_kick(lapb); |
383 | rc = LAPB_OK; |
384 | out_put: |
385 | spin_unlock_bh(lock: &lapb->lock); |
386 | lapb_put(lapb); |
387 | out: |
388 | return rc; |
389 | } |
390 | EXPORT_SYMBOL(lapb_data_request); |
391 | |
392 | int lapb_data_received(struct net_device *dev, struct sk_buff *skb) |
393 | { |
394 | struct lapb_cb *lapb = lapb_devtostruct(dev); |
395 | int rc = LAPB_BADTOKEN; |
396 | |
397 | if (lapb) { |
398 | spin_lock_bh(lock: &lapb->lock); |
399 | lapb_data_input(lapb, skb); |
400 | spin_unlock_bh(lock: &lapb->lock); |
401 | lapb_put(lapb); |
402 | rc = LAPB_OK; |
403 | } |
404 | |
405 | return rc; |
406 | } |
407 | EXPORT_SYMBOL(lapb_data_received); |
408 | |
409 | void lapb_connect_confirmation(struct lapb_cb *lapb, int reason) |
410 | { |
411 | if (lapb->callbacks->connect_confirmation) |
412 | lapb->callbacks->connect_confirmation(lapb->dev, reason); |
413 | } |
414 | |
415 | void lapb_connect_indication(struct lapb_cb *lapb, int reason) |
416 | { |
417 | if (lapb->callbacks->connect_indication) |
418 | lapb->callbacks->connect_indication(lapb->dev, reason); |
419 | } |
420 | |
421 | void lapb_disconnect_confirmation(struct lapb_cb *lapb, int reason) |
422 | { |
423 | if (lapb->callbacks->disconnect_confirmation) |
424 | lapb->callbacks->disconnect_confirmation(lapb->dev, reason); |
425 | } |
426 | |
427 | void lapb_disconnect_indication(struct lapb_cb *lapb, int reason) |
428 | { |
429 | if (lapb->callbacks->disconnect_indication) |
430 | lapb->callbacks->disconnect_indication(lapb->dev, reason); |
431 | } |
432 | |
433 | int lapb_data_indication(struct lapb_cb *lapb, struct sk_buff *skb) |
434 | { |
435 | if (lapb->callbacks->data_indication) |
436 | return lapb->callbacks->data_indication(lapb->dev, skb); |
437 | |
438 | kfree_skb(skb); |
439 | return NET_RX_SUCCESS; /* For now; must be != NET_RX_DROP */ |
440 | } |
441 | |
442 | int lapb_data_transmit(struct lapb_cb *lapb, struct sk_buff *skb) |
443 | { |
444 | int used = 0; |
445 | |
446 | if (lapb->callbacks->data_transmit) { |
447 | lapb->callbacks->data_transmit(lapb->dev, skb); |
448 | used = 1; |
449 | } |
450 | |
451 | return used; |
452 | } |
453 | |
454 | /* Handle device status changes. */ |
455 | static int lapb_device_event(struct notifier_block *this, unsigned long event, |
456 | void *ptr) |
457 | { |
458 | struct net_device *dev = netdev_notifier_info_to_dev(info: ptr); |
459 | struct lapb_cb *lapb; |
460 | |
461 | if (!net_eq(net1: dev_net(dev), net2: &init_net)) |
462 | return NOTIFY_DONE; |
463 | |
464 | if (dev->type != ARPHRD_X25) |
465 | return NOTIFY_DONE; |
466 | |
467 | lapb = lapb_devtostruct(dev); |
468 | if (!lapb) |
469 | return NOTIFY_DONE; |
470 | |
471 | spin_lock_bh(lock: &lapb->lock); |
472 | |
473 | switch (event) { |
474 | case NETDEV_UP: |
475 | lapb_dbg(0, "(%p) Interface up: %s\n" , dev, dev->name); |
476 | |
477 | if (netif_carrier_ok(dev)) { |
478 | lapb_dbg(0, "(%p): Carrier is already up: %s\n" , dev, |
479 | dev->name); |
480 | if (lapb->mode & LAPB_DCE) { |
481 | lapb_start_t1timer(lapb); |
482 | } else { |
483 | if (lapb->state == LAPB_STATE_0) { |
484 | lapb->state = LAPB_STATE_1; |
485 | lapb_establish_data_link(lapb); |
486 | } |
487 | } |
488 | } |
489 | break; |
490 | case NETDEV_GOING_DOWN: |
491 | if (netif_carrier_ok(dev)) |
492 | __lapb_disconnect_request(lapb); |
493 | break; |
494 | case NETDEV_DOWN: |
495 | lapb_dbg(0, "(%p) Interface down: %s\n" , dev, dev->name); |
496 | lapb_dbg(0, "(%p) S%d -> S0\n" , dev, lapb->state); |
497 | lapb_clear_queues(lapb); |
498 | lapb->state = LAPB_STATE_0; |
499 | lapb->n2count = 0; |
500 | lapb_stop_t1timer(lapb); |
501 | lapb_stop_t2timer(lapb); |
502 | break; |
503 | case NETDEV_CHANGE: |
504 | if (netif_carrier_ok(dev)) { |
505 | lapb_dbg(0, "(%p): Carrier detected: %s\n" , dev, |
506 | dev->name); |
507 | if (lapb->mode & LAPB_DCE) { |
508 | lapb_start_t1timer(lapb); |
509 | } else { |
510 | if (lapb->state == LAPB_STATE_0) { |
511 | lapb->state = LAPB_STATE_1; |
512 | lapb_establish_data_link(lapb); |
513 | } |
514 | } |
515 | } else { |
516 | lapb_dbg(0, "(%p) Carrier lost: %s\n" , dev, dev->name); |
517 | lapb_dbg(0, "(%p) S%d -> S0\n" , dev, lapb->state); |
518 | lapb_clear_queues(lapb); |
519 | lapb->state = LAPB_STATE_0; |
520 | lapb->n2count = 0; |
521 | lapb_stop_t1timer(lapb); |
522 | lapb_stop_t2timer(lapb); |
523 | } |
524 | break; |
525 | } |
526 | |
527 | spin_unlock_bh(lock: &lapb->lock); |
528 | lapb_put(lapb); |
529 | return NOTIFY_DONE; |
530 | } |
531 | |
532 | static struct notifier_block lapb_dev_notifier = { |
533 | .notifier_call = lapb_device_event, |
534 | }; |
535 | |
536 | static int __init lapb_init(void) |
537 | { |
538 | return register_netdevice_notifier(nb: &lapb_dev_notifier); |
539 | } |
540 | |
541 | static void __exit lapb_exit(void) |
542 | { |
543 | WARN_ON(!list_empty(&lapb_list)); |
544 | |
545 | unregister_netdevice_notifier(nb: &lapb_dev_notifier); |
546 | } |
547 | |
548 | MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>" ); |
549 | MODULE_DESCRIPTION("The X.25 Link Access Procedure B link layer protocol" ); |
550 | MODULE_LICENSE("GPL" ); |
551 | |
552 | module_init(lapb_init); |
553 | module_exit(lapb_exit); |
554 | |