1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * 6pack.c This module implements the 6pack protocol for kernel-based |
4 | * devices like TTY. It interfaces between a raw TTY and the |
5 | * kernel's AX.25 protocol layers. |
6 | * |
7 | * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de> |
8 | * Ralf Baechle DL5RB <ralf@linux-mips.org> |
9 | * |
10 | * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by |
11 | * |
12 | * Laurence Culhane, <loz@holmes.demon.co.uk> |
13 | * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> |
14 | */ |
15 | |
16 | #include <linux/module.h> |
17 | #include <linux/uaccess.h> |
18 | #include <linux/bitops.h> |
19 | #include <linux/string.h> |
20 | #include <linux/mm.h> |
21 | #include <linux/interrupt.h> |
22 | #include <linux/in.h> |
23 | #include <linux/tty.h> |
24 | #include <linux/errno.h> |
25 | #include <linux/netdevice.h> |
26 | #include <linux/timer.h> |
27 | #include <linux/slab.h> |
28 | #include <net/ax25.h> |
29 | #include <linux/etherdevice.h> |
30 | #include <linux/skbuff.h> |
31 | #include <linux/rtnetlink.h> |
32 | #include <linux/spinlock.h> |
33 | #include <linux/if_arp.h> |
34 | #include <linux/init.h> |
35 | #include <linux/ip.h> |
36 | #include <linux/tcp.h> |
37 | #include <linux/semaphore.h> |
38 | #include <linux/refcount.h> |
39 | |
40 | #define SIXPACK_VERSION "Revision: 0.3.0" |
41 | |
42 | /* sixpack priority commands */ |
43 | #define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ |
44 | #define SIXP_TX_URUN 0x48 /* transmit overrun */ |
45 | #define SIXP_RX_ORUN 0x50 /* receive overrun */ |
46 | #define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ |
47 | |
48 | #define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ |
49 | |
50 | /* masks to get certain bits out of the status bytes sent by the TNC */ |
51 | |
52 | #define SIXP_CMD_MASK 0xC0 |
53 | #define SIXP_CHN_MASK 0x07 |
54 | #define SIXP_PRIO_CMD_MASK 0x80 |
55 | #define SIXP_STD_CMD_MASK 0x40 |
56 | #define SIXP_PRIO_DATA_MASK 0x38 |
57 | #define SIXP_TX_MASK 0x20 |
58 | #define SIXP_RX_MASK 0x10 |
59 | #define SIXP_RX_DCD_MASK 0x18 |
60 | #define SIXP_LEDS_ON 0x78 |
61 | #define SIXP_LEDS_OFF 0x60 |
62 | #define SIXP_CON 0x08 |
63 | #define SIXP_STA 0x10 |
64 | |
65 | #define SIXP_FOUND_TNC 0xe9 |
66 | #define SIXP_CON_ON 0x68 |
67 | #define SIXP_DCD_MASK 0x08 |
68 | #define SIXP_DAMA_OFF 0 |
69 | |
70 | /* default level 2 parameters */ |
71 | #define SIXP_TXDELAY 25 /* 250 ms */ |
72 | #define SIXP_PERSIST 50 /* in 256ths */ |
73 | #define SIXP_SLOTTIME 10 /* 100 ms */ |
74 | #define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */ |
75 | #define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */ |
76 | |
77 | /* 6pack configuration. */ |
78 | #define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ |
79 | #define SIXP_MTU 256 /* Default MTU */ |
80 | |
81 | enum sixpack_flags { |
82 | SIXPF_ERROR, /* Parity, etc. error */ |
83 | }; |
84 | |
85 | struct sixpack { |
86 | /* Various fields. */ |
87 | struct tty_struct *tty; /* ptr to TTY structure */ |
88 | struct net_device *dev; /* easy for intr handling */ |
89 | |
90 | /* These are pointers to the malloc()ed frame buffers. */ |
91 | unsigned char *rbuff; /* receiver buffer */ |
92 | int rcount; /* received chars counter */ |
93 | unsigned char *xbuff; /* transmitter buffer */ |
94 | unsigned char *xhead; /* next byte to XMIT */ |
95 | int xleft; /* bytes left in XMIT queue */ |
96 | |
97 | unsigned char raw_buf[4]; |
98 | unsigned char cooked_buf[400]; |
99 | |
100 | unsigned int rx_count; |
101 | unsigned int rx_count_cooked; |
102 | spinlock_t rxlock; |
103 | |
104 | int mtu; /* Our mtu (to spot changes!) */ |
105 | int buffsize; /* Max buffers sizes */ |
106 | |
107 | unsigned long flags; /* Flag values/ mode etc */ |
108 | unsigned char mode; /* 6pack mode */ |
109 | |
110 | /* 6pack stuff */ |
111 | unsigned char tx_delay; |
112 | unsigned char persistence; |
113 | unsigned char slottime; |
114 | unsigned char duplex; |
115 | unsigned char led_state; |
116 | unsigned char status; |
117 | unsigned char status1; |
118 | unsigned char status2; |
119 | unsigned char tx_enable; |
120 | unsigned char tnc_state; |
121 | |
122 | struct timer_list tx_t; |
123 | struct timer_list resync_t; |
124 | refcount_t refcnt; |
125 | struct completion dead; |
126 | spinlock_t lock; |
127 | }; |
128 | |
129 | #define 0 |
130 | |
131 | static void sixpack_decode(struct sixpack *, const unsigned char[], int); |
132 | static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); |
133 | |
134 | /* |
135 | * Perform the persistence/slottime algorithm for CSMA access. If the |
136 | * persistence check was successful, write the data to the serial driver. |
137 | * Note that in case of DAMA operation, the data is not sent here. |
138 | */ |
139 | |
140 | static void sp_xmit_on_air(struct timer_list *t) |
141 | { |
142 | struct sixpack *sp = from_timer(sp, t, tx_t); |
143 | int actual, when = sp->slottime; |
144 | static unsigned char random; |
145 | |
146 | random = random * 17 + 41; |
147 | |
148 | if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) { |
149 | sp->led_state = 0x70; |
150 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
151 | sp->tx_enable = 1; |
152 | actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); |
153 | sp->xleft -= actual; |
154 | sp->xhead += actual; |
155 | sp->led_state = 0x60; |
156 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
157 | sp->status2 = 0; |
158 | } else |
159 | mod_timer(timer: &sp->tx_t, expires: jiffies + ((when + 1) * HZ) / 100); |
160 | } |
161 | |
162 | /* ----> 6pack timer interrupt handler and friends. <---- */ |
163 | |
164 | /* Encapsulate one AX.25 frame and stuff into a TTY queue. */ |
165 | static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) |
166 | { |
167 | unsigned char *msg, *p = icp; |
168 | int actual, count; |
169 | |
170 | if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ |
171 | msg = "oversized transmit packet!" ; |
172 | goto out_drop; |
173 | } |
174 | |
175 | if (p[0] > 5) { |
176 | msg = "invalid KISS command" ; |
177 | goto out_drop; |
178 | } |
179 | |
180 | if ((p[0] != 0) && (len > 2)) { |
181 | msg = "KISS control packet too long" ; |
182 | goto out_drop; |
183 | } |
184 | |
185 | if ((p[0] == 0) && (len < 15)) { |
186 | msg = "bad AX.25 packet to transmit" ; |
187 | goto out_drop; |
188 | } |
189 | |
190 | count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay); |
191 | set_bit(TTY_DO_WRITE_WAKEUP, addr: &sp->tty->flags); |
192 | |
193 | switch (p[0]) { |
194 | case 1: sp->tx_delay = p[1]; |
195 | return; |
196 | case 2: sp->persistence = p[1]; |
197 | return; |
198 | case 3: sp->slottime = p[1]; |
199 | return; |
200 | case 4: /* ignored */ |
201 | return; |
202 | case 5: sp->duplex = p[1]; |
203 | return; |
204 | } |
205 | |
206 | if (p[0] != 0) |
207 | return; |
208 | |
209 | /* |
210 | * In case of fullduplex or DAMA operation, we don't take care about the |
211 | * state of the DCD or of any timers, as the determination of the |
212 | * correct time to send is the job of the AX.25 layer. We send |
213 | * immediately after data has arrived. |
214 | */ |
215 | if (sp->duplex == 1) { |
216 | sp->led_state = 0x70; |
217 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
218 | sp->tx_enable = 1; |
219 | actual = sp->tty->ops->write(sp->tty, sp->xbuff, count); |
220 | sp->xleft = count - actual; |
221 | sp->xhead = sp->xbuff + actual; |
222 | sp->led_state = 0x60; |
223 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
224 | } else { |
225 | sp->xleft = count; |
226 | sp->xhead = sp->xbuff; |
227 | sp->status2 = count; |
228 | sp_xmit_on_air(t: &sp->tx_t); |
229 | } |
230 | |
231 | return; |
232 | |
233 | out_drop: |
234 | sp->dev->stats.tx_dropped++; |
235 | netif_start_queue(dev: sp->dev); |
236 | if (net_ratelimit()) |
237 | printk(KERN_DEBUG "%s: %s - dropped.\n" , sp->dev->name, msg); |
238 | } |
239 | |
240 | /* Encapsulate an IP datagram and kick it into a TTY queue. */ |
241 | |
242 | static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev) |
243 | { |
244 | struct sixpack *sp = netdev_priv(dev); |
245 | |
246 | if (skb->protocol == htons(ETH_P_IP)) |
247 | return ax25_ip_xmit(skb); |
248 | |
249 | spin_lock_bh(lock: &sp->lock); |
250 | /* We were not busy, so we are now... :-) */ |
251 | netif_stop_queue(dev); |
252 | dev->stats.tx_bytes += skb->len; |
253 | sp_encaps(sp, icp: skb->data, len: skb->len); |
254 | spin_unlock_bh(lock: &sp->lock); |
255 | |
256 | dev_kfree_skb(skb); |
257 | |
258 | return NETDEV_TX_OK; |
259 | } |
260 | |
261 | static int sp_open_dev(struct net_device *dev) |
262 | { |
263 | struct sixpack *sp = netdev_priv(dev); |
264 | |
265 | if (sp->tty == NULL) |
266 | return -ENODEV; |
267 | return 0; |
268 | } |
269 | |
270 | /* Close the low-level part of the 6pack channel. */ |
271 | static int sp_close(struct net_device *dev) |
272 | { |
273 | struct sixpack *sp = netdev_priv(dev); |
274 | |
275 | spin_lock_bh(lock: &sp->lock); |
276 | if (sp->tty) { |
277 | /* TTY discipline is running. */ |
278 | clear_bit(TTY_DO_WRITE_WAKEUP, addr: &sp->tty->flags); |
279 | } |
280 | netif_stop_queue(dev); |
281 | spin_unlock_bh(lock: &sp->lock); |
282 | |
283 | return 0; |
284 | } |
285 | |
286 | static int sp_set_mac_address(struct net_device *dev, void *addr) |
287 | { |
288 | struct sockaddr_ax25 *sa = addr; |
289 | |
290 | netif_tx_lock_bh(dev); |
291 | netif_addr_lock(dev); |
292 | __dev_addr_set(dev, addr: &sa->sax25_call, AX25_ADDR_LEN); |
293 | netif_addr_unlock(dev); |
294 | netif_tx_unlock_bh(dev); |
295 | |
296 | return 0; |
297 | } |
298 | |
299 | static const struct net_device_ops sp_netdev_ops = { |
300 | .ndo_open = sp_open_dev, |
301 | .ndo_stop = sp_close, |
302 | .ndo_start_xmit = sp_xmit, |
303 | .ndo_set_mac_address = sp_set_mac_address, |
304 | }; |
305 | |
306 | static void sp_setup(struct net_device *dev) |
307 | { |
308 | /* Finish setting up the DEVICE info. */ |
309 | dev->netdev_ops = &sp_netdev_ops; |
310 | dev->mtu = SIXP_MTU; |
311 | dev->hard_header_len = AX25_MAX_HEADER_LEN; |
312 | dev->header_ops = &ax25_header_ops; |
313 | |
314 | dev->addr_len = AX25_ADDR_LEN; |
315 | dev->type = ARPHRD_AX25; |
316 | dev->tx_queue_len = 10; |
317 | |
318 | /* Only activated in AX.25 mode */ |
319 | memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN); |
320 | dev_addr_set(dev, addr: (u8 *)&ax25_defaddr); |
321 | |
322 | dev->flags = 0; |
323 | } |
324 | |
325 | /* Send one completely decapsulated IP datagram to the IP layer. */ |
326 | |
327 | /* |
328 | * This is the routine that sends the received data to the kernel AX.25. |
329 | * 'cmd' is the KISS command. For AX.25 data, it is zero. |
330 | */ |
331 | |
332 | static void sp_bump(struct sixpack *sp, char cmd) |
333 | { |
334 | struct sk_buff *skb; |
335 | int count; |
336 | unsigned char *ptr; |
337 | |
338 | count = sp->rcount + 1; |
339 | |
340 | sp->dev->stats.rx_bytes += count; |
341 | |
342 | if ((skb = dev_alloc_skb(length: count + 1)) == NULL) |
343 | goto out_mem; |
344 | |
345 | ptr = skb_put(skb, len: count + 1); |
346 | *ptr++ = cmd; /* KISS command */ |
347 | |
348 | memcpy(ptr, sp->cooked_buf + 1, count); |
349 | skb->protocol = ax25_type_trans(skb, dev: sp->dev); |
350 | netif_rx(skb); |
351 | sp->dev->stats.rx_packets++; |
352 | |
353 | return; |
354 | |
355 | out_mem: |
356 | sp->dev->stats.rx_dropped++; |
357 | } |
358 | |
359 | |
360 | /* ----------------------------------------------------------------------- */ |
361 | |
362 | /* |
363 | * We have a potential race on dereferencing tty->disc_data, because the tty |
364 | * layer provides no locking at all - thus one cpu could be running |
365 | * sixpack_receive_buf while another calls sixpack_close, which zeroes |
366 | * tty->disc_data and frees the memory that sixpack_receive_buf is using. The |
367 | * best way to fix this is to use a rwlock in the tty struct, but for now we |
368 | * use a single global rwlock for all ttys in ppp line discipline. |
369 | */ |
370 | static DEFINE_RWLOCK(disc_data_lock); |
371 | |
372 | static struct sixpack *sp_get(struct tty_struct *tty) |
373 | { |
374 | struct sixpack *sp; |
375 | |
376 | read_lock(&disc_data_lock); |
377 | sp = tty->disc_data; |
378 | if (sp) |
379 | refcount_inc(r: &sp->refcnt); |
380 | read_unlock(&disc_data_lock); |
381 | |
382 | return sp; |
383 | } |
384 | |
385 | static void sp_put(struct sixpack *sp) |
386 | { |
387 | if (refcount_dec_and_test(r: &sp->refcnt)) |
388 | complete(&sp->dead); |
389 | } |
390 | |
391 | /* |
392 | * Called by the TTY driver when there's room for more data. If we have |
393 | * more packets to send, we send them here. |
394 | */ |
395 | static void sixpack_write_wakeup(struct tty_struct *tty) |
396 | { |
397 | struct sixpack *sp = sp_get(tty); |
398 | int actual; |
399 | |
400 | if (!sp) |
401 | return; |
402 | if (sp->xleft <= 0) { |
403 | /* Now serial buffer is almost free & we can start |
404 | * transmission of another packet */ |
405 | sp->dev->stats.tx_packets++; |
406 | clear_bit(TTY_DO_WRITE_WAKEUP, addr: &tty->flags); |
407 | sp->tx_enable = 0; |
408 | netif_wake_queue(dev: sp->dev); |
409 | goto out; |
410 | } |
411 | |
412 | if (sp->tx_enable) { |
413 | actual = tty->ops->write(tty, sp->xhead, sp->xleft); |
414 | sp->xleft -= actual; |
415 | sp->xhead += actual; |
416 | } |
417 | |
418 | out: |
419 | sp_put(sp); |
420 | } |
421 | |
422 | /* ----------------------------------------------------------------------- */ |
423 | |
424 | /* |
425 | * Handle the 'receiver data ready' interrupt. |
426 | * This function is called by the tty module in the kernel when |
427 | * a block of 6pack data has been received, which can now be decapsulated |
428 | * and sent on to some IP layer for further processing. |
429 | */ |
430 | static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp, |
431 | const u8 *fp, size_t count) |
432 | { |
433 | struct sixpack *sp; |
434 | int count1; |
435 | |
436 | if (!count) |
437 | return; |
438 | |
439 | sp = sp_get(tty); |
440 | if (!sp) |
441 | return; |
442 | |
443 | /* Read the characters out of the buffer */ |
444 | count1 = count; |
445 | while (count) { |
446 | count--; |
447 | if (fp && *fp++) { |
448 | if (!test_and_set_bit(nr: SIXPF_ERROR, addr: &sp->flags)) |
449 | sp->dev->stats.rx_errors++; |
450 | continue; |
451 | } |
452 | } |
453 | sixpack_decode(sp, cp, count1); |
454 | |
455 | sp_put(sp); |
456 | tty_unthrottle(tty); |
457 | } |
458 | |
459 | /* |
460 | * Try to resync the TNC. Called by the resync timer defined in |
461 | * decode_prio_command |
462 | */ |
463 | |
464 | #define TNC_UNINITIALIZED 0 |
465 | #define TNC_UNSYNC_STARTUP 1 |
466 | #define TNC_UNSYNCED 2 |
467 | #define TNC_IN_SYNC 3 |
468 | |
469 | static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) |
470 | { |
471 | char *msg; |
472 | |
473 | switch (new_tnc_state) { |
474 | default: /* gcc oh piece-o-crap ... */ |
475 | case TNC_UNSYNC_STARTUP: |
476 | msg = "Synchronizing with TNC" ; |
477 | break; |
478 | case TNC_UNSYNCED: |
479 | msg = "Lost synchronization with TNC\n" ; |
480 | break; |
481 | case TNC_IN_SYNC: |
482 | msg = "Found TNC" ; |
483 | break; |
484 | } |
485 | |
486 | sp->tnc_state = new_tnc_state; |
487 | printk(KERN_INFO "%s: %s\n" , sp->dev->name, msg); |
488 | } |
489 | |
490 | static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state) |
491 | { |
492 | int old_tnc_state = sp->tnc_state; |
493 | |
494 | if (old_tnc_state != new_tnc_state) |
495 | __tnc_set_sync_state(sp, new_tnc_state); |
496 | } |
497 | |
498 | static void resync_tnc(struct timer_list *t) |
499 | { |
500 | struct sixpack *sp = from_timer(sp, t, resync_t); |
501 | static char resync_cmd = 0xe8; |
502 | |
503 | /* clear any data that might have been received */ |
504 | |
505 | sp->rx_count = 0; |
506 | sp->rx_count_cooked = 0; |
507 | |
508 | /* reset state machine */ |
509 | |
510 | sp->status = 1; |
511 | sp->status1 = 1; |
512 | sp->status2 = 0; |
513 | |
514 | /* resync the TNC */ |
515 | |
516 | sp->led_state = 0x60; |
517 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
518 | sp->tty->ops->write(sp->tty, &resync_cmd, 1); |
519 | |
520 | |
521 | /* Start resync timer again -- the TNC might be still absent */ |
522 | mod_timer(timer: &sp->resync_t, expires: jiffies + SIXP_RESYNC_TIMEOUT); |
523 | } |
524 | |
525 | static inline int tnc_init(struct sixpack *sp) |
526 | { |
527 | unsigned char inbyte = 0xe8; |
528 | |
529 | tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP); |
530 | |
531 | sp->tty->ops->write(sp->tty, &inbyte, 1); |
532 | |
533 | mod_timer(timer: &sp->resync_t, expires: jiffies + SIXP_RESYNC_TIMEOUT); |
534 | |
535 | return 0; |
536 | } |
537 | |
538 | /* |
539 | * Open the high-level part of the 6pack channel. |
540 | * This function is called by the TTY module when the |
541 | * 6pack line discipline is called for. Because we are |
542 | * sure the tty line exists, we only have to link it to |
543 | * a free 6pcack channel... |
544 | */ |
545 | static int sixpack_open(struct tty_struct *tty) |
546 | { |
547 | char *rbuff = NULL, *xbuff = NULL; |
548 | struct net_device *dev; |
549 | struct sixpack *sp; |
550 | unsigned long len; |
551 | int err = 0; |
552 | |
553 | if (!capable(CAP_NET_ADMIN)) |
554 | return -EPERM; |
555 | if (tty->ops->write == NULL) |
556 | return -EOPNOTSUPP; |
557 | |
558 | dev = alloc_netdev(sizeof(struct sixpack), "sp%d" , NET_NAME_UNKNOWN, |
559 | sp_setup); |
560 | if (!dev) { |
561 | err = -ENOMEM; |
562 | goto out; |
563 | } |
564 | |
565 | sp = netdev_priv(dev); |
566 | sp->dev = dev; |
567 | |
568 | spin_lock_init(&sp->lock); |
569 | spin_lock_init(&sp->rxlock); |
570 | refcount_set(r: &sp->refcnt, n: 1); |
571 | init_completion(x: &sp->dead); |
572 | |
573 | /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */ |
574 | |
575 | len = dev->mtu * 2; |
576 | |
577 | rbuff = kmalloc(size: len + 4, GFP_KERNEL); |
578 | xbuff = kmalloc(size: len + 4, GFP_KERNEL); |
579 | |
580 | if (rbuff == NULL || xbuff == NULL) { |
581 | err = -ENOBUFS; |
582 | goto out_free; |
583 | } |
584 | |
585 | spin_lock_bh(lock: &sp->lock); |
586 | |
587 | sp->tty = tty; |
588 | |
589 | sp->rbuff = rbuff; |
590 | sp->xbuff = xbuff; |
591 | |
592 | sp->mtu = AX25_MTU + 73; |
593 | sp->buffsize = len; |
594 | sp->rcount = 0; |
595 | sp->rx_count = 0; |
596 | sp->rx_count_cooked = 0; |
597 | sp->xleft = 0; |
598 | |
599 | sp->flags = 0; /* Clear ESCAPE & ERROR flags */ |
600 | |
601 | sp->duplex = 0; |
602 | sp->tx_delay = SIXP_TXDELAY; |
603 | sp->persistence = SIXP_PERSIST; |
604 | sp->slottime = SIXP_SLOTTIME; |
605 | sp->led_state = 0x60; |
606 | sp->status = 1; |
607 | sp->status1 = 1; |
608 | sp->status2 = 0; |
609 | sp->tx_enable = 0; |
610 | |
611 | netif_start_queue(dev); |
612 | |
613 | timer_setup(&sp->tx_t, sp_xmit_on_air, 0); |
614 | |
615 | timer_setup(&sp->resync_t, resync_tnc, 0); |
616 | |
617 | spin_unlock_bh(lock: &sp->lock); |
618 | |
619 | /* Done. We have linked the TTY line to a channel. */ |
620 | tty->disc_data = sp; |
621 | tty->receive_room = 65536; |
622 | |
623 | /* Now we're ready to register. */ |
624 | err = register_netdev(dev); |
625 | if (err) |
626 | goto out_free; |
627 | |
628 | tnc_init(sp); |
629 | |
630 | return 0; |
631 | |
632 | out_free: |
633 | kfree(objp: xbuff); |
634 | kfree(objp: rbuff); |
635 | |
636 | free_netdev(dev); |
637 | |
638 | out: |
639 | return err; |
640 | } |
641 | |
642 | |
643 | /* |
644 | * Close down a 6pack channel. |
645 | * This means flushing out any pending queues, and then restoring the |
646 | * TTY line discipline to what it was before it got hooked to 6pack |
647 | * (which usually is TTY again). |
648 | */ |
649 | static void sixpack_close(struct tty_struct *tty) |
650 | { |
651 | struct sixpack *sp; |
652 | |
653 | write_lock_irq(&disc_data_lock); |
654 | sp = tty->disc_data; |
655 | tty->disc_data = NULL; |
656 | write_unlock_irq(&disc_data_lock); |
657 | if (!sp) |
658 | return; |
659 | |
660 | /* |
661 | * We have now ensured that nobody can start using ap from now on, but |
662 | * we have to wait for all existing users to finish. |
663 | */ |
664 | if (!refcount_dec_and_test(r: &sp->refcnt)) |
665 | wait_for_completion(&sp->dead); |
666 | |
667 | /* We must stop the queue to avoid potentially scribbling |
668 | * on the free buffers. The sp->dead completion is not sufficient |
669 | * to protect us from sp->xbuff access. |
670 | */ |
671 | netif_stop_queue(dev: sp->dev); |
672 | |
673 | unregister_netdev(dev: sp->dev); |
674 | |
675 | del_timer_sync(timer: &sp->tx_t); |
676 | del_timer_sync(timer: &sp->resync_t); |
677 | |
678 | /* Free all 6pack frame buffers after unreg. */ |
679 | kfree(objp: sp->rbuff); |
680 | kfree(objp: sp->xbuff); |
681 | |
682 | free_netdev(dev: sp->dev); |
683 | } |
684 | |
685 | /* Perform I/O control on an active 6pack channel. */ |
686 | static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd, |
687 | unsigned long arg) |
688 | { |
689 | struct sixpack *sp = sp_get(tty); |
690 | struct net_device *dev; |
691 | unsigned int tmp, err; |
692 | |
693 | if (!sp) |
694 | return -ENXIO; |
695 | dev = sp->dev; |
696 | |
697 | switch(cmd) { |
698 | case SIOCGIFNAME: |
699 | err = copy_to_user(to: (void __user *) arg, from: dev->name, |
700 | strlen(dev->name) + 1) ? -EFAULT : 0; |
701 | break; |
702 | |
703 | case SIOCGIFENCAP: |
704 | err = put_user(0, (int __user *) arg); |
705 | break; |
706 | |
707 | case SIOCSIFENCAP: |
708 | if (get_user(tmp, (int __user *) arg)) { |
709 | err = -EFAULT; |
710 | break; |
711 | } |
712 | |
713 | sp->mode = tmp; |
714 | dev->addr_len = AX25_ADDR_LEN; |
715 | dev->hard_header_len = AX25_KISS_HEADER_LEN + |
716 | AX25_MAX_HEADER_LEN + 3; |
717 | dev->type = ARPHRD_AX25; |
718 | |
719 | err = 0; |
720 | break; |
721 | |
722 | case SIOCSIFHWADDR: { |
723 | char addr[AX25_ADDR_LEN]; |
724 | |
725 | if (copy_from_user(to: &addr, |
726 | from: (void __user *)arg, AX25_ADDR_LEN)) { |
727 | err = -EFAULT; |
728 | break; |
729 | } |
730 | |
731 | netif_tx_lock_bh(dev); |
732 | __dev_addr_set(dev, addr: &addr, AX25_ADDR_LEN); |
733 | netif_tx_unlock_bh(dev); |
734 | err = 0; |
735 | break; |
736 | } |
737 | default: |
738 | err = tty_mode_ioctl(tty, cmd, arg); |
739 | } |
740 | |
741 | sp_put(sp); |
742 | |
743 | return err; |
744 | } |
745 | |
746 | static struct tty_ldisc_ops sp_ldisc = { |
747 | .owner = THIS_MODULE, |
748 | .num = N_6PACK, |
749 | .name = "6pack" , |
750 | .open = sixpack_open, |
751 | .close = sixpack_close, |
752 | .ioctl = sixpack_ioctl, |
753 | .receive_buf = sixpack_receive_buf, |
754 | .write_wakeup = sixpack_write_wakeup, |
755 | }; |
756 | |
757 | /* Initialize 6pack control device -- register 6pack line discipline */ |
758 | |
759 | static const char msg_banner[] __initconst = KERN_INFO \ |
760 | "AX.25: 6pack driver, " SIXPACK_VERSION "\n" ; |
761 | static const char msg_regfail[] __initconst = KERN_ERR \ |
762 | "6pack: can't register line discipline (err = %d)\n" ; |
763 | |
764 | static int __init sixpack_init_driver(void) |
765 | { |
766 | int status; |
767 | |
768 | printk(msg_banner); |
769 | |
770 | /* Register the provided line protocol discipline */ |
771 | status = tty_register_ldisc(new_ldisc: &sp_ldisc); |
772 | if (status) |
773 | printk(msg_regfail, status); |
774 | |
775 | return status; |
776 | } |
777 | |
778 | static void __exit sixpack_exit_driver(void) |
779 | { |
780 | tty_unregister_ldisc(ldisc: &sp_ldisc); |
781 | } |
782 | |
783 | /* encode an AX.25 packet into 6pack */ |
784 | |
785 | static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, |
786 | int length, unsigned char tx_delay) |
787 | { |
788 | int count = 0; |
789 | unsigned char checksum = 0, buf[400]; |
790 | int raw_count = 0; |
791 | |
792 | tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK; |
793 | tx_buf_raw[raw_count++] = SIXP_SEOF; |
794 | |
795 | buf[0] = tx_delay; |
796 | for (count = 1; count < length; count++) |
797 | buf[count] = tx_buf[count]; |
798 | |
799 | for (count = 0; count < length; count++) |
800 | checksum += buf[count]; |
801 | buf[length] = (unsigned char) 0xff - checksum; |
802 | |
803 | for (count = 0; count <= length; count++) { |
804 | if ((count % 3) == 0) { |
805 | tx_buf_raw[raw_count++] = (buf[count] & 0x3f); |
806 | tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30); |
807 | } else if ((count % 3) == 1) { |
808 | tx_buf_raw[raw_count++] |= (buf[count] & 0x0f); |
809 | tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c); |
810 | } else { |
811 | tx_buf_raw[raw_count++] |= (buf[count] & 0x03); |
812 | tx_buf_raw[raw_count++] = (buf[count] >> 2); |
813 | } |
814 | } |
815 | if ((length % 3) != 2) |
816 | raw_count++; |
817 | tx_buf_raw[raw_count++] = SIXP_SEOF; |
818 | return raw_count; |
819 | } |
820 | |
821 | /* decode 4 sixpack-encoded bytes into 3 data bytes */ |
822 | |
823 | static void decode_data(struct sixpack *sp, unsigned char inbyte) |
824 | { |
825 | unsigned char *buf; |
826 | |
827 | if (sp->rx_count != 3) { |
828 | sp->raw_buf[sp->rx_count++] = inbyte; |
829 | |
830 | return; |
831 | } |
832 | |
833 | if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) { |
834 | pr_err("6pack: cooked buffer overrun, data loss\n" ); |
835 | sp->rx_count = 0; |
836 | return; |
837 | } |
838 | |
839 | buf = sp->raw_buf; |
840 | sp->cooked_buf[sp->rx_count_cooked++] = |
841 | buf[0] | ((buf[1] << 2) & 0xc0); |
842 | sp->cooked_buf[sp->rx_count_cooked++] = |
843 | (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0); |
844 | sp->cooked_buf[sp->rx_count_cooked++] = |
845 | (buf[2] & 0x03) | (inbyte << 2); |
846 | sp->rx_count = 0; |
847 | } |
848 | |
849 | /* identify and execute a 6pack priority command byte */ |
850 | |
851 | static void decode_prio_command(struct sixpack *sp, unsigned char cmd) |
852 | { |
853 | int actual; |
854 | |
855 | if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ |
856 | |
857 | /* RX and DCD flags can only be set in the same prio command, |
858 | if the DCD flag has been set without the RX flag in the previous |
859 | prio command. If DCD has not been set before, something in the |
860 | transmission has gone wrong. In this case, RX and DCD are |
861 | cleared in order to prevent the decode_data routine from |
862 | reading further data that might be corrupt. */ |
863 | |
864 | if (((sp->status & SIXP_DCD_MASK) == 0) && |
865 | ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { |
866 | if (sp->status != 1) |
867 | printk(KERN_DEBUG "6pack: protocol violation\n" ); |
868 | else |
869 | sp->status = 0; |
870 | cmd &= ~SIXP_RX_DCD_MASK; |
871 | } |
872 | sp->status = cmd & SIXP_PRIO_DATA_MASK; |
873 | } else { /* output watchdog char if idle */ |
874 | if ((sp->status2 != 0) && (sp->duplex == 1)) { |
875 | sp->led_state = 0x70; |
876 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
877 | sp->tx_enable = 1; |
878 | actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2); |
879 | sp->xleft -= actual; |
880 | sp->xhead += actual; |
881 | sp->led_state = 0x60; |
882 | sp->status2 = 0; |
883 | |
884 | } |
885 | } |
886 | |
887 | /* needed to trigger the TNC watchdog */ |
888 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
889 | |
890 | /* if the state byte has been received, the TNC is present, |
891 | so the resync timer can be reset. */ |
892 | |
893 | if (sp->tnc_state == TNC_IN_SYNC) |
894 | mod_timer(timer: &sp->resync_t, expires: jiffies + SIXP_INIT_RESYNC_TIMEOUT); |
895 | |
896 | sp->status1 = cmd & SIXP_PRIO_DATA_MASK; |
897 | } |
898 | |
899 | /* identify and execute a standard 6pack command byte */ |
900 | |
901 | static void decode_std_command(struct sixpack *sp, unsigned char cmd) |
902 | { |
903 | unsigned char checksum = 0, rest = 0; |
904 | short i; |
905 | |
906 | switch (cmd & SIXP_CMD_MASK) { /* normal command */ |
907 | case SIXP_SEOF: |
908 | if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { |
909 | if ((sp->status & SIXP_RX_DCD_MASK) == |
910 | SIXP_RX_DCD_MASK) { |
911 | sp->led_state = 0x68; |
912 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
913 | } |
914 | } else { |
915 | sp->led_state = 0x60; |
916 | /* fill trailing bytes with zeroes */ |
917 | sp->tty->ops->write(sp->tty, &sp->led_state, 1); |
918 | spin_lock_bh(lock: &sp->rxlock); |
919 | rest = sp->rx_count; |
920 | if (rest != 0) |
921 | for (i = rest; i <= 3; i++) |
922 | decode_data(sp, inbyte: 0); |
923 | if (rest == 2) |
924 | sp->rx_count_cooked -= 2; |
925 | else if (rest == 3) |
926 | sp->rx_count_cooked -= 1; |
927 | for (i = 0; i < sp->rx_count_cooked; i++) |
928 | checksum += sp->cooked_buf[i]; |
929 | if (checksum != SIXP_CHKSUM) { |
930 | printk(KERN_DEBUG "6pack: bad checksum %2.2x\n" , checksum); |
931 | } else { |
932 | sp->rcount = sp->rx_count_cooked-2; |
933 | sp_bump(sp, cmd: 0); |
934 | } |
935 | sp->rx_count_cooked = 0; |
936 | spin_unlock_bh(lock: &sp->rxlock); |
937 | } |
938 | break; |
939 | case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n" ); |
940 | break; |
941 | case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n" ); |
942 | break; |
943 | case SIXP_RX_BUF_OVL: |
944 | printk(KERN_DEBUG "6pack: RX buffer overflow\n" ); |
945 | } |
946 | } |
947 | |
948 | /* decode a 6pack packet */ |
949 | |
950 | static void |
951 | sixpack_decode(struct sixpack *sp, const unsigned char *pre_rbuff, int count) |
952 | { |
953 | unsigned char inbyte; |
954 | int count1; |
955 | |
956 | for (count1 = 0; count1 < count; count1++) { |
957 | inbyte = pre_rbuff[count1]; |
958 | if (inbyte == SIXP_FOUND_TNC) { |
959 | tnc_set_sync_state(sp, TNC_IN_SYNC); |
960 | del_timer(timer: &sp->resync_t); |
961 | } |
962 | if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) |
963 | decode_prio_command(sp, cmd: inbyte); |
964 | else if ((inbyte & SIXP_STD_CMD_MASK) != 0) |
965 | decode_std_command(sp, cmd: inbyte); |
966 | else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { |
967 | spin_lock_bh(lock: &sp->rxlock); |
968 | decode_data(sp, inbyte); |
969 | spin_unlock_bh(lock: &sp->rxlock); |
970 | } |
971 | } |
972 | } |
973 | |
974 | MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>" ); |
975 | MODULE_DESCRIPTION("6pack driver for AX.25" ); |
976 | MODULE_LICENSE("GPL" ); |
977 | MODULE_ALIAS_LDISC(N_6PACK); |
978 | |
979 | module_init(sixpack_init_driver); |
980 | module_exit(sixpack_exit_driver); |
981 | |