blob: 992089639ea4824404f42d53e5362eab2c2fb586 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * 6pack.c This module implements the 6pack protocol for kernel-based
3 * devices like TTY. It interfaces between a raw TTY and the
4 * kernel's AX.25 protocol layers.
5 *
Ralf Baechle67332592009-07-17 04:47:19 +00006 * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de>
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 * Ralf Baechle DL5RB <ralf@linux-mips.org>
8 *
9 * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
10 *
11 * Laurence Culhane, <loz@holmes.demon.co.uk>
12 * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
13 */
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/module.h>
16#include <asm/system.h>
17#include <asm/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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090027#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#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>
Matthew Wilcox6188e102008-04-18 22:21:05 -040037#include <linux/semaphore.h>
Arnd Bergmann9646e7c2009-11-06 22:51:16 -080038#include <linux/compat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <asm/atomic.h>
40
41#define SIXPACK_VERSION "Revision: 0.3.0"
42
43/* sixpack priority commands */
44#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */
45#define SIXP_TX_URUN 0x48 /* transmit overrun */
46#define SIXP_RX_ORUN 0x50 /* receive overrun */
47#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */
48
49#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */
50
51/* masks to get certain bits out of the status bytes sent by the TNC */
52
53#define SIXP_CMD_MASK 0xC0
54#define SIXP_CHN_MASK 0x07
55#define SIXP_PRIO_CMD_MASK 0x80
56#define SIXP_STD_CMD_MASK 0x40
57#define SIXP_PRIO_DATA_MASK 0x38
58#define SIXP_TX_MASK 0x20
59#define SIXP_RX_MASK 0x10
60#define SIXP_RX_DCD_MASK 0x18
61#define SIXP_LEDS_ON 0x78
62#define SIXP_LEDS_OFF 0x60
63#define SIXP_CON 0x08
64#define SIXP_STA 0x10
65
66#define SIXP_FOUND_TNC 0xe9
67#define SIXP_CON_ON 0x68
68#define SIXP_DCD_MASK 0x08
69#define SIXP_DAMA_OFF 0
70
71/* default level 2 parameters */
72#define SIXP_TXDELAY (HZ/4) /* in 1 s */
73#define SIXP_PERSIST 50 /* in 256ths */
74#define SIXP_SLOTTIME (HZ/10) /* in 1 s */
75#define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */
76#define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */
77
78/* 6pack configuration. */
79#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */
80#define SIXP_MTU 256 /* Default MTU */
81
82enum sixpack_flags {
83 SIXPF_ERROR, /* Parity, etc. error */
84};
85
86struct sixpack {
87 /* Various fields. */
88 struct tty_struct *tty; /* ptr to TTY structure */
89 struct net_device *dev; /* easy for intr handling */
90
91 /* These are pointers to the malloc()ed frame buffers. */
92 unsigned char *rbuff; /* receiver buffer */
93 int rcount; /* received chars counter */
94 unsigned char *xbuff; /* transmitter buffer */
95 unsigned char *xhead; /* next byte to XMIT */
96 int xleft; /* bytes left in XMIT queue */
97
98 unsigned char raw_buf[4];
99 unsigned char cooked_buf[400];
100
101 unsigned int rx_count;
102 unsigned int rx_count_cooked;
103
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 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 atomic_t refcnt;
125 struct semaphore dead_sem;
126 spinlock_t lock;
127};
128
129#define AX25_6PACK_HEADER_LEN 0
130
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131static void sixpack_decode(struct sixpack *, unsigned char[], int);
132static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
133
134/*
Ralf Baechle DL5RBc0438172005-08-10 10:03:20 -0700135 * Perform the persistence/slottime algorithm for CSMA access. If the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 * 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
140static void sp_xmit_on_air(unsigned long channel)
141{
142 struct sixpack *sp = (struct sixpack *) channel;
Ralf Baechle DL5RBc0438172005-08-10 10:03:20 -0700143 int actual, when = sp->slottime;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 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;
Alan Coxf34d7a52008-04-30 00:54:13 -0700150 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 sp->tx_enable = 1;
Alan Coxf34d7a52008-04-30 00:54:13 -0700152 actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153 sp->xleft -= actual;
154 sp->xhead += actual;
155 sp->led_state = 0x60;
Alan Coxf34d7a52008-04-30 00:54:13 -0700156 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 sp->status2 = 0;
158 } else
Ralf Baechle DL5RBc0438172005-08-10 10:03:20 -0700159 mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160}
161
162/* ----> 6pack timer interrupt handler and friends. <---- */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164/* Encapsulate one AX.25 frame and stuff into a TTY queue. */
165static 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 (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */
176 msg = "oversized transmit packet!";
177 goto out_drop;
178 }
179
180 if (p[0] > 5) {
181 msg = "invalid KISS command";
182 goto out_drop;
183 }
184
185 if ((p[0] != 0) && (len > 2)) {
186 msg = "KISS control packet too long";
187 goto out_drop;
188 }
189
190 if ((p[0] == 0) && (len < 15)) {
191 msg = "bad AX.25 packet to transmit";
192 goto out_drop;
193 }
194
195 count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay);
196 set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
197
198 switch (p[0]) {
199 case 1: sp->tx_delay = p[1];
200 return;
201 case 2: sp->persistence = p[1];
202 return;
203 case 3: sp->slottime = p[1];
204 return;
205 case 4: /* ignored */
206 return;
207 case 5: sp->duplex = p[1];
208 return;
209 }
210
211 if (p[0] != 0)
212 return;
213
214 /*
215 * In case of fullduplex or DAMA operation, we don't take care about the
216 * state of the DCD or of any timers, as the determination of the
217 * correct time to send is the job of the AX.25 layer. We send
218 * immediately after data has arrived.
219 */
220 if (sp->duplex == 1) {
221 sp->led_state = 0x70;
Alan Coxf34d7a52008-04-30 00:54:13 -0700222 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 sp->tx_enable = 1;
Alan Coxf34d7a52008-04-30 00:54:13 -0700224 actual = sp->tty->ops->write(sp->tty, sp->xbuff, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 sp->xleft = count - actual;
226 sp->xhead = sp->xbuff + actual;
227 sp->led_state = 0x60;
Alan Coxf34d7a52008-04-30 00:54:13 -0700228 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 } else {
230 sp->xleft = count;
231 sp->xhead = sp->xbuff;
232 sp->status2 = count;
Ralf Baechle DL5RBc0438172005-08-10 10:03:20 -0700233 sp_xmit_on_air((unsigned long)sp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 }
235
236 return;
237
238out_drop:
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300239 sp->dev->stats.tx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 netif_start_queue(sp->dev);
241 if (net_ratelimit())
242 printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg);
243}
244
245/* Encapsulate an IP datagram and kick it into a TTY queue. */
246
Stephen Hemminger36e4d642009-08-31 19:50:43 +0000247static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248{
249 struct sixpack *sp = netdev_priv(dev);
250
251 spin_lock_bh(&sp->lock);
252 /* We were not busy, so we are now... :-) */
253 netif_stop_queue(dev);
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300254 dev->stats.tx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255 sp_encaps(sp, skb->data, skb->len);
256 spin_unlock_bh(&sp->lock);
257
258 dev_kfree_skb(skb);
259
Patrick McHardy6ed10652009-06-23 06:03:08 +0000260 return NETDEV_TX_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261}
262
263static int sp_open_dev(struct net_device *dev)
264{
265 struct sixpack *sp = netdev_priv(dev);
266
267 if (sp->tty == NULL)
268 return -ENODEV;
269 return 0;
270}
271
272/* Close the low-level part of the 6pack channel. */
273static int sp_close(struct net_device *dev)
274{
275 struct sixpack *sp = netdev_priv(dev);
276
277 spin_lock_bh(&sp->lock);
278 if (sp->tty) {
279 /* TTY discipline is running. */
280 clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
281 }
282 netif_stop_queue(dev);
283 spin_unlock_bh(&sp->lock);
284
285 return 0;
286}
287
288/* Return the frame type ID */
289static int sp_header(struct sk_buff *skb, struct net_device *dev,
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700290 unsigned short type, const void *daddr,
291 const void *saddr, unsigned len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293#ifdef CONFIG_INET
Al Virod9a19d22007-10-14 19:40:49 +0100294 if (type != ETH_P_AX25)
Ralf Baechle6f749982005-09-12 14:21:01 -0700295 return ax25_hard_header(skb, dev, type, daddr, saddr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296#endif
297 return 0;
298}
299
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300static int sp_set_mac_address(struct net_device *dev, void *addr)
301{
302 struct sockaddr_ax25 *sa = addr;
303
Herbert Xu932ff272006-06-09 12:20:56 -0700304 netif_tx_lock_bh(dev);
David S. Millere308a5d2008-07-15 00:13:44 -0700305 netif_addr_lock(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 memcpy(dev->dev_addr, &sa->sax25_call, AX25_ADDR_LEN);
David S. Millere308a5d2008-07-15 00:13:44 -0700307 netif_addr_unlock(dev);
Herbert Xu932ff272006-06-09 12:20:56 -0700308 netif_tx_unlock_bh(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309
310 return 0;
311}
312
313static int sp_rebuild_header(struct sk_buff *skb)
314{
315#ifdef CONFIG_INET
316 return ax25_rebuild_header(skb);
317#else
318 return 0;
319#endif
320}
321
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700322static const struct header_ops sp_header_ops = {
323 .create = sp_header,
324 .rebuild = sp_rebuild_header,
325};
326
Stephen Hemmingerb3672a72009-01-09 13:01:28 +0000327static const struct net_device_ops sp_netdev_ops = {
328 .ndo_open = sp_open_dev,
329 .ndo_stop = sp_close,
330 .ndo_start_xmit = sp_xmit,
331 .ndo_set_mac_address = sp_set_mac_address,
332};
333
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334static void sp_setup(struct net_device *dev)
335{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 /* Finish setting up the DEVICE info. */
Stephen Hemmingerb3672a72009-01-09 13:01:28 +0000337 dev->netdev_ops = &sp_netdev_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 dev->destructor = free_netdev;
Stephen Hemmingerb3672a72009-01-09 13:01:28 +0000339 dev->mtu = SIXP_MTU;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 dev->hard_header_len = AX25_MAX_HEADER_LEN;
Stephen Hemminger3b04ddd2007-10-09 01:40:57 -0700341 dev->header_ops = &sp_header_ops;
342
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 dev->addr_len = AX25_ADDR_LEN;
344 dev->type = ARPHRD_AX25;
345 dev->tx_queue_len = 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
347 /* Only activated in AX.25 mode */
Ralf Baechle15b1c0e2006-12-07 15:47:08 -0800348 memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
349 memcpy(dev->dev_addr, &ax25_defaddr, AX25_ADDR_LEN);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 dev->flags = 0;
352}
353
354/* Send one completely decapsulated IP datagram to the IP layer. */
355
356/*
357 * This is the routine that sends the received data to the kernel AX.25.
358 * 'cmd' is the KISS command. For AX.25 data, it is zero.
359 */
360
361static void sp_bump(struct sixpack *sp, char cmd)
362{
363 struct sk_buff *skb;
364 int count;
365 unsigned char *ptr;
366
367 count = sp->rcount + 1;
368
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300369 sp->dev->stats.rx_bytes += count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
371 if ((skb = dev_alloc_skb(count)) == NULL)
372 goto out_mem;
373
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 ptr = skb_put(skb, count);
375 *ptr++ = cmd; /* KISS command */
376
377 memcpy(ptr, sp->cooked_buf + 1, count);
Arnaldo Carvalho de Melo56cb5152005-04-24 18:53:06 -0700378 skb->protocol = ax25_type_trans(skb, sp->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 netif_rx(skb);
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300380 sp->dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381
382 return;
383
384out_mem:
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300385 sp->dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386}
387
388
389/* ----------------------------------------------------------------------- */
390
391/*
392 * We have a potential race on dereferencing tty->disc_data, because the tty
393 * layer provides no locking at all - thus one cpu could be running
394 * sixpack_receive_buf while another calls sixpack_close, which zeroes
395 * tty->disc_data and frees the memory that sixpack_receive_buf is using. The
396 * best way to fix this is to use a rwlock in the tty struct, but for now we
397 * use a single global rwlock for all ttys in ppp line discipline.
398 */
399static DEFINE_RWLOCK(disc_data_lock);
400
401static struct sixpack *sp_get(struct tty_struct *tty)
402{
403 struct sixpack *sp;
404
405 read_lock(&disc_data_lock);
406 sp = tty->disc_data;
407 if (sp)
408 atomic_inc(&sp->refcnt);
409 read_unlock(&disc_data_lock);
410
411 return sp;
412}
413
414static void sp_put(struct sixpack *sp)
415{
416 if (atomic_dec_and_test(&sp->refcnt))
417 up(&sp->dead_sem);
418}
419
420/*
421 * Called by the TTY driver when there's room for more data. If we have
422 * more packets to send, we send them here.
423 */
424static void sixpack_write_wakeup(struct tty_struct *tty)
425{
426 struct sixpack *sp = sp_get(tty);
427 int actual;
428
429 if (!sp)
430 return;
431 if (sp->xleft <= 0) {
432 /* Now serial buffer is almost free & we can start
433 * transmission of another packet */
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300434 sp->dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
436 sp->tx_enable = 0;
437 netif_wake_queue(sp->dev);
438 goto out;
439 }
440
441 if (sp->tx_enable) {
Alan Coxf34d7a52008-04-30 00:54:13 -0700442 actual = tty->ops->write(tty, sp->xhead, sp->xleft);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 sp->xleft -= actual;
444 sp->xhead += actual;
445 }
446
447out:
448 sp_put(sp);
449}
450
451/* ----------------------------------------------------------------------- */
452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453/*
454 * Handle the 'receiver data ready' interrupt.
455 * This function is called by the 'tty_io' module in the kernel when
456 * a block of 6pack data has been received, which can now be decapsulated
457 * and sent on to some IP layer for further processing.
458 */
Felipe Balbib1c43f82011-03-21 12:25:08 +0200459static unsigned int sixpack_receive_buf(struct tty_struct *tty,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 const unsigned char *cp, char *fp, int count)
461{
462 struct sixpack *sp;
463 unsigned char buf[512];
464 int count1;
465
466 if (!count)
Felipe Balbib1c43f82011-03-21 12:25:08 +0200467 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
469 sp = sp_get(tty);
470 if (!sp)
Felipe Balbib1c43f82011-03-21 12:25:08 +0200471 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472
473 memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf));
474
475 /* Read the characters out of the buffer */
476
477 count1 = count;
478 while (count) {
479 count--;
480 if (fp && *fp++) {
481 if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
Paulius Zaleckasde0561c2008-04-30 01:49:15 +0300482 sp->dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 continue;
484 }
485 }
486 sixpack_decode(sp, buf, count1);
487
488 sp_put(sp);
Alan Cox39c2e602008-04-30 00:54:18 -0700489 tty_unthrottle(tty);
Felipe Balbib1c43f82011-03-21 12:25:08 +0200490
491 return count1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492}
493
494/*
495 * Try to resync the TNC. Called by the resync timer defined in
496 * decode_prio_command
497 */
498
499#define TNC_UNINITIALIZED 0
500#define TNC_UNSYNC_STARTUP 1
501#define TNC_UNSYNCED 2
502#define TNC_IN_SYNC 3
503
504static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
505{
506 char *msg;
507
508 switch (new_tnc_state) {
509 default: /* gcc oh piece-o-crap ... */
510 case TNC_UNSYNC_STARTUP:
511 msg = "Synchronizing with TNC";
512 break;
513 case TNC_UNSYNCED:
514 msg = "Lost synchronization with TNC\n";
515 break;
516 case TNC_IN_SYNC:
517 msg = "Found TNC";
518 break;
519 }
520
521 sp->tnc_state = new_tnc_state;
522 printk(KERN_INFO "%s: %s\n", sp->dev->name, msg);
523}
524
525static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
526{
527 int old_tnc_state = sp->tnc_state;
528
529 if (old_tnc_state != new_tnc_state)
530 __tnc_set_sync_state(sp, new_tnc_state);
531}
532
533static void resync_tnc(unsigned long channel)
534{
535 struct sixpack *sp = (struct sixpack *) channel;
536 static char resync_cmd = 0xe8;
537
538 /* clear any data that might have been received */
539
540 sp->rx_count = 0;
541 sp->rx_count_cooked = 0;
542
543 /* reset state machine */
544
545 sp->status = 1;
546 sp->status1 = 1;
547 sp->status2 = 0;
548
549 /* resync the TNC */
550
551 sp->led_state = 0x60;
Alan Coxf34d7a52008-04-30 00:54:13 -0700552 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
553 sp->tty->ops->write(sp->tty, &resync_cmd, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554
555
556 /* Start resync timer again -- the TNC might be still absent */
557
558 del_timer(&sp->resync_t);
559 sp->resync_t.data = (unsigned long) sp;
560 sp->resync_t.function = resync_tnc;
561 sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
562 add_timer(&sp->resync_t);
563}
564
565static inline int tnc_init(struct sixpack *sp)
566{
567 unsigned char inbyte = 0xe8;
568
569 tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP);
570
Alan Coxf34d7a52008-04-30 00:54:13 -0700571 sp->tty->ops->write(sp->tty, &inbyte, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
573 del_timer(&sp->resync_t);
574 sp->resync_t.data = (unsigned long) sp;
575 sp->resync_t.function = resync_tnc;
576 sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
577 add_timer(&sp->resync_t);
578
579 return 0;
580}
581
582/*
583 * Open the high-level part of the 6pack channel.
584 * This function is called by the TTY module when the
585 * 6pack line discipline is called for. Because we are
586 * sure the tty line exists, we only have to link it to
587 * a free 6pcack channel...
588 */
589static int sixpack_open(struct tty_struct *tty)
590{
591 char *rbuff = NULL, *xbuff = NULL;
592 struct net_device *dev;
593 struct sixpack *sp;
594 unsigned long len;
595 int err = 0;
596
597 if (!capable(CAP_NET_ADMIN))
598 return -EPERM;
Alan Coxf34d7a52008-04-30 00:54:13 -0700599 if (tty->ops->write == NULL)
600 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
602 dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
603 if (!dev) {
604 err = -ENOMEM;
605 goto out;
606 }
607
608 sp = netdev_priv(dev);
609 sp->dev = dev;
610
611 spin_lock_init(&sp->lock);
612 atomic_set(&sp->refcnt, 1);
Thomas Gleixner89d9f102010-09-07 14:32:14 +0000613 sema_init(&sp->dead_sem, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */
616
617 len = dev->mtu * 2;
618
619 rbuff = kmalloc(len + 4, GFP_KERNEL);
620 xbuff = kmalloc(len + 4, GFP_KERNEL);
621
622 if (rbuff == NULL || xbuff == NULL) {
623 err = -ENOBUFS;
624 goto out_free;
625 }
626
627 spin_lock_bh(&sp->lock);
628
629 sp->tty = tty;
630
631 sp->rbuff = rbuff;
632 sp->xbuff = xbuff;
633
634 sp->mtu = AX25_MTU + 73;
635 sp->buffsize = len;
636 sp->rcount = 0;
637 sp->rx_count = 0;
638 sp->rx_count_cooked = 0;
639 sp->xleft = 0;
640
641 sp->flags = 0; /* Clear ESCAPE & ERROR flags */
642
643 sp->duplex = 0;
644 sp->tx_delay = SIXP_TXDELAY;
645 sp->persistence = SIXP_PERSIST;
646 sp->slottime = SIXP_SLOTTIME;
647 sp->led_state = 0x60;
648 sp->status = 1;
649 sp->status1 = 1;
650 sp->status2 = 0;
651 sp->tx_enable = 0;
652
653 netif_start_queue(dev);
654
655 init_timer(&sp->tx_t);
Ralf Baechle84a2ea12005-08-25 19:38:30 +0100656 sp->tx_t.function = sp_xmit_on_air;
657 sp->tx_t.data = (unsigned long) sp;
658
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 init_timer(&sp->resync_t);
660
661 spin_unlock_bh(&sp->lock);
662
663 /* Done. We have linked the TTY line to a channel. */
664 tty->disc_data = sp;
Alan Cox33f0f882006-01-09 20:54:13 -0800665 tty->receive_room = 65536;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666
667 /* Now we're ready to register. */
668 if (register_netdev(dev))
669 goto out_free;
670
671 tnc_init(sp);
672
673 return 0;
674
675out_free:
676 kfree(xbuff);
677 kfree(rbuff);
678
679 if (dev)
680 free_netdev(dev);
681
682out:
683 return err;
684}
685
686
687/*
688 * Close down a 6pack channel.
689 * This means flushing out any pending queues, and then restoring the
690 * TTY line discipline to what it was before it got hooked to 6pack
691 * (which usually is TTY again).
692 */
693static void sixpack_close(struct tty_struct *tty)
694{
695 struct sixpack *sp;
696
697 write_lock(&disc_data_lock);
698 sp = tty->disc_data;
699 tty->disc_data = NULL;
700 write_unlock(&disc_data_lock);
Al Viro79ea13c2008-01-24 02:06:46 -0800701 if (!sp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 return;
703
704 /*
705 * We have now ensured that nobody can start using ap from now on, but
706 * we have to wait for all existing users to finish.
707 */
708 if (!atomic_dec_and_test(&sp->refcnt))
709 down(&sp->dead_sem);
710
711 unregister_netdev(sp->dev);
712
713 del_timer(&sp->tx_t);
714 del_timer(&sp->resync_t);
715
716 /* Free all 6pack frame buffers. */
717 kfree(sp->rbuff);
718 kfree(sp->xbuff);
719}
720
721/* Perform I/O control on an active 6pack channel. */
722static int sixpack_ioctl(struct tty_struct *tty, struct file *file,
723 unsigned int cmd, unsigned long arg)
724{
725 struct sixpack *sp = sp_get(tty);
Julia Lawall0397a262009-01-09 10:23:09 +0000726 struct net_device *dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 unsigned int tmp, err;
728
729 if (!sp)
730 return -ENXIO;
Julia Lawall0397a262009-01-09 10:23:09 +0000731 dev = sp->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
733 switch(cmd) {
734 case SIOCGIFNAME:
735 err = copy_to_user((void __user *) arg, dev->name,
736 strlen(dev->name) + 1) ? -EFAULT : 0;
737 break;
738
739 case SIOCGIFENCAP:
740 err = put_user(0, (int __user *) arg);
741 break;
742
743 case SIOCSIFENCAP:
744 if (get_user(tmp, (int __user *) arg)) {
745 err = -EFAULT;
746 break;
747 }
748
749 sp->mode = tmp;
750 dev->addr_len = AX25_ADDR_LEN;
751 dev->hard_header_len = AX25_KISS_HEADER_LEN +
752 AX25_MAX_HEADER_LEN + 3;
753 dev->type = ARPHRD_AX25;
754
755 err = 0;
756 break;
757
758 case SIOCSIFHWADDR: {
759 char addr[AX25_ADDR_LEN];
760
761 if (copy_from_user(&addr,
762 (void __user *) arg, AX25_ADDR_LEN)) {
Alan Coxd0127532007-11-07 01:27:34 -0800763 err = -EFAULT;
764 break;
765 }
766
767 netif_tx_lock_bh(dev);
768 memcpy(dev->dev_addr, &addr, AX25_ADDR_LEN);
769 netif_tx_unlock_bh(dev);
770
771 err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 break;
773 }
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 default:
Alan Coxd0127532007-11-07 01:27:34 -0800776 err = tty_mode_ioctl(tty, file, cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 }
778
779 sp_put(sp);
780
781 return err;
782}
783
Arnd Bergmann9646e7c2009-11-06 22:51:16 -0800784#ifdef CONFIG_COMPAT
785static long sixpack_compat_ioctl(struct tty_struct * tty, struct file * file,
786 unsigned int cmd, unsigned long arg)
787{
788 switch (cmd) {
789 case SIOCGIFNAME:
790 case SIOCGIFENCAP:
791 case SIOCSIFENCAP:
792 case SIOCSIFHWADDR:
793 return sixpack_ioctl(tty, file, cmd,
794 (unsigned long)compat_ptr(arg));
795 }
796
797 return -ENOIOCTLCMD;
798}
799#endif
800
Alan Coxa352def2008-07-16 21:53:12 +0100801static struct tty_ldisc_ops sp_ldisc = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 .owner = THIS_MODULE,
803 .magic = TTY_LDISC_MAGIC,
804 .name = "6pack",
805 .open = sixpack_open,
806 .close = sixpack_close,
807 .ioctl = sixpack_ioctl,
Arnd Bergmann9646e7c2009-11-06 22:51:16 -0800808#ifdef CONFIG_COMPAT
809 .compat_ioctl = sixpack_compat_ioctl,
810#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 .receive_buf = sixpack_receive_buf,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812 .write_wakeup = sixpack_write_wakeup,
813};
814
815/* Initialize 6pack control device -- register 6pack line discipline */
816
Hannes Edereb33ae22009-02-14 11:33:21 +0000817static const char msg_banner[] __initdata = KERN_INFO \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 "AX.25: 6pack driver, " SIXPACK_VERSION "\n";
Hannes Edereb33ae22009-02-14 11:33:21 +0000819static const char msg_regfail[] __initdata = KERN_ERR \
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 "6pack: can't register line discipline (err = %d)\n";
821
822static int __init sixpack_init_driver(void)
823{
824 int status;
825
826 printk(msg_banner);
827
828 /* Register the provided line protocol discipline */
829 if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0)
830 printk(msg_regfail, status);
831
832 return status;
833}
834
835static const char msg_unregfail[] __exitdata = KERN_ERR \
836 "6pack: can't unregister line discipline (err = %d)\n";
837
838static void __exit sixpack_exit_driver(void)
839{
840 int ret;
841
Alexey Dobriyan64ccd712005-06-23 00:10:33 -0700842 if ((ret = tty_unregister_ldisc(N_6PACK)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 printk(msg_unregfail, ret);
844}
845
846/* encode an AX.25 packet into 6pack */
847
848static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
849 int length, unsigned char tx_delay)
850{
851 int count = 0;
852 unsigned char checksum = 0, buf[400];
853 int raw_count = 0;
854
855 tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
856 tx_buf_raw[raw_count++] = SIXP_SEOF;
857
858 buf[0] = tx_delay;
859 for (count = 1; count < length; count++)
860 buf[count] = tx_buf[count];
861
862 for (count = 0; count < length; count++)
863 checksum += buf[count];
864 buf[length] = (unsigned char) 0xff - checksum;
865
866 for (count = 0; count <= length; count++) {
867 if ((count % 3) == 0) {
868 tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
869 tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
870 } else if ((count % 3) == 1) {
871 tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
872 tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c);
873 } else {
874 tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
875 tx_buf_raw[raw_count++] = (buf[count] >> 2);
876 }
877 }
878 if ((length % 3) != 2)
879 raw_count++;
880 tx_buf_raw[raw_count++] = SIXP_SEOF;
881 return raw_count;
882}
883
884/* decode 4 sixpack-encoded bytes into 3 data bytes */
885
886static void decode_data(struct sixpack *sp, unsigned char inbyte)
887{
888 unsigned char *buf;
889
890 if (sp->rx_count != 3) {
891 sp->raw_buf[sp->rx_count++] = inbyte;
892
893 return;
894 }
895
896 buf = sp->raw_buf;
897 sp->cooked_buf[sp->rx_count_cooked++] =
898 buf[0] | ((buf[1] << 2) & 0xc0);
899 sp->cooked_buf[sp->rx_count_cooked++] =
900 (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
901 sp->cooked_buf[sp->rx_count_cooked++] =
902 (buf[2] & 0x03) | (inbyte << 2);
903 sp->rx_count = 0;
904}
905
906/* identify and execute a 6pack priority command byte */
907
908static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
909{
910 unsigned char channel;
911 int actual;
912
913 channel = cmd & SIXP_CHN_MASK;
914 if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */
915
916 /* RX and DCD flags can only be set in the same prio command,
917 if the DCD flag has been set without the RX flag in the previous
918 prio command. If DCD has not been set before, something in the
919 transmission has gone wrong. In this case, RX and DCD are
920 cleared in order to prevent the decode_data routine from
921 reading further data that might be corrupt. */
922
923 if (((sp->status & SIXP_DCD_MASK) == 0) &&
924 ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
925 if (sp->status != 1)
926 printk(KERN_DEBUG "6pack: protocol violation\n");
927 else
928 sp->status = 0;
Jean Delvare95f61342006-11-23 11:48:28 -0800929 cmd &= ~SIXP_RX_DCD_MASK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 }
931 sp->status = cmd & SIXP_PRIO_DATA_MASK;
932 } else { /* output watchdog char if idle */
933 if ((sp->status2 != 0) && (sp->duplex == 1)) {
934 sp->led_state = 0x70;
Alan Coxf34d7a52008-04-30 00:54:13 -0700935 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 sp->tx_enable = 1;
Alan Coxf34d7a52008-04-30 00:54:13 -0700937 actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 sp->xleft -= actual;
939 sp->xhead += actual;
940 sp->led_state = 0x60;
941 sp->status2 = 0;
942
943 }
944 }
945
946 /* needed to trigger the TNC watchdog */
Alan Coxf34d7a52008-04-30 00:54:13 -0700947 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
949 /* if the state byte has been received, the TNC is present,
950 so the resync timer can be reset. */
951
952 if (sp->tnc_state == TNC_IN_SYNC) {
953 del_timer(&sp->resync_t);
954 sp->resync_t.data = (unsigned long) sp;
955 sp->resync_t.function = resync_tnc;
956 sp->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT;
957 add_timer(&sp->resync_t);
958 }
959
960 sp->status1 = cmd & SIXP_PRIO_DATA_MASK;
961}
962
963/* identify and execute a standard 6pack command byte */
964
965static void decode_std_command(struct sixpack *sp, unsigned char cmd)
966{
967 unsigned char checksum = 0, rest = 0, channel;
968 short i;
969
970 channel = cmd & SIXP_CHN_MASK;
971 switch (cmd & SIXP_CMD_MASK) { /* normal command */
972 case SIXP_SEOF:
973 if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) {
974 if ((sp->status & SIXP_RX_DCD_MASK) ==
975 SIXP_RX_DCD_MASK) {
976 sp->led_state = 0x68;
Alan Coxf34d7a52008-04-30 00:54:13 -0700977 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700978 }
979 } else {
980 sp->led_state = 0x60;
981 /* fill trailing bytes with zeroes */
Alan Coxf34d7a52008-04-30 00:54:13 -0700982 sp->tty->ops->write(sp->tty, &sp->led_state, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 rest = sp->rx_count;
984 if (rest != 0)
985 for (i = rest; i <= 3; i++)
986 decode_data(sp, 0);
987 if (rest == 2)
988 sp->rx_count_cooked -= 2;
989 else if (rest == 3)
990 sp->rx_count_cooked -= 1;
991 for (i = 0; i < sp->rx_count_cooked; i++)
992 checksum += sp->cooked_buf[i];
993 if (checksum != SIXP_CHKSUM) {
994 printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum);
995 } else {
996 sp->rcount = sp->rx_count_cooked-2;
997 sp_bump(sp, 0);
998 }
999 sp->rx_count_cooked = 0;
1000 }
1001 break;
1002 case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n");
1003 break;
1004 case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n");
1005 break;
1006 case SIXP_RX_BUF_OVL:
1007 printk(KERN_DEBUG "6pack: RX buffer overflow\n");
1008 }
1009}
1010
1011/* decode a 6pack packet */
1012
1013static void
1014sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count)
1015{
1016 unsigned char inbyte;
1017 int count1;
1018
1019 for (count1 = 0; count1 < count; count1++) {
1020 inbyte = pre_rbuff[count1];
1021 if (inbyte == SIXP_FOUND_TNC) {
1022 tnc_set_sync_state(sp, TNC_IN_SYNC);
1023 del_timer(&sp->resync_t);
1024 }
1025 if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
1026 decode_prio_command(sp, inbyte);
1027 else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
1028 decode_std_command(sp, inbyte);
1029 else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
1030 decode_data(sp, inbyte);
1031 }
1032}
1033
1034MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>");
1035MODULE_DESCRIPTION("6pack driver for AX.25");
1036MODULE_LICENSE("GPL");
1037MODULE_ALIAS_LDISC(N_6PACK);
1038
1039module_init(sixpack_init_driver);
1040module_exit(sixpack_exit_driver);