[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