patch-2.1.94 linux/drivers/net/hamradio/6pack.c
Next file: linux/drivers/net/hamradio/6pack.h
Previous file: linux/drivers/net/epic100.c
Back to the patch index
Back to the overall index
- Lines: 1130
- Date:
Wed Apr 8 17:31:27 1998
- Orig file:
v2.1.93/linux/drivers/net/hamradio/6pack.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.93/linux/drivers/net/hamradio/6pack.c linux/drivers/net/hamradio/6pack.c
@@ -0,0 +1,1129 @@
+/*
+ * 6pack.c This module implements the 6pack protocol for kernel-based
+ * devices like TTY. It interfaces between a raw TTY and the
+ * kernel's AX.25 protocol layers.
+ *
+ * Version: @(#)6pack.c 0.3.0 04/07/98
+ *
+ * Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de>
+ *
+ * Quite a lot of stuff "stolen" by Jörg 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>
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/timer.h>
+#include <net/ax25.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/if_slip.h>
+#include <linux/init.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+/*
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+*/
+
+#include "6pack.h"
+
+typedef unsigned char byte;
+
+
+typedef struct sixpack_ctrl {
+ char if_name[8]; /* "sp0\0" .. "sp99999\0" */
+ struct sixpack ctrl; /* 6pack things */
+ struct device dev; /* the device */
+} sixpack_ctrl_t;
+static sixpack_ctrl_t **sixpack_ctrls = NULL;
+int sixpack_maxdev = SIXP_NRUNIT; /* Can be overridden with insmod! */
+
+static struct tty_ldisc sp_ldisc;
+
+static void sp_start_tx_timer(struct sixpack *);
+static void sp_xmit_on_air(unsigned long);
+static void resync_tnc(unsigned long);
+void sixpack_decode(struct sixpack *, unsigned char[], int);
+int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
+
+void decode_prio_command(byte, struct sixpack *);
+void decode_std_command(byte, struct sixpack *);
+void decode_data(byte, struct sixpack *);
+
+static int tnc_init(struct sixpack *);
+
+/* Find a free 6pack channel, and link in this `tty' line. */
+static inline struct sixpack *
+sp_alloc(void)
+{
+ sixpack_ctrl_t *spp = NULL;
+ int i;
+
+ if (sixpack_ctrls == NULL) return NULL; /* Master array missing ! */
+
+ for (i = 0; i < sixpack_maxdev; i++)
+ {
+ spp = sixpack_ctrls[i];
+
+ if (spp == NULL)
+ break;
+
+ if (!test_and_set_bit(SIXPF_INUSE, &spp->ctrl.flags))
+ break;
+ }
+
+ /* Too many devices... */
+ if (i >= sixpack_maxdev)
+ return NULL;
+
+ /* If no channels are available, allocate one */
+ if (!spp &&
+ (sixpack_ctrls[i] = (sixpack_ctrl_t *)kmalloc(sizeof(sixpack_ctrl_t),
+ GFP_KERNEL)) != NULL)
+ {
+ spp = sixpack_ctrls[i];
+ memset(spp, 0, sizeof(sixpack_ctrl_t));
+
+ /* Initialize channel control data */
+ set_bit(SIXPF_INUSE, &spp->ctrl.flags);
+ spp->ctrl.tty = NULL;
+ sprintf(spp->if_name, "sp%d", i);
+ spp->dev.name = spp->if_name;
+ spp->dev.base_addr = i;
+ spp->dev.priv = (void*)&(spp->ctrl);
+ spp->dev.next = NULL;
+ spp->dev.init = sixpack_init;
+ }
+
+ if (spp != NULL)
+ {
+ /* register device so that it can be ifconfig'ed */
+ /* sixpack_init() will be called as a side-effect */
+ /* SIDE-EFFECT WARNING: sixpack_init() CLEARS spp->ctrl ! */
+
+ if (register_netdev(&(spp->dev)) == 0)
+ {
+ set_bit(SIXPF_INUSE, &spp->ctrl.flags);
+ spp->ctrl.dev = &(spp->dev);
+ spp->dev.priv = (void*)&(spp->ctrl);
+
+ return (&(spp->ctrl));
+ } else {
+ clear_bit(SIXPF_INUSE,&(spp->ctrl.flags));
+ printk(KERN_WARNING "sp_alloc() - register_netdev() failure.\n");
+ }
+ }
+
+ return NULL;
+}
+
+
+/* Free a 6pack channel. */
+static inline void
+sp_free(struct sixpack *sp)
+{
+ /* Free all 6pack frame buffers. */
+ if (sp->rbuff)
+ kfree(sp->rbuff);
+ sp->rbuff = NULL;
+ if (sp->xbuff) {
+ kfree(sp->xbuff);
+ }
+ sp->xbuff = NULL;
+
+ if (!test_and_clear_bit(SIXPF_INUSE, &sp->flags))
+ {
+ printk(KERN_WARNING "%s: sp_free for already free unit.\n", sp->dev->name);
+ }
+}
+
+
+/* Set the "sending" flag. */
+static inline void
+sp_lock(struct sixpack *sp)
+{
+ if (test_and_set_bit(0, (void *) &sp->dev->tbusy))
+ printk(KERN_WARNING "%s: trying to lock already locked device!\n", sp->dev->name);
+}
+
+
+/* Clear the "sending" flag. */
+static inline void
+sp_unlock(struct sixpack *sp)
+{
+ if (!test_and_clear_bit(0, (void *)&sp->dev->tbusy))
+ printk(KERN_WARNING "%s: trying to unlock already unlocked device!\n", sp->dev->name);
+}
+
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+
+/* This is the routine that sends the received data to the kernel AX.25.
+ 'cmd' is the KISS command. For AX.25 data, it is zero. */
+
+static void
+sp_bump(struct sixpack *sp, char cmd)
+{
+ struct sk_buff *skb;
+ int count;
+ unsigned char *ptr;
+
+ count = sp->rcount+1;
+
+ sp->rx_bytes+=count;
+
+ skb = dev_alloc_skb(count);
+ if (skb == NULL)
+ {
+ printk(KERN_DEBUG "%s: memory squeeze, dropping packet.\n", sp->dev->name);
+ sp->rx_dropped++;
+ return;
+ }
+
+ skb->dev = sp->dev;
+ ptr = skb_put(skb, count);
+ *ptr++ = cmd; /* KISS command */
+
+ memcpy(ptr, (sp->cooked_buf)+1, count);
+ skb->mac.raw=skb->data;
+ skb->protocol=htons(ETH_P_AX25);
+ netif_rx(skb);
+ sp->rx_packets++;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/* Encapsulate one AX.25 frame and stuff into a TTY queue. */
+static void
+sp_encaps(struct sixpack *sp, unsigned char *icp, int len)
+{
+ unsigned char *p;
+ int actual, count;
+
+ if (len > sp->mtu) /* sp->mtu = AX25_MTU = max. PACLEN = 256 */
+ {
+ len = sp->mtu;
+ printk(KERN_DEBUG "%s: truncating oversized transmit packet!\n", sp->dev->name);
+ sp->tx_dropped++;
+ sp_unlock(sp);
+ return;
+ }
+
+ p = icp;
+
+ if (p[0] > 5)
+ {
+ printk(KERN_DEBUG "%s: invalid KISS command -- dropped\n", sp->dev->name);
+ sp_unlock(sp);
+ return;
+ }
+
+ if ((p[0] != 0) && (len > 2))
+ {
+ printk(KERN_DEBUG "%s: KISS control packet too long -- dropped\n", sp->dev->name);
+ sp_unlock(sp);
+ return;
+ }
+
+ if ((p[0] == 0) && (len < 15))
+ {
+ printk(KERN_DEBUG "%s: bad AX.25 packet to transmit -- dropped\n", sp->dev->name);
+ sp_unlock(sp);
+ sp->tx_dropped++;
+ return;
+ }
+
+ count = encode_sixpack(p, (unsigned char *) sp->xbuff, len, sp->tx_delay);
+ sp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+
+ switch(p[0])
+ {
+ case 1: sp->tx_delay = p[1]; return;
+ case 2: sp->persistance = p[1]; return;
+ case 3: sp->slottime = p[1]; return;
+ case 4: /* ignored */ return;
+ case 5: sp->duplex = p[1]; return;
+ }
+
+ if (p[0] == 0) {
+ /* in case of fullduplex or DAMA operation, we don't take care
+ about the state of the DCD or of any timers, as the determination
+ of the correct time to send is the job of the AX.25 layer. We send
+ immediately after data has arrived. */
+ if (sp->duplex == 1){
+ sp->led_state = 0x70;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
+ sp->tx_enable = 1;
+ actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, count);
+ sp->xleft = count - actual;
+ sp->xhead = sp->xbuff + actual;
+ sp->led_state = 0x60;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
+ }
+ else {
+ sp->xleft = count;
+ sp->xhead = sp->xbuff;
+ sp->status2 = count;
+ if (sp->duplex == 0)
+ sp_start_tx_timer(sp);
+ }
+ }
+}
+
+/*
+ * Called by the TTY driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void sixpack_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct sixpack *sp = (struct sixpack *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sp || sp->magic != SIXPACK_MAGIC || !sp->dev->start) {
+ return;
+ }
+ if (sp->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ sp->tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ sp_unlock(sp);
+ sp->tx_enable = 0;
+ mark_bh(NET_BH);
+ return;
+ }
+
+ if (sp->tx_enable == 1) {
+ actual = tty->driver.write(tty, 0, sp->xhead, sp->xleft);
+ sp->xleft -= actual;
+ sp->xhead += actual;
+ }
+}
+
+/* ----------------------------------------------------------------------- */
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+
+static int
+sp_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+
+ if (!dev->start)
+ {
+ printk(KERN_WARNING "%s: xmit call when iface is down\n", dev->name);
+ return 1;
+ }
+
+ if (dev->tbusy)
+ return 1;
+
+ /* We were not busy, so we are now... :-) */
+ if (skb != NULL) {
+ sp_lock(sp);
+ sp->tx_bytes+=skb->len; /*---2.1.x---*/
+ sp_encaps(sp, skb->data, skb->len);
+ dev_kfree_skb(skb);
+ }
+ return 0;
+}
+/* #endif */
+
+
+/* perform the persistence/slottime algorithm for CSMA access. If the persistence
+ check was successful, write the data to the serial driver. Note that in case
+ of DAMA operation, the data is not sent here. */
+
+static
+void sp_xmit_on_air(unsigned long channel)
+{
+ struct sixpack *sp = (struct sixpack *) channel;
+ int actual;
+ static unsigned char random;
+
+ random = random * 17 + 41;
+
+ if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistance)) {
+ sp->led_state = 0x70;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state),1);
+ sp->tx_enable = 1;
+ actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, sp->status2);
+ sp->xleft -= actual;
+ sp->xhead += actual;
+ sp->led_state = 0x60;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state),1);
+ sp->status2 = 0;
+ } else
+ sp_start_tx_timer(sp);
+} /* sp_xmit */
+
+/* #if defined(CONFIG_6PACK) || defined(CONFIG_6PACK_MODULE) */
+
+/* Return the frame type ID */
+static int sp_header(struct sk_buff *skb, struct device *dev, unsigned short type,
+ void *daddr, void *saddr, unsigned len)
+{
+#ifdef CONFIG_INET
+ if (type != htons(ETH_P_AX25))
+ return ax25_encapsulate(skb, dev, type, daddr, saddr, len);
+#endif
+ return 0;
+}
+
+
+static int sp_rebuild_header(struct sk_buff *skb)
+{
+#ifdef CONFIG_INET
+ return ax25_rebuild_header(skb);
+#else
+ return 0;
+#endif
+}
+
+/* #endif */ /* CONFIG_{AX25,AX25_MODULE} */
+
+/* Open the low-level part of the 6pack channel. */
+static int
+sp_open(struct device *dev)
+{
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+ unsigned long len;
+
+ if (sp->tty == NULL)
+ return -ENODEV;
+
+ /*
+ * Allocate the 6pack frame buffers:
+ *
+ * rbuff Receive buffer.
+ * xbuff Transmit buffer.
+ * cbuff Temporary compression buffer.
+ */
+
+ /* !!! length of the buffers. MTU is IP MTU, not PACLEN!
+ */
+
+ len = dev->mtu * 2;
+
+ sp->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sp->rbuff == NULL)
+ return -ENOMEM;
+
+ sp->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sp->xbuff == NULL)
+ {
+ kfree(sp->rbuff);
+ return -ENOMEM;
+ }
+
+ sp->mtu = AX25_MTU + 73;
+ sp->buffsize = len;
+ sp->rcount = 0;
+ sp->rx_count = 0;
+ sp->rx_count_cooked = 0;
+ sp->xleft = 0;
+
+ sp->flags &= (1 << SIXPF_INUSE); /* Clear ESCAPE & ERROR flags */
+
+ sp->duplex = 0;
+ sp->tx_delay = SIXP_TXDELAY;
+ sp->persistance = SIXP_PERSIST;
+ sp->slottime = SIXP_SLOTTIME;
+ sp->led_state = 0x60;
+ sp->status = 1;
+ sp->status1 = 1;
+ sp->status2 = 0;
+ sp->tnc_ok = 0;
+ sp->tx_enable = 0;
+
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ init_timer(&sp->tx_t);
+ init_timer(&sp->resync_t);
+ return 0;
+}
+
+
+/* Close the low-level part of the 6pack channel. */
+static int
+sp_close(struct device *dev)
+{
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+
+ if (sp->tty == NULL) {
+ return -EBUSY;
+ }
+ sp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ return 0;
+}
+
+static int
+sixpack_receive_room(struct tty_struct *tty)
+{
+ return 65536; /* We can handle an infinite amount of data. :-) */
+}
+
+/* !!! receive state machine */
+
+/*
+ * 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)
+{
+ unsigned char buf[512];
+ unsigned long flags;
+ int count1;
+
+ struct sixpack *sp = (struct sixpack *) tty->disc_data;
+
+ if (!sp || sp->magic != SIXPACK_MAGIC || !sp->dev->start || !count)
+ return;
+
+ save_flags(flags);
+ cli();
+ memcpy(buf, cp, count<sizeof(buf)? count:sizeof(buf));
+ restore_flags(flags);
+
+ /* 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->rx_errors++;
+ }
+ continue;
+ }
+ }
+ sixpack_decode(sp, buf, count1);
+}
+
+/*
+ * 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)
+{
+ struct sixpack *sp = (struct sixpack *) tty->disc_data;
+ int err;
+
+ /* First make sure we're not already connected. */
+
+ if (sp && sp->magic == SIXPACK_MAGIC)
+ return -EEXIST;
+
+ /* OK. Find a free 6pack channel to use. */
+ if ((sp = sp_alloc()) == NULL)
+ return -ENFILE;
+ sp->tty = tty;
+ tty->disc_data = sp;
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+
+
+ /* Restore default settings */
+ sp->dev->type = ARPHRD_AX25;
+
+ /* Perform the low-level 6pack initialization. */
+ if ((err = sp_open(sp->dev)))
+ return err;
+
+ MOD_INC_USE_COUNT;
+
+ /* Done. We have linked the TTY line to a channel. */
+
+ tnc_init(sp);
+
+ return sp->dev->base_addr;
+}
+
+
+/*
+ * Close down a 6pack channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to 6pack
+ * (which usually is TTY again).
+ */
+static void
+sixpack_close(struct tty_struct *tty)
+{
+ struct sixpack *sp = (struct sixpack *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!sp || sp->magic != SIXPACK_MAGIC)
+ return;
+
+ rtnl_lock();
+ if (sp->dev->flags & IFF_UP)
+ (void) dev_close(sp->dev);
+
+ del_timer(&(sp->tx_t));
+ del_timer(&(sp->resync_t));
+
+ tty->disc_data = 0;
+ sp->tty = NULL;
+ /* VSV = very important to remove timers */
+
+ sp_free(sp);
+ unregister_netdev(sp->dev);
+ rtnl_unlock();
+ MOD_DEC_USE_COUNT;
+}
+
+
+static struct net_device_stats *
+sp_get_stats(struct device *dev)
+{
+ static struct net_device_stats stats;
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+
+ memset(&stats, 0, sizeof(struct net_device_stats));
+
+ stats.rx_packets = sp->rx_packets;
+ stats.tx_packets = sp->tx_packets;
+ stats.rx_bytes = sp->rx_bytes;
+ stats.tx_bytes = sp->tx_bytes;
+ stats.rx_dropped = sp->rx_dropped;
+ stats.tx_dropped = sp->tx_dropped;
+ stats.tx_errors = sp->tx_errors;
+ stats.rx_errors = sp->rx_errors;
+ stats.rx_over_errors = sp->rx_over_errors;
+ return (&stats);
+}
+
+
+int
+sp_set_mac_address(struct device *dev, void *addr)
+{
+ int err;
+
+ err = verify_area(VERIFY_READ, addr, AX25_ADDR_LEN);
+ if (err) {
+ return err;
+ }
+
+ copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN); /* addr is an AX.25 shifted ASCII mac address */
+
+ return 0;
+}
+
+static int
+sp_set_dev_mac_address(struct device *dev, void *addr)
+{
+ struct sockaddr *sa=addr;
+ memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
+ return 0;
+}
+
+
+/* Perform I/O control on an active 6pack channel. */
+static int
+sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+ struct sixpack *sp = (struct sixpack *) tty->disc_data;
+ int err;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!sp || sp->magic != SIXPACK_MAGIC) {
+ return -EINVAL;
+ }
+
+ switch(cmd) {
+ case SIOCGIFNAME:
+ err = verify_area(VERIFY_WRITE, arg, strlen(sp->dev->name) + 1);
+ if (err) {
+ return err;
+ }
+ copy_to_user(arg, sp->dev->name, strlen(sp->dev->name) + 1);
+ return 0;
+
+ case SIOCGIFENCAP:
+ err = verify_area(VERIFY_WRITE, arg, sizeof(int));
+ if (err) {
+ return err;
+ }
+ put_user(0, (int *)arg);
+ return 0;
+
+ case SIOCSIFENCAP:
+ err = verify_area(VERIFY_READ, arg, sizeof(int));
+ if (err) {
+ return err;
+ }
+ get_user(tmp,(int *)arg);
+
+ sp->mode = tmp;
+ sp->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */
+ sp->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
+ sp->dev->type = ARPHRD_AX25;
+
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return sp_set_mac_address(sp->dev, arg);
+
+ /* Allow stty to read, but not set, the serial port */
+ case TCGETS:
+ case TCGETA:
+ return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg);
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static int sp_open_dev(struct device *dev)
+{
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+ if(sp->tty==NULL)
+ return -ENODEV;
+ return 0;
+}
+
+/* Initialize 6pack control device -- register 6pack line discipline */
+
+#ifdef MODULE
+static int sixpack_init_ctrl_dev(void)
+#else /* !MODULE */
+__initfunc sixpack_init_ctrl_dev(struct device *dummy)
+#endif /* !MODULE */
+{
+ int status;
+
+ if (sixpack_maxdev < 4) sixpack_maxdev = 4; /* Sanity */
+
+ printk(KERN_INFO "6pack: %s (dynamic channels, max=%d)\n",
+ SIXPACK_VERSION, sixpack_maxdev);
+
+ sixpack_ctrls = (sixpack_ctrl_t **) kmalloc(sizeof(void*)*sixpack_maxdev, GFP_KERNEL);
+ if (sixpack_ctrls == NULL)
+ {
+ printk(KERN_WARNING "6pack: Can't allocate sixpack_ctrls[] array! Uaargh! (-> No 6pack available)\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(sixpack_ctrls, 0, sizeof(void*)*sixpack_maxdev); /* Pointers */
+
+
+ /* Fill in our line protocol discipline, and register it */
+ memset(&sp_ldisc, 0, sizeof(sp_ldisc));
+ sp_ldisc.magic = TTY_LDISC_MAGIC;
+ sp_ldisc.name = "6pack";
+ sp_ldisc.flags = 0;
+ sp_ldisc.open = sixpack_open;
+ sp_ldisc.close = sixpack_close;
+ sp_ldisc.read = NULL;
+ sp_ldisc.write = NULL;
+ sp_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) sixpack_ioctl;
+ sp_ldisc.poll = NULL;
+ sp_ldisc.receive_buf = sixpack_receive_buf;
+ sp_ldisc.receive_room = sixpack_receive_room;
+ sp_ldisc.write_wakeup = sixpack_write_wakeup;
+ if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) {
+ printk(KERN_WARNING "6pack: can't register line discipline (err = %d)\n", status);
+ }
+
+#ifdef MODULE
+ return status;
+#else
+ /* Return "not found", so that dev_init() will unlink
+ * the placeholder device entry for us.
+ */
+ return ENODEV;
+#endif
+}
+
+/* Initialize the 6pack driver. Called by DDI. */
+int
+sixpack_init(struct device *dev)
+{
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
+
+ static char ax25_bcast[AX25_ADDR_LEN] =
+ {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1};
+ static char ax25_test[AX25_ADDR_LEN] =
+ {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1};
+
+ if (sp == NULL) /* Allocation failed ?? */
+ return -ENODEV;
+
+ /* Set up the "6pack Control Block". (And clear statistics) */
+
+ memset(sp, 0, sizeof (struct sixpack));
+ sp->magic = SIXPACK_MAGIC;
+ sp->dev = dev;
+
+ /* Finish setting up the DEVICE info. */
+ dev->mtu = SIXP_MTU;
+ dev->hard_start_xmit = sp_xmit;
+ dev->open = sp_open_dev;
+ dev->stop = sp_close;
+ dev->hard_header = sp_header;
+ dev->get_stats = sp_get_stats;
+ dev->set_mac_address = sp_set_dev_mac_address;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN;
+ dev->addr_len = AX25_ADDR_LEN;
+ dev->type = ARPHRD_AX25;
+ dev->tx_queue_len = 10;
+ dev->rebuild_header = sp_rebuild_header;
+
+ memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); /* Only activated in AX.25 mode */
+ memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); /* "" "" "" "" */
+
+ dev_init_buffers(dev);
+
+ /* New-style flags. */
+ dev->flags = 0;
+
+ return 0;
+}
+
+#ifdef MODULE
+
+int
+init_module(void)
+{
+ return sixpack_init_ctrl_dev();
+}
+
+void
+cleanup_module(void)
+{
+ int i;
+
+ if (sixpack_ctrls != NULL)
+ {
+ for (i = 0; i < sixpack_maxdev; i++)
+ {
+ if (sixpack_ctrls[i])
+ {
+ /*
+ * VSV = if dev->start==0, then device
+ * unregistered while close proc.
+ */
+ if (sixpack_ctrls[i]->dev.start)
+ unregister_netdev(&(sixpack_ctrls[i]->dev));
+
+ kfree(sixpack_ctrls[i]);
+ sixpack_ctrls[i] = NULL;
+ }
+ }
+ kfree(sixpack_ctrls);
+ sixpack_ctrls = NULL;
+ }
+ if ((i = tty_register_ldisc(N_6PACK, NULL)))
+ {
+ printk(KERN_WARNING "6pack: can't unregister line discipline (err = %d)\n", i);
+ }
+}
+#endif /* MODULE */
+
+/* ----> 6pack timer interrupt handler and friends. <---- */
+static void
+sp_start_tx_timer(struct sixpack *sp)
+{
+ 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));
+}
+
+
+/* encode an AX.25 packet into 6pack */
+
+int encode_sixpack(byte *tx_buf, byte *tx_buf_raw, int length, byte tx_delay)
+{
+ int count = 0;
+ byte 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] = (byte)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);
+ } /* else */
+ } /* for */
+ if ((length % 3) != 2)
+ raw_count++;
+ tx_buf_raw[raw_count++] = SIXP_SEOF;
+ return(raw_count);
+}
+
+
+/* decode a 6pack packet */
+
+void
+sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count)
+{
+ byte inbyte;
+ int count1;
+
+ for (count1 = 0; count1 < count; count1++) {
+ inbyte = pre_rbuff[count1];
+ if (inbyte == SIXP_FOUND_TNC) {
+ printk(KERN_INFO "6pack: TNC found.\n");
+ sp->tnc_ok = 1;
+ del_timer(&(sp->resync_t));
+ }
+ if((inbyte & SIXP_PRIO_CMD_MASK) != 0)
+ decode_prio_command(inbyte, sp);
+ else if((inbyte & SIXP_STD_CMD_MASK) != 0)
+ decode_std_command(inbyte, sp);
+ else {
+ if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
+ decode_data(inbyte, sp);
+ } /* else */
+ } /* for */
+}
+
+static int
+tnc_init(struct sixpack *sp)
+{
+ static byte inbyte;
+
+ inbyte = 0xe8;
+ sp->tty->driver.write(sp->tty, 0, &inbyte, 1);
+
+ 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));
+
+ return 0;
+}
+
+
+/* identify and execute a 6pack priority command byte */
+
+void decode_prio_command(byte cmd, struct sixpack *sp)
+{
+ byte 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;
+ } /* if */
+ else { /* output watchdog char if idle */
+ if ((sp->status2 != 0) && (sp->duplex == 1)) {
+ sp->led_state = 0x70;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
+ sp->tx_enable = 1;
+ actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, sp->status2);
+ sp->xleft -= actual;
+ sp->xhead += actual;
+ sp->led_state = 0x60;
+ sp->status2 = 0;
+
+ } /* if */
+ } /* else */
+
+ /* needed to trigger the TNC watchdog */
+ sp->tty->driver.write(sp->tty, 0, &(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_ok == 1) {
+ 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;
+}
+
+/* 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 = 0xe8;
+ struct sixpack *sp = (struct sixpack *) channel;
+
+ printk(KERN_INFO "6pack: resyncing TNC\n");
+
+ /* clear any data that might have been received */
+
+ sp->rx_count = 0;
+ sp->rx_count_cooked = 0;
+
+ /* reset state machine */
+
+ sp->status = 1;
+ sp->status1 = 1;
+ sp->status2 = 0;
+ sp->tnc_ok = 0;
+
+ /* resync the TNC */
+
+ sp->led_state = 0x60;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
+ sp->tty->driver.write(sp->tty, 0, &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));
+}
+
+
+
+/* identify and execute a standard 6pack command byte */
+
+void decode_std_command(byte cmd, struct sixpack *sp)
+{
+ byte 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, 0, &(sp->led_state), 1);
+ } /* if */
+ } else {
+ sp->led_state = 0x60;
+ /* fill trailing bytes with zeroes */
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
+ rest = sp->rx_count;
+ if (rest != 0)
+ for(i=rest; i<=3; i++)
+ decode_data(0, sp);
+ 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);
+ } /* else */
+ sp->rx_count_cooked = 0;
+ } /* else */
+ 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");
+ } /* switch */
+} /* function */
+
+/* decode 4 sixpack-encoded bytes into 3 data bytes */
+
+void decode_data(byte inbyte, struct sixpack *sp)
+{
+
+ unsigned char *buf;
+
+ if (sp->rx_count != 3)
+ sp->raw_buf[sp->rx_count++] = inbyte;
+ else {
+ 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;
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov