109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * 6pack.c This module implements the 6pack protocol for kernel-based
41da177e4SLinus Torvalds * devices like TTY. It interfaces between a raw TTY and the
51da177e4SLinus Torvalds * kernel's AX.25 protocol layers.
61da177e4SLinus Torvalds *
767332595SRalf Baechle * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de>
81da177e4SLinus Torvalds * Ralf Baechle DL5RB <ralf@linux-mips.org>
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * Laurence Culhane, <loz@holmes.demon.co.uk>
131da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
141da177e4SLinus Torvalds */
151da177e4SLinus Torvalds
161da177e4SLinus Torvalds #include <linux/module.h>
177c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
181da177e4SLinus Torvalds #include <linux/bitops.h>
191da177e4SLinus Torvalds #include <linux/string.h>
201da177e4SLinus Torvalds #include <linux/mm.h>
211da177e4SLinus Torvalds #include <linux/interrupt.h>
221da177e4SLinus Torvalds #include <linux/in.h>
231da177e4SLinus Torvalds #include <linux/tty.h>
241da177e4SLinus Torvalds #include <linux/errno.h>
251da177e4SLinus Torvalds #include <linux/netdevice.h>
261da177e4SLinus Torvalds #include <linux/timer.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
281da177e4SLinus Torvalds #include <net/ax25.h>
291da177e4SLinus Torvalds #include <linux/etherdevice.h>
301da177e4SLinus Torvalds #include <linux/skbuff.h>
311da177e4SLinus Torvalds #include <linux/rtnetlink.h>
321da177e4SLinus Torvalds #include <linux/spinlock.h>
331da177e4SLinus Torvalds #include <linux/if_arp.h>
341da177e4SLinus Torvalds #include <linux/init.h>
351da177e4SLinus Torvalds #include <linux/ip.h>
361da177e4SLinus Torvalds #include <linux/tcp.h>
376188e10dSMatthew Wilcox #include <linux/semaphore.h>
38956cc1e7SElena Reshetova #include <linux/refcount.h>
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds #define SIXPACK_VERSION "Revision: 0.3.0"
411da177e4SLinus Torvalds
421da177e4SLinus Torvalds /* sixpack priority commands */
431da177e4SLinus Torvalds #define SIXP_SEOF 0x40 /* start and end of a 6pack frame */
441da177e4SLinus Torvalds #define SIXP_TX_URUN 0x48 /* transmit overrun */
451da177e4SLinus Torvalds #define SIXP_RX_ORUN 0x50 /* receive overrun */
461da177e4SLinus Torvalds #define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds #define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds /* masks to get certain bits out of the status bytes sent by the TNC */
511da177e4SLinus Torvalds
521da177e4SLinus Torvalds #define SIXP_CMD_MASK 0xC0
531da177e4SLinus Torvalds #define SIXP_CHN_MASK 0x07
541da177e4SLinus Torvalds #define SIXP_PRIO_CMD_MASK 0x80
551da177e4SLinus Torvalds #define SIXP_STD_CMD_MASK 0x40
561da177e4SLinus Torvalds #define SIXP_PRIO_DATA_MASK 0x38
571da177e4SLinus Torvalds #define SIXP_TX_MASK 0x20
581da177e4SLinus Torvalds #define SIXP_RX_MASK 0x10
591da177e4SLinus Torvalds #define SIXP_RX_DCD_MASK 0x18
601da177e4SLinus Torvalds #define SIXP_LEDS_ON 0x78
611da177e4SLinus Torvalds #define SIXP_LEDS_OFF 0x60
621da177e4SLinus Torvalds #define SIXP_CON 0x08
631da177e4SLinus Torvalds #define SIXP_STA 0x10
641da177e4SLinus Torvalds
651da177e4SLinus Torvalds #define SIXP_FOUND_TNC 0xe9
661da177e4SLinus Torvalds #define SIXP_CON_ON 0x68
671da177e4SLinus Torvalds #define SIXP_DCD_MASK 0x08
681da177e4SLinus Torvalds #define SIXP_DAMA_OFF 0
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds /* default level 2 parameters */
713c0d2a46SGuenter Roeck #define SIXP_TXDELAY 25 /* 250 ms */
721da177e4SLinus Torvalds #define SIXP_PERSIST 50 /* in 256ths */
733c0d2a46SGuenter Roeck #define SIXP_SLOTTIME 10 /* 100 ms */
741da177e4SLinus Torvalds #define SIXP_INIT_RESYNC_TIMEOUT (3*HZ/2) /* in 1 s */
751da177e4SLinus Torvalds #define SIXP_RESYNC_TIMEOUT 5*HZ /* in 1 s */
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds /* 6pack configuration. */
781da177e4SLinus Torvalds #define SIXP_NRUNIT 31 /* MAX number of 6pack channels */
791da177e4SLinus Torvalds #define SIXP_MTU 256 /* Default MTU */
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds enum sixpack_flags {
821da177e4SLinus Torvalds SIXPF_ERROR, /* Parity, etc. error */
831da177e4SLinus Torvalds };
841da177e4SLinus Torvalds
851da177e4SLinus Torvalds struct sixpack {
861da177e4SLinus Torvalds /* Various fields. */
871da177e4SLinus Torvalds struct tty_struct *tty; /* ptr to TTY structure */
881da177e4SLinus Torvalds struct net_device *dev; /* easy for intr handling */
891da177e4SLinus Torvalds
901da177e4SLinus Torvalds /* These are pointers to the malloc()ed frame buffers. */
911da177e4SLinus Torvalds unsigned char *rbuff; /* receiver buffer */
921da177e4SLinus Torvalds int rcount; /* received chars counter */
931da177e4SLinus Torvalds unsigned char *xbuff; /* transmitter buffer */
941da177e4SLinus Torvalds unsigned char *xhead; /* next byte to XMIT */
951da177e4SLinus Torvalds int xleft; /* bytes left in XMIT queue */
961da177e4SLinus Torvalds
971da177e4SLinus Torvalds unsigned char raw_buf[4];
981da177e4SLinus Torvalds unsigned char cooked_buf[400];
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds unsigned int rx_count;
1011da177e4SLinus Torvalds unsigned int rx_count_cooked;
1022b04495eSXu Jia spinlock_t rxlock;
1031da177e4SLinus Torvalds
1041da177e4SLinus Torvalds int mtu; /* Our mtu (to spot changes!) */
1051da177e4SLinus Torvalds int buffsize; /* Max buffers sizes */
1061da177e4SLinus Torvalds
1071da177e4SLinus Torvalds unsigned long flags; /* Flag values/ mode etc */
1081da177e4SLinus Torvalds unsigned char mode; /* 6pack mode */
1091da177e4SLinus Torvalds
1101da177e4SLinus Torvalds /* 6pack stuff */
1111da177e4SLinus Torvalds unsigned char tx_delay;
1121da177e4SLinus Torvalds unsigned char persistence;
1131da177e4SLinus Torvalds unsigned char slottime;
1141da177e4SLinus Torvalds unsigned char duplex;
1151da177e4SLinus Torvalds unsigned char led_state;
1161da177e4SLinus Torvalds unsigned char status;
1171da177e4SLinus Torvalds unsigned char status1;
1181da177e4SLinus Torvalds unsigned char status2;
1191da177e4SLinus Torvalds unsigned char tx_enable;
1201da177e4SLinus Torvalds unsigned char tnc_state;
1211da177e4SLinus Torvalds
1221da177e4SLinus Torvalds struct timer_list tx_t;
1231da177e4SLinus Torvalds struct timer_list resync_t;
124956cc1e7SElena Reshetova refcount_t refcnt;
125c2c79a32SArnd Bergmann struct completion dead;
1261da177e4SLinus Torvalds spinlock_t lock;
1271da177e4SLinus Torvalds };
1281da177e4SLinus Torvalds
1291da177e4SLinus Torvalds #define AX25_6PACK_HEADER_LEN 0
1301da177e4SLinus Torvalds
131ad979896SAlan Cox static void sixpack_decode(struct sixpack *, const unsigned char[], int);
1321da177e4SLinus Torvalds static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
1331da177e4SLinus Torvalds
1341da177e4SLinus Torvalds /*
135c0438174SRalf Baechle DL5RB * Perform the persistence/slottime algorithm for CSMA access. If the
1361da177e4SLinus Torvalds * persistence check was successful, write the data to the serial driver.
1371da177e4SLinus Torvalds * Note that in case of DAMA operation, the data is not sent here.
1381da177e4SLinus Torvalds */
1391da177e4SLinus Torvalds
sp_xmit_on_air(struct timer_list * t)1408e763de0SKees Cook static void sp_xmit_on_air(struct timer_list *t)
1411da177e4SLinus Torvalds {
1428e763de0SKees Cook struct sixpack *sp = from_timer(sp, t, tx_t);
143c0438174SRalf Baechle DL5RB int actual, when = sp->slottime;
1441da177e4SLinus Torvalds static unsigned char random;
1451da177e4SLinus Torvalds
1461da177e4SLinus Torvalds random = random * 17 + 41;
1471da177e4SLinus Torvalds
1481da177e4SLinus Torvalds if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistence)) {
1491da177e4SLinus Torvalds sp->led_state = 0x70;
150f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
1511da177e4SLinus Torvalds sp->tx_enable = 1;
152f34d7a5bSAlan Cox actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2);
1531da177e4SLinus Torvalds sp->xleft -= actual;
1541da177e4SLinus Torvalds sp->xhead += actual;
1551da177e4SLinus Torvalds sp->led_state = 0x60;
156f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
1571da177e4SLinus Torvalds sp->status2 = 0;
1581da177e4SLinus Torvalds } else
159c0438174SRalf Baechle DL5RB mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100);
1601da177e4SLinus Torvalds }
1611da177e4SLinus Torvalds
1621da177e4SLinus Torvalds /* ----> 6pack timer interrupt handler and friends. <---- */
1631da177e4SLinus Torvalds
1641da177e4SLinus Torvalds /* Encapsulate one AX.25 frame and stuff into a TTY queue. */
sp_encaps(struct sixpack * sp,unsigned char * icp,int len)1651da177e4SLinus Torvalds static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len)
1661da177e4SLinus Torvalds {
1671da177e4SLinus Torvalds unsigned char *msg, *p = icp;
1681da177e4SLinus Torvalds int actual, count;
1691da177e4SLinus Torvalds
1701da177e4SLinus Torvalds if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */
1711da177e4SLinus Torvalds msg = "oversized transmit packet!";
1721da177e4SLinus Torvalds goto out_drop;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds
1751da177e4SLinus Torvalds if (p[0] > 5) {
1761da177e4SLinus Torvalds msg = "invalid KISS command";
1771da177e4SLinus Torvalds goto out_drop;
1781da177e4SLinus Torvalds }
1791da177e4SLinus Torvalds
1801da177e4SLinus Torvalds if ((p[0] != 0) && (len > 2)) {
1811da177e4SLinus Torvalds msg = "KISS control packet too long";
1821da177e4SLinus Torvalds goto out_drop;
1831da177e4SLinus Torvalds }
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds if ((p[0] == 0) && (len < 15)) {
1861da177e4SLinus Torvalds msg = "bad AX.25 packet to transmit";
1871da177e4SLinus Torvalds goto out_drop;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds
1901da177e4SLinus Torvalds count = encode_sixpack(p, sp->xbuff, len, sp->tx_delay);
1911da177e4SLinus Torvalds set_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
1921da177e4SLinus Torvalds
1931da177e4SLinus Torvalds switch (p[0]) {
1941da177e4SLinus Torvalds case 1: sp->tx_delay = p[1];
1951da177e4SLinus Torvalds return;
1961da177e4SLinus Torvalds case 2: sp->persistence = p[1];
1971da177e4SLinus Torvalds return;
1981da177e4SLinus Torvalds case 3: sp->slottime = p[1];
1991da177e4SLinus Torvalds return;
2001da177e4SLinus Torvalds case 4: /* ignored */
2011da177e4SLinus Torvalds return;
2021da177e4SLinus Torvalds case 5: sp->duplex = p[1];
2031da177e4SLinus Torvalds return;
2041da177e4SLinus Torvalds }
2051da177e4SLinus Torvalds
2061da177e4SLinus Torvalds if (p[0] != 0)
2071da177e4SLinus Torvalds return;
2081da177e4SLinus Torvalds
2091da177e4SLinus Torvalds /*
2101da177e4SLinus Torvalds * In case of fullduplex or DAMA operation, we don't take care about the
2111da177e4SLinus Torvalds * state of the DCD or of any timers, as the determination of the
2121da177e4SLinus Torvalds * correct time to send is the job of the AX.25 layer. We send
2131da177e4SLinus Torvalds * immediately after data has arrived.
2141da177e4SLinus Torvalds */
2151da177e4SLinus Torvalds if (sp->duplex == 1) {
2161da177e4SLinus Torvalds sp->led_state = 0x70;
217f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
2181da177e4SLinus Torvalds sp->tx_enable = 1;
219f34d7a5bSAlan Cox actual = sp->tty->ops->write(sp->tty, sp->xbuff, count);
2201da177e4SLinus Torvalds sp->xleft = count - actual;
2211da177e4SLinus Torvalds sp->xhead = sp->xbuff + actual;
2221da177e4SLinus Torvalds sp->led_state = 0x60;
223f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
2241da177e4SLinus Torvalds } else {
2251da177e4SLinus Torvalds sp->xleft = count;
2261da177e4SLinus Torvalds sp->xhead = sp->xbuff;
2271da177e4SLinus Torvalds sp->status2 = count;
2288e763de0SKees Cook sp_xmit_on_air(&sp->tx_t);
2291da177e4SLinus Torvalds }
2301da177e4SLinus Torvalds
2311da177e4SLinus Torvalds return;
2321da177e4SLinus Torvalds
2331da177e4SLinus Torvalds out_drop:
234de0561c4SPaulius Zaleckas sp->dev->stats.tx_dropped++;
2351da177e4SLinus Torvalds netif_start_queue(sp->dev);
2361da177e4SLinus Torvalds if (net_ratelimit())
2371da177e4SLinus Torvalds printk(KERN_DEBUG "%s: %s - dropped.\n", sp->dev->name, msg);
2381da177e4SLinus Torvalds }
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds /* Encapsulate an IP datagram and kick it into a TTY queue. */
2411da177e4SLinus Torvalds
sp_xmit(struct sk_buff * skb,struct net_device * dev)24236e4d64aSStephen Hemminger static netdev_tx_t sp_xmit(struct sk_buff *skb, struct net_device *dev)
2431da177e4SLinus Torvalds {
2441da177e4SLinus Torvalds struct sixpack *sp = netdev_priv(dev);
2451da177e4SLinus Torvalds
2461d5da757SEric W. Biederman if (skb->protocol == htons(ETH_P_IP))
2471d5da757SEric W. Biederman return ax25_ip_xmit(skb);
2481d5da757SEric W. Biederman
2491da177e4SLinus Torvalds spin_lock_bh(&sp->lock);
2501da177e4SLinus Torvalds /* We were not busy, so we are now... :-) */
2511da177e4SLinus Torvalds netif_stop_queue(dev);
252de0561c4SPaulius Zaleckas dev->stats.tx_bytes += skb->len;
2531da177e4SLinus Torvalds sp_encaps(sp, skb->data, skb->len);
2541da177e4SLinus Torvalds spin_unlock_bh(&sp->lock);
2551da177e4SLinus Torvalds
2561da177e4SLinus Torvalds dev_kfree_skb(skb);
2571da177e4SLinus Torvalds
2586ed10654SPatrick McHardy return NETDEV_TX_OK;
2591da177e4SLinus Torvalds }
2601da177e4SLinus Torvalds
sp_open_dev(struct net_device * dev)2611da177e4SLinus Torvalds static int sp_open_dev(struct net_device *dev)
2621da177e4SLinus Torvalds {
2631da177e4SLinus Torvalds struct sixpack *sp = netdev_priv(dev);
2641da177e4SLinus Torvalds
2651da177e4SLinus Torvalds if (sp->tty == NULL)
2661da177e4SLinus Torvalds return -ENODEV;
2671da177e4SLinus Torvalds return 0;
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds
2701da177e4SLinus Torvalds /* Close the low-level part of the 6pack channel. */
sp_close(struct net_device * dev)2711da177e4SLinus Torvalds static int sp_close(struct net_device *dev)
2721da177e4SLinus Torvalds {
2731da177e4SLinus Torvalds struct sixpack *sp = netdev_priv(dev);
2741da177e4SLinus Torvalds
2751da177e4SLinus Torvalds spin_lock_bh(&sp->lock);
2761da177e4SLinus Torvalds if (sp->tty) {
2771da177e4SLinus Torvalds /* TTY discipline is running. */
2781da177e4SLinus Torvalds clear_bit(TTY_DO_WRITE_WAKEUP, &sp->tty->flags);
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds netif_stop_queue(dev);
2811da177e4SLinus Torvalds spin_unlock_bh(&sp->lock);
2821da177e4SLinus Torvalds
2831da177e4SLinus Torvalds return 0;
2841da177e4SLinus Torvalds }
2851da177e4SLinus Torvalds
sp_set_mac_address(struct net_device * dev,void * addr)2861da177e4SLinus Torvalds static int sp_set_mac_address(struct net_device *dev, void *addr)
2871da177e4SLinus Torvalds {
2881da177e4SLinus Torvalds struct sockaddr_ax25 *sa = addr;
2891da177e4SLinus Torvalds
290932ff279SHerbert Xu netif_tx_lock_bh(dev);
291e308a5d8SDavid S. Miller netif_addr_lock(dev);
29220c3d9e4SJakub Kicinski __dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN);
293e308a5d8SDavid S. Miller netif_addr_unlock(dev);
294932ff279SHerbert Xu netif_tx_unlock_bh(dev);
2951da177e4SLinus Torvalds
2961da177e4SLinus Torvalds return 0;
2971da177e4SLinus Torvalds }
2981da177e4SLinus Torvalds
299b3672a73SStephen Hemminger static const struct net_device_ops sp_netdev_ops = {
300b3672a73SStephen Hemminger .ndo_open = sp_open_dev,
301b3672a73SStephen Hemminger .ndo_stop = sp_close,
302b3672a73SStephen Hemminger .ndo_start_xmit = sp_xmit,
303b3672a73SStephen Hemminger .ndo_set_mac_address = sp_set_mac_address,
304b3672a73SStephen Hemminger };
305b3672a73SStephen Hemminger
sp_setup(struct net_device * dev)3061da177e4SLinus Torvalds static void sp_setup(struct net_device *dev)
3071da177e4SLinus Torvalds {
3081da177e4SLinus Torvalds /* Finish setting up the DEVICE info. */
309b3672a73SStephen Hemminger dev->netdev_ops = &sp_netdev_ops;
310b3672a73SStephen Hemminger dev->mtu = SIXP_MTU;
3111da177e4SLinus Torvalds dev->hard_header_len = AX25_MAX_HEADER_LEN;
312204d2dcaSEric W. Biederman dev->header_ops = &ax25_header_ops;
3133b04dddeSStephen Hemminger
3141da177e4SLinus Torvalds dev->addr_len = AX25_ADDR_LEN;
3151da177e4SLinus Torvalds dev->type = ARPHRD_AX25;
3161da177e4SLinus Torvalds dev->tx_queue_len = 10;
3171da177e4SLinus Torvalds
3181da177e4SLinus Torvalds /* Only activated in AX.25 mode */
31915b1c0e8SRalf Baechle memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
32020c3d9e4SJakub Kicinski dev_addr_set(dev, (u8 *)&ax25_defaddr);
3211da177e4SLinus Torvalds
3221da177e4SLinus Torvalds dev->flags = 0;
3231da177e4SLinus Torvalds }
3241da177e4SLinus Torvalds
3251da177e4SLinus Torvalds /* Send one completely decapsulated IP datagram to the IP layer. */
3261da177e4SLinus Torvalds
3271da177e4SLinus Torvalds /*
3281da177e4SLinus Torvalds * This is the routine that sends the received data to the kernel AX.25.
3291da177e4SLinus Torvalds * 'cmd' is the KISS command. For AX.25 data, it is zero.
3301da177e4SLinus Torvalds */
3311da177e4SLinus Torvalds
sp_bump(struct sixpack * sp,char cmd)3321da177e4SLinus Torvalds static void sp_bump(struct sixpack *sp, char cmd)
3331da177e4SLinus Torvalds {
3341da177e4SLinus Torvalds struct sk_buff *skb;
3351da177e4SLinus Torvalds int count;
3361da177e4SLinus Torvalds unsigned char *ptr;
3371da177e4SLinus Torvalds
3381da177e4SLinus Torvalds count = sp->rcount + 1;
3391da177e4SLinus Torvalds
340de0561c4SPaulius Zaleckas sp->dev->stats.rx_bytes += count;
3411da177e4SLinus Torvalds
342b82573fdSChristophe JAILLET if ((skb = dev_alloc_skb(count + 1)) == NULL)
3431da177e4SLinus Torvalds goto out_mem;
3441da177e4SLinus Torvalds
345b82573fdSChristophe JAILLET ptr = skb_put(skb, count + 1);
3461da177e4SLinus Torvalds *ptr++ = cmd; /* KISS command */
3471da177e4SLinus Torvalds
3481da177e4SLinus Torvalds memcpy(ptr, sp->cooked_buf + 1, count);
34956cb5156SArnaldo Carvalho de Melo skb->protocol = ax25_type_trans(skb, sp->dev);
3501da177e4SLinus Torvalds netif_rx(skb);
351de0561c4SPaulius Zaleckas sp->dev->stats.rx_packets++;
3521da177e4SLinus Torvalds
3531da177e4SLinus Torvalds return;
3541da177e4SLinus Torvalds
3551da177e4SLinus Torvalds out_mem:
356de0561c4SPaulius Zaleckas sp->dev->stats.rx_dropped++;
3571da177e4SLinus Torvalds }
3581da177e4SLinus Torvalds
3591da177e4SLinus Torvalds
3601da177e4SLinus Torvalds /* ----------------------------------------------------------------------- */
3611da177e4SLinus Torvalds
3621da177e4SLinus Torvalds /*
3631da177e4SLinus Torvalds * We have a potential race on dereferencing tty->disc_data, because the tty
3641da177e4SLinus Torvalds * layer provides no locking at all - thus one cpu could be running
3651da177e4SLinus Torvalds * sixpack_receive_buf while another calls sixpack_close, which zeroes
3661da177e4SLinus Torvalds * tty->disc_data and frees the memory that sixpack_receive_buf is using. The
3671da177e4SLinus Torvalds * best way to fix this is to use a rwlock in the tty struct, but for now we
3681da177e4SLinus Torvalds * use a single global rwlock for all ttys in ppp line discipline.
3691da177e4SLinus Torvalds */
3701da177e4SLinus Torvalds static DEFINE_RWLOCK(disc_data_lock);
3711da177e4SLinus Torvalds
sp_get(struct tty_struct * tty)3721da177e4SLinus Torvalds static struct sixpack *sp_get(struct tty_struct *tty)
3731da177e4SLinus Torvalds {
3741da177e4SLinus Torvalds struct sixpack *sp;
3751da177e4SLinus Torvalds
3761da177e4SLinus Torvalds read_lock(&disc_data_lock);
3771da177e4SLinus Torvalds sp = tty->disc_data;
3781da177e4SLinus Torvalds if (sp)
379956cc1e7SElena Reshetova refcount_inc(&sp->refcnt);
3801da177e4SLinus Torvalds read_unlock(&disc_data_lock);
3811da177e4SLinus Torvalds
3821da177e4SLinus Torvalds return sp;
3831da177e4SLinus Torvalds }
3841da177e4SLinus Torvalds
sp_put(struct sixpack * sp)3851da177e4SLinus Torvalds static void sp_put(struct sixpack *sp)
3861da177e4SLinus Torvalds {
387956cc1e7SElena Reshetova if (refcount_dec_and_test(&sp->refcnt))
388c2c79a32SArnd Bergmann complete(&sp->dead);
3891da177e4SLinus Torvalds }
3901da177e4SLinus Torvalds
3911da177e4SLinus Torvalds /*
3921da177e4SLinus Torvalds * Called by the TTY driver when there's room for more data. If we have
3931da177e4SLinus Torvalds * more packets to send, we send them here.
3941da177e4SLinus Torvalds */
sixpack_write_wakeup(struct tty_struct * tty)3951da177e4SLinus Torvalds static void sixpack_write_wakeup(struct tty_struct *tty)
3961da177e4SLinus Torvalds {
3971da177e4SLinus Torvalds struct sixpack *sp = sp_get(tty);
3981da177e4SLinus Torvalds int actual;
3991da177e4SLinus Torvalds
4001da177e4SLinus Torvalds if (!sp)
4011da177e4SLinus Torvalds return;
4021da177e4SLinus Torvalds if (sp->xleft <= 0) {
4031da177e4SLinus Torvalds /* Now serial buffer is almost free & we can start
4041da177e4SLinus Torvalds * transmission of another packet */
405de0561c4SPaulius Zaleckas sp->dev->stats.tx_packets++;
4061da177e4SLinus Torvalds clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
4071da177e4SLinus Torvalds sp->tx_enable = 0;
4081da177e4SLinus Torvalds netif_wake_queue(sp->dev);
4091da177e4SLinus Torvalds goto out;
4101da177e4SLinus Torvalds }
4111da177e4SLinus Torvalds
4121da177e4SLinus Torvalds if (sp->tx_enable) {
413f34d7a5bSAlan Cox actual = tty->ops->write(tty, sp->xhead, sp->xleft);
4141da177e4SLinus Torvalds sp->xleft -= actual;
4151da177e4SLinus Torvalds sp->xhead += actual;
4161da177e4SLinus Torvalds }
4171da177e4SLinus Torvalds
4181da177e4SLinus Torvalds out:
4191da177e4SLinus Torvalds sp_put(sp);
4201da177e4SLinus Torvalds }
4211da177e4SLinus Torvalds
4221da177e4SLinus Torvalds /* ----------------------------------------------------------------------- */
4231da177e4SLinus Torvalds
4241da177e4SLinus Torvalds /*
4251da177e4SLinus Torvalds * Handle the 'receiver data ready' interrupt.
426ad979896SAlan Cox * This function is called by the tty module in the kernel when
4271da177e4SLinus Torvalds * a block of 6pack data has been received, which can now be decapsulated
4281da177e4SLinus Torvalds * and sent on to some IP layer for further processing.
4291da177e4SLinus Torvalds */
sixpack_receive_buf(struct tty_struct * tty,const u8 * cp,const u8 * fp,size_t count)430a8d9cd23SJiri Slaby (SUSE) static void sixpack_receive_buf(struct tty_struct *tty, const u8 *cp,
431*892bc209SJiri Slaby (SUSE) const u8 *fp, size_t count)
4321da177e4SLinus Torvalds {
4331da177e4SLinus Torvalds struct sixpack *sp;
4341da177e4SLinus Torvalds int count1;
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds if (!count)
43755db4c64SLinus Torvalds return;
4381da177e4SLinus Torvalds
4391da177e4SLinus Torvalds sp = sp_get(tty);
4401da177e4SLinus Torvalds if (!sp)
44155db4c64SLinus Torvalds return;
4421da177e4SLinus Torvalds
4431da177e4SLinus Torvalds /* Read the characters out of the buffer */
4441da177e4SLinus Torvalds count1 = count;
4451da177e4SLinus Torvalds while (count) {
4461da177e4SLinus Torvalds count--;
4471da177e4SLinus Torvalds if (fp && *fp++) {
4481da177e4SLinus Torvalds if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
449de0561c4SPaulius Zaleckas sp->dev->stats.rx_errors++;
4501da177e4SLinus Torvalds continue;
4511da177e4SLinus Torvalds }
4521da177e4SLinus Torvalds }
453ad979896SAlan Cox sixpack_decode(sp, cp, count1);
4541da177e4SLinus Torvalds
4551da177e4SLinus Torvalds sp_put(sp);
45639c2e60fSAlan Cox tty_unthrottle(tty);
4571da177e4SLinus Torvalds }
4581da177e4SLinus Torvalds
4591da177e4SLinus Torvalds /*
4601da177e4SLinus Torvalds * Try to resync the TNC. Called by the resync timer defined in
4611da177e4SLinus Torvalds * decode_prio_command
4621da177e4SLinus Torvalds */
4631da177e4SLinus Torvalds
4641da177e4SLinus Torvalds #define TNC_UNINITIALIZED 0
4651da177e4SLinus Torvalds #define TNC_UNSYNC_STARTUP 1
4661da177e4SLinus Torvalds #define TNC_UNSYNCED 2
4671da177e4SLinus Torvalds #define TNC_IN_SYNC 3
4681da177e4SLinus Torvalds
__tnc_set_sync_state(struct sixpack * sp,int new_tnc_state)4691da177e4SLinus Torvalds static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
4701da177e4SLinus Torvalds {
4711da177e4SLinus Torvalds char *msg;
4721da177e4SLinus Torvalds
4731da177e4SLinus Torvalds switch (new_tnc_state) {
4741da177e4SLinus Torvalds default: /* gcc oh piece-o-crap ... */
4751da177e4SLinus Torvalds case TNC_UNSYNC_STARTUP:
4761da177e4SLinus Torvalds msg = "Synchronizing with TNC";
4771da177e4SLinus Torvalds break;
4781da177e4SLinus Torvalds case TNC_UNSYNCED:
4791da177e4SLinus Torvalds msg = "Lost synchronization with TNC\n";
4801da177e4SLinus Torvalds break;
4811da177e4SLinus Torvalds case TNC_IN_SYNC:
4821da177e4SLinus Torvalds msg = "Found TNC";
4831da177e4SLinus Torvalds break;
4841da177e4SLinus Torvalds }
4851da177e4SLinus Torvalds
4861da177e4SLinus Torvalds sp->tnc_state = new_tnc_state;
4871da177e4SLinus Torvalds printk(KERN_INFO "%s: %s\n", sp->dev->name, msg);
4881da177e4SLinus Torvalds }
4891da177e4SLinus Torvalds
tnc_set_sync_state(struct sixpack * sp,int new_tnc_state)4901da177e4SLinus Torvalds static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
4911da177e4SLinus Torvalds {
4921da177e4SLinus Torvalds int old_tnc_state = sp->tnc_state;
4931da177e4SLinus Torvalds
4941da177e4SLinus Torvalds if (old_tnc_state != new_tnc_state)
4951da177e4SLinus Torvalds __tnc_set_sync_state(sp, new_tnc_state);
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds
resync_tnc(struct timer_list * t)4988e763de0SKees Cook static void resync_tnc(struct timer_list *t)
4991da177e4SLinus Torvalds {
5008e763de0SKees Cook struct sixpack *sp = from_timer(sp, t, resync_t);
5011da177e4SLinus Torvalds static char resync_cmd = 0xe8;
5021da177e4SLinus Torvalds
5031da177e4SLinus Torvalds /* clear any data that might have been received */
5041da177e4SLinus Torvalds
5051da177e4SLinus Torvalds sp->rx_count = 0;
5061da177e4SLinus Torvalds sp->rx_count_cooked = 0;
5071da177e4SLinus Torvalds
5081da177e4SLinus Torvalds /* reset state machine */
5091da177e4SLinus Torvalds
5101da177e4SLinus Torvalds sp->status = 1;
5111da177e4SLinus Torvalds sp->status1 = 1;
5121da177e4SLinus Torvalds sp->status2 = 0;
5131da177e4SLinus Torvalds
5141da177e4SLinus Torvalds /* resync the TNC */
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds sp->led_state = 0x60;
517f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
518f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &resync_cmd, 1);
5191da177e4SLinus Torvalds
5201da177e4SLinus Torvalds
5211da177e4SLinus Torvalds /* Start resync timer again -- the TNC might be still absent */
522202700e3SEric Dumazet mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT);
5231da177e4SLinus Torvalds }
5241da177e4SLinus Torvalds
tnc_init(struct sixpack * sp)5251da177e4SLinus Torvalds static inline int tnc_init(struct sixpack *sp)
5261da177e4SLinus Torvalds {
5271da177e4SLinus Torvalds unsigned char inbyte = 0xe8;
5281da177e4SLinus Torvalds
5291da177e4SLinus Torvalds tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP);
5301da177e4SLinus Torvalds
531f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &inbyte, 1);
5321da177e4SLinus Torvalds
533202700e3SEric Dumazet mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT);
5341da177e4SLinus Torvalds
5351da177e4SLinus Torvalds return 0;
5361da177e4SLinus Torvalds }
5371da177e4SLinus Torvalds
5381da177e4SLinus Torvalds /*
5391da177e4SLinus Torvalds * Open the high-level part of the 6pack channel.
5401da177e4SLinus Torvalds * This function is called by the TTY module when the
5411da177e4SLinus Torvalds * 6pack line discipline is called for. Because we are
5421da177e4SLinus Torvalds * sure the tty line exists, we only have to link it to
5431da177e4SLinus Torvalds * a free 6pcack channel...
5441da177e4SLinus Torvalds */
sixpack_open(struct tty_struct * tty)5451da177e4SLinus Torvalds static int sixpack_open(struct tty_struct *tty)
5461da177e4SLinus Torvalds {
5471da177e4SLinus Torvalds char *rbuff = NULL, *xbuff = NULL;
5481da177e4SLinus Torvalds struct net_device *dev;
5491da177e4SLinus Torvalds struct sixpack *sp;
5501da177e4SLinus Torvalds unsigned long len;
5511da177e4SLinus Torvalds int err = 0;
5521da177e4SLinus Torvalds
5531da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN))
5541da177e4SLinus Torvalds return -EPERM;
555f34d7a5bSAlan Cox if (tty->ops->write == NULL)
556f34d7a5bSAlan Cox return -EOPNOTSUPP;
5571da177e4SLinus Torvalds
558c835a677STom Gundersen dev = alloc_netdev(sizeof(struct sixpack), "sp%d", NET_NAME_UNKNOWN,
559c835a677STom Gundersen sp_setup);
5601da177e4SLinus Torvalds if (!dev) {
5611da177e4SLinus Torvalds err = -ENOMEM;
5621da177e4SLinus Torvalds goto out;
5631da177e4SLinus Torvalds }
5641da177e4SLinus Torvalds
5651da177e4SLinus Torvalds sp = netdev_priv(dev);
5661da177e4SLinus Torvalds sp->dev = dev;
5671da177e4SLinus Torvalds
5681da177e4SLinus Torvalds spin_lock_init(&sp->lock);
5692b04495eSXu Jia spin_lock_init(&sp->rxlock);
570956cc1e7SElena Reshetova refcount_set(&sp->refcnt, 1);
571c2c79a32SArnd Bergmann init_completion(&sp->dead);
5721da177e4SLinus Torvalds
5731da177e4SLinus Torvalds /* !!! length of the buffers. MTU is IP MTU, not PACLEN! */
5741da177e4SLinus Torvalds
5751da177e4SLinus Torvalds len = dev->mtu * 2;
5761da177e4SLinus Torvalds
5771da177e4SLinus Torvalds rbuff = kmalloc(len + 4, GFP_KERNEL);
5781da177e4SLinus Torvalds xbuff = kmalloc(len + 4, GFP_KERNEL);
5791da177e4SLinus Torvalds
5801da177e4SLinus Torvalds if (rbuff == NULL || xbuff == NULL) {
5811da177e4SLinus Torvalds err = -ENOBUFS;
5821da177e4SLinus Torvalds goto out_free;
5831da177e4SLinus Torvalds }
5841da177e4SLinus Torvalds
5851da177e4SLinus Torvalds spin_lock_bh(&sp->lock);
5861da177e4SLinus Torvalds
5871da177e4SLinus Torvalds sp->tty = tty;
5881da177e4SLinus Torvalds
5891da177e4SLinus Torvalds sp->rbuff = rbuff;
5901da177e4SLinus Torvalds sp->xbuff = xbuff;
5911da177e4SLinus Torvalds
5921da177e4SLinus Torvalds sp->mtu = AX25_MTU + 73;
5931da177e4SLinus Torvalds sp->buffsize = len;
5941da177e4SLinus Torvalds sp->rcount = 0;
5951da177e4SLinus Torvalds sp->rx_count = 0;
5961da177e4SLinus Torvalds sp->rx_count_cooked = 0;
5971da177e4SLinus Torvalds sp->xleft = 0;
5981da177e4SLinus Torvalds
5991da177e4SLinus Torvalds sp->flags = 0; /* Clear ESCAPE & ERROR flags */
6001da177e4SLinus Torvalds
6011da177e4SLinus Torvalds sp->duplex = 0;
6021da177e4SLinus Torvalds sp->tx_delay = SIXP_TXDELAY;
6031da177e4SLinus Torvalds sp->persistence = SIXP_PERSIST;
6041da177e4SLinus Torvalds sp->slottime = SIXP_SLOTTIME;
6051da177e4SLinus Torvalds sp->led_state = 0x60;
6061da177e4SLinus Torvalds sp->status = 1;
6071da177e4SLinus Torvalds sp->status1 = 1;
6081da177e4SLinus Torvalds sp->status2 = 0;
6091da177e4SLinus Torvalds sp->tx_enable = 0;
6101da177e4SLinus Torvalds
6111da177e4SLinus Torvalds netif_start_queue(dev);
6121da177e4SLinus Torvalds
6138e763de0SKees Cook timer_setup(&sp->tx_t, sp_xmit_on_air, 0);
61484a2ea1cSRalf Baechle
6158e763de0SKees Cook timer_setup(&sp->resync_t, resync_tnc, 0);
6161da177e4SLinus Torvalds
6171da177e4SLinus Torvalds spin_unlock_bh(&sp->lock);
6181da177e4SLinus Torvalds
6191da177e4SLinus Torvalds /* Done. We have linked the TTY line to a channel. */
6201da177e4SLinus Torvalds tty->disc_data = sp;
62133f0f88fSAlan Cox tty->receive_room = 65536;
6221da177e4SLinus Torvalds
6231da177e4SLinus Torvalds /* Now we're ready to register. */
624769f01efSJulia Lawall err = register_netdev(dev);
625769f01efSJulia Lawall if (err)
6261da177e4SLinus Torvalds goto out_free;
6271da177e4SLinus Torvalds
6281da177e4SLinus Torvalds tnc_init(sp);
6291da177e4SLinus Torvalds
6301da177e4SLinus Torvalds return 0;
6311da177e4SLinus Torvalds
6321da177e4SLinus Torvalds out_free:
6331da177e4SLinus Torvalds kfree(xbuff);
6341da177e4SLinus Torvalds kfree(rbuff);
6351da177e4SLinus Torvalds
6361da177e4SLinus Torvalds free_netdev(dev);
6371da177e4SLinus Torvalds
6381da177e4SLinus Torvalds out:
6391da177e4SLinus Torvalds return err;
6401da177e4SLinus Torvalds }
6411da177e4SLinus Torvalds
6421da177e4SLinus Torvalds
6431da177e4SLinus Torvalds /*
6441da177e4SLinus Torvalds * Close down a 6pack channel.
6451da177e4SLinus Torvalds * This means flushing out any pending queues, and then restoring the
6461da177e4SLinus Torvalds * TTY line discipline to what it was before it got hooked to 6pack
6471da177e4SLinus Torvalds * (which usually is TTY again).
6481da177e4SLinus Torvalds */
sixpack_close(struct tty_struct * tty)6491da177e4SLinus Torvalds static void sixpack_close(struct tty_struct *tty)
6501da177e4SLinus Torvalds {
6511da177e4SLinus Torvalds struct sixpack *sp;
6521da177e4SLinus Torvalds
6535c9934b6SEric Dumazet write_lock_irq(&disc_data_lock);
6541da177e4SLinus Torvalds sp = tty->disc_data;
6551da177e4SLinus Torvalds tty->disc_data = NULL;
6565c9934b6SEric Dumazet write_unlock_irq(&disc_data_lock);
65779ea13ceSAl Viro if (!sp)
6581da177e4SLinus Torvalds return;
6591da177e4SLinus Torvalds
6601da177e4SLinus Torvalds /*
6611da177e4SLinus Torvalds * We have now ensured that nobody can start using ap from now on, but
6621da177e4SLinus Torvalds * we have to wait for all existing users to finish.
6631da177e4SLinus Torvalds */
664956cc1e7SElena Reshetova if (!refcount_dec_and_test(&sp->refcnt))
665c2c79a32SArnd Bergmann wait_for_completion(&sp->dead);
6661da177e4SLinus Torvalds
66760aa3b08SOne Thousand Gnomes /* We must stop the queue to avoid potentially scribbling
668c2c79a32SArnd Bergmann * on the free buffers. The sp->dead completion is not sufficient
66960aa3b08SOne Thousand Gnomes * to protect us from sp->xbuff access.
67060aa3b08SOne Thousand Gnomes */
67160aa3b08SOne Thousand Gnomes netif_stop_queue(sp->dev);
67260aa3b08SOne Thousand Gnomes
673efe4186eSDuoming Zhou unregister_netdev(sp->dev);
674efe4186eSDuoming Zhou
675acf673a3SDavid Miller del_timer_sync(&sp->tx_t);
676acf673a3SDavid Miller del_timer_sync(&sp->resync_t);
6771da177e4SLinus Torvalds
6780b911192SLin Ma /* Free all 6pack frame buffers after unreg. */
6791da177e4SLinus Torvalds kfree(sp->rbuff);
6801da177e4SLinus Torvalds kfree(sp->xbuff);
681acf673a3SDavid Miller
6820b911192SLin Ma free_netdev(sp->dev);
6831da177e4SLinus Torvalds }
6841da177e4SLinus Torvalds
6851da177e4SLinus Torvalds /* Perform I/O control on an active 6pack channel. */
sixpack_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)686d78328bcSJiri Slaby static int sixpack_ioctl(struct tty_struct *tty, unsigned int cmd,
687d78328bcSJiri Slaby unsigned long arg)
6881da177e4SLinus Torvalds {
6891da177e4SLinus Torvalds struct sixpack *sp = sp_get(tty);
6900397a264SJulia Lawall struct net_device *dev;
6911da177e4SLinus Torvalds unsigned int tmp, err;
6921da177e4SLinus Torvalds
6931da177e4SLinus Torvalds if (!sp)
6941da177e4SLinus Torvalds return -ENXIO;
6950397a264SJulia Lawall dev = sp->dev;
6961da177e4SLinus Torvalds
6971da177e4SLinus Torvalds switch(cmd) {
6981da177e4SLinus Torvalds case SIOCGIFNAME:
6991da177e4SLinus Torvalds err = copy_to_user((void __user *) arg, dev->name,
7001da177e4SLinus Torvalds strlen(dev->name) + 1) ? -EFAULT : 0;
7011da177e4SLinus Torvalds break;
7021da177e4SLinus Torvalds
7031da177e4SLinus Torvalds case SIOCGIFENCAP:
7041da177e4SLinus Torvalds err = put_user(0, (int __user *) arg);
7051da177e4SLinus Torvalds break;
7061da177e4SLinus Torvalds
7071da177e4SLinus Torvalds case SIOCSIFENCAP:
7081da177e4SLinus Torvalds if (get_user(tmp, (int __user *) arg)) {
7091da177e4SLinus Torvalds err = -EFAULT;
7101da177e4SLinus Torvalds break;
7111da177e4SLinus Torvalds }
7121da177e4SLinus Torvalds
7131da177e4SLinus Torvalds sp->mode = tmp;
7141da177e4SLinus Torvalds dev->addr_len = AX25_ADDR_LEN;
7151da177e4SLinus Torvalds dev->hard_header_len = AX25_KISS_HEADER_LEN +
7161da177e4SLinus Torvalds AX25_MAX_HEADER_LEN + 3;
7171da177e4SLinus Torvalds dev->type = ARPHRD_AX25;
7181da177e4SLinus Torvalds
7191da177e4SLinus Torvalds err = 0;
7201da177e4SLinus Torvalds break;
7211da177e4SLinus Torvalds
7221da177e4SLinus Torvalds case SIOCSIFHWADDR: {
7231da177e4SLinus Torvalds char addr[AX25_ADDR_LEN];
7241da177e4SLinus Torvalds
7251da177e4SLinus Torvalds if (copy_from_user(&addr,
7261da177e4SLinus Torvalds (void __user *)arg, AX25_ADDR_LEN)) {
7271da177e4SLinus Torvalds err = -EFAULT;
7281da177e4SLinus Torvalds break;
7291da177e4SLinus Torvalds }
7301da177e4SLinus Torvalds
731932ff279SHerbert Xu netif_tx_lock_bh(dev);
73220c3d9e4SJakub Kicinski __dev_addr_set(dev, &addr, AX25_ADDR_LEN);
733932ff279SHerbert Xu netif_tx_unlock_bh(dev);
7341da177e4SLinus Torvalds err = 0;
7351da177e4SLinus Torvalds break;
7361da177e4SLinus Torvalds }
7371da177e4SLinus Torvalds default:
738dcc223e8SJiri Slaby err = tty_mode_ioctl(tty, cmd, arg);
7391da177e4SLinus Torvalds }
7401da177e4SLinus Torvalds
7411da177e4SLinus Torvalds sp_put(sp);
7421da177e4SLinus Torvalds
7431da177e4SLinus Torvalds return err;
7441da177e4SLinus Torvalds }
7451da177e4SLinus Torvalds
746a352def2SAlan Cox static struct tty_ldisc_ops sp_ldisc = {
7471da177e4SLinus Torvalds .owner = THIS_MODULE,
748fbadf70aSJiri Slaby .num = N_6PACK,
7491da177e4SLinus Torvalds .name = "6pack",
7501da177e4SLinus Torvalds .open = sixpack_open,
7511da177e4SLinus Torvalds .close = sixpack_close,
7521da177e4SLinus Torvalds .ioctl = sixpack_ioctl,
7531da177e4SLinus Torvalds .receive_buf = sixpack_receive_buf,
7541da177e4SLinus Torvalds .write_wakeup = sixpack_write_wakeup,
7551da177e4SLinus Torvalds };
7561da177e4SLinus Torvalds
7571da177e4SLinus Torvalds /* Initialize 6pack control device -- register 6pack line discipline */
7581da177e4SLinus Torvalds
759c477ebd8SAndi Kleen static const char msg_banner[] __initconst = KERN_INFO \
7601da177e4SLinus Torvalds "AX.25: 6pack driver, " SIXPACK_VERSION "\n";
761c477ebd8SAndi Kleen static const char msg_regfail[] __initconst = KERN_ERR \
7621da177e4SLinus Torvalds "6pack: can't register line discipline (err = %d)\n";
7631da177e4SLinus Torvalds
sixpack_init_driver(void)7641da177e4SLinus Torvalds static int __init sixpack_init_driver(void)
7651da177e4SLinus Torvalds {
7661da177e4SLinus Torvalds int status;
7671da177e4SLinus Torvalds
7681da177e4SLinus Torvalds printk(msg_banner);
7691da177e4SLinus Torvalds
7701da177e4SLinus Torvalds /* Register the provided line protocol discipline */
771fbadf70aSJiri Slaby status = tty_register_ldisc(&sp_ldisc);
772fbadf70aSJiri Slaby if (status)
7731da177e4SLinus Torvalds printk(msg_regfail, status);
7741da177e4SLinus Torvalds
7751da177e4SLinus Torvalds return status;
7761da177e4SLinus Torvalds }
7771da177e4SLinus Torvalds
sixpack_exit_driver(void)7781da177e4SLinus Torvalds static void __exit sixpack_exit_driver(void)
7791da177e4SLinus Torvalds {
780357a6a87SJiri Slaby tty_unregister_ldisc(&sp_ldisc);
7811da177e4SLinus Torvalds }
7821da177e4SLinus Torvalds
7831da177e4SLinus Torvalds /* encode an AX.25 packet into 6pack */
7841da177e4SLinus Torvalds
encode_sixpack(unsigned char * tx_buf,unsigned char * tx_buf_raw,int length,unsigned char tx_delay)7851da177e4SLinus Torvalds static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
7861da177e4SLinus Torvalds int length, unsigned char tx_delay)
7871da177e4SLinus Torvalds {
7881da177e4SLinus Torvalds int count = 0;
7891da177e4SLinus Torvalds unsigned char checksum = 0, buf[400];
7901da177e4SLinus Torvalds int raw_count = 0;
7911da177e4SLinus Torvalds
7921da177e4SLinus Torvalds tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
7931da177e4SLinus Torvalds tx_buf_raw[raw_count++] = SIXP_SEOF;
7941da177e4SLinus Torvalds
7951da177e4SLinus Torvalds buf[0] = tx_delay;
7961da177e4SLinus Torvalds for (count = 1; count < length; count++)
7971da177e4SLinus Torvalds buf[count] = tx_buf[count];
7981da177e4SLinus Torvalds
7991da177e4SLinus Torvalds for (count = 0; count < length; count++)
8001da177e4SLinus Torvalds checksum += buf[count];
8011da177e4SLinus Torvalds buf[length] = (unsigned char) 0xff - checksum;
8021da177e4SLinus Torvalds
8031da177e4SLinus Torvalds for (count = 0; count <= length; count++) {
8041da177e4SLinus Torvalds if ((count % 3) == 0) {
8051da177e4SLinus Torvalds tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
8061da177e4SLinus Torvalds tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
8071da177e4SLinus Torvalds } else if ((count % 3) == 1) {
8081da177e4SLinus Torvalds tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
8091da177e4SLinus Torvalds tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c);
8101da177e4SLinus Torvalds } else {
8111da177e4SLinus Torvalds tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
8121da177e4SLinus Torvalds tx_buf_raw[raw_count++] = (buf[count] >> 2);
8131da177e4SLinus Torvalds }
8141da177e4SLinus Torvalds }
8151da177e4SLinus Torvalds if ((length % 3) != 2)
8161da177e4SLinus Torvalds raw_count++;
8171da177e4SLinus Torvalds tx_buf_raw[raw_count++] = SIXP_SEOF;
8181da177e4SLinus Torvalds return raw_count;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds
8211da177e4SLinus Torvalds /* decode 4 sixpack-encoded bytes into 3 data bytes */
8221da177e4SLinus Torvalds
decode_data(struct sixpack * sp,unsigned char inbyte)8231da177e4SLinus Torvalds static void decode_data(struct sixpack *sp, unsigned char inbyte)
8241da177e4SLinus Torvalds {
8251da177e4SLinus Torvalds unsigned char *buf;
8261da177e4SLinus Torvalds
8271da177e4SLinus Torvalds if (sp->rx_count != 3) {
8281da177e4SLinus Torvalds sp->raw_buf[sp->rx_count++] = inbyte;
8291da177e4SLinus Torvalds
8301da177e4SLinus Torvalds return;
8311da177e4SLinus Torvalds }
8321da177e4SLinus Torvalds
83319d1532aSPavel Skripkin if (sp->rx_count_cooked + 2 >= sizeof(sp->cooked_buf)) {
83419d1532aSPavel Skripkin pr_err("6pack: cooked buffer overrun, data loss\n");
83519d1532aSPavel Skripkin sp->rx_count = 0;
83619d1532aSPavel Skripkin return;
83719d1532aSPavel Skripkin }
83819d1532aSPavel Skripkin
8391da177e4SLinus Torvalds buf = sp->raw_buf;
8401da177e4SLinus Torvalds sp->cooked_buf[sp->rx_count_cooked++] =
8411da177e4SLinus Torvalds buf[0] | ((buf[1] << 2) & 0xc0);
8421da177e4SLinus Torvalds sp->cooked_buf[sp->rx_count_cooked++] =
8431da177e4SLinus Torvalds (buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
8441da177e4SLinus Torvalds sp->cooked_buf[sp->rx_count_cooked++] =
8451da177e4SLinus Torvalds (buf[2] & 0x03) | (inbyte << 2);
8461da177e4SLinus Torvalds sp->rx_count = 0;
8471da177e4SLinus Torvalds }
8481da177e4SLinus Torvalds
8491da177e4SLinus Torvalds /* identify and execute a 6pack priority command byte */
8501da177e4SLinus Torvalds
decode_prio_command(struct sixpack * sp,unsigned char cmd)8511da177e4SLinus Torvalds static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
8521da177e4SLinus Torvalds {
8531da177e4SLinus Torvalds int actual;
8541da177e4SLinus Torvalds
8551da177e4SLinus Torvalds if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */
8561da177e4SLinus Torvalds
8571da177e4SLinus Torvalds /* RX and DCD flags can only be set in the same prio command,
8581da177e4SLinus Torvalds if the DCD flag has been set without the RX flag in the previous
8591da177e4SLinus Torvalds prio command. If DCD has not been set before, something in the
8601da177e4SLinus Torvalds transmission has gone wrong. In this case, RX and DCD are
8611da177e4SLinus Torvalds cleared in order to prevent the decode_data routine from
8621da177e4SLinus Torvalds reading further data that might be corrupt. */
8631da177e4SLinus Torvalds
8641da177e4SLinus Torvalds if (((sp->status & SIXP_DCD_MASK) == 0) &&
8651da177e4SLinus Torvalds ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
8661da177e4SLinus Torvalds if (sp->status != 1)
8671da177e4SLinus Torvalds printk(KERN_DEBUG "6pack: protocol violation\n");
8681da177e4SLinus Torvalds else
8691da177e4SLinus Torvalds sp->status = 0;
87095f6134eSJean Delvare cmd &= ~SIXP_RX_DCD_MASK;
8711da177e4SLinus Torvalds }
8721da177e4SLinus Torvalds sp->status = cmd & SIXP_PRIO_DATA_MASK;
8731da177e4SLinus Torvalds } else { /* output watchdog char if idle */
8741da177e4SLinus Torvalds if ((sp->status2 != 0) && (sp->duplex == 1)) {
8751da177e4SLinus Torvalds sp->led_state = 0x70;
876f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
8771da177e4SLinus Torvalds sp->tx_enable = 1;
878f34d7a5bSAlan Cox actual = sp->tty->ops->write(sp->tty, sp->xbuff, sp->status2);
8791da177e4SLinus Torvalds sp->xleft -= actual;
8801da177e4SLinus Torvalds sp->xhead += actual;
8811da177e4SLinus Torvalds sp->led_state = 0x60;
8821da177e4SLinus Torvalds sp->status2 = 0;
8831da177e4SLinus Torvalds
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds }
8861da177e4SLinus Torvalds
8871da177e4SLinus Torvalds /* needed to trigger the TNC watchdog */
888f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
8891da177e4SLinus Torvalds
8901da177e4SLinus Torvalds /* if the state byte has been received, the TNC is present,
8911da177e4SLinus Torvalds so the resync timer can be reset. */
8921da177e4SLinus Torvalds
893202700e3SEric Dumazet if (sp->tnc_state == TNC_IN_SYNC)
894202700e3SEric Dumazet mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT);
8951da177e4SLinus Torvalds
8961da177e4SLinus Torvalds sp->status1 = cmd & SIXP_PRIO_DATA_MASK;
8971da177e4SLinus Torvalds }
8981da177e4SLinus Torvalds
8991da177e4SLinus Torvalds /* identify and execute a standard 6pack command byte */
9001da177e4SLinus Torvalds
decode_std_command(struct sixpack * sp,unsigned char cmd)9011da177e4SLinus Torvalds static void decode_std_command(struct sixpack *sp, unsigned char cmd)
9021da177e4SLinus Torvalds {
903827ad90cSColin Ian King unsigned char checksum = 0, rest = 0;
9041da177e4SLinus Torvalds short i;
9051da177e4SLinus Torvalds
9061da177e4SLinus Torvalds switch (cmd & SIXP_CMD_MASK) { /* normal command */
9071da177e4SLinus Torvalds case SIXP_SEOF:
9081da177e4SLinus Torvalds if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) {
9091da177e4SLinus Torvalds if ((sp->status & SIXP_RX_DCD_MASK) ==
9101da177e4SLinus Torvalds SIXP_RX_DCD_MASK) {
9111da177e4SLinus Torvalds sp->led_state = 0x68;
912f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
9131da177e4SLinus Torvalds }
9141da177e4SLinus Torvalds } else {
9151da177e4SLinus Torvalds sp->led_state = 0x60;
9161da177e4SLinus Torvalds /* fill trailing bytes with zeroes */
917f34d7a5bSAlan Cox sp->tty->ops->write(sp->tty, &sp->led_state, 1);
9182b04495eSXu Jia spin_lock_bh(&sp->rxlock);
9191da177e4SLinus Torvalds rest = sp->rx_count;
9201da177e4SLinus Torvalds if (rest != 0)
9211da177e4SLinus Torvalds for (i = rest; i <= 3; i++)
9221da177e4SLinus Torvalds decode_data(sp, 0);
9231da177e4SLinus Torvalds if (rest == 2)
9241da177e4SLinus Torvalds sp->rx_count_cooked -= 2;
9251da177e4SLinus Torvalds else if (rest == 3)
9261da177e4SLinus Torvalds sp->rx_count_cooked -= 1;
9271da177e4SLinus Torvalds for (i = 0; i < sp->rx_count_cooked; i++)
9281da177e4SLinus Torvalds checksum += sp->cooked_buf[i];
9291da177e4SLinus Torvalds if (checksum != SIXP_CHKSUM) {
9301da177e4SLinus Torvalds printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum);
9311da177e4SLinus Torvalds } else {
9321da177e4SLinus Torvalds sp->rcount = sp->rx_count_cooked-2;
9331da177e4SLinus Torvalds sp_bump(sp, 0);
9341da177e4SLinus Torvalds }
9351da177e4SLinus Torvalds sp->rx_count_cooked = 0;
9362b04495eSXu Jia spin_unlock_bh(&sp->rxlock);
9371da177e4SLinus Torvalds }
9381da177e4SLinus Torvalds break;
9391da177e4SLinus Torvalds case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n");
9401da177e4SLinus Torvalds break;
9411da177e4SLinus Torvalds case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n");
9421da177e4SLinus Torvalds break;
9431da177e4SLinus Torvalds case SIXP_RX_BUF_OVL:
9441da177e4SLinus Torvalds printk(KERN_DEBUG "6pack: RX buffer overflow\n");
9451da177e4SLinus Torvalds }
9461da177e4SLinus Torvalds }
9471da177e4SLinus Torvalds
9481da177e4SLinus Torvalds /* decode a 6pack packet */
9491da177e4SLinus Torvalds
9501da177e4SLinus Torvalds static void
sixpack_decode(struct sixpack * sp,const unsigned char * pre_rbuff,int count)951ad979896SAlan Cox sixpack_decode(struct sixpack *sp, const unsigned char *pre_rbuff, int count)
9521da177e4SLinus Torvalds {
9531da177e4SLinus Torvalds unsigned char inbyte;
9541da177e4SLinus Torvalds int count1;
9551da177e4SLinus Torvalds
9561da177e4SLinus Torvalds for (count1 = 0; count1 < count; count1++) {
9571da177e4SLinus Torvalds inbyte = pre_rbuff[count1];
9581da177e4SLinus Torvalds if (inbyte == SIXP_FOUND_TNC) {
9591da177e4SLinus Torvalds tnc_set_sync_state(sp, TNC_IN_SYNC);
9601da177e4SLinus Torvalds del_timer(&sp->resync_t);
9611da177e4SLinus Torvalds }
9621da177e4SLinus Torvalds if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
9631da177e4SLinus Torvalds decode_prio_command(sp, inbyte);
9641da177e4SLinus Torvalds else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
9651da177e4SLinus Torvalds decode_std_command(sp, inbyte);
9662b04495eSXu Jia else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) {
9672b04495eSXu Jia spin_lock_bh(&sp->rxlock);
9681da177e4SLinus Torvalds decode_data(sp, inbyte);
9692b04495eSXu Jia spin_unlock_bh(&sp->rxlock);
9702b04495eSXu Jia }
9711da177e4SLinus Torvalds }
9721da177e4SLinus Torvalds }
9731da177e4SLinus Torvalds
9741da177e4SLinus Torvalds MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>");
9751da177e4SLinus Torvalds MODULE_DESCRIPTION("6pack driver for AX.25");
9761da177e4SLinus Torvalds MODULE_LICENSE("GPL");
9771da177e4SLinus Torvalds MODULE_ALIAS_LDISC(N_6PACK);
9781da177e4SLinus Torvalds
9791da177e4SLinus Torvalds module_init(sixpack_init_driver);
9801da177e4SLinus Torvalds module_exit(sixpack_exit_driver);
981