summaryrefslogtreecommitdiffstats
path: root/6pack/m6pack.c
diff options
context:
space:
mode:
Diffstat (limited to '6pack/m6pack.c')
-rw-r--r--6pack/m6pack.c666
1 files changed, 666 insertions, 0 deletions
diff --git a/6pack/m6pack.c b/6pack/m6pack.c
new file mode 100644
index 0000000..65f85c4
--- /dev/null
+++ b/6pack/m6pack.c
@@ -0,0 +1,666 @@
+/* Hey Emacs! this is -*- linux-c -*-
+ * from /usr/src/linux/Documentation/CodingStyle
+ *
+ * m6pack.c
+ *
+ * Fake out AX.25 code into supporting 6pack TNC rings by routing serial
+ * port data to/from pseudo ttys.
+ *
+ * @(#)m6pack.c $Revision: 1.1 $ $Date: 2005/12/10 16:17:28 $
+ *
+ * Author(s):
+ *
+ * Iñaki Arenaza (EB2EBU) <iarenaza@escomposlinux.org>
+ *
+ * History:
+ *
+ * $Log: m6pack.c,v $
+ * Revision 1.1 2005/12/10 16:17:28 dl9sau
+ * support for 6pack tnc rings (patch found at sf.net)
+ * from Iñaki Arenaza EB2EBU <iarenaza@escomposlinux.org>
+ *
+ * Revision 0.9 2002/04/28 15:05:39 hermes-team
+ * Minor fixes to make it part of ax25-tools-0.0.8
+ *
+ * Revision 0.7 1999/04/25 11:03:51 hermes-team
+ * Minor cosmetic changes.
+ *
+ * Revision 0.6 1999/04/25 10:30:51 hermes-team
+ * TTY unlocking was missing from SIGTERM handler. Thanks to Tommi Manninen
+ * for pointing it out.
+ *
+ * Revision 0.5 1999/04/07 09:16:06 hermes-team
+ * First public beta release. It _seems_ to work with two TNC's in a ring
+ * (I only have two TNC's to test :-(
+ *
+ * Revision 0.4 1999/04/06 14:35:56 hermes-team
+ * Removed the code that checked that trailing SEOF's address was equal to
+ * leading SEOF's address (it seems this hasn't to be so, but it wasn't very
+ * clear in DG2FEF & HB9JNX's paper)
+ *
+ * Revision 0.3 1999/01/31 23:02:09 hermes-team
+ * Modified code that calls write(2) in sixpack_tx to handle big data blocks.
+ *
+ * Revision 0.2 1999/01/31 15:56:51 hermes-team
+ * Initial Release. This is still alpha software.
+ *
+ *********************************************************************
+ *
+ * Quite a lot of stuff "stolen" from mkiss.c, written by
+ *
+ * Kevin Uhlir
+ * Ron Curry
+ * Jonathan Naylor
+ * Tomi Manninen
+ *
+ * Copyright (C) 1999 Iñaki Arenaza
+ * mkiss.c was GPLed, so I guess this one is GPLed too ;-)
+ *
+ *********************************************************************
+ *
+ * REMARK:
+ *
+ * See document '6pack.ps' by Matthias Welwarsky (DG2FEF), translated
+ * by Thomas Sailer (HB9JNX) found in ax25-doc-1.0.tgz for details about
+ * 6pack protocol specifications.
+ *
+ *********************************************************************
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <ctype.h>
+#include <syslog.h>
+
+#include <netax25/ttyutils.h>
+#include <netax25/daemon.h>
+
+#include <config.h>
+
+static char *version ="$Id: m6pack.c,v 1.1 2005/12/10 16:17:28 dl9sau Exp $";
+
+typedef unsigned char __u8;
+typedef enum {data, command} frame_t;
+
+#define SIXP_MAX_ADDR ((__u8) 8)
+#define SIZE 4096
+#define MAX_PTYS SIXP_MAX_ADDR /* Max number of TNCs in a 6PACK ring */
+
+/*
+ * Keep these off the stack.
+ */
+static __u8 ibuf[SIZE]; /* buffer for input operations */
+static __u8 obuf[SIZE]; /* buffer for sixpack_tx() */
+
+static int invalid_ports = 0;
+
+static char *usage_string = "usage: m6pack [-l] [-s speed] [-v] "
+ "ttyinterface pty ...\n";
+
+static int dump_report = FALSE;
+static int logging = FALSE;
+
+static struct iface *pty[MAX_PTYS];
+static struct iface *tty;
+static int numptys;
+
+struct iface
+{
+ char *name; /* Interface name (/dev/???) */
+ int fd; /* File descriptor */
+ int pty_id; /* 6pack address asigned to pty */
+ __u8 seof; /* leading SEOF of this frame */
+ __u8 databuf[SIZE]; /* TX buffer (data frames) */
+ __u8 cmdbuf[1]; /* TX buffer (command frames) */
+ __u8 *optr; /* Next byte to transmit */
+ unsigned int sixp_cnt; /* 6pack-coded octets count */
+ unsigned int decod_cnt; /* 6pack-decoded octets cont */
+ unsigned int errors; /* 6PACK frame error count */
+ unsigned int rxpackets; /* RX frames count */
+ unsigned int txpackets; /* TX frames count */
+ unsigned long rxbytes; /* RX bytes count */
+ unsigned long txbytes; /* TX bytes count */
+};
+
+#define PTY_ID_TTY (-1)
+
+/* 6PACK "TNC Address" commands are special in that the address they
+ * carry is not the address of the destination TNC but the initial
+ * address for the firt TNC in the ring (when sent PC->TNC Ring), or
+ * the last TNC address+1 (when received TNC ring -> PC). So we can't
+ * use the address in the command to select a pty to pass the
+ * command, and we have to keep track of what ptys have sent "TNC
+ * Address" commands to sent them back the response (until we modify
+ * the kernel 6pack driver to support multiple TNC's (i.e., network
+ * devices) on one serial port). We deliver the responses with a FIFO
+ * policy.
+ */
+static int cmd_addr_fifo[SIXP_MAX_ADDR];
+static int head; /* We retrieve from head position */
+static int tail; /* We insert at tail position */
+
+/* Masks to tell apart 6pack command and data frames
+ */
+#define SIXP_IS_DATA(x) ((((__u8)(x)) & (__u8)0xC0) == (__u8)0)
+#define SIXP_IS_CMD(x) !(SIXP_IS_DATA(x))
+
+/* Masks to split commands in opcode and TNC address
+ */
+#define SIXP_ADDR_MASK ((__u8) (SIXP_MAX_ADDR-1))
+#define SIXP_CMD_MASK ((__u8) ~SIXP_ADDR_MASK)
+#define SIXP_CMD(x) (((__u8)x) & SIXP_CMD_MASK)
+#define SIXP_ADDR(x) (((__u8)x) & SIXP_ADDR_MASK)
+
+/* Macro to build a 6pack TNC command from opcode and TNC address
+ */
+#define SIXP_MAKE_CMD(opcode,addr) (((__u8)opcode) | ((__u8)addr))
+
+/* 6pack protocol commands (relevant bits only)
+ */
+#define SIXP_CMD_SEOF ((__u8) 0x40) /* start/end of 6pack frame */
+#define SIXP_CMD_TX_ORUN ((__u8) 0x48) /* transmit overrun */
+#define SIXP_CMD_RX_ORUN ((__u8) 0x50) /* receive overrun */
+#define SIXP_CMD_RX_BUF_OVL ((__u8) 0x58) /* receive buffer overflow */
+#define SIXP_CMD_LED ((__u8) 0x60) /* set LED status */
+#define SIXP_CMD_TX_1 ((__u8) 0xA0) /* TX counter + 1 */
+#define SIXP_CMD_RX_1 ((__u8) 0x90) /* RX counter + 1 */
+#define SIXP_CMD_DCD ((__u8) 0x88) /* DCD state */
+#define SIXP_CMD_CAL ((__u8) 0xE0) /* send calibration pattern */
+#define SIXP_CMD_ADDR ((__u8) 0xE8) /* set TNC address */
+
+/* Valid checksum of a 6pack frame
+ */
+#define SIXP_CHKSUM ((__u8) 0xFF)
+
+static int sixpack_rx(struct iface *ifp, __u8 c, __u8 *tnc_addr, frame_t *type)
+{
+ int i, len;
+ __u8 checksum;
+
+ /* Is it a data octect?
+ */
+ if (SIXP_IS_DATA(c)) {
+ /* Decode the 6PACK octect and store the resultant
+ * bits in the output buffer.
+ */
+ switch (ifp->sixp_cnt % 4) {
+ case 0:
+ *ifp->optr = (c & 0x3F);
+ break;
+ case 1:
+ *ifp->optr++ |= (c & 0x30) << 2;
+ *ifp->optr = (c & 0x0F);
+ break;
+ case 2:
+ *ifp->optr++ |= (c & 0x3C) << 2;
+ *ifp->optr = (c & 0x03);
+ break;
+ default:
+ *ifp->optr++ |= (c & 0x3F) << 2;
+ }
+ ifp->sixp_cnt++;
+ return 0;
+ }
+
+ /* Nope, it's a command octect. See which kind of command.
+ * Anything but a SEOF command is a one-octect command, so
+ * process it immediately and return.
+ */
+ if (SIXP_CMD(c) != SIXP_CMD_SEOF) {
+ if (SIXP_CMD(c) == SIXP_CMD_ADDR) {
+ /* 6PACK "TNC Address" commans are
+ * "special". The address field must be
+ * rewritten.
+ */
+ if (PTY_ID_TTY == ifp->pty_id) {
+ /* TNC Ring -> pty
+ * Dequeue a TNC Address request.
+ */
+ *ifp->cmdbuf = SIXP_MAKE_CMD(SIXP_CMD(c),1);
+ *tnc_addr = cmd_addr_fifo[head];
+ head = (head + 1) % SIXP_MAX_ADDR;
+ }
+ else {
+ /* pty -> TNC Ring
+ * Enqueue a TNC Address request.
+ */
+ *ifp->cmdbuf = SIXP_MAKE_CMD(SIXP_CMD(c), 0);
+ *tnc_addr = 0;
+ cmd_addr_fifo[tail] = ifp->pty_id;
+ tail = (tail + 1) % SIXP_MAX_ADDR;
+ }
+ }
+ else {
+ /* We need a separate buffer here for 6PACK commands
+ * as the protocol allows the transmission of command
+ * frames in the middle of data frames. Don't touch
+ * anything else! A data frame may be "en route".
+ */
+ *ifp->cmdbuf = SIXP_CMD(c);
+ *tnc_addr = SIXP_ADDR(c);
+ }
+ /* Update statistics and return.
+ */
+ ifp->rxpackets++;
+ ifp->rxbytes++;
+ *type = command;
+ return 1;
+ }
+
+ /* We're dealing with a SEOF command.
+ */
+ len = ifp->optr - ifp->databuf;
+ if (len > 0) {
+ if (len < 3) {
+ /* Data frames have at least 3 octets:
+ * TxDelay, Datum, CheckSum.
+ * Signal error and reset state.
+ */
+ goto error_reset_state;
+ }
+
+ /* Now that we've decoded 6PACK octects,
+ * check the checksum of the frame.
+ */
+ checksum = 0;
+ for (i = 0; i < len; i++) {
+ checksum += ifp->databuf[i];
+ }
+ /* Address of this frame must be taken from the
+ * leading SEOF.
+ */
+ *tnc_addr = SIXP_ADDR(ifp->seof);
+ checksum += *tnc_addr;
+
+ if (checksum != SIXP_CHKSUM) {
+ /* Signal error and reset state.
+ */
+ goto error_reset_state;
+ }
+
+ /* Now remove checksum from the frame (this makes
+ * sixpack_tx easier).
+ */
+ len--;/* Minus Checksum */
+
+ /* Finally update statistics and return all
+ * needed parameters
+ */
+ ifp->rxpackets++;
+ ifp->rxbytes += len;
+ *type = data;
+ }
+
+ ifp->optr = ifp->databuf;
+ ifp->seof = c;
+ ifp->sixp_cnt = 0;
+ ifp->decod_cnt = 0;
+ return len;
+
+ error_reset_state:
+ ifp->errors++;
+ ifp->optr = ifp->databuf;
+ ifp->sixp_cnt = 0;
+ ifp->decod_cnt = 0;
+ return 0;
+}
+
+static void sixpack_tx(int fd, __u8 tnc_addr, __u8 *ibuf, int len, frame_t type)
+{
+ __u8 *ptr = obuf;
+ __u8 checksum;
+ int count, written;
+
+ if (type == command) {
+ if (SIXP_CMD(*ibuf) == SIXP_CMD_ADDR) {
+ /* 6PACK "TNC Address" commans are
+ * "special". We must send them untouched here.
+ */
+ *ptr++ = *ibuf;
+ }
+ else {
+ /* Commands, except SEOF, are 1 byte long.
+ */
+ *ptr++ = SIXP_MAKE_CMD(SIXP_CMD(*ibuf), tnc_addr);
+ }
+ }
+ else {
+ *ptr++ = SIXP_MAKE_CMD(SIXP_CMD_SEOF, tnc_addr);
+ checksum = 0;
+ for (count = 0; count < len; count++) {
+ switch (count % 3) {
+ case 0:
+ *ptr++ = (ibuf[count] & 0x3F);
+ *ptr = (ibuf[count] & 0xC0) >> 2;
+ break;
+ case 1:
+ *ptr++ |= (ibuf[count] & 0x0F);
+ *ptr = (ibuf[count] & 0xF0) >> 2;
+ break;
+ default:
+ *ptr++ |= (ibuf[count] & 0x03);
+ *ptr++ = (ibuf[count] & 0xFC) >> 2;
+ break;
+ }
+ checksum += ibuf[count];
+ }
+
+ checksum += tnc_addr;
+ checksum = 0xFF - checksum;
+ switch (count % 3) {
+ case 0:
+ *ptr++ = (checksum & 0x3F);
+ *ptr++ = (checksum & 0xC0) >> 2;
+ break;
+ case 1:
+ *ptr++ |= (checksum & 0x0F);
+ *ptr++ = (checksum & 0xF0) >> 2;
+ break;
+ default:
+ *ptr++ |= (checksum & 0x03);
+ *ptr++ = (checksum & 0xFC) >> 2;
+ break;
+ }
+
+ /* Trailing SEOF doesn't carry TNC address
+ */
+ *ptr++ = SIXP_MAKE_CMD(SIXP_CMD_SEOF, 0);
+ }
+
+ count = ptr - obuf;
+ written = 0;
+ ptr = obuf;
+ while (count > 0) {
+ written = write (fd, ptr, count);
+ count -= written;
+ ptr += written;
+ }
+}
+
+static void sigterm_handler(int sig)
+{
+ int i;
+
+ if (logging) {
+ syslog(LOG_INFO, "terminating on SIGTERM\n");
+ closelog();
+ }
+
+ tty_unlock(tty->name);
+ for (i=0; i < numptys; i++) {
+ tty_unlock(pty[i]->name);
+ }
+
+ close(tty->fd);
+ free(tty);
+ for (i = 0; i < numptys; i++) {
+ close(pty[i]->fd);
+ free(pty[i]);
+ }
+
+ exit(0);
+}
+
+static void sigusr1_handler(int sig)
+{
+ signal(SIGUSR1, sigusr1_handler);
+ dump_report = TRUE;
+}
+
+static void report(struct iface *tty, struct iface **pty, int numptys)
+{
+ int i;
+ long t;
+
+ time(&t);
+ syslog(LOG_INFO, "version %s.", VERSION);
+ syslog(LOG_INFO, "Status report at %s", ctime(&t));
+ syslog(LOG_INFO, "ttyinterface is %s (fd=%d)", tty->name, tty->fd);
+ for (i = 0; i < numptys; i++)
+ syslog(LOG_INFO, "pty%d is %s (fd=%d)", i, pty[i]->name,
+ pty[i]->fd);
+ syslog(LOG_INFO, "Invalid ports: %d", invalid_ports);
+ syslog(LOG_INFO, "Interface TX frames TX bytes RX frames RX "
+ "bytes Errors");
+ syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u",
+ tty->name,
+ tty->txpackets, tty->txbytes,
+ tty->rxpackets, tty->rxbytes,
+ tty->errors);
+ for (i = 0; i < numptys; i++) {
+ syslog(LOG_INFO, "%-11s %-9u %-9lu %-9u %-9lu %u",
+ pty[i]->name,
+ pty[i]->txpackets, pty[i]->txbytes,
+ pty[i]->rxpackets, pty[i]->rxbytes,
+ pty[i]->errors);
+ }
+ return;
+}
+
+int main(int argc, char *argv[])
+{
+ __u8 *icp, tnc_addr;
+ int topfd;
+ fd_set readfd;
+ int retval, i, size, len;
+ int speed = -1;
+ frame_t type;
+
+ head = tail = 0;
+ while ((size = getopt(argc, argv, "ls:v")) != -1) {
+ switch (size) {
+ case 'l':
+ logging = TRUE;
+ break;
+ case 's':
+ speed = atoi(optarg);
+ break;
+ case 'v':
+ printf("m6pack: %s\n", VERSION);
+ return 1;
+ case ':':
+ case '?':
+ fprintf(stderr, usage_string);
+ return 1;
+ }
+ }
+
+ if ((argc - optind) < 2) {
+ fprintf(stderr, usage_string);
+ return 1;
+ }
+ if ((numptys = argc - optind - 1) > MAX_PTYS) {
+ fprintf(stderr, "m6pack: max %d pty interfaces allowed.\n",
+ MAX_PTYS);
+ return 1;
+ }
+
+ /*
+ * Check for lock files before opening any TTYs
+ */
+ if (tty_is_locked(argv[optind])) {
+ fprintf(stderr, "m6pack: tty %s is locked by another "
+ "process\n", argv[optind]);
+ return 1;
+ }
+ for (i = 0; i < numptys; i++) {
+ if (tty_is_locked(argv[optind + i + 1])) {
+ fprintf(stderr, "m6pack: tty %s is locked by "
+ "another process\n", argv[optind + i + 1]);
+ return 1;
+ }
+ }
+
+ /*
+ * Open and configure the tty interface. Open() is
+ * non-blocking so it won't block regardless of the modem
+ * status lines.
+ */
+ if ((tty = calloc(1, sizeof(struct iface))) == NULL) {
+ perror("m6pack: malloc");
+ return 1;
+ }
+
+ if ((tty->fd = open(argv[optind], O_RDWR | O_NDELAY)) == -1) {
+ perror("m6pack: open");
+ return 1;
+ }
+
+ tty->name = argv[optind];
+ if (speed != -1) tty_speed(tty->fd, speed);
+ tty_raw(tty->fd, FALSE);
+ tty->pty_id = PTY_ID_TTY;
+ tty->optr = tty->databuf;
+ topfd = tty->fd;
+ /*
+ * Make it block again...
+ */
+ fcntl(tty->fd, F_SETFL, 0);
+
+ /*
+ * Open and configure the pty interfaces
+ */
+ for (i = 0; i < numptys; i++) {
+ if ((pty[i] = calloc(1, sizeof(struct iface))) == NULL) {
+ perror("m6pack: malloc");
+ return 1;
+ }
+ if ((pty[i]->fd = open(argv[optind + i + 1], O_RDWR)) == -1) {
+ perror("m6pack: open");
+ return 1;
+ }
+ pty[i]->name = argv[optind + i + 1];
+ tty_raw(pty[i]->fd, FALSE);
+ pty[i]->pty_id = i;
+ pty[i]->optr = pty[i]->databuf;
+ topfd = (pty[i]->fd > topfd) ? pty[i]->fd : topfd;
+ }
+
+ /*
+ * Now all the ports are open, lock them.
+ */
+ tty_lock(argv[optind]);
+ for (i = 0; i < numptys; i++)
+ tty_lock(argv[optind + i + 1]);
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGUSR1, sigusr1_handler);
+ signal(SIGTERM, sigterm_handler);
+
+ if (!daemon_start(FALSE)) {
+ fprintf(stderr, "m6pack: cannot become a daemon\n");
+ return 1;
+ }
+
+ if (logging) {
+ openlog("m6pack", LOG_PID, LOG_DAEMON);
+ syslog(LOG_INFO, "starting");
+ }
+
+ /*
+ * Loop until an error occurs on a read.
+ */
+ while (TRUE) {
+ FD_ZERO(&readfd);
+ FD_SET(tty->fd, &readfd);
+ for (i = 0; i < numptys; i++)
+ FD_SET(pty[i]->fd, &readfd);
+
+ errno = 0;
+ retval = select(topfd + 1, &readfd, NULL, NULL, NULL);
+
+ if (retval == -1) {
+ if (dump_report) {
+ if (logging)
+ report(tty, pty, numptys);
+ dump_report = FALSE;
+ continue;
+ } else {
+ perror("m6pack: select");
+ continue;
+ }
+ }
+
+
+ /*
+ * A character has arrived on the ttyinterface.
+ */
+ if (FD_ISSET(tty->fd, &readfd)) {
+ if ((size = read(tty->fd, ibuf, SIZE)) < 0
+ && errno != EINTR) {
+ if (logging)
+ syslog(LOG_ERR, "tty->fd: %m");
+ break;
+ }
+
+ for (icp = ibuf; size > 0; size--, icp++) {
+ if ((len = sixpack_rx(tty,*icp,&tnc_addr,
+ &type)) != 0) {
+ if (tnc_addr <= numptys) {
+ sixpack_tx(pty[tnc_addr]->fd,
+ 0,
+ (type == data) ?
+ tty->databuf :
+ tty->cmdbuf,
+ len,
+ type);
+ pty[tnc_addr]->txpackets++;
+ pty[tnc_addr]->txbytes += len;
+ } else
+ invalid_ports++;
+ }
+ }
+ }
+
+ for (i = 0; i < numptys; i++) {
+ /*
+ * A character has arrived on pty[i].
+ */
+ if (FD_ISSET(pty[i]->fd, &readfd)) {
+ if ((size = read(pty[i]->fd, ibuf, SIZE)) < 0
+ && errno != EINTR) {
+ if (logging)
+ syslog(LOG_ERR,
+ "pty[%d]->fd: %m\n",i);
+ goto end;
+ }
+
+ for (icp = ibuf; size > 0; size--, icp++) {
+ if ((len = sixpack_rx(pty[i],
+ *icp,
+ &tnc_addr,
+ &type)) != 0) {
+ sixpack_tx(tty->fd, i,
+ (type == data) ?
+ pty[i]->databuf :
+ pty[i]->cmdbuf,
+ len, type);
+ tty->txpackets++;
+ tty->txbytes += len;
+ }
+ }
+ }
+ }
+ }
+
+ end:
+ if (logging)
+ closelog();
+
+ close(tty->fd);
+ free(tty);
+
+ for (i = 0; i < numptys; i++) {
+ close(pty[i]->fd);
+ free(pty[i]);
+ }
+ return 1;
+}