All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] First cut of PR430 / extended 6pack driver
@ 2005-07-11  8:21 Ralf Baechle DL5RB
  2005-07-11 21:59 ` YAPP File Transfer with Linux Bill Vodall WA7NWP
  0 siblings, 1 reply; 52+ messages in thread
From: Ralf Baechle DL5RB @ 2005-07-11  8:21 UTC (permalink / raw)
  To: linux-hams

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

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2005-08-02 13:20 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2005-07-11  8:21 [PATCH] First cut of PR430 / extended 6pack driver Ralf Baechle DL5RB
2005-07-11 21:59 ` YAPP File Transfer with Linux Bill Vodall WA7NWP
2005-07-11 22:06   ` Curt, WE7U
2005-07-11 22:12   ` Curt, WE7U
2005-07-12 11:42   ` Rodolfo Brasnarof
2005-07-12 14:13     ` Bill Vodall
2005-07-12 14:21       ` Digi-ned output file and logrotate Bill Vodall
2005-07-12 15:24         ` Jim Bayer
2005-07-12 14:56       ` YAPP File Transfer with Linux Bob Nielsen
2005-07-12 14:55         ` Bill Vodall
2005-07-12 15:28           ` Bob Nielsen
2005-07-12 17:05             ` Bill Vodall
2005-07-12 18:07               ` Robert Eliassen
2005-07-12 18:51                 ` Jeremy Utley
2005-07-12 19:11                   ` Bill Vodall WA7NWP
2005-07-13  7:53                     ` Robert Eliassen
2005-07-13 11:03                       ` Tomi Manninen
2005-07-13 14:41                         ` Chuck Hast
2005-07-13 17:51                       ` Dave Platt
2005-07-14  0:19                         ` Bob Nielsen
2005-07-12 20:51               ` Michael Taylor
2005-07-12 22:03                 ` Bill - WA7NWP
2005-07-12 23:56                   ` Chuck Hast
2005-07-12 15:13         ` Robert Eliassen
2005-07-12 15:22           ` SSH and the NONE option Bill Vodall
2005-07-12 16:55             ` Ralf Baechle DL5RB
2005-07-12 17:02               ` Bill Vodall
2005-07-12 18:04             ` Jonathan Lassoff
2005-07-12 19:08               ` Bill Vodall WA7NWP
2005-07-12 20:00                 ` Jim Bayer
2005-07-12 20:43                   ` Michael Taylor
2005-07-12 20:41                 ` Michael Taylor
2005-07-12 21:57                   ` Bill - WA7NWP
2005-07-12 22:19             ` Dennis Boone
2005-07-14  7:59             ` Ralf Baechle DL5RB
2005-07-14  9:47               ` Per Crusefalk
2005-07-14 14:53                 ` Jim Bayer
2005-07-14 15:12                   ` Andrew Bates
2005-07-14 17:01                     ` Dave Platt
2005-07-14 15:27                   ` Bob Snyder
2005-07-14 16:28                     ` Jonathan Lassoff
2005-07-14 19:02                       ` Bob Snyder
2005-07-14 19:28                         ` Curt, WE7U
2005-07-14 20:43                           ` Bob Snyder
2005-07-30  1:31                             ` SSH and the NONE option - more Bill - WA7NWP
2005-07-30  8:19                               ` Robert Snyder
2005-08-01 11:34                               ` Ralf Baechle DL5RB
2005-08-02 13:20                                 ` Bill Vodall
2005-07-14 19:51                         ` SSH and the NONE option Andrew Bates
2005-07-14 16:01                   ` Ralf Baechle DL5RB
2005-07-16  9:28                   ` Arno Verhoeven - PE1ICQ
2005-07-13 12:39       ` YAPP File Transfer with Linux Rodolfo Brasnarof

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.