378 lines
12 KiB
C++
378 lines
12 KiB
C++
/*
|
|
|
|
Using code from 6pack Linux Kernel driver with the following licence and credits
|
|
|
|
* 6pack driver version 0.4.2, 1999/08/22
|
|
*
|
|
* This module:
|
|
* This module is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This module implements the AX.25 protocol for kernel-based
|
|
* devices like TTYs. It interfaces between a raw TTY, and the
|
|
* kernel's AX.25 protocol layers, just like slip.c.
|
|
* AX.25 needs to be separated from slip.c while slip.c is no
|
|
* longer a static kernel device since it is a module.
|
|
*
|
|
* Author: Andreas Könsgen <ajk@ccac.rwth-aachen.de>
|
|
*
|
|
* Lots of stuff has been taken from mkiss.c, written by
|
|
* Hans Alblas <hans@esrac.ele.tue.nl>
|
|
*
|
|
* with the fixes from
|
|
*
|
|
* Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15.
|
|
* Matthias (DG2FEF) Fixed bug in ax25_close(): dev_lock_wait() was
|
|
* called twice, causing a deadlock.
|
|
*/
|
|
|
|
|
|
// 6pack needs fast response to received characters, and I want to be able to operate over TCP links as well as serial.
|
|
// So I think the character level stuff may need to run in a separate thread, probably using select.
|
|
//
|
|
// I also need to support multiple 6pack ports.
|
|
|
|
// ?? Do we add this as a backend to KISS driver or a separate Driver. KISS Driver is already quite messy. Not decided yet.
|
|
|
|
// ?? If using serial/real TNC we need to be able to interleave control and data bytes, but I think with TCP/QtSM it won't be necessary
|
|
// ?? Also a don't see any point in running multiple copies of QtSM on one port, but maybe should treat the QtSM channels as
|
|
// multidropped ports for scheduling (?? only if on same radio ??)
|
|
|
|
// ?? I think it needs to look like a KISS (L2) driver but will need a transmit scheduler level to do DCD/CSMA/PTT processing,
|
|
// ideally with an interlock to other drivers on same port. This needs some thought with QtSM KISS with multiple modems on one channel
|
|
|
|
|
|
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
|
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
/****************************************************************************
|
|
* Defines for the 6pack driver.
|
|
****************************************************************************/
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#define AX25_MAXDEV 16 /* MAX number of AX25 channels;
|
|
This can be overridden with
|
|
insmod -oax25_maxdev=nnn */
|
|
#define AX_MTU 236
|
|
|
|
/* 6pack protocol bytes/masks. */
|
|
#define SIXP_INIT_CMD 0xE8
|
|
#define SIXP_TNC_FOUND 0xE9
|
|
#define SIXP_CMD_MASK 0xC0
|
|
#define SIXP_PRIO_CMD_MASK 0x80
|
|
#define SIXP_PRIO_DATA_MASK 0x38
|
|
#define SIXP_STD_CMD_MASK 0x40
|
|
#define SIXP_DCD_MASK 0x08
|
|
#define SIXP_RX_DCD_MASK 0x18
|
|
#define SIXP_CHN_MASK 0x07
|
|
#define SIXP_TX_MASK 0x20
|
|
#define SIXP_CON_LED_ON 0x68
|
|
#define SIXP_STA_LED_ON 0x70
|
|
#define SIXP_LED_OFF 0x60
|
|
|
|
/* checksum for a valid 6pack encapsulated packet */
|
|
#define SIXP_CHKSUM 0xFF
|
|
|
|
/* priority commands */
|
|
#define SIXP_SEOF 0x40 /* TX underrun */
|
|
#define SIXP_TX_URUN 0x48 /* TX underrun */
|
|
#define SIXP_RX_ORUN 0x50 /* RX overrun */
|
|
#define SIXP_RX_BUF_OVL 0x58 /* RX overrun */
|
|
|
|
struct ax_disp {
|
|
int magic;
|
|
|
|
char * name;
|
|
/* Various fields. */
|
|
// struct tty_struct *tty; /* ptr to TTY structure */
|
|
// struct device *dev; /* easy for intr handling */
|
|
struct ax_disp *sixpack; /* mkiss txport if mkiss channel*/
|
|
|
|
/* These are pointers to the malloc()ed frame buffers. */
|
|
unsigned char *rbuff; /* receiver buffer */
|
|
int rcount; /* received chars counter */
|
|
unsigned char *xbuff; /* transmitter buffer */
|
|
unsigned char *xhead; /* pointer to next byte to XMIT */
|
|
int xleft; /* bytes left in XMIT queue */
|
|
|
|
/* SLIP interface statistics. */
|
|
unsigned long rx_packets; /* inbound frames counter */
|
|
unsigned long tx_packets; /* outbound frames counter */
|
|
unsigned long rx_errors; /* Parity, etc. errors */
|
|
unsigned long tx_errors; /* Planned stuff */
|
|
unsigned long rx_dropped; /* No memory for skb */
|
|
unsigned long tx_dropped; /* When MTU change */
|
|
unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */
|
|
|
|
/* Detailed SLIP statistics. */
|
|
int mtu; /* Our mtu (to spot changes!) */
|
|
int buffsize; /* Max buffers sizes */
|
|
|
|
|
|
unsigned char flags; /* Flag values/ mode etc */
|
|
#define AXF_INUSE 0 /* Channel in use */
|
|
#define AXF_ESCAPE 1 /* ESC received */
|
|
#define AXF_ERROR 2 /* Parity, etc. error */
|
|
#define AXF_KEEPTEST 3 /* Keepalive test flag */
|
|
#define AXF_OUTWAIT 4 /* is outpacket was flag */
|
|
|
|
int mode;
|
|
|
|
|
|
/* variables for the state machine */
|
|
unsigned char tnc_ok;
|
|
unsigned char status;
|
|
unsigned char status1;
|
|
unsigned char status2;
|
|
|
|
unsigned char duplex;
|
|
unsigned char led_state;
|
|
unsigned char tx_enable;
|
|
|
|
unsigned char raw_buf[4]; /* receive buffer */
|
|
unsigned char cooked_buf[400]; /* receive buffer after 6pack decoding */
|
|
|
|
unsigned int rx_count; /* counter for receive buffer */
|
|
unsigned int rx_count_cooked; /* counter for receive buffer after 6pack decoding */
|
|
|
|
unsigned char tx_delay;
|
|
unsigned char persistance;
|
|
unsigned char slottime;
|
|
|
|
};
|
|
|
|
struct sixpack_channel {
|
|
int magic; /* magic word */
|
|
int init; /* channel exists? */
|
|
struct tty_struct *tty; /* link to tty control structure */
|
|
};
|
|
|
|
#define AX25_MAGIC 0x5316
|
|
#define SIXP_DRIVER_MAGIC 0x5304
|
|
|
|
#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */
|
|
#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */
|
|
|
|
/* default radio channel access parameters */
|
|
#define SIXP_TXDELAY 25 /* in 10 ms */
|
|
#define SIXP_PERSIST 50
|
|
#define SIXP_SLOTTIME 10 /* in 10 ms */
|
|
|
|
static int sixpack_encaps(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay);
|
|
static void sixpack_decaps(struct ax_disp *, unsigned char);
|
|
|
|
static void decode_prio_command(unsigned char, struct ax_disp *);
|
|
static void decode_std_command(unsigned char, struct ax_disp *);
|
|
static void decode_data(unsigned char, struct ax_disp *);
|
|
static void resync_tnc(unsigned long);
|
|
static void xmit_on_air(struct ax_disp *ax);
|
|
static void start_tx_timer(struct ax_disp *ax);
|
|
|
|
extern "C" void Debugprintf(const char * format, ...);
|
|
|
|
void Process6PackByte(unsigned char inbyte);
|
|
|
|
struct ax_disp axdisp;
|
|
|
|
/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
|
|
static void ax_bump(struct ax_disp *ax)
|
|
{
|
|
}
|
|
|
|
|
|
void Process6PackData(unsigned char * Bytes, int Len)
|
|
{
|
|
while(Len--)
|
|
Process6PackByte(Bytes++[0]);
|
|
|
|
}
|
|
|
|
void Process6PackByte(unsigned char inbyte)
|
|
{
|
|
struct ax_disp *ax = &axdisp;
|
|
|
|
if (inbyte == SIXP_INIT_CMD)
|
|
{
|
|
Debugprintf("6pack: SIXP_INIT_CMD received.\n");
|
|
{
|
|
// Reset state machine and allocate a 6pack struct for each modem.
|
|
|
|
// reply with INIT_CMD with the channel no of last modem
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
|
|
decode_prio_command(inbyte, ax);
|
|
else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
|
|
decode_std_command(inbyte, ax);
|
|
else {
|
|
if ((ax->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
|
|
decode_data(inbyte, ax);
|
|
} /* else */
|
|
}
|
|
|
|
/* identify and execute a 6pack priority command byte */
|
|
|
|
void decode_prio_command(unsigned char cmd, struct ax_disp *ax)
|
|
{
|
|
unsigned char channel;
|
|
|
|
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 (((ax->status & SIXP_DCD_MASK) == 0) &&
|
|
((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
|
|
if (ax->status != 1)
|
|
Debugprintf("6pack: protocol violation\n");
|
|
else
|
|
ax->status = 0;
|
|
cmd &= !SIXP_RX_DCD_MASK;
|
|
}
|
|
ax->status = cmd & SIXP_PRIO_DATA_MASK;
|
|
} /* if */
|
|
|
|
|
|
/* if the state byte has been received, the TNC is present,
|
|
so the resync timer can be reset. */
|
|
|
|
if (ax->tnc_ok == 1) {
|
|
// del_timer(&(ax->resync_t));
|
|
// ax->resync_t.data = (unsigned long) ax;
|
|
// ax->resync_t.function = resync_tnc;
|
|
// ax->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT;
|
|
// add_timer(&(ax->resync_t));
|
|
}
|
|
|
|
ax->status1 = cmd & SIXP_PRIO_DATA_MASK;
|
|
}
|
|
|
|
/* try to resync the TNC. Called by the resync timer defined in
|
|
decode_prio_command */
|
|
|
|
static void
|
|
resync_tnc(unsigned long channel)
|
|
{
|
|
static char resync_cmd = SIXP_INIT_CMD;
|
|
struct ax_disp *ax = (struct ax_disp *) channel;
|
|
|
|
Debugprintf("6pack: resyncing TNC\n");
|
|
|
|
/* clear any data that might have been received */
|
|
|
|
ax->rx_count = 0;
|
|
ax->rx_count_cooked = 0;
|
|
|
|
/* reset state machine */
|
|
|
|
ax->status = 1;
|
|
ax->status1 = 1;
|
|
ax->status2 = 0;
|
|
ax->tnc_ok = 0;
|
|
|
|
/* resync the TNC */
|
|
|
|
ax->led_state = SIXP_LED_OFF;
|
|
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
|
|
// ax->tty->driver.write(ax->tty, 0, &resync_cmd, 1);
|
|
|
|
|
|
/* Start resync timer again -- the TNC might be still absent */
|
|
|
|
// del_timer(&(ax->resync_t));
|
|
// ax->resync_t.data = (unsigned long) ax;
|
|
// ax->resync_t.function = resync_tnc;
|
|
// ax->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
|
|
// add_timer(&(ax->resync_t));
|
|
}
|
|
|
|
|
|
|
|
/* identify and execute a standard 6pack command byte */
|
|
|
|
void decode_std_command(unsigned char cmd, struct ax_disp *ax)
|
|
{
|
|
unsigned char checksum = 0, channel;
|
|
unsigned int i;
|
|
|
|
channel = cmd & SIXP_CHN_MASK;
|
|
switch (cmd & SIXP_CMD_MASK) { /* normal command */
|
|
case SIXP_SEOF:
|
|
if ((ax->rx_count == 0) && (ax->rx_count_cooked == 0)) {
|
|
if ((ax->status & SIXP_RX_DCD_MASK) ==
|
|
SIXP_RX_DCD_MASK) {
|
|
ax->led_state = SIXP_CON_LED_ON;
|
|
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
|
|
} /* if */
|
|
}
|
|
else {
|
|
ax->led_state = SIXP_LED_OFF;
|
|
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
|
|
/* fill trailing bytes with zeroes */
|
|
if (ax->rx_count == 2) {
|
|
decode_data(0, ax);
|
|
decode_data(0, ax);
|
|
ax->rx_count_cooked -= 2;
|
|
}
|
|
else if (ax->rx_count == 3) {
|
|
decode_data(0, ax);
|
|
ax->rx_count_cooked -= 1;
|
|
}
|
|
for (i = 0; i < ax->rx_count_cooked; i++)
|
|
checksum += ax->cooked_buf[i];
|
|
if (checksum != SIXP_CHKSUM) {
|
|
Debugprintf("6pack: bad checksum %2.2x\n", checksum);
|
|
}
|
|
else {
|
|
ax->rcount = ax->rx_count_cooked - 1;
|
|
ax_bump(ax);
|
|
} /* else */
|
|
ax->rx_count_cooked = 0;
|
|
} /* else */
|
|
break;
|
|
case SIXP_TX_URUN:
|
|
Debugprintf("6pack: TX underrun\n");
|
|
break;
|
|
case SIXP_RX_ORUN:
|
|
Debugprintf("6pack: RX overrun\n");
|
|
break;
|
|
case SIXP_RX_BUF_OVL:
|
|
Debugprintf("6pack: RX buffer overflow\n");
|
|
} /* switch */
|
|
} /* function */
|
|
|
|
/* decode 4 sixpack-encoded bytes into 3 data bytes */
|
|
|
|
void decode_data(unsigned char inbyte, struct ax_disp *ax)
|
|
{
|
|
unsigned char *buf;
|
|
|
|
if (ax->rx_count != 3)
|
|
ax->raw_buf[ax->rx_count++] = inbyte;
|
|
else {
|
|
buf = ax->raw_buf;
|
|
ax->cooked_buf[ax->rx_count_cooked++] =
|
|
buf[0] | ((buf[1] << 2) & 0xc0);
|
|
ax->cooked_buf[ax->rx_count_cooked++] =
|
|
(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
|
|
ax->cooked_buf[ax->rx_count_cooked++] =
|
|
(buf[2] & 0x03) | (inbyte << 2);
|
|
ax->rx_count = 0;
|
|
}
|
|
}
|