Thread (52 messages) 52 messages, 18 authors, 2005-08-02

[PATCH] First cut of PR430 / extended 6pack driver

From: Ralf Baechle DL5RB <hidden>
Date: 2005-07-11 08:21:02

Below there's a first cut of a extended 6pack driver featuring support
for the PR-430 driver.  It should work unchanged for non-PR430 6pack
hardware.  PR430 drivers frequency etc. can be configured through sysfs:

[ralf@dea linux-cvs]$ ssh -l root 172.20.1.2
root@172.20.1.2's password:
Last login: Sun Jul 10 20:52:26 2005
[root@kiste ~]# cd /sys/class/net/sp0
[root@kiste sp0]# ls -l tnc
total 0
-r--r--r--  1 root root 4096 Jul 10 20:52 author
-rw-r--r--  1 root root 4096 Jul 10 20:52 bitrate
-r--r--r--  1 root root 4096 Jul 10 20:52 hwtype
-r--r--r--  1 root root 4096 Jul 10 20:52 major
-r--r--r--  1 root root 4096 Jul 10 20:52 minor
-rw-r--r--  1 root root 4096 Jul 10 20:52 qrg
-rw-r--r--  1 root root 4096 Jul 10 20:52 shift
-rw-r--r--  1 root root 4096 Jul 10 20:52 txpower
[root@kiste sp0]#

The use of sysfs is the main difference to DL9SAU's PR430 driver which
was (ab-)using the 802.11 wireless tool for configuration; otherwise this
driver is largely based on his work.  Patch is against 2.6.12.

73 de DL5RB op Ralf

--
Loc. JN47BS / CQ 14 / ITU 28 / DOK A21

 6pack.c | 1385 ++++++++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 1083 insertions(+), 302 deletions(-)

Index: linux-cvs/drivers/net/hamradio/6pack.c
===================================================================
--- linux-cvs.orig/drivers/net/hamradio/6pack.c	2005-07-05 20:24:56.000000000 +0100
+++ linux-cvs/drivers/net/hamradio/6pack.c	2005-07-10 22:10:03.000000000 +0100
@@ -5,11 +5,6 @@
  *
  * Authors:	Andreas Könsgen <ajk@iehk.rwth-aachen.de>
  *              Ralf Baechle DL5RB <ralf@linux-mips.org>
- *
- * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
- *
- *		Laurence Culhane, <loz@holmes.demon.co.uk>
- *		Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
  */
 
 #include <linux/config.h>
@@ -24,6 +19,7 @@
 #include <linux/tty.h>
 #include <linux/errno.h>
 #include <linux/netdevice.h>
+#include <linux/sysfs.h>
 #include <linux/timer.h>
 #include <net/ax25.h>
 #include <linux/etherdevice.h>
@@ -74,12 +70,61 @@
 #define SIXP_INIT_RESYNC_TIMEOUT	(3*HZ/2) /* in 1 s */
 #define SIXP_RESYNC_TIMEOUT		5*HZ	/* in 1 s */
 
-/* 6pack configuration. */
-#define SIXP_NRUNIT			31      /* MAX number of 6pack channels */
 #define SIXP_MTU			256	/* Default MTU */
 
+/*
+ * TNC430 specefic
+ */
+#define DO_AFSK		1		/* Achtung, TNC kodiert das um
+					   gegenueber seinem Mode */
+#define DO_FM           16
+#define DO_DELAY_TX	128		/* vielleicht ein Provisorium: ein paar
+					   ms txdelay */
+
 enum sixpack_flags {
-	SIXPF_ERROR,	/* Parity, etc. error	*/
+	SIXPF_ERROR,			/* Parity, etc. error	*/
+};
+
+struct hf_param_struct {
+	unsigned int	qrg;            /* Hz */
+	int		shift;          /* Shift */
+	unsigned char	mode;           /* 0:Unknown */
+					/* 1: FSK */
+					/* 2: AFSK */
+					/* 3: voice */
+	unsigned int	baud;           /* 12 = 1k2, 96 = 9k6, etc. */
+	unsigned char	txpower;	/* 0 reserved */
+					/*, 16, 64, 255 (low, mid, high) */
+	unsigned char	status254_cmd;  /* sent status 254 command /
+	                                   received info for status 254 cmd */
+	unsigned long	status254_cmd_t;/* time of last tx/rx */
+};
+
+struct hw_type_struct {
+	unsigned char	type;           /* 0: default, 4: pr430 */
+	unsigned char	author;		/* 7: dk7wj */
+	unsigned char	major;
+	unsigned char	minor;
+	unsigned char	announced;      /* announce_hardware() */
+};
+
+struct hw_struct {
+					/* meassurements */
+	char		smeter;		/* SNR  caveat: signed */
+	char		center;		/* Middlefreq */
+	unsigned char	hub;		/* messured HUB */
+	unsigned char	txpower_auto;	/* automatical power tune */
+					/* based on SN/R */
+	struct hf_param_struct	hf_want;
+	struct hf_param_struct	hf_act;
+	struct hw_type_struct	hw_type;
+};
+
+enum tnc_state {
+	TNC_UNINITIALIZED,
+	TNC_UNSYNC_STARTUP,
+	TNC_UNSYNCED,
+	TNC_IN_SYNC
 };
 
 struct sixpack {
@@ -103,7 +148,7 @@
 	/* 6pack interface statistics. */
 	struct net_device_stats stats;
 
-	int			mtu;		/* Our mtu (to spot changes!) */
+	int			mtu;		/* Our MTU (to spot changes!) */
 	int			buffsize;       /* Max buffers sizes */
 
 	unsigned long		flags;		/* Flag values/ mode etc */
@@ -119,20 +164,26 @@
 	unsigned char		status1;
 	unsigned char		status2;
 	unsigned char		tx_enable;
-	unsigned char		tnc_state;
+	enum tnc_state		tnc_state;
 
 	struct timer_list	tx_t;
 	struct timer_list	resync_t;
+	struct timer_list	status_t;
 	atomic_t		refcnt;
 	struct semaphore	dead_sem;
 	spinlock_t		lock;
+
+	struct hw_struct	hw;
 };
 
+static inline int is_hw_tnc430(const struct sixpack *sp)
+{
+	return (sp->hw.hw_type.type == 4 && sp->hw.hw_type.author == 7);
+}
+
 #define AX25_6PACK_HEADER_LEN 0
 
 static void sp_start_tx_timer(struct sixpack *);
-static void sixpack_decode(struct sixpack *, unsigned char[], int);
-static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
 
 /*
  * perform the persistence/slottime algorithm for CSMA access. If the
@@ -167,11 +218,45 @@
 {
 	int when = sp->slottime;
 
-	del_timer(&sp->tx_t);
-	sp->tx_t.data = (unsigned long) sp;
-	sp->tx_t.function = sp_xmit_on_air;
-	sp->tx_t.expires = jiffies + ((when + 1) * HZ) / 100;
-	add_timer(&sp->tx_t);
+	mod_timer(&sp->tx_t, jiffies + ((when + 1) * HZ) / 100);
+}
+
+/* encode an AX.25 packet into 6pack */
+
+static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
+	int length, unsigned char tx_delay)
+{
+	int count = 0;
+	unsigned char checksum = 0, buf[400];
+	int raw_count = 0;
+
+	tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
+	tx_buf_raw[raw_count++] = SIXP_SEOF;
+
+	buf[0] = tx_delay;
+	for (count = 1; count < length; count++)
+		buf[count] = tx_buf[count];
+
+	for (count = 0; count < length; count++)
+		checksum += buf[count];
+	buf[length] = (unsigned char) 0xff - checksum;
+
+	for (count = 0; count <= length; count++) {
+		if ((count % 3) == 0) {
+			tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
+			tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
+		} else if ((count % 3) == 1) {
+			tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
+			tx_buf_raw[raw_count] =	((buf[count] >> 2) & 0x3c);
+		} else {
+			tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
+			tx_buf_raw[raw_count++] = (buf[count] >> 2);
+		}
+	}
+	if ((length % 3) != 2)
+		raw_count++;
+	tx_buf_raw[raw_count++] = SIXP_SEOF;
+	return raw_count;
 }
 
 /* Encapsulate one AX.25 frame and stuff into a TTY queue. */
@@ -369,12 +454,391 @@
 	memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
 	memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);
 
-	SET_MODULE_OWNER(dev);
-
 	dev->flags		= 0;
 }
 
-/* Send one completely decapsulated IP datagram to the IP layer. */
+static void trace_frame(struct sixpack *sp, char *s)
+{
+	unsigned int i;
+
+	if (s)
+		printk(KERN_DEBUG "6pack: %s - ", s);
+	for (i = 0/*+1*/; i < sp->rx_count_cooked; i++) {
+		printk(KERN_DEBUG "0x%02x%s", sp->cooked_buf[i], ((i < sp->rx_count_cooked-1) ? " " : ""));
+	}
+	printk(KERN_DEBUG "\n");
+	sp->stats.rx_dropped++;
+}
+
+/* xmit status requests to tnc */
+
+static int sp_xmit_status_request(struct sixpack *sp, unsigned char *data, int len)
+{
+	unsigned char xbuff[SIXP_MTU * 2 + 4]; // needed by encode_sixpack()*2
+
+	if (len * 2 + 4 > sizeof(xbuff)) {
+		printk("6pack: status request too long: %d", len);
+		return -1;
+	}
+
+	len = encode_sixpack(data, xbuff, len, data[0]);
+
+	// still running?
+	if (!sp)
+		return -1;
+
+	if (sp->xleft <= 0) {
+		// don't interrupt me
+		//netif_stop_queue(sp->dev);
+		//sp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+		//sp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		sp->led_state = 0x60;
+		if (sp->tty->driver->write(sp->tty, &sp->led_state, 1) < 1 || 
+				(sp->tty->driver->write(sp->tty, xbuff, len) < len)) {
+			//sp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+printk(KERN_DEBUG "tty busy\n");
+			return -1;
+		}
+		//if (sp->xleft > 0)
+			//sp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+		// hmm, the tty driver counts these status requests, so we have to correct :(
+		// but only when already > 0..
+		//if (sp->stats.tx_packets)
+			//sp->stats.tx_packets--;
+		return 0;
+	}
+	return -1;
+	// re-enable queue only if the normal xbuff is empty
+	//if (sp->xleft <= 0) {
+		//sp->tx_enable = 0;
+		//netif_wake_queue(sp->dev);
+	//}
+}
+
+/* some TNCs have status reports - polled by timer */
+
+static void status_request(unsigned long data)
+{
+	struct sixpack *sp = (struct sixpack *) data;
+
+	if (!sp->hw.hw_type.type) {
+		del_timer(&sp->status_t); /* stop timer for unknown hardware */
+		return;
+	}
+
+	/*
+	 * Provisorisch (?) hier angeordnet: TNC-Parameter abfragen
+	 * spaeter auch zyklisch, aber nicht so oft, ansonsten bei Aenderung
+	 * soll/ist 
+	 * status request is encoded as param txdelay:
+	 * txd >251 is reserved for those kinds of hack
+	 */
+	if (sp->xleft <= 0) {
+		unsigned char data[3*2];
+
+		if (!is_hw_tnc430(sp)) {
+			/* Don't start timer for unsupported hardware */
+			return;
+		}
+
+		if (sp->hw.hf_want.status254_cmd_t + HZ < jiffies) {
+
+			// every second
+
+			sp->hw.hf_want.status254_cmd_t = jiffies;
+			//if (sp->hw.hf_want.status254_cmd && sp->hw.hf_act.status254_cmd_t && sp->hw.hf_act.status254_cmd != sp->hw.hf_want.status254_cmd) {
+				//if (sp->hw.hf_act.status254_cmd_t + 10*HZ > jiffies) {
+					//goto give_some_more_time;
+				//}
+				//printk(KERN_INFO "6pack: status254 cmd %d not acked for 10s. last received was %d. got lost?\n", sp->hw.hf_want.status254_cmd, sp->hw.hf_act.status254_cmd);
+			//}
+
+			data[0] = 254;	/* Kennung TNC430-Special */
+			switch (sp->hw.hf_want.status254_cmd) {
+			case 1:
+				data[1] = 2;	/* Mode */
+				break;
+			default:
+				data[1] = 1;	/* Frequenz */
+			}
+			//printk(KERN_DEBUG "requesting %ld data[0] %d data[1] = %d, last was %d\n", jiffies, data[0], data[1], sp->hw.hf_want.status254_cmd);
+			if (sp_xmit_status_request(sp, data, 2) >= 0) {
+				// success
+				sp->hw.hf_want.status254_cmd = data[1];
+			}
+		} else {
+//give_some_more_time:
+
+			/*
+			 * nothing important to do
+			 * data[len++] = 16;	// S-Meter
+			 * sp_xmit_status_request(sp, data, len);
+			 */
+
+			/*
+			 * we send prio command 0xe8 (1110 1000): channel 0
+			 * this will put the tnc in mode 0xe8
+			 * and automaticaly leads to an s-meter response,
+			 */
+
+			/*
+			 * dummy - not actually sent.  Triggers 0xe8 via
+			 * sp_xmit_status_request() / * encode_sixpack()
+			 */
+			data[0] = 254;
+			sp_xmit_status_request(sp, data, 0);
+		}
+	}
+
+	/*
+	 * Cyclic 100ms timer
+	 */
+	mod_timer(&sp->status_t, jiffies + (HZ/10));
+}
+
+/* TNC has hardware information */
+
+static void initialize_hardware(struct sixpack *sp)
+{
+	char *type, *author;
+	int i;
+
+	sp->hw.hw_type.announced = 1;
+
+	switch (sp->hw.hw_type.type) {
+	case 4:
+		type = "PR430";
+		break;
+	case 0:
+		return;					/* do not announce */
+	default:
+		type = "[unknown]";
+	}
+
+	printk(KERN_INFO "6pack: TNC is extended-6pack capable.\n");
+
+	switch (sp->hw.hw_type.author) {
+	case 7:
+		author = "DK7WJ";
+		break;
+	default:
+		author = "[unknown]";
+	}
+	printk(KERN_INFO "6pack: TNC type %d: %s. author %d: %s. extra: %d, %d.\n",
+	       sp->hw.hw_type.type, type,
+	       sp->hw.hw_type.author, author,
+	       sp->hw.hw_type.major, sp->hw.hw_type.minor);
+
+	/*
+	 * Poll config - supported TNC types only
+	 */
+
+	if (is_hw_tnc430(sp)) {
+		unsigned char data[2];
+
+		data[0] = 254;
+		for (i = 1; i < /* 3 */ 4; i++) {
+			data[1] = i;
+			if (sp_xmit_status_request(sp, data, 2) < 0)
+				break;
+		}
+	}
+
+	/*
+	 * New request thread for known hardware
+	 */
+	if (sp->hw.hw_type.type)
+		__mod_timer(&sp->status_t, jiffies + (HZ/10));
+}
+
+/* several status reports for privilliged commands encoded as "txd's" > 251 */
+
+static void trace_unknown_status_report(struct sixpack *sp)
+{
+	//sp->led_state = 0x68;
+	//sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+	//#ifdef DEBUG
+	trace_frame(sp, "status info");
+	//#endif
+}
+
+/* pr430 hardware status */
+
+static void decode_status254_tnc430(struct sixpack *sp)
+{
+	int len;
+	unsigned char *args;
+
+	/*
+	 * len: sp->rxcount is already without checksum
+	 * and without pos0 (txdelay) and refers pure data
+	 */
+	len = sp->rcount;
+	/* cooked_buf[0] is still txdelay == 254 */
+	args = &sp->cooked_buf[1];
+
+	if (len == 1 && args[0] == 0 && args[1] == 1 /* checksum */) {
+		// answer was 0xfe + 0x00 + 0x01 == 0xff (checksum ok, as verified before)
+		printk(KERN_INFO "6pack: tnc is alive (response: 0xfe 0x00 0x01)\n");
+		return;
+	}
+	// remember last status we received from tnc. is it the status we asked for?
+	// note that for s-meter (16) we poll regulary
+	if (args[0] != 16) {
+		// hack: sometimes there's the answer in the rx buffer. give
+		//   time for 2 pending status commands (more never seen)
+		static int complain = 0;
+		if (sp->hw.hf_want.status254_cmd && args[0] != sp->hw.hf_want.status254_cmd) {
+			if (complain > 1) {
+				printk(KERN_INFO "6pack: decode_status254_tnc430: asked for %d, got %d\n", sp->hw.hf_want.status254_cmd, args[0]);
+				complain = 0;
+			} else
+				complain++;
+		} else
+			complain = 0;
+		//sp->hw.hf_act.status254_cmd = args[0];
+		//sp->hw.hf_act.status254_cmd_t = jiffies;
+	}
+	//printk(KERN_DEBUG "6pack: decode_status254_tnc430() called, cmd %d, len %d %ld\n", args[0], len, jiffies);
+
+	switch (args[0]) {
+	case 1:	// QRG and Shift
+		if (len == 4) {
+			unsigned int freq;
+			int shift;
+
+			freq  = (args[1] * 256 + args[2]) * 12500 + 400000000;
+			shift = args[3] * 100000;
+			if (sp->hw.hf_act.qrg != freq) {
+				if (sp->hw.hf_want.qrg)
+					printk(KERN_INFO "6pack: qrg change: %d to %d\n", sp->hw.hf_act.qrg, freq);
+				sp->hw.hf_act.qrg = freq;
+			}
+			if (sp->hw.hf_act.shift != shift) {
+				if (sp->hw.hf_want.shift)
+					printk(KERN_INFO "6pack: shift change: %d to %d\n", sp->hw.hf_act.shift, shift);
+				sp->hw.hf_act.shift = shift;
+			}
+			if (sp->hw.hf_act.qrg != sp->hw.hf_want.qrg) {
+				printk(KERN_INFO "6pack: qrg change: %d\n", freq);
+			}
+			if (sp->hw.hf_act.shift != sp->hw.hf_want.shift) {
+				printk(KERN_INFO "6pack: shift change: %d\n", shift);
+			}
+			// never initialized?
+			if (!sp->hw.hf_want.qrg) {
+				sp->hw.hf_want.qrg = sp->hw.hf_act.qrg;
+				sp->hw.hf_want.shift = sp->hw.hf_act.shift;
+			}
+		}
+		break;
+	case 2:	// Mode
+		if (len == 2) {
+			char new_mode;
+			if (args[1] & DO_FM) {
+				new_mode = 3;
+				sp->hw.hf_act.baud = 0;	// wg. Signalisierung in Parms
+			}
+			else if ((args[1] & ~DO_DELAY_TX) == DO_AFSK) {
+				new_mode = 2;	// AFSK
+				sp->hw.hf_act.baud = 12;
+			}
+			else {
+				new_mode = 1;	// FSK
+				sp->hw.hf_act.baud = 96;
+			}
+			if (sp->hw.hf_act.mode != new_mode) {
+				if (sp->hw.hf_want.mode) {
+					printk(KERN_INFO "6pack: mode change: %d %d\n", new_mode, sp->hw.hf_act.baud);
+				}
+				sp->hw.hf_act.mode = new_mode;
+				// never initialized?
+				if (!sp->hw.hf_want.mode) {
+					sp->hw.hf_want.mode = sp->hw.hf_act.mode;
+					sp->hw.hf_want.baud = sp->hw.hf_act.baud;
+				}
+			}
+		}
+		break;
+	case 3: /* current tx-power */
+		//printk(KERN_DEBUG "txpower act %d want %d got %d\n",
+		//       sp->hw.hf_act.txpower, sp->hw.hf_want.txpower, args[1]);
+		if (len == 2) {
+			if (sp->hw.hf_act.txpower != args[1]) {
+				//if (sp->hw.hf_want.txpower)
+					printk(KERN_INFO "6pack: tx power change %d\n", args[1]);
+				sp->hw.hf_act.txpower = args[1];
+			}
+			if (!sp->hw.hf_want.txpower)
+				sp->hw.hf_want.txpower = sp->hw.hf_act.txpower;
+		}
+		break;
+	case 16:
+		/*
+		 * S-Meter, Hub etc..
+		 * sometimes it's 79*4 316 196dbm - what does that mean???
+		 * args[1] = args[1] & 0xf;
+		 * sometimes on tx pr430 reports arg1=14 arg2=255 arg3=57.
+		 * perhaps not a center information in arg2,3. currently
+		 * ignore values >= 192
+		 */
+		if (args[2] >= 192)
+			break;
+		if (/* no, off */ 0 && sp->hw.smeter != args[1]*4) {
+			char tmpbuf[16];
+			if (args[1] < 16)
+				sprintf(tmpbuf, "%d", (args[1] - 1) * 4 / 6);
+			else
+				sprintf(tmpbuf, "9+%d", (args[1]-15) * 10);
+			printk(KERN_DEBUG "6pack: s-meter change: %d*4 %d %ddbm -> S%s\n", args[1], args[1]*4, args[1]*4-120, tmpbuf);
+		}
+		sp->hw.smeter = args[1] * 4;
+		if (len > 2) {
+			//if (sp->hw.center != (args[2] ^ 0x80))
+				//printk(KERN_DEBUG "6pack: center change: %dHz (%d)\n", ((1000/5) * (char ) (args[2] ^ 0x80)), (char ) (args[2] ^ 0x80));
+			// inform if signal is >7kHz out ouf center
+			if (abs((char) (args[2] ^ 0x80)) > 7*5/1)
+				printk(KERN_DEBUG "6pack: info: center out of range: %dHz (%d)\n", ((1000/5) * (char ) (args[2] ^ 0x80)), (char ) args[2] ^ 0x80);
+			sp->hw.center = (char ) (args[2] ^ 0x80);	// 128->0
+		}
+		if (len > 3) {
+			//if (sp->hw.hub != args[3])
+				//printk(KERN_DEBUG "6pack: hub change: %dHz (%d)\n", ((3000/25) * args[3]), args[3]);
+			//if (args[3] > 4*25/3)
+				//printk(KERN_DEBUG "6pack: info: great hub: %dHz (%d)\n", 3000/25 * args[3], args[3]);
+			sp->hw.hub = args[3];
+		}
+		break;
+	default:
+		printk("6pack: unknown status from tnc430: %d cmd %d len %d\n", sp->cooked_buf[0], args[0], len);
+		trace_unknown_status_report(sp);
+	}
+
+	//printk(KERN_DEBUG "6pack: debug: cmd %d %d qrg %d shift %d mode %d baud %d smeter %d center %d hub %d\n",
+	//		                     args[0], args[1], (int ) sp->hw.hf_act.qrg, (int ) sp->hw.hf_act.shift, (int ) sp->hw.hf_act.mode, (int ) sp->hw.hf_act.baud, (int ) sp->hw.smeter, (int ) sp->hw.center, (int ) sp->hw.hub);
+}
+
+static void decode_status254(struct sixpack *sp)
+{
+	if (is_hw_tnc430(sp))
+		decode_status254_tnc430(sp);
+	else
+		trace_unknown_status_report(sp);
+}
+
+static void decode_status255(struct sixpack *sp)
+{
+	if (sp->rcount >= 4) {
+		sp->hw.hw_type.type	= sp->cooked_buf[1];
+		sp->hw.hw_type.author	= sp->cooked_buf[2];
+		sp->hw.hw_type.major	= sp->cooked_buf[3];
+		sp->hw.hw_type.minor	= sp->cooked_buf[4];
+
+		if (!sp->hw.hw_type.announced)
+			initialize_hardware(sp);
+	} else
+		trace_unknown_status_report(sp);
+}
 
 /*
  * This is the routine that sends the received data to the kernel AX.25.
@@ -409,6 +873,28 @@
 	sp->stats.rx_dropped++;
 }
 
+/* interprete 6pack extension or rx data */
+
+static void sp_rxhandler(struct sixpack *sp)
+{
+	// tx_delay > 251 have special means
+	switch (sp->cooked_buf[0]) {
+	case 255:
+		decode_status255(sp);
+		break;
+	case 254:
+		decode_status254(sp);
+		break;
+	default:
+		if (sp->rcount >= 5) {
+			sp_bump(sp, 0);
+			break;
+		}
+
+		sp->stats.rx_length_errors++;
+	}
+}
+
 
 /* ----------------------------------------------------------------------- */
 
@@ -480,62 +966,17 @@
 }
 
 /*
- * Handle the 'receiver data ready' interrupt.
- * This function is called by the 'tty_io' module in the kernel when
- * a block of 6pack data has been received, which can now be decapsulated
- * and sent on to some IP layer for further processing.
- */
-static void sixpack_receive_buf(struct tty_struct *tty,
-	const unsigned char *cp, char *fp, int count)
-{
-	struct sixpack *sp;
-	unsigned char buf[512];
-	int count1;
-
-	if (!count)
-		return;
-
-	sp = sp_get(tty);
-	if (!sp)
-		return;
-
-	memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf));
-
-	/* Read the characters out of the buffer */
-
-	count1 = count;
-	while (count) {
-		count--;
-		if (fp && *fp++) {
-			if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
-				sp->stats.rx_errors++;
-			continue;
-		}
-	}
-	sixpack_decode(sp, buf, count1);
-
-	sp_put(sp);
-	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
-	    && tty->driver->unthrottle)
-		tty->driver->unthrottle(tty);
-}
-
-/*
  * Try to resync the TNC. Called by the resync timer defined in
  * decode_prio_command
  */
 
-#define TNC_UNINITIALIZED	0
-#define TNC_UNSYNC_STARTUP	1
-#define TNC_UNSYNCED		2
-#define TNC_IN_SYNC		3
-
-static void __tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+static void __tnc_set_sync_state(struct sixpack *sp,
+	enum tnc_state new_tnc_state)
 {
 	char *msg;
 
 	switch (new_tnc_state) {
-	default:			/* gcc oh piece-o-crap ... */
+	default:
 	case TNC_UNSYNC_STARTUP:
 		msg = "Synchronizing with TNC";
 		break;
@@ -551,7 +992,8 @@
 	printk(KERN_INFO "%s: %s\n", sp->dev->name, msg);
 }
 
-static inline void tnc_set_sync_state(struct sixpack *sp, int new_tnc_state)
+static inline void tnc_set_sync_state(struct sixpack *sp,
+	enum tnc_state new_tnc_state)
 {
 	int old_tnc_state = sp->tnc_state;
 
@@ -575,70 +1017,582 @@
 	sp->status1 = 1;
 	sp->status2 = 0;
 
-	/* resync the TNC */
+	/* Resync the TNC */
 
 	sp->led_state = 0x60;
 	sp->tty->driver->write(sp->tty, &sp->led_state, 1);
 	sp->tty->driver->write(sp->tty, &resync_cmd, 1);
 
-
 	/* Start resync timer again -- the TNC might be still absent */
-
-	del_timer(&sp->resync_t);
-	sp->resync_t.data	= (unsigned long) sp;
-	sp->resync_t.function	= resync_tnc;
-	sp->resync_t.expires	= jiffies + SIXP_RESYNC_TIMEOUT;
-	add_timer(&sp->resync_t);
+	mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT);
 }
 
-static inline int tnc_init(struct sixpack *sp)
+static inline void tnc_init(struct sixpack *sp)
 {
-	unsigned char inbyte = 0xe8;
+	static const unsigned char inbyte = 0xe8;
 
 	tnc_set_sync_state(sp, TNC_UNSYNC_STARTUP);
 
 	sp->tty->driver->write(sp->tty, &inbyte, 1);
 
-	del_timer(&sp->resync_t);
+	init_timer(&sp->resync_t);
 	sp->resync_t.data = (unsigned long) sp;
 	sp->resync_t.function = resync_tnc;
-	sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
-	add_timer(&sp->resync_t);
-
-	return 0;
+	__mod_timer(&sp->resync_t, jiffies + SIXP_RESYNC_TIMEOUT);
 }
 
-/*
- * Open the high-level part of the 6pack channel.
- * This function is called by the TTY module when the
- * 6pack line discipline is called for.  Because we are
- * sure the tty line exists, we only have to link it to
- * a free 6pcack channel...
- */
-static int sixpack_open(struct tty_struct *tty)
+/* decode 4 sixpack-encoded bytes into 3 data bytes */
+
+static void decode_data(struct sixpack *sp, unsigned char inbyte)
 {
-	char *rbuff = NULL, *xbuff = NULL;
-	struct net_device *dev;
-	struct sixpack *sp;
-	unsigned long len;
-	int err = 0;
+	unsigned char *buf;
 
-	if (!capable(CAP_NET_ADMIN))
-		return -EPERM;
+	if (sp->rx_count != 3) {
+		sp->raw_buf[sp->rx_count++] = inbyte;
 
-	dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
-	if (!dev) {
-		err = -ENOMEM;
-		goto out;
+		return;
 	}
 
-	sp = netdev_priv(dev);
-	sp->dev = dev;
-
-	spin_lock_init(&sp->lock);
-	atomic_set(&sp->refcnt, 1);
-	init_MUTEX_LOCKED(&sp->dead_sem);
-
+	buf = sp->raw_buf;
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		buf[0] | ((buf[1] << 2) & 0xc0);
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
+	sp->cooked_buf[sp->rx_count_cooked++] =
+		(buf[2] & 0x03) | (inbyte << 2);
+	sp->rx_count = 0;
+}
+
+/* identify and execute a 6pack priority command byte */
+
+static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
+{
+	unsigned char channel;
+	int actual;
+
+	channel = cmd & SIXP_CHN_MASK;
+	if ((cmd & SIXP_PRIO_DATA_MASK) != 0) {     /* idle ? */
+
+	/* RX and DCD flags can only be set in the same prio command,
+	   if the DCD flag has been set without the RX flag in the previous
+	   prio command. If DCD has not been set before, something in the
+	   transmission has gone wrong. In this case, RX and DCD are
+	   cleared in order to prevent the decode_data routine from
+	   reading further data that might be corrupt. */
+
+		if (((sp->status & SIXP_DCD_MASK) == 0) &&
+			((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
+				if (sp->status != 1)
+					printk(KERN_DEBUG "6pack: protocol violation\n");
+				else
+					sp->status = 0;
+				cmd &= !SIXP_RX_DCD_MASK;
+		}
+		sp->status = cmd & SIXP_PRIO_DATA_MASK;
+	} else { /* output watchdog char if idle */
+		if ((sp->status2 != 0) && (sp->duplex == 1)) {
+			sp->led_state = 0x70;
+			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			sp->tx_enable = 1;
+			actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2);
+			sp->xleft -= actual;
+			sp->xhead += actual;
+			sp->led_state = 0x60;
+			sp->status2 = 0;
+
+		}
+	}
+
+	/* needed to trigger the TNC watchdog */
+	sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+
+	/*
+	 * If the state byte has been received, the TNC is present,
+         * so the resync timer can be reset.
+	 */
+	if (sp->tnc_state == TNC_IN_SYNC)
+		mod_timer(&sp->resync_t, jiffies + SIXP_INIT_RESYNC_TIMEOUT);
+
+	sp->status1 = cmd & SIXP_PRIO_DATA_MASK;
+}
+
+/* identify and execute a standard 6pack command byte */
+
+static void decode_std_command(struct sixpack *sp, unsigned char cmd)
+{
+	unsigned char checksum = 0, rest = 0, channel;
+	short i;
+
+	channel = cmd & SIXP_CHN_MASK;
+	switch (cmd & SIXP_CMD_MASK) {     /* normal command */
+	case SIXP_SEOF:
+		if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) {
+			if ((sp->status & SIXP_RX_DCD_MASK) ==
+				SIXP_RX_DCD_MASK) {
+				sp->led_state = 0x68;
+				sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			}
+		} else {
+			sp->led_state = 0x60;
+			/* fill trailing bytes with zeroes */
+			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
+			rest = sp->rx_count;
+			if (rest != 0)
+				 for (i = rest; i <= 3; i++)
+					decode_data(sp, 0);
+			if (rest == 2)
+				sp->rx_count_cooked -= 2;
+			else if (rest == 3)
+				sp->rx_count_cooked -= 1;
+			for (i = 0; i < sp->rx_count_cooked; i++)
+				checksum += sp->cooked_buf[i];
+			if (checksum != SIXP_CHKSUM) {
+				sp->stats.rx_crc_errors++;
+				printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum);
+			} else {
+				sp->rcount = sp->rx_count_cooked - 2;
+				sp_rxhandler(sp);
+			}
+			sp->rx_count_cooked = 0;
+		}
+		break;
+	case SIXP_TX_URUN:
+		sp->stats.tx_fifo_errors++;
+		printk(KERN_DEBUG "6pack: TX underrun\n");
+		break;
+	case SIXP_RX_ORUN:
+		sp->stats.rx_fifo_errors++;
+		printk(KERN_DEBUG "6pack: RX overrun\n");
+		break;
+	case SIXP_RX_BUF_OVL:
+		sp->stats.rx_length_errors++;
+		printk(KERN_DEBUG "6pack: RX buffer overflow\n");
+	default:
+		printk(KERN_DEBUG "6pack: unknown std command: %d (%2.2x)\n", cmd, cmd);
+	}
+}
+
+/* decode a 6pack packet */
+
+static inline void
+sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count)
+{
+	unsigned char inbyte;
+	int count1;
+
+	for (count1 = 0; count1 < count; count1++) {
+		inbyte = pre_rbuff[count1];
+		if (inbyte == SIXP_FOUND_TNC) {
+			tnc_set_sync_state(sp, TNC_IN_SYNC);
+			mod_timer(&sp->resync_t,
+			          jiffies + SIXP_INIT_RESYNC_TIMEOUT);
+		}
+		if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
+			decode_prio_command(sp, inbyte);
+		else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
+			decode_std_command(sp, inbyte);
+		else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
+			decode_data(sp, inbyte);
+	}
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of 6pack data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+static void sixpack_receive_buf(struct tty_struct *tty,
+	const unsigned char *cp, char *fp, int count)
+{
+	struct sixpack *sp;
+	unsigned char buf[512];
+	int count1;
+
+	if (!count)
+		return;
+
+	sp = sp_get(tty);
+	if (!sp)
+		return;
+
+	memcpy(buf, cp, count < sizeof(buf) ? count : sizeof(buf));
+
+	/* Read the characters out of the buffer */
+
+	count1 = count;
+	while (count) {
+		count--;
+		if (fp && *fp++) {
+			if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
+				sp->stats.rx_errors++;
+			continue;
+		}
+	}
+	sixpack_decode(sp, buf, count1);
+
+	sp_put(sp);
+	if (test_and_clear_bit(TTY_THROTTLED, &tty->flags)
+	    && tty->driver->unthrottle)
+		tty->driver->unthrottle(tty);
+}
+
+/*
+ * PR-430 specific configuration
+ */
+
+/*
+ * Shift is a byte, so there's a design limit.
+ */
+#define	SHIFT_MAX	256*100000
+
+/*
+ * 12.5 kHz steps from 400Mhz up
+ */
+#define	QRG2CHANNEL(x)	(((x - 400000000) * 2 + 1) / 25000)
+
+static int change_qrg_shift(struct sixpack *sp, unsigned long freq, long shift)
+{
+	unsigned int qrg, txqrg;
+	unsigned char data[5];
+
+	if (freq < (long ) -1 * SHIFT_MAX)
+		return -EINVAL;
+
+	qrg = sp->hw.hf_act.qrg;
+
+	printk(KERN_DEBUG "qrg %d act.qrg %d\n", qrg, sp->hw.hf_act.qrg);
+
+	if ((long ) freq < (long ) SHIFT_MAX) {
+		// new shift
+		shift = (freq / 100000) * 100000 /* rounded */;
+		if (shift)
+			printk(KERN_DEBUG "switching to shift %ld\n", shift);
+		else
+			printk(KERN_DEBUG "switching to simplex\n");
+	} else {
+		//printk(KERN_DEBUG "test2: %ld > %d\n",
+		//       (long ) freq /1000, SHIFT_MAX / 1000);
+		/*
+		 * New QRG
+		 */
+		qrg = freq;
+	}
+	txqrg = qrg + shift;
+#ifdef US_QRG
+	if (qrg > 420000000 && qrg < 450000000
+		&& txqrg > 420000000 && txqrg < 450000000)
+#else
+	if (qrg > 430000000 && qrg < 440000000
+		&& txqrg > 430000000 && txqrg < 440000000)
+#endif
+	{
+		sp->hw.hf_want.qrg = qrg;
+		sp->hw.hf_want.shift = shift;
+		data[0] = 254;	// Kennung TNC430-Special
+		data[1] = 1;
+		data[2] = QRG2CHANNEL(qrg) / 256;
+		data[3] = QRG2CHANNEL(qrg);
+		data[4] = shift / 100000;
+		sp->hw.hf_want.status254_cmd = data[1];
+		{
+			printk(KERN_DEBUG "New qrg/shift: "
+			       "%d %d, sending %d %d %d %d %d\n",
+			       sp->hw.hf_want.qrg, sp->hw.hf_want.shift,
+			       data[0], data[1], data[2], data[3], data[4]);
+
+			if (sp_xmit_status_request(sp, data, 5) < 0)
+				return -EADDRNOTAVAIL;
+		}
+		/*
+		 * And poll
+		 */
+		//sp_xmit_status_request(sp, data, 2);
+
+		return 0;
+	}
+
+	printk(KERN_INFO "6pack: refusing qsy: out of band range: "
+	       "qrg %d shift %ld -> tx-qrg %ld.\n", qrg, shift, qrg + shift);
+
+	return -EINVAL;
+}
+
+static int change_qrg(struct net_device *dev, unsigned long freq)
+{
+	struct sixpack *sp = netdev_priv(dev);
+
+	return change_qrg_shift(sp, freq, sp->hw.hf_act.shift);
+}
+
+static int change_shift(struct net_device *dev, unsigned long txshift)
+{
+	struct sixpack *sp = netdev_priv(dev);
+	long shift = txshift;
+
+	return change_qrg_shift(sp, sp->hw.hf_act.qrg, shift);
+}
+
+static int change_txpower(struct net_device *dev, unsigned long txpower)
+{
+	struct sixpack *sp = netdev_priv(dev);
+	unsigned char data[3];
+
+	data[0] = 254;
+	data[1] = 3;
+	switch (txpower) {
+	case 0:
+		sp->hw.hf_want.txpower = 0;
+		break;
+
+	case 1: // ord
+	case 16: // cmd
+	case 25: // dBm
+		sp->hw.hf_want.txpower = 16;
+		break;
+
+	case 2: // ord
+	case 64: // cmd
+	case 30: // dBm
+		sp->hw.hf_want.txpower = 64;
+		break;
+
+	case 3: // ord
+	case 255: // cmd
+	case 38: // dBm
+		sp->hw.hf_want.txpower = 255;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+	data[2] = sp->hw.hf_want.txpower;
+
+	sp->hw.hf_want.status254_cmd = data[1];
+	printk(KERN_DEBUG "New txpower: %d (%ld), sending %d %d %d\n",
+	       sp->hw.hf_want.txpower, txpower, data[0], data[1], data[2]);
+
+	if (sp_xmit_status_request(sp, data, 3) < 0)
+		return -EADDRNOTAVAIL;
+
+	/*
+	 * and poll
+	 */
+	// sp_xmit_status_request(sp, data, 2);
+
+	return 0;
+}
+
+/*
+ * For now changing the bitrate implies changing to AFSK for 1200bps
+ * and to FSK for 9600bps.  This may eventually change, you've been
+ * warned.
+ */
+static int change_bitrate(struct net_device *dev, unsigned long bitrate)
+{
+	struct sixpack *sp = netdev_priv(dev);
+	unsigned char data [3];
+
+	data[0] = 254;
+	data[1] = 2;
+
+	switch (bitrate) {
+	case 2:
+	case 1200:
+		sp->hw.hf_want.baud = 12;
+		data[2] = DO_AFSK | DO_DELAY_TX;
+		sp->hw.hf_want.mode = 2;
+		break;
+	case 1:
+	case 9600:
+		sp->hw.hf_want.baud = 96;
+		data[2] = DO_DELAY_TX;
+		sp->hw.hf_want.mode = 1;
+		break;
+#if 0
+	case 3:
+		/*
+		 * Voice - currently not supported
+		 */
+		sp->hw.hf_want.baud = 0;
+		data[2] = DO_FM;
+		sp->hw.hf_want.mode = 3;
+		break;
+#endif
+	default:
+		return -EINVAL;
+	}
+
+	sp->hw.hf_want.status254_cmd = data[1];
+	printk(KERN_DEBUG "New mode: %d (%ld, %d), sending %d %d %d\n",
+	       sp->hw.hf_want.mode, bitrate, sp->hw.hf_want.baud * 100,
+	       data[0], data[1], data[2]);
+
+	if (sp_xmit_status_request(sp, data, 3) < 0)
+		return -EADDRNOTAVAIL;
+	/*
+	 * and poll
+	 */
+	//sp_xmit_status_request(sp, data, 2);
+
+	return 0;
+}
+
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+
+static const char fmt_hex[] = "%#x\n";
+static const char fmt_dec[] = "%d\n";
+static const char fmt_ulong[] = "%lu\n";
+
+static inline int dev_isalive(const struct net_device *dev)
+{
+	return dev->reg_state == NETREG_REGISTERED;
+}
+
+/* helper function that does all the locking etc for wireless stats */
+static ssize_t tnc_show(struct class_device *cd, char *buf,
+                        ssize_t (*format)(const struct sixpack *, char *))
+{
+	struct net_device *dev = to_net_dev(cd);
+	const struct sixpack *sp = netdev_priv(dev);
+	ssize_t ret = -EINVAL;
+
+	read_lock(&dev_base_lock);
+	if (dev_isalive(dev) && is_hw_tnc430(sp))
+		ret = format(sp, buf);
+	read_unlock(&dev_base_lock);
+
+	return ret;
+}
+
+/*
+ * Common code for r/w and r/o variables
+ */
+#define TNC_SHOW_VAR(name, field, format_string)			\
+									\
+static ssize_t format_sp_##name(const struct sixpack *sp, char *buf)	\
+{									\
+	return sprintf(buf, format_string, sp->field);			\
+}									\
+									\
+static ssize_t show_sp_##name(struct class_device *cd, char *buf)	\
+{									\
+	return tnc_show(cd, buf, format_sp_##name);			\
+}
+
+/*
+ * Macro to define a read-only variable
+ */
+#define TNC_RO_VAR(name, field, format_string)				\
+									\
+TNC_SHOW_VAR(name, field, format_string)				\
+									\
+static CLASS_DEVICE_ATTR(name, S_IRUGO, show_sp_##name, NULL)
+
+/*
+ * Macro to define a read-write variable
+ */
+#define TNC_RW_VAR(name, field, format_string)				\
+									\
+TNC_SHOW_VAR(name, field, format_string)				\
+									\
+static ssize_t store_sp_##name(struct class_device *dev,		\
+                               const char *buf, size_t len)		\
+{									\
+	return netdev_store(dev, buf, len, change_##name);		\
+}									\
+									\
+static CLASS_DEVICE_ATTR(name, S_IRUGO | S_IWUSR,			\
+                         show_sp_##name, store_sp_##name)
+
+/*
+ * Use same locking and permission rules as SIF* ioctl's
+ */
+static ssize_t netdev_store(struct class_device *dev,
+			    const char *buf, size_t len,
+			    int (*set)(struct net_device *, unsigned long))
+{
+	struct net_device *net = to_net_dev(dev);
+	char *endp;
+	unsigned long new;
+	int ret = -EINVAL;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	new = simple_strtoul(buf, &endp, 0);
+	if (endp == buf)
+		goto err;
+
+	rtnl_lock();
+	if (dev_isalive(net)) {
+		if ((ret = (*set)(net, new)) == 0)
+			ret = len;
+	}
+	rtnl_unlock();
+ err:
+	return ret;
+}
+
+TNC_RO_VAR(hwtype, hw.hw_type.type, fmt_dec);
+TNC_RO_VAR(author, hw.hw_type.author, fmt_dec);
+TNC_RO_VAR(major, hw.hw_type.major, fmt_dec);
+TNC_RO_VAR(minor, hw.hw_type.minor, fmt_dec);
+
+TNC_RW_VAR(qrg, hw.hf_act.qrg, fmt_dec);
+TNC_RW_VAR(shift, hw.hf_act.shift, fmt_dec);
+//TNC_RW_VAR(mode, hw.hf_act.mode, fmt_dec);
+TNC_RW_VAR(bitrate, hw.hf_act.baud, fmt_dec);
+TNC_RW_VAR(txpower, hw.hf_act.txpower, fmt_dec);
+
+static struct attribute *tnc_attrs[] = {
+	&class_device_attr_hwtype.attr,
+	&class_device_attr_author.attr,
+	&class_device_attr_major.attr,
+	&class_device_attr_minor.attr,
+
+	&class_device_attr_qrg.attr,
+	&class_device_attr_shift.attr,
+	//&class_device_attr_mode.attr,
+	&class_device_attr_bitrate.attr,
+	&class_device_attr_txpower.attr,
+	NULL
+};
+
+static struct attribute_group tnc_group = {
+	.name	= "tnc",
+	.attrs	= tnc_attrs,
+};
+
+/*
+ * Open the high-level part of the 6pack channel.  This function is called by
+ * the TTY module when the 6pack line discipline is called for.  Because we are
+ * sure the tty line exists, we only have to link it to a free 6pack channel ...
+ */
+static int sixpack_open(struct tty_struct *tty)
+{
+	char *rbuff = NULL, *xbuff = NULL;
+	struct net_device *dev;
+	struct sixpack *sp;
+	unsigned long len;
+	int err = 0;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	dev = alloc_netdev(sizeof(struct sixpack), "sp%d", sp_setup);
+	if (!dev) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	sp = netdev_priv(dev);
+	sp->dev = dev;
+
+	spin_lock_init(&sp->lock);
+	atomic_set(&sp->refcnt, 1);
+	init_MUTEX_LOCKED(&sp->dead_sem);
+
 	/* !!! length of the buffers. MTU is IP MTU, not PACLEN!  */
 
 	len = dev->mtu * 2;
@@ -680,7 +1634,12 @@
 	netif_start_queue(dev);
 
 	init_timer(&sp->tx_t);
-	init_timer(&sp->resync_t);
+	sp->tx_t.function = sp_xmit_on_air;
+	sp->tx_t.data = (unsigned long) sp;
+
+	init_timer(&sp->status_t);
+	sp->status_t.function = status_request;
+	sp->status_t.data = (unsigned long) sp;
 
 	spin_unlock_bh(&sp->lock);
 
@@ -691,10 +1650,17 @@
 	if (register_netdev(dev))
 		goto out_free;
 
+	err = sysfs_create_group(&dev->class_dev.kobj, &tnc_group);
+	if (err)
+		goto out_unregister;
+
 	tnc_init(sp);
 
 	return 0;
 
+out_unregister:
+	unregister_netdev(dev);
+
 out_free:
 	kfree(xbuff);
 	kfree(rbuff);
@@ -721,20 +1687,23 @@
 	sp = tty->disc_data;
 	tty->disc_data = NULL;
 	write_unlock(&disc_data_lock);
+
 	if (sp == 0)
 		return;
 
 	/*
-	 * We have now ensured that nobody can start using ap from now on, but
+	 * We have now ensured that nobody can start using sp from now on, but
 	 * we have to wait for all existing users to finish.
 	 */
 	if (!atomic_dec_and_test(&sp->refcnt))
 		down(&sp->dead_sem);
 
+	sysfs_remove_group(&sp->dev->class_dev.kobj, &tnc_group);
 	unregister_netdev(sp->dev);
 
 	del_timer(&sp->tx_t);
-	del_timer(&sp->resync_t);
+	del_timer_sync(&sp->resync_t);
+	del_timer_sync(&sp->status_t);
 
 	/* Free all 6pack frame buffers. */
 	kfree(sp->rbuff);
@@ -852,195 +1821,7 @@
 		printk(msg_unregfail, ret);
 }
 
-/* encode an AX.25 packet into 6pack */
-
-static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw,
-	int length, unsigned char tx_delay)
-{
-	int count = 0;
-	unsigned char checksum = 0, buf[400];
-	int raw_count = 0;
-
-	tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
-	tx_buf_raw[raw_count++] = SIXP_SEOF;
-
-	buf[0] = tx_delay;
-	for (count = 1; count < length; count++)
-		buf[count] = tx_buf[count];
-
-	for (count = 0; count < length; count++)
-		checksum += buf[count];
-	buf[length] = (unsigned char) 0xff - checksum;
-
-	for (count = 0; count <= length; count++) {
-		if ((count % 3) == 0) {
-			tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
-			tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
-		} else if ((count % 3) == 1) {
-			tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
-			tx_buf_raw[raw_count] =	((buf[count] >> 2) & 0x3c);
-		} else {
-			tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
-			tx_buf_raw[raw_count++] = (buf[count] >> 2);
-		}
-	}
-	if ((length % 3) != 2)
-		raw_count++;
-	tx_buf_raw[raw_count++] = SIXP_SEOF;
-	return raw_count;
-}
-
-/* decode 4 sixpack-encoded bytes into 3 data bytes */
-
-static void decode_data(struct sixpack *sp, unsigned char inbyte)
-{
-	unsigned char *buf;
-
-	if (sp->rx_count != 3) {
-		sp->raw_buf[sp->rx_count++] = inbyte;
-
-		return;
-	}
-
-	buf = sp->raw_buf;
-	sp->cooked_buf[sp->rx_count_cooked++] =
-		buf[0] | ((buf[1] << 2) & 0xc0);
-	sp->cooked_buf[sp->rx_count_cooked++] =
-		(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
-	sp->cooked_buf[sp->rx_count_cooked++] =
-		(buf[2] & 0x03) | (inbyte << 2);
-	sp->rx_count = 0;
-}
-
-/* identify and execute a 6pack priority command byte */
-
-static void decode_prio_command(struct sixpack *sp, unsigned char cmd)
-{
-	unsigned char channel;
-	int actual;
-
-	channel = cmd & SIXP_CHN_MASK;
-	if ((cmd & SIXP_PRIO_DATA_MASK) != 0) {     /* idle ? */
-
-	/* RX and DCD flags can only be set in the same prio command,
-	   if the DCD flag has been set without the RX flag in the previous
-	   prio command. If DCD has not been set before, something in the
-	   transmission has gone wrong. In this case, RX and DCD are
-	   cleared in order to prevent the decode_data routine from
-	   reading further data that might be corrupt. */
-
-		if (((sp->status & SIXP_DCD_MASK) == 0) &&
-			((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
-				if (sp->status != 1)
-					printk(KERN_DEBUG "6pack: protocol violation\n");
-				else
-					sp->status = 0;
-				cmd &= !SIXP_RX_DCD_MASK;
-		}
-		sp->status = cmd & SIXP_PRIO_DATA_MASK;
-	} else { /* output watchdog char if idle */
-		if ((sp->status2 != 0) && (sp->duplex == 1)) {
-			sp->led_state = 0x70;
-			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
-			sp->tx_enable = 1;
-			actual = sp->tty->driver->write(sp->tty, sp->xbuff, sp->status2);
-			sp->xleft -= actual;
-			sp->xhead += actual;
-			sp->led_state = 0x60;
-			sp->status2 = 0;
-
-		}
-	}
-
-	/* needed to trigger the TNC watchdog */
-	sp->tty->driver->write(sp->tty, &sp->led_state, 1);
-
-        /* if the state byte has been received, the TNC is present,
-           so the resync timer can be reset. */
-
-	if (sp->tnc_state == TNC_IN_SYNC) {
-		del_timer(&sp->resync_t);
-		sp->resync_t.data	= (unsigned long) sp;
-		sp->resync_t.function	= resync_tnc;
-		sp->resync_t.expires	= jiffies + SIXP_INIT_RESYNC_TIMEOUT;
-		add_timer(&sp->resync_t);
-	}
-
-	sp->status1 = cmd & SIXP_PRIO_DATA_MASK;
-}
-
-/* identify and execute a standard 6pack command byte */
-
-static void decode_std_command(struct sixpack *sp, unsigned char cmd)
-{
-	unsigned char checksum = 0, rest = 0, channel;
-	short i;
-
-	channel = cmd & SIXP_CHN_MASK;
-	switch (cmd & SIXP_CMD_MASK) {     /* normal command */
-	case SIXP_SEOF:
-		if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) {
-			if ((sp->status & SIXP_RX_DCD_MASK) ==
-				SIXP_RX_DCD_MASK) {
-				sp->led_state = 0x68;
-				sp->tty->driver->write(sp->tty, &sp->led_state, 1);
-			}
-		} else {
-			sp->led_state = 0x60;
-			/* fill trailing bytes with zeroes */
-			sp->tty->driver->write(sp->tty, &sp->led_state, 1);
-			rest = sp->rx_count;
-			if (rest != 0)
-				 for (i = rest; i <= 3; i++)
-					decode_data(sp, 0);
-			if (rest == 2)
-				sp->rx_count_cooked -= 2;
-			else if (rest == 3)
-				sp->rx_count_cooked -= 1;
-			for (i = 0; i < sp->rx_count_cooked; i++)
-				checksum += sp->cooked_buf[i];
-			if (checksum != SIXP_CHKSUM) {
-				printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum);
-			} else {
-				sp->rcount = sp->rx_count_cooked-2;
-				sp_bump(sp, 0);
-			}
-			sp->rx_count_cooked = 0;
-		}
-		break;
-	case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n");
-		break;
-	case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n");
-		break;
-	case SIXP_RX_BUF_OVL:
-		printk(KERN_DEBUG "6pack: RX buffer overflow\n");
-	}
-}
-
-/* decode a 6pack packet */
-
-static void
-sixpack_decode(struct sixpack *sp, unsigned char *pre_rbuff, int count)
-{
-	unsigned char inbyte;
-	int count1;
-
-	for (count1 = 0; count1 < count; count1++) {
-		inbyte = pre_rbuff[count1];
-		if (inbyte == SIXP_FOUND_TNC) {
-			tnc_set_sync_state(sp, TNC_IN_SYNC);
-			del_timer(&sp->resync_t);
-		}
-		if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
-			decode_prio_command(sp, inbyte);
-		else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
-			decode_std_command(sp, inbyte);
-		else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
-			decode_data(sp, inbyte);
-	}
-}
-
-MODULE_AUTHOR("Ralf Baechle DO1GRB <ralf@linux-mips.org>");
+MODULE_AUTHOR("Ralf Baechle DL5RB <ralf@linux-mips.org>");
 MODULE_DESCRIPTION("6pack driver for AX.25");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_LDISC(N_6PACK);
-
To unsubscribe from this list: send the line "unsubscribe linux-hams" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help