summaryrefslogtreecommitdiffstats
path: root/drivers/net/hamradio/6pack.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/hamradio/6pack.c')
-rw-r--r--drivers/net/hamradio/6pack.c900
1 files changed, 396 insertions, 504 deletions
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 98b941224..9d483d1ea 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -1,11 +1,12 @@
/*
* 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.
+ * kernel's new AX.25 protocol layer.
*
- * Version: @(#)6pack.c 0.3.0 04/07/98
+ * Version: @(#)6pack.c 0.4.0 03/05/2000
*
* Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de>
+ * Jens David (rework) <dg1kjd@afthd.tu-darmstadt.de>
*
* Quite a lot of stuff "stolen" by Jörg Reuter from slip.c, written by
*
@@ -28,145 +29,65 @@
#include <linux/netdevice.h>
#include <linux/timer.h>
#include <net/ax25.h>
+#include <net/ax25dev.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>
-#define SIXPACK_VERSION "Revision: 0.3.0"
-
-/* sixpack priority commands */
-#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */
-#define SIXP_TX_URUN 0x48 /* transmit overrun */
-#define SIXP_RX_ORUN 0x50 /* receive overrun */
-#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */
-
-#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */
-
-/* masks to get certain bits out of the status bytes sent by the TNC */
-
-#define SIXP_CMD_MASK 0xC0
-#define SIXP_CHN_MASK 0x07
-#define SIXP_PRIO_CMD_MASK 0x80
-#define SIXP_STD_CMD_MASK 0x40
-#define SIXP_PRIO_DATA_MASK 0x38
-#define SIXP_TX_MASK 0x20
-#define SIXP_RX_MASK 0x10
-#define SIXP_RX_DCD_MASK 0x18
-#define SIXP_LEDS_ON 0x78
-#define SIXP_LEDS_OFF 0x60
-#define SIXP_CON 0x08
-#define SIXP_STA 0x10
-
-#define SIXP_FOUND_TNC 0xe9
-#define SIXP_CON_ON 0x68
-#define SIXP_DCD_MASK 0x08
-#define SIXP_DAMA_OFF 0
-
-/* default level 2 parameters */
-#define SIXP_TXDELAY 25 /* in 10 ms */
-#define SIXP_PERSIST 50 /* in 256ths */
-#define SIXP_SLOTTIME 10 /* in 10 ms */
-#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */
-#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */
-
-/* 6pack configuration. */
-#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */
-#define SIXP_MTU 256 /* Default MTU */
-
-enum sixpack_flags {
- SIXPF_INUSE, /* Channel in use */
- SIXPF_ERROR, /* Parity, etc. error */
-};
+#include "6pack.h"
-struct sixpack {
- int magic;
-
- /* Various fields. */
- struct tty_struct *tty; /* ptr to TTY structure */
- struct net_device *dev; /* easy for intr handling */
-
- /* 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 */
-
- unsigned char raw_buf[4];
- unsigned char cooked_buf[400];
-
- unsigned int rx_count;
- unsigned int rx_count_cooked;
-
- /* 6pack interface statistics. */
- struct net_device_stats stats;
-
- int mtu; /* Our mtu (to spot changes!) */
- int buffsize; /* Max buffers sizes */
-
- unsigned long flags; /* Flag values/ mode etc */
- unsigned char mode; /* 6pack mode */
-
- /* 6pack stuff */
- unsigned char tx_delay;
- unsigned char persistance;
- unsigned char slottime;
- unsigned char duplex;
- unsigned char led_state;
- unsigned char status;
- unsigned char status1;
- unsigned char status2;
- unsigned char tx_enable;
- unsigned char tnc_ok;
-
- struct timer_list tx_t;
- struct timer_list resync_t;
-};
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
-#define AX25_6PACK_HEADER_LEN 0
-#define SIXPACK_MAGIC 0x5304
+/* ----------------------------------------------------------------------- */
-typedef struct sixpack_ctrl {
- struct sixpack ctrl; /* 6pack things */
- struct net_device dev; /* the device */
-} sixpack_ctrl_t;
-static sixpack_ctrl_t **sixpack_ctrls;
+static struct sixpack_ctrl_t **sixpack_ctrls = NULL;
+int sixpack_maxdev = SIXP_NRUNIT; /* override with insmod */
-int sixpack_maxdev = SIXP_NRUNIT; /* Can be overridden with insmod! */
-MODULE_PARM(sixpack_maxdev, "i");
-MODULE_PARM_DESC(sixpack_maxdev, "number of 6PACK devices");
+/* ----------------------------------------------------------------------- */
-static void sp_start_tx_timer(struct sixpack *);
-static void sp_xmit_on_air(unsigned long);
+/* prototypes */
static void resync_tnc(unsigned long);
static void sixpack_decode(struct sixpack *, unsigned char[], int);
-static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char);
-static int sixpack_init(struct net_device *dev);
-
+static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned int);
static void decode_prio_command(unsigned char, struct sixpack *);
static void decode_std_command(unsigned char, struct sixpack *);
static void decode_data(unsigned char, struct sixpack *);
-
static int tnc_init(struct sixpack *);
+static unsigned int sixpack_ddi_report_dcd(struct net_device *dev);
+static unsigned int sixpack_ddi_report_ptt(struct net_device *dev);
+static void sixpack_parameter_change_notify(struct net_device *dev, int valueno,
+ int old, int new);
+
+static int sixpack_init(struct net_device *dev);
+
+/* ----------------------------------------------------------------------- */
/* Find a free 6pack channel, and link in this `tty' line. */
-static inline struct sixpack *sp_alloc(void)
+static inline struct sixpack *sp_alloc(struct tty_struct *tty)
{
- sixpack_ctrl_t *spp = NULL;
+ struct sixpack_ctrl_t *spp = NULL;
int i;
- for (i = 0; i < sixpack_maxdev; 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))
+ if (!spp->ctrl.flags.in_use)
+ {
+ spp->ctrl.flags.in_use = 1;
break;
+ }
}
/* Too many devices... */
@@ -174,42 +95,42 @@ static inline struct sixpack *sp_alloc(void)
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) {
+ if (!spp && (sixpack_ctrls[i] = (struct sixpack_ctrl_t *) kmalloc(sizeof(struct sixpack_ctrl_t), GFP_KERNEL)) != NULL) {
spp = sixpack_ctrls[i];
- memset(spp, 0, sizeof(sixpack_ctrl_t));
+ memset(spp, 0, sizeof(struct sixpack_ctrl_t));
/* Initialize channel control data */
- set_bit(SIXPF_INUSE, &spp->ctrl.flags);
- spp->ctrl.tty = NULL;
+ spp->ctrl.flags.in_use= 1;
sprintf(spp->dev.name, "sp%d", i);
spp->dev.base_addr = i;
- spp->dev.priv = (void *) &spp->ctrl;
+ 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;
- SET_MODULE_OWNER(&spp->dev);
- return &spp->ctrl;
+ if (spp != NULL)
+ {
+ spp->ctrl.tty = tty;
+ /*
+ * 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)
+ {
+ spp->ctrl.flags.in_use = 1;
+ spp->ctrl.dev = &(spp->dev);
+ spp->dev.priv = (void*)&(spp->ctrl);
+ return (&(spp->ctrl));
} else {
- clear_bit(SIXPF_INUSE, &spp->ctrl.flags);
+ spp->ctrl.flags.in_use = 0;
printk(KERN_WARNING "sp_alloc() - register_netdev() failure.\n");
}
}
-
return NULL;
}
+/* ----------------------------------------------------------------------- */
/* Free a 6pack channel. */
static inline void sp_free(struct sixpack *sp)
@@ -218,48 +139,40 @@ static inline void sp_free(struct sixpack *sp)
if (sp->rbuff)
kfree(sp->rbuff);
sp->rbuff = NULL;
- if (sp->xbuff)
+ 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);
}
+/* ----------------------------------------------------------------------- */
/* 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;
-
+ count = sp->rcount;
sp->stats.rx_bytes += count;
- if ((skb = dev_alloc_skb(count)) == NULL) {
+ skb = dev_alloc_skb(count);
+ if (skb == NULL)
+ {
printk(KERN_DEBUG "%s: memory squeeze, dropping packet.\n", sp->dev->name);
sp->stats.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);
+ skb->mac.raw=skb->data;
+ skb->protocol=htons(ETH_P_AX25);
netif_rx(skb);
sp->stats.rx_packets++;
}
-
/* ----------------------------------------------------------------------- */
/* Encapsulate one AX.25 frame and stuff into a TTY queue. */
@@ -267,70 +180,38 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len)
{
unsigned char *p;
int actual, count;
+ int tx_delay;
- if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */
- printk(KERN_DEBUG "%s: truncating oversized transmit packet!\n", sp->dev->name);
+ if (len > sp->mtu) {
+ len = sp->mtu;
+ printk(KERN_DEBUG "%s: dropping oversized transmit packet!\n", sp->dev->name);
sp->stats.tx_dropped++;
- netif_start_queue(sp->dev);
+ netif_wake_queue(sp->dev);
return;
}
p = icp;
- if (p[0] > 5) {
- printk(KERN_DEBUG "%s: invalid KISS command -- dropped\n", sp->dev->name);
- netif_start_queue(sp->dev);
- return;
- }
+ tx_delay = ax25_dev_get_value(sp->dev, AX25_VALUES_MEDIA_TXDELAY);
+ if (tx_delay > 2550) tx_delay = 2550;
- if ((p[0] != 0) && (len > 2)) {
- printk(KERN_DEBUG "%s: KISS control packet too long -- dropped\n", sp->dev->name);
- netif_start_queue(sp->dev);
- return;
- }
-
- if ((p[0] == 0) && (len < 15)) {
- printk(KERN_DEBUG "%s: bad AX.25 packet to transmit -- dropped\n", sp->dev->name);
- netif_start_queue(sp->dev);
- sp->stats.tx_dropped++;
- return;
- }
-
- count = encode_sixpack(p, (unsigned char *) sp->xbuff, len, sp->tx_delay);
+ count = encode_sixpack(p, (unsigned char *) sp->xbuff, len, 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;
- }
+ sp->led_state = 0x70;
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
- 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);
- }
- }
+ sp->tx_counter++; /* TX counter +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);
}
+/* ----------------------------------------------------------------------- */
+
/*
* Called by the TTY driver when there's room for more data. If we have
* more packets to send, we send them here.
@@ -341,9 +222,9 @@ static void sixpack_write_wakeup(struct tty_struct *tty)
struct sixpack *sp = (struct sixpack *) tty->disc_data;
/* First make sure we're connected. */
- if (!sp || sp->magic != SIXPACK_MAGIC ||
- !netif_running(sp->dev))
+ if (!sp || !netif_running(sp->dev)) {
return;
+ }
if (sp->xleft <= 0) {
/* Now serial buffer is almost free & we can start
@@ -365,83 +246,36 @@ static void sixpack_write_wakeup(struct tty_struct *tty)
/* ----------------------------------------------------------------------- */
/* Encapsulate an IP datagram and kick it into a TTY queue. */
-
static int sp_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct sixpack *sp = (struct sixpack *) dev->priv;
/* We were not busy, so we are now... :-) */
- netif_stop_queue(dev);
- sp->stats.tx_bytes += skb->len;
- sp_encaps(sp, skb->data, skb->len);
- dev_kfree_skb(skb);
- return 0;
-}
-
-
-/* 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);
-}
-
-
-/* Return the frame type ID */
-static int sp_header(struct sk_buff *skb, struct net_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
+ if (skb != NULL) {
+ netif_stop_queue(dev);
+ sp->stats.tx_bytes += skb->len;
+ sp_encaps(sp, skb->data, skb->len);
+ dev_kfree_skb(skb);
+ }
return 0;
-#endif
}
+/* ----------------------------------------------------------------------- */
/* Open the low-level part of the 6pack channel. */
static int sp_open(struct net_device *dev)
{
- struct sixpack *sp = (struct sixpack *) dev->priv;
+ 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!
@@ -449,62 +283,62 @@ static int sp_open(struct net_device *dev)
len = dev->mtu * 2;
- if ((sp->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
+ sp->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL);
+ if (sp->rbuff == NULL)
return -ENOMEM;
- if ((sp->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) {
+ 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->mtu = SIXP_MTU;
+ 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->xleft = 0;
+ sp->flags.error = 0;
sp->led_state = 0x60;
sp->status = 1;
sp->status1 = 1;
sp->status2 = 0;
sp->tnc_ok = 0;
sp->tx_enable = 0;
+ sp->tx_counter = 0;
netif_start_queue(dev);
- 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 net_device *dev)
{
- struct sixpack *sp = (struct sixpack *) dev->priv;
+ struct sixpack *sp = (struct sixpack*)(dev->priv);
- if (sp->tty == NULL)
+ if (sp->tty == NULL) {
return -EBUSY;
-
+ }
sp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
netif_stop_queue(dev);
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.
@@ -517,38 +351,41 @@ static void sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp,
unsigned char buf[512];
unsigned long flags;
int count1;
-
struct sixpack *sp = (struct sixpack *) tty->disc_data;
- if (!sp || sp->magic != SIXPACK_MAGIC ||
- !netif_running(sp->dev) || !count)
+ if (!sp || !netif_running(sp->dev) || !count)
return;
save_flags(flags);
cli();
- memcpy(buf, cp, count<sizeof(buf)? count:sizeof(buf));
+ memcpy(buf, cp, min(count, sizeof(buf)));
restore_flags(flags);
/* Read the characters out of the buffer */
-
count1 = count;
- while (count) {
+ while(count)
+ {
count--;
if (fp && *fp++) {
- if (!test_and_set_bit(SIXPF_ERROR, &sp->flags))
+ if (!sp->flags.error)
+ {
+ sp->flags.error = 1;
sp->stats.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...
+ * a free 6pack channel...
*/
static int sixpack_open(struct tty_struct *tty)
{
@@ -556,35 +393,32 @@ static int sixpack_open(struct tty_struct *tty)
int err;
/* First make sure we're not already connected. */
-
- if (sp && sp->magic == SIXPACK_MAGIC)
- return -EEXIST;
+ if (sp) return -EEXIST;
/* OK. Find a free 6pack channel to use. */
- if ((sp = sp_alloc()) == NULL)
+ if ((sp = sp_alloc(tty)) == 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.
@@ -597,14 +431,11 @@ 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;
-
+ if (!sp) return;
rtnl_lock();
- dev_close(sp->dev);
-
- del_timer(&sp->tx_t);
- del_timer(&sp->resync_t);
+ if (sp->dev->flags & IFF_UP)
+ dev_close(sp->dev);
+ del_timer(&(sp->resync_t));
tty->disc_data = 0;
sp->tty = NULL;
@@ -612,28 +443,37 @@ static void sixpack_close(struct tty_struct *tty)
sp_free(sp);
unregister_netdevice(sp->dev);
rtnl_unlock();
+ MOD_DEC_USE_COUNT;
}
+/* ----------------------------------------------------------------------- */
static struct net_device_stats *sp_get_stats(struct net_device *dev)
{
- struct sixpack *sp = (struct sixpack *) dev->priv;
- return &sp->stats;
+ struct sixpack *sp = (struct sixpack*) (dev->priv);
+ return (&sp->stats);
}
+/* ----------------------------------------------------------------------- */
-static int sp_set_mac_address(struct net_device *dev, void *addr)
+static int sixpack_set_mac_address(struct net_device *dev, void *addr)
{
- return copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN) ? -EFAULT : 0;
+ if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN))
+ return -EFAULT;
+ return 0;
}
-static int sp_set_dev_mac_address(struct net_device *dev, void *addr)
+/* ----------------------------------------------------------------------- */
+
+static int sixpack_dev_set_mac_address(struct net_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)
@@ -642,29 +482,28 @@ static int sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
unsigned int tmp;
/* First make sure we're connected. */
- if (!sp || sp->magic != SIXPACK_MAGIC)
+ if (!sp)
return -EINVAL;
switch(cmd) {
case SIOCGIFNAME:
- return copy_to_user(arg, sp->dev->name, strlen(sp->dev->name) + 1) ? -EFAULT : 0;
+ if (copy_to_user(arg, sp->dev->name, IFNAMSIZ))
+ return -EFAULT;
+ return 0;
case SIOCGIFENCAP:
- return put_user(0, (int *)arg);
+ if (put_user(sp->mode, (int *)arg))
+ return -EFAULT;
+ return 0;
case SIOCSIFENCAP:
- if (get_user(tmp, (int *) arg))
+ if (get_user(tmp,(int *)arg))
return -EFAULT;
-
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);
+ case SIOCSIFHWADDR:
+ return sixpack_set_mac_address(sp->dev, arg);
/* Allow stty to read, but not set, the serial port */
case TCGETS:
@@ -676,155 +515,115 @@ static int sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
}
}
-static int sp_open_dev(struct net_device *dev)
+/* ----------------------------------------------------------------------- */
+
+/* report DCD state to DDI layer */
+static unsigned int sixpack_ddi_report_dcd(struct net_device *dev)
{
struct sixpack *sp = (struct sixpack *) dev->priv;
- if (sp->tty == NULL)
- return -ENODEV;
- return 0;
+ return (sp->status1 & SIXP_DCD_MASK);
}
-/* Fill in our line protocol discipline */
-static struct tty_ldisc sp_ldisc = {
- magic: TTY_LDISC_MAGIC,
- name: "6pack",
- open: sixpack_open,
- close: sixpack_close,
- ioctl: (int (*)(struct tty_struct *, struct file *,
- unsigned int, unsigned long)) sixpack_ioctl,
- receive_buf: sixpack_receive_buf,
- receive_room: sixpack_receive_room,
- write_wakeup: sixpack_write_wakeup,
-};
-
-/* Initialize 6pack control device -- register 6pack line discipline */
-
-static const char msg_banner[] __initdata = KERN_INFO "AX.25: 6pack driver, " SIXPACK_VERSION " (dynamic channels, max=%d)\n";
-static const char msg_invparm[] __initdata = KERN_ERR "6pack: sixpack_maxdev parameter too large.\n";
-static const char msg_nomem[] __initdata = KERN_ERR "6pack: can't allocate sixpack_ctrls[] array! No 6pack available.\n";
-static const char msg_regfail[] __initdata = KERN_ERR "6pack: can't register line discipline (err = %d)\n";
+/* ----------------------------------------------------------------------- */
-static int __init sixpack_init_driver(void)
+/* report PTT state to DDI layer */
+static unsigned int sixpack_ddi_report_ptt(struct net_device *dev)
{
- int status;
-
- /* Do sanity checks on maximum device parameter. */
- if (sixpack_maxdev < 4)
- sixpack_maxdev = 4;
- if (sixpack_maxdev * sizeof(void*) >= KMALLOC_MAXSIZE) {
- printk(msg_invparm);
- return -ENFILE;
- }
-
- printk(msg_banner, sixpack_maxdev);
-
- sixpack_ctrls = (sixpack_ctrl_t **) kmalloc(sizeof(void*)*sixpack_maxdev, GFP_KERNEL);
- if (sixpack_ctrls == NULL) {
- printk(msg_nomem);
- return -ENOMEM;
- }
+ struct sixpack *sp = (struct sixpack *) dev->priv;
+ return !!sp->tx_counter;
+}
- /* Clear the pointer array, we allocate devices when we need them */
- memset(sixpack_ctrls, 0, sizeof(void*)*sixpack_maxdev); /* Pointers */
+/* ----------------------------------------------------------------------- */
- /* Register the provided line protocol discipline */
- if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) {
- printk(msg_regfail, status);
- kfree(sixpack_ctrls);
+/* this function if called by DDI layer whenever a parameter changes. */
+static void sixpack_parameter_change_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ struct sixpack *sp = (struct sixpack *) dev->priv;
+ int br;
+
+ switch (valueno) {
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ /*
+ * If anybody knows how to set TTY's baud rate
+ * from kernel mode please add it here. We currently
+ * reject baud rate change requests.
+ */
+ br = tty_get_baud_rate(sp->tty);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, br);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, br);
+ break;
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ if (new >= 2500) ax25_dev_set_value(dev, valueno, 2490);
+ break;
+ case AX25_VALUES_MEDIA_DUPLEX:
+ default:
+ /* just let them do it. */
+ break;
}
-
- return status;
+ return;
}
-static const char msg_unregfail[] __exitdata = KERN_ERR "6pack: can't unregister line discipline (err = %d)\n";
+/* ----------------------------------------------------------------------- */
-static void __exit sixpack_exit_driver(void)
+static int sp_open_dev(struct net_device *dev)
{
- int i;
+ struct sixpack *sp = (struct sixpack*) (dev->priv);
- if ((i = tty_register_ldisc(N_6PACK, NULL)))
- printk(msg_unregfail, i);
-
- for (i = 0; i < sixpack_maxdev; i++) {
- if (sixpack_ctrls[i]) {
- /*
- * VSV = if dev->start==0, then device
- * unregistered while close proc.
- */
- if (netif_running(&sixpack_ctrls[i]->dev))
- unregister_netdev(&sixpack_ctrls[i]->dev);
-
- kfree(sixpack_ctrls[i]);
- }
- }
- kfree(sixpack_ctrls);
+ if (sp->tty == NULL) return -ENODEV;
+ return 0;
}
+/* ----------------------------------------------------------------------- */
-/* Initialize the 6pack driver. Called by DDI. */
+/* Initialize the 6pack driver. Called by core/dev.c on registration */
static int sixpack_init(struct net_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 ?? */
+ if (sp == NULL)
return -ENODEV;
/* Set up the "6pack Control Block". (And clear statistics) */
-
- memset(sp, 0, sizeof (struct sixpack));
- sp->magic = SIXPACK_MAGIC;
- sp->dev = dev;
+ 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->hard_header = NULL;
dev->get_stats = sp_get_stats;
- dev->set_mac_address = sp_set_dev_mac_address;
+ dev->set_mac_address = sixpack_dev_set_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;
+ dev->rebuild_header = NULL;
dev->tx_timeout = NULL;
-
- memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); /* Only activated in AX.25 mode */
- memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); /* "" "" "" "" */
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+ AX25_PTR(dev) = &sp->ax25dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+ AX25_PTR(dev)->hw.fast = 0;
+ AX25_PTR(dev)->hw.dcd = sixpack_ddi_report_dcd;
+ AX25_PTR(dev)->hw.ptt = sixpack_ddi_report_ptt;
+ AX25_PTR(dev)->hw.cts = NULL;
+ AX25_PTR(dev)->hw.rts = NULL;
+ AX25_PTR(dev)->hw.parameter_change_notify = sixpack_parameter_change_notify;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 200);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 20);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 1);
dev_init_buffers(dev);
-
- /* New-style flags. */
- dev->flags = 0;
-
return 0;
}
-
-
-
-/* ----> 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 */
-
-static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay)
+int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned int tx_delay)
{
int count = 0;
unsigned char checksum = 0, buf[400];
@@ -833,37 +632,39 @@ static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int
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];
+ buf[0] = tx_delay/10; /* 10 ms units */
+ memcpy(&buf[1], tx_buf, length);
+ length++;
- for (count = 0; count < length; 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) {
+ 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) {
+ }
+ else if((count % 3) == 1) {
tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
- tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c);
+ 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);
+ tx_buf_raw[raw_count++] =
+ (buf[count] >> 2);
}
}
if ((length % 3) != 2)
raw_count++;
tx_buf_raw[raw_count++] = SIXP_SEOF;
- return raw_count;
+ return(raw_count);
}
+/* ----------------------------------------------------------------------- */
/* decode a 6pack packet */
-
-static void
-sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count)
+void sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count)
{
unsigned char inbyte;
int count1;
@@ -873,134 +674,147 @@ sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count)
if (inbyte == SIXP_FOUND_TNC) {
printk(KERN_INFO "6pack: TNC found.\n");
sp->tnc_ok = 1;
- del_timer(&sp->resync_t);
+ sp->tx_counter = 0;
+ del_timer(&(sp->resync_t));
}
- if ((inbyte & SIXP_PRIO_CMD_MASK) != 0)
+ if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) {
decode_prio_command(inbyte, sp);
- else if ((inbyte & SIXP_STD_CMD_MASK) != 0)
+ } 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)
+ } else {
decode_data(inbyte, sp);
+ }
}
}
+/* ----------------------------------------------------------------------- */
+
static int tnc_init(struct sixpack *sp)
{
- unsigned char inbyte = 0xe8;
+ static unsigned char inbyte = 0xe8;
sp->tty->driver.write(sp->tty, 0, &inbyte, 1);
- del_timer(&sp->resync_t);
+ 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);
+ add_timer(&(sp->resync_t));
return 0;
}
+/* ----------------------------------------------------------------------- */
/* identify and execute a 6pack priority command byte */
-
-static void decode_prio_command(unsigned char cmd, struct sixpack *sp)
+void decode_prio_command(unsigned char cmd, struct sixpack *sp)
{
unsigned char channel;
int actual;
+ int duplex;
+ struct net_device *dev = sp->dev;
+ duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX);
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 ((cmd & SIXP_PRIO_DATA_MASK) != 0) {
+ /*
+ * not 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)
+ if (sp->status != 1) {
printk(KERN_DEBUG "6pack: protocol violation\n");
- else
+ sp->tnc_ok = 0;
+ } 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)) {
+ } else {
+ /* output watchdog char if idle */
+ if ((sp->status2 != 0) && (duplex == 1)) {
sp->led_state = 0x70;
- sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1);
+ 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;
-
}
}
- /* needed to trigger the TNC watchdog */
- sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1);
+ if (((cmd & 0xc0) == 0x80) && (cmd & 0x20)) {
+ /* dirty hack for now */
+ if (--sp->tx_counter < 0)
+ sp->tnc_ok = 0;
+ }
- /* if the state byte has been received, the TNC is present,
- so the resync timer can be reset. */
+ /* 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);
+ 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);
+ 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 */
+/* ----------------------------------------------------------------------- */
+/*
+ * 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");
+ printk(KERN_INFO "6pack: resyncing TNC...\n");
/* clear any data that might have been received */
-
- sp->rx_count = 0;
- sp->rx_count_cooked = 0;
+ 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;
+ 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, &(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);
+ 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);
+ add_timer(&(sp->resync_t));
}
-
+/* ----------------------------------------------------------------------- */
/* identify and execute a standard 6pack command byte */
-
-static void decode_std_command(unsigned char cmd, struct sixpack *sp)
+void decode_std_command(unsigned char cmd, struct sixpack *sp)
{
unsigned char checksum = 0, rest = 0, channel;
short i;
@@ -1009,27 +823,27 @@ static void decode_std_command(unsigned char cmd, struct sixpack *sp)
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) {
+ 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);
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
}
} else {
sp->led_state = 0x60;
/* fill trailing bytes with zeroes */
- sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1);
+ sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1);
rest = sp->rx_count;
if (rest != 0)
- for (i = rest; i <= 3; i++)
+ for(i=rest; i<=3; i++)
decode_data(0, sp);
- if (rest == 2)
+ if (rest == 2) {
sp->rx_count_cooked -= 2;
- else if (rest == 3)
+ } else if (rest == 3) {
sp->rx_count_cooked -= 1;
- for (i = 0; i < sp->rx_count_cooked; i++)
- checksum += sp->cooked_buf[i];
+ }
+ 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);
+ sp->stats.rx_crc_errors++;
} else {
sp->rcount = sp->rx_count_cooked-2;
sp_bump(sp, 0);
@@ -1037,24 +851,30 @@ static void decode_std_command(unsigned char cmd, struct sixpack *sp)
sp->rx_count_cooked = 0;
}
break;
- case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n");
+
+ case SIXP_TX_URUN:
+ sp->stats.tx_fifo_errors++;
break;
- case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n");
+
+ case SIXP_RX_ORUN:
+ sp->stats.rx_over_errors++;
break;
+
case SIXP_RX_BUF_OVL:
- printk(KERN_DEBUG "6pack: RX buffer overflow\n");
+ sp->stats.rx_length_errors++;
}
}
-/* decode 4 sixpack-encoded bytes into 3 data bytes */
+/* ----------------------------------------------------------------------- */
-static void decode_data(unsigned char inbyte, struct sixpack *sp)
+/* decode 4 sixpack-encoded bytes into 3 data bytes */
+void decode_data(unsigned char inbyte, struct sixpack *sp)
{
unsigned char *buf;
- if (sp->rx_count != 3)
+ if (sp->rx_count != 3) {
sp->raw_buf[sp->rx_count++] = inbyte;
- else {
+ } else {
buf = sp->raw_buf;
sp->cooked_buf[sp->rx_count_cooked++] =
buf[0] | ((buf[1] << 2) & 0xc0);
@@ -1066,9 +886,81 @@ static void decode_data(unsigned char inbyte, struct sixpack *sp)
}
}
+/* ----------------------------------------------------------------------- */
+
+/* Fill in our line protocol discipline */
+static struct tty_ldisc sp_ldisc = {
+ magic: TTY_LDISC_MAGIC,
+ name: "6pack",
+ open: sixpack_open,
+ close: sixpack_close,
+ ioctl: (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) sixpack_ioctl,
+ receive_buf: sixpack_receive_buf,
+ receive_room: sixpack_receive_room,
+ write_wakeup: sixpack_write_wakeup,
+};
+
+
+/* Initialize 6pack control device -- register 6pack line discipline */
+int __init sixpack_init_ctrl_dev(void)
+{
+ int status;
+
+ /* Do sanity checks on maximum device parameter. */
+ if (sixpack_maxdev < 4) sixpack_maxdev = 4;
+
+ printk(KERN_INFO "AX.25: 6pack driver, %s (dynamic channels, max=%d)\n",
+ SIXPACK_VERSION, sixpack_maxdev);
+
+ sixpack_ctrls = (struct 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);
+
+ if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) {
+ printk(KERN_WARNING "6pack: can't register line discipline (err = %d)\n", status);
+ kfree(sixpack_ctrls);
+ }
+
+ return 0;
+}
+
+void __exit sixpack_exit_driver(void)
+{
+ int i;
+
+ if (sixpack_ctrls != NULL) {
+ if ((i = tty_register_ldisc(N_6PACK, NULL)))
+ printk(KERN_WARNING "6pack: can't unregister line discipline (err = %d)\n", i);
+
+ for (i = 0; i < sixpack_maxdev; i++) {
+ if (sixpack_ctrls[i]) {
+ /*
+ * VSV = if netif_running(), then device
+ * unregistered while close proc.
+ */
+ if (netif_running(&(sixpack_ctrls[i]->dev)))
+ unregister_netdev(&(sixpack_ctrls[i]->dev));
+
+ kfree(sixpack_ctrls[i]);
+ sixpack_ctrls[i] = NULL;
+ }
+ }
+ kfree(sixpack_ctrls);
+ sixpack_ctrls = NULL;
+ }
+}
MODULE_AUTHOR("Andreas Könsgen <ajk@ccac.rwth-aachen.de>");
MODULE_DESCRIPTION("6pack driver for AX.25");
+MODULE_PARM(sixpack_maxdev, "i");
+MODULE_PARM_DESC(sixpack_maxdev, "number of 6PACK devices");
-module_init(sixpack_init_driver);
+module_init(sixpack_init_ctrl_dev);
module_exit(sixpack_exit_driver);