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:
[EMAIL PROTECTED] linux-cvs]$ ssh -l root 172.20.1.2
[EMAIL PROTECTED]'s password:
Last login: Sun Jul 10 20:52:26 2005
[EMAIL PROTECTED] ~]# cd /sys/class/net/sp0
[EMAIL PROTECTED] 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
[EMAIL PROTECTED] 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 <[EMAIL PROTECTED]>
* Ralf Baechle DL5RB <[EMAIL PROTECTED]>
- *
- * Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
- *
- * Laurence Culhane, <[EMAIL PROTECTED]>
- * Fred N. van Kempen, <[EMAIL PROTECTED]>
*/
#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 <[EMAIL PROTECTED]>");
+MODULE_AUTHOR("Ralf Baechle DL5RB <[EMAIL PROTECTED]>");
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 [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html