brintos

brintos / linux-shallow public Read only

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