162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) Hans Alblas PE1AYX <hans@esrac.ele.tue.nl>
562306a36Sopenharmony_ci * Copyright (C) 2004, 05 Ralf Baechle DL5RB <ralf@linux-mips.org>
662306a36Sopenharmony_ci * Copyright (C) 2004, 05 Thomas Osterried DL9SAU <thomas@x-berg.in-berlin.de>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/bitops.h>
1062306a36Sopenharmony_ci#include <linux/uaccess.h>
1162306a36Sopenharmony_ci#include <linux/crc16.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/in.h>
1662306a36Sopenharmony_ci#include <linux/inet.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/tty.h>
1962306a36Sopenharmony_ci#include <linux/errno.h>
2062306a36Sopenharmony_ci#include <linux/netdevice.h>
2162306a36Sopenharmony_ci#include <linux/major.h>
2262306a36Sopenharmony_ci#include <linux/init.h>
2362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
2462306a36Sopenharmony_ci#include <linux/etherdevice.h>
2562306a36Sopenharmony_ci#include <linux/skbuff.h>
2662306a36Sopenharmony_ci#include <linux/if_arp.h>
2762306a36Sopenharmony_ci#include <linux/jiffies.h>
2862306a36Sopenharmony_ci#include <linux/refcount.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <net/ax25.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#define AX_MTU		236
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci/* some arch define END as assembly function ending, just undef it */
3562306a36Sopenharmony_ci#undef	END
3662306a36Sopenharmony_ci/* SLIP/KISS protocol characters. */
3762306a36Sopenharmony_ci#define END             0300		/* indicates end of frame	*/
3862306a36Sopenharmony_ci#define ESC             0333		/* indicates byte stuffing	*/
3962306a36Sopenharmony_ci#define ESC_END         0334		/* ESC ESC_END means END 'data'	*/
4062306a36Sopenharmony_ci#define ESC_ESC         0335		/* ESC ESC_ESC means ESC 'data'	*/
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistruct mkiss {
4362306a36Sopenharmony_ci	struct tty_struct	*tty;	/* ptr to TTY structure		*/
4462306a36Sopenharmony_ci	struct net_device	*dev;	/* easy for intr handling	*/
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* These are pointers to the malloc()ed frame buffers. */
4762306a36Sopenharmony_ci	spinlock_t		buflock;/* lock for rbuf and xbuf */
4862306a36Sopenharmony_ci	unsigned char		*rbuff;	/* receiver buffer		*/
4962306a36Sopenharmony_ci	int			rcount;	/* received chars counter       */
5062306a36Sopenharmony_ci	unsigned char		*xbuff;	/* transmitter buffer		*/
5162306a36Sopenharmony_ci	unsigned char		*xhead;	/* pointer to next byte to XMIT */
5262306a36Sopenharmony_ci	int			xleft;	/* bytes left in XMIT queue     */
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Detailed SLIP statistics. */
5562306a36Sopenharmony_ci	int		mtu;		/* Our mtu (to spot changes!)   */
5662306a36Sopenharmony_ci	int		buffsize;	/* Max buffers sizes            */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	unsigned long	flags;		/* Flag values/ mode etc	*/
5962306a36Sopenharmony_ci					/* long req'd: used by set_bit --RR */
6062306a36Sopenharmony_ci#define AXF_INUSE	0		/* Channel in use               */
6162306a36Sopenharmony_ci#define AXF_ESCAPE	1               /* ESC received                 */
6262306a36Sopenharmony_ci#define AXF_ERROR	2               /* Parity, etc. error           */
6362306a36Sopenharmony_ci#define AXF_KEEPTEST	3		/* Keepalive test flag		*/
6462306a36Sopenharmony_ci#define AXF_OUTWAIT	4		/* is outpacket was flag	*/
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	int		mode;
6762306a36Sopenharmony_ci        int		crcmode;	/* MW: for FlexNet, SMACK etc.  */
6862306a36Sopenharmony_ci	int		crcauto;	/* CRC auto mode */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define CRC_MODE_NONE		0
7162306a36Sopenharmony_ci#define CRC_MODE_FLEX		1
7262306a36Sopenharmony_ci#define CRC_MODE_SMACK		2
7362306a36Sopenharmony_ci#define CRC_MODE_FLEX_TEST	3
7462306a36Sopenharmony_ci#define CRC_MODE_SMACK_TEST	4
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	refcount_t		refcnt;
7762306a36Sopenharmony_ci	struct completion	dead;
7862306a36Sopenharmony_ci};
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*---------------------------------------------------------------------------*/
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic const unsigned short crc_flex_table[] = {
8362306a36Sopenharmony_ci	0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38,
8462306a36Sopenharmony_ci	0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770,
8562306a36Sopenharmony_ci	0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9,
8662306a36Sopenharmony_ci	0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1,
8762306a36Sopenharmony_ci	0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a,
8862306a36Sopenharmony_ci	0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672,
8962306a36Sopenharmony_ci	0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb,
9062306a36Sopenharmony_ci	0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3,
9162306a36Sopenharmony_ci	0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c,
9262306a36Sopenharmony_ci	0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574,
9362306a36Sopenharmony_ci	0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd,
9462306a36Sopenharmony_ci	0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5,
9562306a36Sopenharmony_ci	0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e,
9662306a36Sopenharmony_ci	0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476,
9762306a36Sopenharmony_ci	0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf,
9862306a36Sopenharmony_ci	0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7,
9962306a36Sopenharmony_ci	0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30,
10062306a36Sopenharmony_ci	0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378,
10162306a36Sopenharmony_ci	0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1,
10262306a36Sopenharmony_ci	0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9,
10362306a36Sopenharmony_ci	0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32,
10462306a36Sopenharmony_ci	0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a,
10562306a36Sopenharmony_ci	0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3,
10662306a36Sopenharmony_ci	0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb,
10762306a36Sopenharmony_ci	0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34,
10862306a36Sopenharmony_ci	0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c,
10962306a36Sopenharmony_ci	0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5,
11062306a36Sopenharmony_ci	0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd,
11162306a36Sopenharmony_ci	0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36,
11262306a36Sopenharmony_ci	0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e,
11362306a36Sopenharmony_ci	0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7,
11462306a36Sopenharmony_ci	0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic unsigned short calc_crc_flex(unsigned char *cp, int size)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	unsigned short crc = 0xffff;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	while (size--)
12262306a36Sopenharmony_ci		crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	return crc;
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic int check_crc_flex(unsigned char *cp, int size)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	unsigned short crc = 0xffff;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (size < 3)
13262306a36Sopenharmony_ci		return -1;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	while (size--)
13562306a36Sopenharmony_ci		crc = (crc << 8) ^ crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if ((crc & 0xffff) != 0x7070)
13862306a36Sopenharmony_ci		return -1;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cistatic int check_crc_16(unsigned char *cp, int size)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	unsigned short crc = 0x0000;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	if (size < 3)
14862306a36Sopenharmony_ci		return -1;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	crc = crc16(0, cp, size);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (crc != 0x0000)
15362306a36Sopenharmony_ci		return -1;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return 0;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/*
15962306a36Sopenharmony_ci * Standard encapsulation
16062306a36Sopenharmony_ci */
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int kiss_esc(unsigned char *s, unsigned char *d, int len)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	unsigned char *ptr = d;
16562306a36Sopenharmony_ci	unsigned char c;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/*
16862306a36Sopenharmony_ci	 * Send an initial END character to flush out any data that may have
16962306a36Sopenharmony_ci	 * accumulated in the receiver due to line noise.
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	*ptr++ = END;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	while (len-- > 0) {
17562306a36Sopenharmony_ci		switch (c = *s++) {
17662306a36Sopenharmony_ci		case END:
17762306a36Sopenharmony_ci			*ptr++ = ESC;
17862306a36Sopenharmony_ci			*ptr++ = ESC_END;
17962306a36Sopenharmony_ci			break;
18062306a36Sopenharmony_ci		case ESC:
18162306a36Sopenharmony_ci			*ptr++ = ESC;
18262306a36Sopenharmony_ci			*ptr++ = ESC_ESC;
18362306a36Sopenharmony_ci			break;
18462306a36Sopenharmony_ci		default:
18562306a36Sopenharmony_ci			*ptr++ = c;
18662306a36Sopenharmony_ci			break;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci	}
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	*ptr++ = END;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return ptr - d;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/*
19662306a36Sopenharmony_ci * MW:
19762306a36Sopenharmony_ci * OK its ugly, but tell me a better solution without copying the
19862306a36Sopenharmony_ci * packet to a temporary buffer :-)
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc,
20162306a36Sopenharmony_ci	int len)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	unsigned char *ptr = d;
20462306a36Sopenharmony_ci	unsigned char c=0;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	*ptr++ = END;
20762306a36Sopenharmony_ci	while (len > 0) {
20862306a36Sopenharmony_ci		if (len > 2)
20962306a36Sopenharmony_ci			c = *s++;
21062306a36Sopenharmony_ci		else if (len > 1)
21162306a36Sopenharmony_ci			c = crc >> 8;
21262306a36Sopenharmony_ci		else
21362306a36Sopenharmony_ci			c = crc & 0xff;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci		len--;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		switch (c) {
21862306a36Sopenharmony_ci		case END:
21962306a36Sopenharmony_ci			*ptr++ = ESC;
22062306a36Sopenharmony_ci			*ptr++ = ESC_END;
22162306a36Sopenharmony_ci			break;
22262306a36Sopenharmony_ci		case ESC:
22362306a36Sopenharmony_ci			*ptr++ = ESC;
22462306a36Sopenharmony_ci			*ptr++ = ESC_ESC;
22562306a36Sopenharmony_ci			break;
22662306a36Sopenharmony_ci		default:
22762306a36Sopenharmony_ci			*ptr++ = c;
22862306a36Sopenharmony_ci			break;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci	*ptr++ = END;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	return ptr - d;
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
23762306a36Sopenharmony_cistatic void ax_bump(struct mkiss *ax)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct sk_buff *skb;
24062306a36Sopenharmony_ci	int count;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	spin_lock_bh(&ax->buflock);
24362306a36Sopenharmony_ci	if (ax->rbuff[0] > 0x0f) {
24462306a36Sopenharmony_ci		if (ax->rbuff[0] & 0x80) {
24562306a36Sopenharmony_ci			if (check_crc_16(ax->rbuff, ax->rcount) < 0) {
24662306a36Sopenharmony_ci				ax->dev->stats.rx_errors++;
24762306a36Sopenharmony_ci				spin_unlock_bh(&ax->buflock);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci				return;
25062306a36Sopenharmony_ci			}
25162306a36Sopenharmony_ci			if (ax->crcmode != CRC_MODE_SMACK && ax->crcauto) {
25262306a36Sopenharmony_ci				printk(KERN_INFO
25362306a36Sopenharmony_ci				       "mkiss: %s: Switching to crc-smack\n",
25462306a36Sopenharmony_ci				       ax->dev->name);
25562306a36Sopenharmony_ci				ax->crcmode = CRC_MODE_SMACK;
25662306a36Sopenharmony_ci			}
25762306a36Sopenharmony_ci			ax->rcount -= 2;
25862306a36Sopenharmony_ci			*ax->rbuff &= ~0x80;
25962306a36Sopenharmony_ci		} else if (ax->rbuff[0] & 0x20)  {
26062306a36Sopenharmony_ci			if (check_crc_flex(ax->rbuff, ax->rcount) < 0) {
26162306a36Sopenharmony_ci				ax->dev->stats.rx_errors++;
26262306a36Sopenharmony_ci				spin_unlock_bh(&ax->buflock);
26362306a36Sopenharmony_ci				return;
26462306a36Sopenharmony_ci			}
26562306a36Sopenharmony_ci			if (ax->crcmode != CRC_MODE_FLEX && ax->crcauto) {
26662306a36Sopenharmony_ci				printk(KERN_INFO
26762306a36Sopenharmony_ci				       "mkiss: %s: Switching to crc-flexnet\n",
26862306a36Sopenharmony_ci				       ax->dev->name);
26962306a36Sopenharmony_ci				ax->crcmode = CRC_MODE_FLEX;
27062306a36Sopenharmony_ci			}
27162306a36Sopenharmony_ci			ax->rcount -= 2;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci			/*
27462306a36Sopenharmony_ci			 * dl9sau bugfix: the trailling two bytes flexnet crc
27562306a36Sopenharmony_ci			 * will not be passed to the kernel. thus we have to
27662306a36Sopenharmony_ci			 * correct the kissparm signature, because it indicates
27762306a36Sopenharmony_ci			 * a crc but there's none
27862306a36Sopenharmony_ci			 */
27962306a36Sopenharmony_ci			*ax->rbuff &= ~0x20;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	count = ax->rcount;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	if ((skb = dev_alloc_skb(count)) == NULL) {
28662306a36Sopenharmony_ci		printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n",
28762306a36Sopenharmony_ci		       ax->dev->name);
28862306a36Sopenharmony_ci		ax->dev->stats.rx_dropped++;
28962306a36Sopenharmony_ci		spin_unlock_bh(&ax->buflock);
29062306a36Sopenharmony_ci		return;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	skb_put_data(skb, ax->rbuff, count);
29462306a36Sopenharmony_ci	skb->protocol = ax25_type_trans(skb, ax->dev);
29562306a36Sopenharmony_ci	netif_rx(skb);
29662306a36Sopenharmony_ci	ax->dev->stats.rx_packets++;
29762306a36Sopenharmony_ci	ax->dev->stats.rx_bytes += count;
29862306a36Sopenharmony_ci	spin_unlock_bh(&ax->buflock);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic void kiss_unesc(struct mkiss *ax, unsigned char s)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	switch (s) {
30462306a36Sopenharmony_ci	case END:
30562306a36Sopenharmony_ci		/* drop keeptest bit = VSV */
30662306a36Sopenharmony_ci		if (test_bit(AXF_KEEPTEST, &ax->flags))
30762306a36Sopenharmony_ci			clear_bit(AXF_KEEPTEST, &ax->flags);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
31062306a36Sopenharmony_ci			ax_bump(ax);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		clear_bit(AXF_ESCAPE, &ax->flags);
31362306a36Sopenharmony_ci		ax->rcount = 0;
31462306a36Sopenharmony_ci		return;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	case ESC:
31762306a36Sopenharmony_ci		set_bit(AXF_ESCAPE, &ax->flags);
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci	case ESC_ESC:
32062306a36Sopenharmony_ci		if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
32162306a36Sopenharmony_ci			s = ESC;
32262306a36Sopenharmony_ci		break;
32362306a36Sopenharmony_ci	case ESC_END:
32462306a36Sopenharmony_ci		if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
32562306a36Sopenharmony_ci			s = END;
32662306a36Sopenharmony_ci		break;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	spin_lock_bh(&ax->buflock);
33062306a36Sopenharmony_ci	if (!test_bit(AXF_ERROR, &ax->flags)) {
33162306a36Sopenharmony_ci		if (ax->rcount < ax->buffsize) {
33262306a36Sopenharmony_ci			ax->rbuff[ax->rcount++] = s;
33362306a36Sopenharmony_ci			spin_unlock_bh(&ax->buflock);
33462306a36Sopenharmony_ci			return;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		ax->dev->stats.rx_over_errors++;
33862306a36Sopenharmony_ci		set_bit(AXF_ERROR, &ax->flags);
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci	spin_unlock_bh(&ax->buflock);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic int ax_set_mac_address(struct net_device *dev, void *addr)
34462306a36Sopenharmony_ci{
34562306a36Sopenharmony_ci	struct sockaddr_ax25 *sa = addr;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	netif_tx_lock_bh(dev);
34862306a36Sopenharmony_ci	netif_addr_lock(dev);
34962306a36Sopenharmony_ci	__dev_addr_set(dev, &sa->sax25_call, AX25_ADDR_LEN);
35062306a36Sopenharmony_ci	netif_addr_unlock(dev);
35162306a36Sopenharmony_ci	netif_tx_unlock_bh(dev);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	return 0;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/*---------------------------------------------------------------------------*/
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void ax_changedmtu(struct mkiss *ax)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct net_device *dev = ax->dev;
36162306a36Sopenharmony_ci	unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
36262306a36Sopenharmony_ci	int len;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	len = dev->mtu * 2;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/*
36762306a36Sopenharmony_ci	 * allow for arrival of larger UDP packets, even if we say not to
36862306a36Sopenharmony_ci	 * also fixes a bug in which SunOS sends 512-byte packets even with
36962306a36Sopenharmony_ci	 * an MSS of 128
37062306a36Sopenharmony_ci	 */
37162306a36Sopenharmony_ci	if (len < 576 * 2)
37262306a36Sopenharmony_ci		len = 576 * 2;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	xbuff = kmalloc(len + 4, GFP_ATOMIC);
37562306a36Sopenharmony_ci	rbuff = kmalloc(len + 4, GFP_ATOMIC);
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (xbuff == NULL || rbuff == NULL)  {
37862306a36Sopenharmony_ci		printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, "
37962306a36Sopenharmony_ci		       "MTU change cancelled.\n",
38062306a36Sopenharmony_ci		       ax->dev->name);
38162306a36Sopenharmony_ci		dev->mtu = ax->mtu;
38262306a36Sopenharmony_ci		kfree(xbuff);
38362306a36Sopenharmony_ci		kfree(rbuff);
38462306a36Sopenharmony_ci		return;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	spin_lock_bh(&ax->buflock);
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	oxbuff    = ax->xbuff;
39062306a36Sopenharmony_ci	ax->xbuff = xbuff;
39162306a36Sopenharmony_ci	orbuff    = ax->rbuff;
39262306a36Sopenharmony_ci	ax->rbuff = rbuff;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (ax->xleft) {
39562306a36Sopenharmony_ci		if (ax->xleft <= len) {
39662306a36Sopenharmony_ci			memcpy(ax->xbuff, ax->xhead, ax->xleft);
39762306a36Sopenharmony_ci		} else  {
39862306a36Sopenharmony_ci			ax->xleft = 0;
39962306a36Sopenharmony_ci			dev->stats.tx_dropped++;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ax->xhead = ax->xbuff;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (ax->rcount) {
40662306a36Sopenharmony_ci		if (ax->rcount <= len) {
40762306a36Sopenharmony_ci			memcpy(ax->rbuff, orbuff, ax->rcount);
40862306a36Sopenharmony_ci		} else  {
40962306a36Sopenharmony_ci			ax->rcount = 0;
41062306a36Sopenharmony_ci			dev->stats.rx_over_errors++;
41162306a36Sopenharmony_ci			set_bit(AXF_ERROR, &ax->flags);
41262306a36Sopenharmony_ci		}
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ax->mtu      = dev->mtu + 73;
41662306a36Sopenharmony_ci	ax->buffsize = len;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_unlock_bh(&ax->buflock);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	kfree(oxbuff);
42162306a36Sopenharmony_ci	kfree(orbuff);
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
42562306a36Sopenharmony_cistatic void ax_encaps(struct net_device *dev, unsigned char *icp, int len)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct mkiss *ax = netdev_priv(dev);
42862306a36Sopenharmony_ci	unsigned char *p;
42962306a36Sopenharmony_ci	int actual, count;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (ax->mtu != ax->dev->mtu + 73)	/* Someone has been ifconfigging */
43262306a36Sopenharmony_ci		ax_changedmtu(ax);
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	if (len > ax->mtu) {		/* Sigh, shouldn't occur BUT ... */
43562306a36Sopenharmony_ci		printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
43662306a36Sopenharmony_ci		dev->stats.tx_dropped++;
43762306a36Sopenharmony_ci		netif_start_queue(dev);
43862306a36Sopenharmony_ci		return;
43962306a36Sopenharmony_ci	}
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	p = icp;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	spin_lock_bh(&ax->buflock);
44462306a36Sopenharmony_ci	if ((*p & 0x0f) != 0) {
44562306a36Sopenharmony_ci		/* Configuration Command (kissparms(1).
44662306a36Sopenharmony_ci		 * Protocol spec says: never append CRC.
44762306a36Sopenharmony_ci		 * This fixes a very old bug in the linux
44862306a36Sopenharmony_ci		 * kiss driver. -- dl9sau */
44962306a36Sopenharmony_ci		switch (*p & 0xff) {
45062306a36Sopenharmony_ci		case 0x85:
45162306a36Sopenharmony_ci			/* command from userspace especially for us,
45262306a36Sopenharmony_ci			 * not for delivery to the tnc */
45362306a36Sopenharmony_ci			if (len > 1) {
45462306a36Sopenharmony_ci				int cmd = (p[1] & 0xff);
45562306a36Sopenharmony_ci				switch(cmd) {
45662306a36Sopenharmony_ci				case 3:
45762306a36Sopenharmony_ci				  ax->crcmode = CRC_MODE_SMACK;
45862306a36Sopenharmony_ci				  break;
45962306a36Sopenharmony_ci				case 2:
46062306a36Sopenharmony_ci				  ax->crcmode = CRC_MODE_FLEX;
46162306a36Sopenharmony_ci				  break;
46262306a36Sopenharmony_ci				case 1:
46362306a36Sopenharmony_ci				  ax->crcmode = CRC_MODE_NONE;
46462306a36Sopenharmony_ci				  break;
46562306a36Sopenharmony_ci				case 0:
46662306a36Sopenharmony_ci				default:
46762306a36Sopenharmony_ci				  ax->crcmode = CRC_MODE_SMACK_TEST;
46862306a36Sopenharmony_ci				  cmd = 0;
46962306a36Sopenharmony_ci				}
47062306a36Sopenharmony_ci				ax->crcauto = (cmd ? 0 : 1);
47162306a36Sopenharmony_ci				printk(KERN_INFO "mkiss: %s: crc mode set to %d\n",
47262306a36Sopenharmony_ci				       ax->dev->name, cmd);
47362306a36Sopenharmony_ci			}
47462306a36Sopenharmony_ci			spin_unlock_bh(&ax->buflock);
47562306a36Sopenharmony_ci			netif_start_queue(dev);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci			return;
47862306a36Sopenharmony_ci		default:
47962306a36Sopenharmony_ci			count = kiss_esc(p, ax->xbuff, len);
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci	} else {
48262306a36Sopenharmony_ci		unsigned short crc;
48362306a36Sopenharmony_ci		switch (ax->crcmode) {
48462306a36Sopenharmony_ci		case CRC_MODE_SMACK_TEST:
48562306a36Sopenharmony_ci			ax->crcmode  = CRC_MODE_FLEX_TEST;
48662306a36Sopenharmony_ci			printk(KERN_INFO "mkiss: %s: Trying crc-smack\n", ax->dev->name);
48762306a36Sopenharmony_ci			fallthrough;
48862306a36Sopenharmony_ci		case CRC_MODE_SMACK:
48962306a36Sopenharmony_ci			*p |= 0x80;
49062306a36Sopenharmony_ci			crc = swab16(crc16(0, p, len));
49162306a36Sopenharmony_ci			count = kiss_esc_crc(p, ax->xbuff, crc, len+2);
49262306a36Sopenharmony_ci			break;
49362306a36Sopenharmony_ci		case CRC_MODE_FLEX_TEST:
49462306a36Sopenharmony_ci			ax->crcmode = CRC_MODE_NONE;
49562306a36Sopenharmony_ci			printk(KERN_INFO "mkiss: %s: Trying crc-flexnet\n", ax->dev->name);
49662306a36Sopenharmony_ci			fallthrough;
49762306a36Sopenharmony_ci		case CRC_MODE_FLEX:
49862306a36Sopenharmony_ci			*p |= 0x20;
49962306a36Sopenharmony_ci			crc = calc_crc_flex(p, len);
50062306a36Sopenharmony_ci			count = kiss_esc_crc(p, ax->xbuff, crc, len+2);
50162306a36Sopenharmony_ci			break;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		default:
50462306a36Sopenharmony_ci			count = kiss_esc(p, ax->xbuff, len);
50562306a36Sopenharmony_ci		}
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci	spin_unlock_bh(&ax->buflock);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	set_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags);
51062306a36Sopenharmony_ci	actual = ax->tty->ops->write(ax->tty, ax->xbuff, count);
51162306a36Sopenharmony_ci	dev->stats.tx_packets++;
51262306a36Sopenharmony_ci	dev->stats.tx_bytes += actual;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	netif_trans_update(ax->dev);
51562306a36Sopenharmony_ci	ax->xleft = count - actual;
51662306a36Sopenharmony_ci	ax->xhead = ax->xbuff + actual;
51762306a36Sopenharmony_ci}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
52062306a36Sopenharmony_cistatic netdev_tx_t ax_xmit(struct sk_buff *skb, struct net_device *dev)
52162306a36Sopenharmony_ci{
52262306a36Sopenharmony_ci	struct mkiss *ax = netdev_priv(dev);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (skb->protocol == htons(ETH_P_IP))
52562306a36Sopenharmony_ci		return ax25_ip_xmit(skb);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (!netif_running(dev))  {
52862306a36Sopenharmony_ci		printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
52962306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (netif_queue_stopped(dev)) {
53362306a36Sopenharmony_ci		/*
53462306a36Sopenharmony_ci		 * May be we must check transmitter timeout here ?
53562306a36Sopenharmony_ci		 *      14 Oct 1994 Dmitry Gorodchanin.
53662306a36Sopenharmony_ci		 */
53762306a36Sopenharmony_ci		if (time_before(jiffies, dev_trans_start(dev) + 20 * HZ)) {
53862306a36Sopenharmony_ci			/* 20 sec timeout not reached */
53962306a36Sopenharmony_ci			return NETDEV_TX_BUSY;
54062306a36Sopenharmony_ci		}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
54362306a36Sopenharmony_ci		       (tty_chars_in_buffer(ax->tty) || ax->xleft) ?
54462306a36Sopenharmony_ci		       "bad line quality" : "driver error");
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		ax->xleft = 0;
54762306a36Sopenharmony_ci		clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags);
54862306a36Sopenharmony_ci		netif_start_queue(dev);
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	/* We were not busy, so we are now... :-) */
55262306a36Sopenharmony_ci	netif_stop_queue(dev);
55362306a36Sopenharmony_ci	ax_encaps(dev, skb->data, skb->len);
55462306a36Sopenharmony_ci	kfree_skb(skb);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return NETDEV_TX_OK;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int ax_open_dev(struct net_device *dev)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct mkiss *ax = netdev_priv(dev);
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (ax->tty == NULL)
56462306a36Sopenharmony_ci		return -ENODEV;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	return 0;
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci/* Open the low-level part of the AX25 channel. Easy! */
57062306a36Sopenharmony_cistatic int ax_open(struct net_device *dev)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	struct mkiss *ax = netdev_priv(dev);
57362306a36Sopenharmony_ci	unsigned long len;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	if (ax->tty == NULL)
57662306a36Sopenharmony_ci		return -ENODEV;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	/*
57962306a36Sopenharmony_ci	 * Allocate the frame buffers:
58062306a36Sopenharmony_ci	 *
58162306a36Sopenharmony_ci	 * rbuff	Receive buffer.
58262306a36Sopenharmony_ci	 * xbuff	Transmit buffer.
58362306a36Sopenharmony_ci	 */
58462306a36Sopenharmony_ci	len = dev->mtu * 2;
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	/*
58762306a36Sopenharmony_ci	 * allow for arrival of larger UDP packets, even if we say not to
58862306a36Sopenharmony_ci	 * also fixes a bug in which SunOS sends 512-byte packets even with
58962306a36Sopenharmony_ci	 * an MSS of 128
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci	if (len < 576 * 2)
59262306a36Sopenharmony_ci		len = 576 * 2;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
59562306a36Sopenharmony_ci		goto norbuff;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
59862306a36Sopenharmony_ci		goto noxbuff;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	ax->mtu	     = dev->mtu + 73;
60162306a36Sopenharmony_ci	ax->buffsize = len;
60262306a36Sopenharmony_ci	ax->rcount   = 0;
60362306a36Sopenharmony_ci	ax->xleft    = 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	ax->flags   &= (1 << AXF_INUSE);      /* Clear ESCAPE & ERROR flags */
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	spin_lock_init(&ax->buflock);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return 0;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_cinoxbuff:
61262306a36Sopenharmony_ci	kfree(ax->rbuff);
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cinorbuff:
61562306a36Sopenharmony_ci	return -ENOMEM;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci/* Close the low-level part of the AX25 channel. Easy! */
62062306a36Sopenharmony_cistatic int ax_close(struct net_device *dev)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	struct mkiss *ax = netdev_priv(dev);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (ax->tty)
62562306a36Sopenharmony_ci		clear_bit(TTY_DO_WRITE_WAKEUP, &ax->tty->flags);
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	netif_stop_queue(dev);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistatic const struct net_device_ops ax_netdev_ops = {
63362306a36Sopenharmony_ci	.ndo_open            = ax_open_dev,
63462306a36Sopenharmony_ci	.ndo_stop            = ax_close,
63562306a36Sopenharmony_ci	.ndo_start_xmit	     = ax_xmit,
63662306a36Sopenharmony_ci	.ndo_set_mac_address = ax_set_mac_address,
63762306a36Sopenharmony_ci};
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_cistatic void ax_setup(struct net_device *dev)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	/* Finish setting up the DEVICE info. */
64262306a36Sopenharmony_ci	dev->mtu             = AX_MTU;
64362306a36Sopenharmony_ci	dev->hard_header_len = AX25_MAX_HEADER_LEN;
64462306a36Sopenharmony_ci	dev->addr_len        = AX25_ADDR_LEN;
64562306a36Sopenharmony_ci	dev->type            = ARPHRD_AX25;
64662306a36Sopenharmony_ci	dev->tx_queue_len    = 10;
64762306a36Sopenharmony_ci	dev->header_ops      = &ax25_header_ops;
64862306a36Sopenharmony_ci	dev->netdev_ops	     = &ax_netdev_ops;
64962306a36Sopenharmony_ci
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
65262306a36Sopenharmony_ci	dev_addr_set(dev, (u8 *)&ax25_defaddr);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	dev->flags      = IFF_BROADCAST | IFF_MULTICAST;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci/*
65862306a36Sopenharmony_ci * We have a potential race on dereferencing tty->disc_data, because the tty
65962306a36Sopenharmony_ci * layer provides no locking at all - thus one cpu could be running
66062306a36Sopenharmony_ci * sixpack_receive_buf while another calls sixpack_close, which zeroes
66162306a36Sopenharmony_ci * tty->disc_data and frees the memory that sixpack_receive_buf is using.  The
66262306a36Sopenharmony_ci * best way to fix this is to use a rwlock in the tty struct, but for now we
66362306a36Sopenharmony_ci * use a single global rwlock for all ttys in ppp line discipline.
66462306a36Sopenharmony_ci */
66562306a36Sopenharmony_cistatic DEFINE_RWLOCK(disc_data_lock);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic struct mkiss *mkiss_get(struct tty_struct *tty)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct mkiss *ax;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	read_lock(&disc_data_lock);
67262306a36Sopenharmony_ci	ax = tty->disc_data;
67362306a36Sopenharmony_ci	if (ax)
67462306a36Sopenharmony_ci		refcount_inc(&ax->refcnt);
67562306a36Sopenharmony_ci	read_unlock(&disc_data_lock);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return ax;
67862306a36Sopenharmony_ci}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_cistatic void mkiss_put(struct mkiss *ax)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	if (refcount_dec_and_test(&ax->refcnt))
68362306a36Sopenharmony_ci		complete(&ax->dead);
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_cistatic int crc_force = 0;	/* Can be overridden with insmod */
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_cistatic int mkiss_open(struct tty_struct *tty)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	struct net_device *dev;
69162306a36Sopenharmony_ci	struct mkiss *ax;
69262306a36Sopenharmony_ci	int err;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
69562306a36Sopenharmony_ci		return -EPERM;
69662306a36Sopenharmony_ci	if (tty->ops->write == NULL)
69762306a36Sopenharmony_ci		return -EOPNOTSUPP;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(struct mkiss), "ax%d", NET_NAME_UNKNOWN,
70062306a36Sopenharmony_ci			   ax_setup);
70162306a36Sopenharmony_ci	if (!dev) {
70262306a36Sopenharmony_ci		err = -ENOMEM;
70362306a36Sopenharmony_ci		goto out;
70462306a36Sopenharmony_ci	}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	ax = netdev_priv(dev);
70762306a36Sopenharmony_ci	ax->dev = dev;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	spin_lock_init(&ax->buflock);
71062306a36Sopenharmony_ci	refcount_set(&ax->refcnt, 1);
71162306a36Sopenharmony_ci	init_completion(&ax->dead);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	ax->tty = tty;
71462306a36Sopenharmony_ci	tty->disc_data = ax;
71562306a36Sopenharmony_ci	tty->receive_room = 65535;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	tty_driver_flush_buffer(tty);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	/* Restore default settings */
72062306a36Sopenharmony_ci	dev->type = ARPHRD_AX25;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* Perform the low-level AX25 initialization. */
72362306a36Sopenharmony_ci	err = ax_open(ax->dev);
72462306a36Sopenharmony_ci	if (err)
72562306a36Sopenharmony_ci		goto out_free_netdev;
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	err = register_netdev(dev);
72862306a36Sopenharmony_ci	if (err)
72962306a36Sopenharmony_ci		goto out_free_buffers;
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	/* after register_netdev() - because else printk smashes the kernel */
73262306a36Sopenharmony_ci	switch (crc_force) {
73362306a36Sopenharmony_ci	case 3:
73462306a36Sopenharmony_ci		ax->crcmode  = CRC_MODE_SMACK;
73562306a36Sopenharmony_ci		printk(KERN_INFO "mkiss: %s: crc mode smack forced.\n",
73662306a36Sopenharmony_ci		       ax->dev->name);
73762306a36Sopenharmony_ci		break;
73862306a36Sopenharmony_ci	case 2:
73962306a36Sopenharmony_ci		ax->crcmode  = CRC_MODE_FLEX;
74062306a36Sopenharmony_ci		printk(KERN_INFO "mkiss: %s: crc mode flexnet forced.\n",
74162306a36Sopenharmony_ci		       ax->dev->name);
74262306a36Sopenharmony_ci		break;
74362306a36Sopenharmony_ci	case 1:
74462306a36Sopenharmony_ci		ax->crcmode  = CRC_MODE_NONE;
74562306a36Sopenharmony_ci		printk(KERN_INFO "mkiss: %s: crc mode disabled.\n",
74662306a36Sopenharmony_ci		       ax->dev->name);
74762306a36Sopenharmony_ci		break;
74862306a36Sopenharmony_ci	case 0:
74962306a36Sopenharmony_ci	default:
75062306a36Sopenharmony_ci		crc_force = 0;
75162306a36Sopenharmony_ci		printk(KERN_INFO "mkiss: %s: crc mode is auto.\n",
75262306a36Sopenharmony_ci		       ax->dev->name);
75362306a36Sopenharmony_ci		ax->crcmode  = CRC_MODE_SMACK_TEST;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci	ax->crcauto = (crc_force ? 0 : 1);
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	netif_start_queue(dev);
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_ci	/* Done.  We have linked the TTY line to a channel. */
76062306a36Sopenharmony_ci	return 0;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ciout_free_buffers:
76362306a36Sopenharmony_ci	kfree(ax->rbuff);
76462306a36Sopenharmony_ci	kfree(ax->xbuff);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ciout_free_netdev:
76762306a36Sopenharmony_ci	free_netdev(dev);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ciout:
77062306a36Sopenharmony_ci	return err;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistatic void mkiss_close(struct tty_struct *tty)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	struct mkiss *ax;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	write_lock_irq(&disc_data_lock);
77862306a36Sopenharmony_ci	ax = tty->disc_data;
77962306a36Sopenharmony_ci	tty->disc_data = NULL;
78062306a36Sopenharmony_ci	write_unlock_irq(&disc_data_lock);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (!ax)
78362306a36Sopenharmony_ci		return;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/*
78662306a36Sopenharmony_ci	 * We have now ensured that nobody can start using ap from now on, but
78762306a36Sopenharmony_ci	 * we have to wait for all existing users to finish.
78862306a36Sopenharmony_ci	 */
78962306a36Sopenharmony_ci	if (!refcount_dec_and_test(&ax->refcnt))
79062306a36Sopenharmony_ci		wait_for_completion(&ax->dead);
79162306a36Sopenharmony_ci	/*
79262306a36Sopenharmony_ci	 * Halt the transmit queue so that a new transmit cannot scribble
79362306a36Sopenharmony_ci	 * on our buffers
79462306a36Sopenharmony_ci	 */
79562306a36Sopenharmony_ci	netif_stop_queue(ax->dev);
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	unregister_netdev(ax->dev);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* Free all AX25 frame buffers after unreg. */
80062306a36Sopenharmony_ci	kfree(ax->rbuff);
80162306a36Sopenharmony_ci	kfree(ax->xbuff);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	ax->tty = NULL;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	free_netdev(ax->dev);
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/* Perform I/O control on an active ax25 channel. */
80962306a36Sopenharmony_cistatic int mkiss_ioctl(struct tty_struct *tty, unsigned int cmd,
81062306a36Sopenharmony_ci		unsigned long arg)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	struct mkiss *ax = mkiss_get(tty);
81362306a36Sopenharmony_ci	struct net_device *dev;
81462306a36Sopenharmony_ci	unsigned int tmp, err;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	/* First make sure we're connected. */
81762306a36Sopenharmony_ci	if (ax == NULL)
81862306a36Sopenharmony_ci		return -ENXIO;
81962306a36Sopenharmony_ci	dev = ax->dev;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	switch (cmd) {
82262306a36Sopenharmony_ci	case SIOCGIFNAME:
82362306a36Sopenharmony_ci		err = copy_to_user((void __user *) arg, ax->dev->name,
82462306a36Sopenharmony_ci		                   strlen(ax->dev->name) + 1) ? -EFAULT : 0;
82562306a36Sopenharmony_ci		break;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	case SIOCGIFENCAP:
82862306a36Sopenharmony_ci		err = put_user(4, (int __user *) arg);
82962306a36Sopenharmony_ci		break;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	case SIOCSIFENCAP:
83262306a36Sopenharmony_ci		if (get_user(tmp, (int __user *) arg)) {
83362306a36Sopenharmony_ci			err = -EFAULT;
83462306a36Sopenharmony_ci			break;
83562306a36Sopenharmony_ci		}
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci		ax->mode = tmp;
83862306a36Sopenharmony_ci		dev->addr_len        = AX25_ADDR_LEN;
83962306a36Sopenharmony_ci		dev->hard_header_len = AX25_KISS_HEADER_LEN +
84062306a36Sopenharmony_ci		                       AX25_MAX_HEADER_LEN + 3;
84162306a36Sopenharmony_ci		dev->type            = ARPHRD_AX25;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci		err = 0;
84462306a36Sopenharmony_ci		break;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	case SIOCSIFHWADDR: {
84762306a36Sopenharmony_ci		char addr[AX25_ADDR_LEN];
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		if (copy_from_user(&addr,
85062306a36Sopenharmony_ci		                   (void __user *) arg, AX25_ADDR_LEN)) {
85162306a36Sopenharmony_ci			err = -EFAULT;
85262306a36Sopenharmony_ci			break;
85362306a36Sopenharmony_ci		}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci		netif_tx_lock_bh(dev);
85662306a36Sopenharmony_ci		__dev_addr_set(dev, addr, AX25_ADDR_LEN);
85762306a36Sopenharmony_ci		netif_tx_unlock_bh(dev);
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci		err = 0;
86062306a36Sopenharmony_ci		break;
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci	default:
86362306a36Sopenharmony_ci		err = -ENOIOCTLCMD;
86462306a36Sopenharmony_ci	}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	mkiss_put(ax);
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return err;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci/*
87262306a36Sopenharmony_ci * Handle the 'receiver data ready' interrupt.
87362306a36Sopenharmony_ci * This function is called by the 'tty_io' module in the kernel when
87462306a36Sopenharmony_ci * a block of data has been received, which can now be decapsulated
87562306a36Sopenharmony_ci * and sent on to the AX.25 layer for further processing.
87662306a36Sopenharmony_ci */
87762306a36Sopenharmony_cistatic void mkiss_receive_buf(struct tty_struct *tty, const u8 *cp,
87862306a36Sopenharmony_ci			      const u8 *fp, size_t count)
87962306a36Sopenharmony_ci{
88062306a36Sopenharmony_ci	struct mkiss *ax = mkiss_get(tty);
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	if (!ax)
88362306a36Sopenharmony_ci		return;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	/*
88662306a36Sopenharmony_ci	 * Argh! mtu change time! - costs us the packet part received
88762306a36Sopenharmony_ci	 * at the change
88862306a36Sopenharmony_ci	 */
88962306a36Sopenharmony_ci	if (ax->mtu != ax->dev->mtu + 73)
89062306a36Sopenharmony_ci		ax_changedmtu(ax);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	/* Read the characters out of the buffer */
89362306a36Sopenharmony_ci	while (count--) {
89462306a36Sopenharmony_ci		if (fp != NULL && *fp++) {
89562306a36Sopenharmony_ci			if (!test_and_set_bit(AXF_ERROR, &ax->flags))
89662306a36Sopenharmony_ci				ax->dev->stats.rx_errors++;
89762306a36Sopenharmony_ci			cp++;
89862306a36Sopenharmony_ci			continue;
89962306a36Sopenharmony_ci		}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		kiss_unesc(ax, *cp++);
90262306a36Sopenharmony_ci	}
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	mkiss_put(ax);
90562306a36Sopenharmony_ci	tty_unthrottle(tty);
90662306a36Sopenharmony_ci}
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci/*
90962306a36Sopenharmony_ci * Called by the driver when there's room for more data.  If we have
91062306a36Sopenharmony_ci * more packets to send, we send them here.
91162306a36Sopenharmony_ci */
91262306a36Sopenharmony_cistatic void mkiss_write_wakeup(struct tty_struct *tty)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	struct mkiss *ax = mkiss_get(tty);
91562306a36Sopenharmony_ci	int actual;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (!ax)
91862306a36Sopenharmony_ci		return;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (ax->xleft <= 0)  {
92162306a36Sopenharmony_ci		/* Now serial buffer is almost free & we can start
92262306a36Sopenharmony_ci		 * transmission of another packet
92362306a36Sopenharmony_ci		 */
92462306a36Sopenharmony_ci		clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		netif_wake_queue(ax->dev);
92762306a36Sopenharmony_ci		goto out;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	actual = tty->ops->write(tty, ax->xhead, ax->xleft);
93162306a36Sopenharmony_ci	ax->xleft -= actual;
93262306a36Sopenharmony_ci	ax->xhead += actual;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ciout:
93562306a36Sopenharmony_ci	mkiss_put(ax);
93662306a36Sopenharmony_ci}
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_cistatic struct tty_ldisc_ops ax_ldisc = {
93962306a36Sopenharmony_ci	.owner		= THIS_MODULE,
94062306a36Sopenharmony_ci	.num		= N_AX25,
94162306a36Sopenharmony_ci	.name		= "mkiss",
94262306a36Sopenharmony_ci	.open		= mkiss_open,
94362306a36Sopenharmony_ci	.close		= mkiss_close,
94462306a36Sopenharmony_ci	.ioctl		= mkiss_ioctl,
94562306a36Sopenharmony_ci	.receive_buf	= mkiss_receive_buf,
94662306a36Sopenharmony_ci	.write_wakeup	= mkiss_write_wakeup
94762306a36Sopenharmony_ci};
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_cistatic const char banner[] __initconst = KERN_INFO \
95062306a36Sopenharmony_ci	"mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n";
95162306a36Sopenharmony_cistatic const char msg_regfail[] __initconst = KERN_ERR \
95262306a36Sopenharmony_ci	"mkiss: can't register line discipline (err = %d)\n";
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_cistatic int __init mkiss_init_driver(void)
95562306a36Sopenharmony_ci{
95662306a36Sopenharmony_ci	int status;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	printk(banner);
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	status = tty_register_ldisc(&ax_ldisc);
96162306a36Sopenharmony_ci	if (status != 0)
96262306a36Sopenharmony_ci		printk(msg_regfail, status);
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	return status;
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_cistatic void __exit mkiss_exit_driver(void)
96862306a36Sopenharmony_ci{
96962306a36Sopenharmony_ci	tty_unregister_ldisc(&ax_ldisc);
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ciMODULE_AUTHOR("Ralf Baechle DL5RB <ralf@linux-mips.org>");
97362306a36Sopenharmony_ciMODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
97462306a36Sopenharmony_cimodule_param(crc_force, int, 0);
97562306a36Sopenharmony_ciMODULE_PARM_DESC(crc_force, "crc [0 = auto | 1 = none | 2 = flexnet | 3 = smack]");
97662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
97762306a36Sopenharmony_ciMODULE_ALIAS_LDISC(N_AX25);
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cimodule_init(mkiss_init_driver);
98062306a36Sopenharmony_cimodule_exit(mkiss_exit_driver);
981