xref: /openbmc/linux/drivers/net/hamradio/6pack.c (revision 892bc209)
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