summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/hamradio/6pack.c900
-rw-r--r--drivers/net/hamradio/6pack.h106
-rw-r--r--drivers/net/hamradio/Config.in3
-rw-r--r--drivers/net/hamradio/Makefile14
-rw-r--r--drivers/net/hamradio/ax25_userdev.c596
-rw-r--r--drivers/net/hamradio/baycom_epp.c7
-rw-r--r--drivers/net/hamradio/bpqether.c247
-rw-r--r--drivers/net/hamradio/dmascc.h191
-rw-r--r--drivers/net/hamradio/hdlcdrv.c21
-rw-r--r--drivers/net/hamradio/kiss.c1276
-rw-r--r--drivers/net/hamradio/kiss.h90
-rw-r--r--drivers/net/hamradio/mkiss.c1016
-rw-r--r--drivers/net/hamradio/mkiss.h61
-rw-r--r--drivers/net/hamradio/pciscc4.c2494
-rw-r--r--drivers/net/hamradio/scc.c2529
-rw-r--r--drivers/net/hamradio/scc.c-noprocfs1926
-rw-r--r--drivers/net/hamradio/soundmodem/Makefile42
-rw-r--r--drivers/net/hamradio/soundmodem/sm.c759
-rw-r--r--drivers/net/hamradio/soundmodem/sm.h386
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk1200.c272
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk2400_7.c296
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk2400_8.c296
-rw-r--r--drivers/net/hamradio/soundmodem/sm_afsk2666.c356
-rw-r--r--drivers/net/hamradio/soundmodem/sm_fsk9600.c391
-rw-r--r--drivers/net/hamradio/soundmodem/sm_hapn4800.c560
-rw-r--r--drivers/net/hamradio/soundmodem/sm_psk4800.c418
-rw-r--r--drivers/net/hamradio/soundmodem/sm_sbc.c941
-rw-r--r--drivers/net/hamradio/soundmodem/sm_wss.c965
-rw-r--r--drivers/net/hamradio/soundmodem/smdma.h217
-rw-r--r--drivers/net/hamradio/yam.c547
-rw-r--r--include/asm-alpha/termios.h2
-rw-r--r--include/asm-arm/termios.h2
-rw-r--r--include/asm-i386/termios.h2
-rw-r--r--include/asm-m68k/termios.h2
-rw-r--r--include/asm-mips/termios.h2
-rw-r--r--include/asm-ppc/termios.h2
-rw-r--r--include/asm-sparc/termios.h2
-rw-r--r--include/asm-sparc64/termios.h2
-rw-r--r--include/linux/ax25.h72
-rw-r--r--include/linux/ax25_userdev.h120
-rw-r--r--include/linux/miscdevice.h1
-rw-r--r--include/linux/netdevice.h1
-rw-r--r--include/linux/netlink.h1
-rw-r--r--include/linux/scc.h198
-rw-r--r--include/linux/sysctl.h88
-rw-r--r--include/net/ax25.h381
-rw-r--r--include/net/ax25_uid.h33
-rw-r--r--include/net/ax25call.h2
-rw-r--r--include/net/ax25dev.h164
-rw-r--r--include/net/netrom.h3
-rw-r--r--include/net/rose.h3
-rw-r--r--net/ax25/Config.in37
-rw-r--r--net/ax25/Makefile15
-rw-r--r--net/ax25/af_ax25.c1740
-rw-r--r--net/ax25/af_ax25.h21
-rw-r--r--net/ax25/ax25_addr.c303
-rw-r--r--net/ax25/ax25_core.c513
-rw-r--r--net/ax25/ax25_core.h21
-rw-r--r--net/ax25/ax25_ctl.c133
-rw-r--r--net/ax25/ax25_ctl.h6
-rw-r--r--net/ax25/ax25_ddi.c1186
-rw-r--r--net/ax25/ax25_ddi.h39
-rw-r--r--net/ax25/ax25_dev.c213
-rw-r--r--net/ax25/ax25_ds_in.c312
-rw-r--r--net/ax25/ax25_ds_subr.c217
-rw-r--r--net/ax25/ax25_ds_timer.c224
-rw-r--r--net/ax25/ax25_iface.c268
-rw-r--r--net/ax25/ax25_in.c984
-rw-r--r--net/ax25/ax25_in.h20
-rw-r--r--net/ax25/ax25_ip.c211
-rw-r--r--net/ax25/ax25_ipax.c708
-rw-r--r--net/ax25/ax25_lapb.c789
-rw-r--r--net/ax25/ax25_lapb.h11
-rw-r--r--net/ax25/ax25_netlink.c110
-rw-r--r--net/ax25/ax25_netlink.h10
-rw-r--r--net/ax25/ax25_out.c356
-rw-r--r--net/ax25/ax25_route.c594
-rw-r--r--net/ax25/ax25_route.h25
-rw-r--r--net/ax25/ax25_std_in.c467
-rw-r--r--net/ax25/ax25_std_subr.c102
-rw-r--r--net/ax25/ax25_std_timer.c170
-rw-r--r--net/ax25/ax25_subr.c388
-rw-r--r--net/ax25/ax25_subr.h38
-rw-r--r--net/ax25/ax25_timer.c569
-rw-r--r--net/ax25/ax25_timer.h11
-rw-r--r--net/ax25/ax25_uid.c197
-rw-r--r--net/ax25/ax25_vj.c724
-rw-r--r--net/ax25/ax25_vj.h73
-rw-r--r--net/ax25/sysctl_net_ax25.c188
-rw-r--r--net/ipv4/arp.c27
-rw-r--r--net/netrom/af_netrom.c130
-rw-r--r--net/netrom/nr_loopback.c6
-rw-r--r--net/netrom/nr_route.c98
-rw-r--r--net/netsyms.c1
-rw-r--r--net/rose/af_rose.c107
-rw-r--r--net/rose/rose_link.c16
-rw-r--r--net/rose/rose_route.c75
-rw-r--r--net/rose/rose_subr.c7
98 files changed, 16587 insertions, 14879 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);
diff --git a/drivers/net/hamradio/6pack.h b/drivers/net/hamradio/6pack.h
new file mode 100644
index 000000000..ac4ad0052
--- /dev/null
+++ b/drivers/net/hamradio/6pack.h
@@ -0,0 +1,106 @@
+/*
+ * 6pack.h Define the 6pack device driver interface and constants.
+ *
+ * Version: @(#)6pack.h 0.4.0 03/05/2000
+ *
+ * Author: Andreas Könsgen <ajk@iehk.rwth-aachen.de>
+ * Jens David <dg1kjd@afthd.tu-darmstadt.de>
+ *
+ * This file is derived from slip.h, written by
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+
+#ifndef _LINUX_6PACK_H
+#define _LINUX_6PACK_H
+
+#include <net/ax25dev.h>
+
+#define SIXPACK_VERSION "Revision: 0.4.0"
+
+#ifdef __KERNEL__
+
+/* 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 250 /* in ms */
+#define SIXP_INIT_RESYNC_TIMEOUT 1500 /* in ms */
+#define SIXP_RESYNC_TIMEOUT 5000 /* in ms */
+
+/* 6pack configuration. */
+#define SIXP_NRUNIT 256 /* MAX number of 6pack channels */
+#define SIXP_MTU (256+MAX_HEADER)
+
+struct sixpack {
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct net_device *dev; /* easy for intr handling */
+ struct ax25_dev ax25dev; /* AX.25 control structure */
+ struct net_device_stats stats; /* statistics */
+
+ spinlock_t lock;
+
+ /* 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;
+
+ int mtu; /* Our mtu (to spot changes!) */
+ int buffsize; /* Max buffers sizes */
+
+ struct {
+ int in_use:1;
+ int error:1;
+ } flags;
+
+ unsigned char mode; /* 6pack mode */
+
+ /* 6pack stuff */
+ unsigned char led_state;
+ unsigned char status;
+ unsigned char status1;
+ unsigned char status2;
+ int tx_counter; /* counts "unack'ed" TX packets */
+ unsigned char tx_enable;
+ unsigned char tnc_ok;
+
+ struct timer_list resync_t;
+};
+
+struct sixpack_ctrl_t {
+ char if_name[8]; /* "sp0\0" .. "sp99999\0" */
+ struct sixpack ctrl; /* 6pack things */
+ struct net_device dev; /* the device */
+};
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_6PACK.H */
diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in
index 1d45998f4..877169c27 100644
--- a/drivers/net/hamradio/Config.in
+++ b/drivers/net/hamradio/Config.in
@@ -1,6 +1,7 @@
comment 'AX.25 network device drivers'
-dep_tristate 'Serial port KISS driver' CONFIG_MKISS $CONFIG_AX25
+dep_tristate 'Userspace device driver support' CONFIG_AX25_USERDEV $CONFIG_AX25
+dep_tristate 'Serial port KISS driver' CONFIG_KISS $CONFIG_AX25
dep_tristate 'Serial port 6PACK driver' CONFIG_6PACK $CONFIG_AX25
dep_tristate 'BPQ Ethernet driver' CONFIG_BPQETHER $CONFIG_AX25
diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile
index 964a184c4..0af115315 100644
--- a/drivers/net/hamradio/Makefile
+++ b/drivers/net/hamradio/Makefile
@@ -10,28 +10,22 @@
# Christoph Hellwig <hch@caldera.de>
#
-
O_TARGET := hamradio.o
export-objs = hdlcdrv.o
-
obj-$(CONFIG_DMASCC) += dmascc.o
obj-$(CONFIG_SCC) += scc.o
-obj-$(CONFIG_MKISS) += mkiss.o
+obj-$(CONFIG_KISS) += kiss.o
obj-$(CONFIG_6PACK) += 6pack.o
obj-$(CONFIG_YAM) += yam.o
+obj-$(CONFIG_PI) += pi2.o
+obj-$(CONFIG_PT) += pt.o
obj-$(CONFIG_BPQETHER) += bpqether.o
+obj-$(CONFIG_AX25_USERDEV) += ax25_userdev.o
obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o
obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o
obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o
obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o
-obj-$(CONFIG_SOUNDMODEM) += hdlcdrv.o
-
-subdir-$(CONFIG_SOUNDMODEM) += soundmodem
-
-ifeq ($(CONFIG_SOUNDMODEM),y)
-obj-y += soundmodem/soundmodem.o
-endif
include $(TOPDIR)/Rules.make
diff --git a/drivers/net/hamradio/ax25_userdev.c b/drivers/net/hamradio/ax25_userdev.c
new file mode 100644
index 000000000..198df9134
--- /dev/null
+++ b/drivers/net/hamradio/ax25_userdev.c
@@ -0,0 +1,596 @@
+/*
+ * AX.25 user space device driver
+ * Portions Copyright 2001, by Joerg Reuter <jreuter@yaina.de>
+ *
+ * based on TUN - Universal TUN/TAP device driver.
+ * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/random.h>
+
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/miscdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/ax25_userdev.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+
+/* Network device part of the driver */
+
+static unsigned int ax25_udev_ddi_report_dcd(struct net_device *dev);
+static unsigned int ax25_udev_ddi_report_ptt(struct net_device *dev);
+static unsigned int ax25_udev_ddi_report_cts(struct net_device *dev);
+static void ax25_udev_ddi_set_rts(struct net_device *dev);
+static void ax25_udev_ddi_parameter_change_notify(struct net_device *dev, int item, int old, int new);
+
+/* Net device open. */
+static int ax25_udev_net_open(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+
+#ifdef notdef
+ /* I'm not quite sure if these are the right semantics: I think
+ this will make the MAC code believe that we actually are in
+ full duplex mode...
+ */
+
+ if (ax25_udev->capabilities.duplex)
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 1);
+ else
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+#endif
+
+ AX25_PTR(dev)->hw.dcd = ax25_udev_ddi_report_dcd;
+ AX25_PTR(dev)->hw.ptt = ax25_udev_ddi_report_ptt;
+ AX25_PTR(dev)->hw.fast = 0;
+
+ switch(ax25_udev->capabilities.arbitration)
+ {
+ case AX25_UDEV_CAP_ADVANCED_ARBITRATION:
+ AX25_PTR(dev)->hw.cts = ax25_udev_ddi_report_cts;
+ AX25_PTR(dev)->hw.rts = ax25_udev_ddi_set_rts;
+ break;
+ case AX25_UDEV_CAP_OWN_ARBITRATION:
+ AX25_PTR(dev)->hw.fast = 1;
+ /* fall through */
+ default:
+ AX25_PTR(dev)->hw.cts = NULL;
+ AX25_PTR(dev)->hw.rts = NULL;
+ }
+
+ AX25_PTR(dev)->hw.parameter_change_notify = ax25_udev_ddi_parameter_change_notify;
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Net device close. */
+static int ax25_udev_net_close(struct net_device *dev)
+{
+ netif_stop_queue(dev);
+ return 0;
+}
+
+/* Net device start xmit */
+
+static int ax25_udev_net_do_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv;
+
+ /* Queue frame */
+ skb_queue_tail(&ax25_udev->txq, skb);
+ if (skb_queue_len(&ax25_udev->txq) >= AX25_UDEV_TXQ_SIZE)
+ netif_stop_queue(dev);
+
+ if (ax25_udev->flags & AX25_UDEV_FASYNC)
+ kill_fasync(&ax25_udev->fasync, SIGIO, POLL_IN);
+
+ /* Wake up process */
+ wake_up_interruptible(&ax25_udev->read_wait);
+
+ return 0;
+}
+
+
+static int ax25_udev_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv;
+ DBG(KERN_INFO "%s: ax25_udev_net_xmit %d\n", ax25_udev->name, skb->len);
+
+ /* prepend packet type */
+ *((ax25_udev_pt_t *) skb_push(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_DATA;
+ return ax25_udev_net_do_xmit(skb, dev);
+}
+
+static void ax25_udev_net_mclist(struct net_device *dev)
+{
+ /* Nothing to do for multicast filters.
+ * We always accept all frames.
+ */
+ return;
+}
+
+static int ax25_udev_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+static struct net_device_stats *ax25_udev_net_stats(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv;
+
+ return &ax25_udev->stats;
+}
+
+/* Initialize net device. */
+int ax25_udev_net_init(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv;
+
+ DBG(KERN_INFO "%s: ax25_udev_net_init\n", ax25_udev->name);
+
+ SET_MODULE_OWNER(dev);
+ dev->open = ax25_udev_net_open;
+ dev->stop = ax25_udev_net_close;
+ dev->hard_start_xmit = ax25_udev_net_xmit;
+ dev->get_stats = ax25_udev_net_stats;
+ dev->set_mac_address = ax25_udev_set_mac_address;
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ AX25_PTR(dev) = &ax25_udev->ax25_dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+
+ ax25_udev->capabilities.duplex = AX25_UDEV_CAP_HALF_DUPLEX;
+ ax25_udev->capabilities.arbitration = AX25_UDEV_CAP_SIMPLE_ARBITRATION;
+
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 160);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 10);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 0);
+
+ return 0;
+}
+
+static unsigned int ax25_udev_ddi_report_dcd(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+ return ax25_udev->status.dcd;
+}
+
+static unsigned int ax25_udev_ddi_report_ptt(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+ return ax25_udev->status.ptt;
+}
+
+static unsigned int ax25_udev_ddi_report_cts(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+ return ax25_udev->status.cts;
+}
+
+static void ax25_udev_ddi_set_rts(struct net_device *dev)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(sizeof(ax25_udev_pt_t));
+ if (skb == NULL) return; /* Ouch! */
+
+ skb->dev = &ax25_udev->dev;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+
+ *((ax25_udev_pt_t *) skb_put(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_REQUEST_RTS;
+
+ /* question: is this safe or must we protect it with a spin lock? */
+ ax25_udev_net_do_xmit(skb, dev);
+}
+
+static void ax25_udev_ddi_parameter_change_notify(struct net_device *dev, int item, int old, int new)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(sizeof(ax25_udev_pt_t)+3*sizeof(ax25_udev_val_t));
+ if (skb == NULL) return; /* Ouch! */
+
+ skb->dev = &ax25_udev->dev;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+
+ *((ax25_udev_pt_t *) skb_put(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_SET_MAC_VALUE;
+ *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = item;
+ *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = old;
+ *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = new;
+
+ /* question: is this safe or must we protect it with a spin lock? */
+ ax25_udev_net_do_xmit(skb, dev);
+}
+
+
+/* Character device part */
+
+/* Poll */
+static unsigned int ax25_udev_chr_poll(struct file *file, poll_table * wait)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_poll\n", ax25_udev->name);
+
+ poll_wait(file, &ax25_udev->read_wait, wait);
+
+ if (skb_queue_len(&ax25_udev->txq))
+ return POLLIN | POLLRDNORM;
+
+ return POLLOUT | POLLWRNORM;
+}
+
+
+/* Write to netdevice (ie. read from daemon) */
+
+static ssize_t ax25_udev_chr_write(struct file * file, const char * buf,
+ size_t count, loff_t *pos)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+ struct sk_buff *skb;
+ ax25_udev_pt_t packet_type;
+ ax25_udev_val_t * arg_ptr;
+ ax25_udev_val_t item, value;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_write %d\n", ax25_udev->name, count);
+
+ if (!(ax25_udev->flags & AX25_UDEV_IFF_SET))
+ return -EBUSY;
+
+ if (verify_area(VERIFY_READ, buf, count))
+ return -EFAULT;
+
+ if (count > AX25_UDEV_MAX_FRAME)
+ return -EINVAL;
+
+ skb = dev_alloc_skb(count + MAX_HEADER);
+ if (skb == NULL)
+ return -ENOMEM;
+
+ skb_reserve(skb, MAX_HEADER);
+ skb->dev = &ax25_udev->dev;
+ copy_from_user(skb_put(skb, count), buf, count);
+
+ packet_type = *((ax25_udev_pt_t *) skb->data);
+ arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_pt_t));
+
+ switch(packet_type)
+ {
+ case AX25_UDEV_DATA:
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ netif_rx(skb);
+ ax25_udev->stats.rx_packets++;
+ ax25_udev->stats.rx_bytes += count;
+ return count;
+
+ case AX25_UDEV_CAPABILITIES:
+ while (skb->len >= sizeof(ax25_udev_val_t))
+ {
+ switch (*arg_ptr)
+ {
+ case AX25_UDEV_CAP_HALF_DUPLEX:
+ case AX25_UDEV_CAP_FULL_DUPLEX:
+ ax25_udev->capabilities.duplex = *arg_ptr;
+ break;
+ case AX25_UDEV_CAP_ADVANCED_ARBITRATION:
+ case AX25_UDEV_CAP_OWN_ARBITRATION:
+ case AX25_UDEV_CAP_SIMPLE_ARBITRATION:
+ ax25_udev->capabilities.arbitration = *arg_ptr;
+ break;
+ default:
+ break;
+
+ }
+ arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t));
+ }
+ break;
+
+ case AX25_UDEV_DCD_STATUS:
+ ax25_udev->status.dcd = *arg_ptr;
+ break;
+
+ case AX25_UDEV_CTS_STATUS:
+ ax25_udev->status.cts = *arg_ptr;
+ break;
+ case AX25_UDEV_PTT_STATUS:
+ ax25_udev->status.ptt = *arg_ptr;
+ break;
+ case AX25_UDEV_SET_MAC_VALUE:
+ while (skb->len >= sizeof(ax25_udev_val_t) * 2)
+ {
+ item = *arg_ptr;
+ arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t));
+ value = *arg_ptr;
+ arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t));
+
+ if (item < 0 || item > AX25_MAX_VALUES)
+ {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+
+ ax25_dev_set_value(skb->dev, item, value);
+ }
+
+ break;
+ default:
+ break;
+ }
+
+ kfree_skb(skb);
+
+ return count;
+}
+
+/* Put packet to user space buffer(already verified) */
+static __inline__ ssize_t ax25_udev_put_user(struct ax25_udev_struct *ax25_udev,
+ struct sk_buff *skb,
+ char *buf, int count)
+{
+ int len = count, total = 0;
+ char *ptr = buf;
+
+ len = (skb->len < len)? skb->len:len;
+ copy_to_user(ptr, skb->data, len);
+ total += len;
+
+ ax25_udev->stats.tx_packets++;
+ ax25_udev->stats.tx_bytes += len;
+
+ return total;
+}
+
+
+/* Read */
+static ssize_t ax25_udev_chr_read(struct file * file, char * buf,
+ size_t count, loff_t *pos)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ struct sk_buff *skb;
+ ssize_t ret = 0;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_read\n", ax25_udev->name);
+
+ add_wait_queue(&ax25_udev->read_wait, &wait);
+ while (count) {
+ current->state = TASK_INTERRUPTIBLE;
+
+ /* Read frames from device queue */
+ if (!(skb=skb_dequeue(&ax25_udev->txq))) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ /* Nothing to read, let's sleep */
+ schedule();
+ continue;
+ }
+ netif_start_queue(&ax25_udev->dev);
+
+ if (!verify_area(VERIFY_WRITE, buf, count))
+ ret = ax25_udev_put_user(ax25_udev, skb, buf, count);
+ else
+ ret = -EFAULT;
+
+ kfree_skb(skb);
+ break;
+ }
+
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&ax25_udev->read_wait, &wait);
+
+ return ret;
+}
+
+static loff_t ax25_udev_chr_lseek(struct file * file, loff_t offset, int origin)
+{
+ return -ESPIPE;
+}
+
+static int ax25_udev_set_iff(struct ax25_udev_struct *ax25_udev, unsigned long arg)
+{
+ struct ifreq ifr;
+ char *mask;
+
+ if (copy_from_user(&ifr, (void *)arg, sizeof(ifr)))
+ return -EFAULT;
+ ifr.ifr_name[IFNAMSIZ-1] = '\0';
+
+ if (ax25_udev->flags & AX25_UDEV_IFF_SET)
+ return -EEXIST;
+
+ mask = "ax%d";
+ if (*ifr.ifr_name)
+ strcpy(ax25_udev->dev.name, ifr.ifr_name);
+ else
+ strcpy(ax25_udev->dev.name, mask);
+
+ /* Register net device */
+ if (register_netdev(&ax25_udev->dev))
+ return -EBUSY;
+
+ ax25_udev->flags |= AX25_UDEV_IFF_SET;
+ strcpy(ax25_udev->name, ax25_udev->dev.name);
+
+ /* Return iface info to the user space */
+ strcpy(ifr.ifr_name, ax25_udev->dev.name);
+ copy_to_user((void *)arg, &ifr, sizeof(ifr));
+
+ return 0;
+}
+
+static int ax25_udev_chr_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_ioctl\n", ax25_udev->name);
+
+ switch (cmd) {
+ case AX25_UDEV_SETIFF:
+ return ax25_udev_set_iff(ax25_udev, arg);
+
+#ifdef AX25_UDEV_DEBUG
+ case AX25_UDEV_SETDEBUG:
+ ax25_udev->debug = arg;
+ break;
+#endif
+
+ default:
+ return -EINVAL;
+ };
+
+ return 0;
+}
+
+static int ax25_udev_chr_fasync(int fd, struct file *file, int on)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+ int ret;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_fasync %d\n", ax25_udev->name, on);
+
+ if ((ret = fasync_helper(fd, file, on, &ax25_udev->fasync)) < 0)
+ return ret;
+
+ if (on)
+ ax25_udev->flags |= AX25_UDEV_FASYNC;
+ else
+ ax25_udev->flags &= ~AX25_UDEV_FASYNC;
+
+ return 0;
+}
+
+static int ax25_udev_chr_open(struct inode *inode, struct file * file)
+{
+ struct ax25_udev_struct *ax25_udev = NULL;
+
+// DBG1(KERN_INFO "ax25_udevX: ax25_udev_chr_open\n");
+
+ ax25_udev = kmalloc(sizeof(struct ax25_udev_struct), GFP_KERNEL);
+ if (ax25_udev == NULL)
+ return -ENOMEM;
+
+ memset(ax25_udev, 0, sizeof(struct ax25_udev_struct));
+ file->private_data = ax25_udev;
+
+ skb_queue_head_init(&ax25_udev->txq);
+ init_waitqueue_head(&ax25_udev->read_wait);
+
+ sprintf(ax25_udev->name, "ax25");
+
+ ax25_udev->dev.init = ax25_udev_net_init;
+ ax25_udev->dev.priv = ax25_udev;
+
+ return 0;
+}
+
+static int ax25_udev_chr_close(struct inode *inode, struct file *file)
+{
+ struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data;
+
+ DBG(KERN_INFO "%s: ax25_udev_chr_close\n", ax25_udev->name);
+
+ if (ax25_udev->flags & AX25_UDEV_IFF_SET) {
+ rtnl_lock();
+ dev_close(&ax25_udev->dev);
+ rtnl_unlock();
+
+ /* Drop TX queue */
+ skb_queue_purge(&ax25_udev->txq);
+
+ unregister_netdev(&ax25_udev->dev);
+ }
+
+ kfree(ax25_udev);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static struct file_operations ax25_udev_fops = {
+ owner: THIS_MODULE,
+ llseek: ax25_udev_chr_lseek,
+ read: ax25_udev_chr_read,
+ write: ax25_udev_chr_write,
+ poll: ax25_udev_chr_poll,
+ ioctl: ax25_udev_chr_ioctl,
+ open: ax25_udev_chr_open,
+ release:ax25_udev_chr_close,
+ fasync: ax25_udev_chr_fasync
+};
+
+static struct miscdevice ax25_udev_miscdev=
+{
+ AX25_UDEV_MINOR,
+ "net/ax25",
+ &ax25_udev_fops
+};
+
+int __init ax25_udev_init(void)
+{
+ printk(KERN_INFO "AX.25: userspace network device support driver version %s\n", AX25_UDEV_VER);
+
+ if (misc_register(&ax25_udev_miscdev)) {
+ printk(KERN_ERR "ax25_udev: Can't register misc device %d\n", AX25_UDEV_MINOR);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void ax25_udev_cleanup(void)
+{
+ misc_deregister(&ax25_udev_miscdev);
+}
+
+module_init(ax25_udev_init);
+module_exit(ax25_udev_cleanup);
diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
index 0e41265fe..cd6815df7 100644
--- a/drivers/net/hamradio/baycom_epp.c
+++ b/drivers/net/hamradio/baycom_epp.c
@@ -814,7 +814,7 @@ static int receive(struct net_device *dev, int cnt)
#ifdef __i386__
#define GETTICK(x) \
({ \
- if (cpu_has_tsc) \
+ if (current_cpu_data.x86_capability & X86_FEATURE_TSC) \
__asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\
})
#else /* __i386__ */
@@ -1023,8 +1023,7 @@ static int epp_open(struct net_device *dev)
struct baycom_state *bc;
struct parport *pp;
const struct tq_struct run_bh = {
- routine: (void *)(void *)epp_bh,
- data: dev
+ 0, 0, (void *)(void *)epp_bh, dev
};
unsigned int i, j;
unsigned char tmp[128];
@@ -1507,7 +1506,7 @@ module_exit(cleanup_baycomepp);
static int __init baycom_epp_setup(char *str)
{
- static unsigned __initdata nr_dev = 0;
+ static unsigned __initlocaldata nr_dev = 0;
int ints[2];
if (nr_dev >= NR_PORTS)
diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c
index 75539ecef..db96de7b9 100644
--- a/drivers/net/hamradio/bpqether.c
+++ b/drivers/net/hamradio/bpqether.c
@@ -1,5 +1,5 @@
/*
- * G8BPQ compatible "AX.25 via ethernet" driver release 004
+ * G8BPQ compatible "AX.25 via ethernet" driver release 003
*
* This code REQUIRES 2.0.0 or higher/ NET3.029
*
@@ -62,10 +62,9 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/net.h>
-#include <net/ax25.h>
+#include <net/ax25dev.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
-#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/skbuff.h>
#include <net/sock.h>
@@ -77,25 +76,16 @@
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <linux/netfilter.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
-
#include <net/ip.h>
#include <net/arp.h>
-
#include <linux/bpqether.h>
-static const char banner[] __initdata = KERN_INFO "AX.25: bpqether driver version 004\n";
-
-static unsigned char ax25_bcast[AX25_ADDR_LEN] =
- {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static unsigned char ax25_defaddr[AX25_ADDR_LEN] =
- {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+/* ------------------------------------------------------------------------ */
static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
-
static char bpq_eth_addr[6];
static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
@@ -103,56 +93,62 @@ static int bpq_device_event(struct notifier_block *, unsigned long, void *);
static char *bpq_print_ethaddr(unsigned char *);
static struct packet_type bpq_packet_type = {
- type: __constant_htons(ETH_P_BPQ),
- func: bpq_rcv,
+ 0, /* ntohs(ETH_P_BPQ),*/
+ 0, /* copy */
+ bpq_rcv,
+ NULL,
+ NULL,
};
static struct notifier_block bpq_dev_notifier = {
- notifier_call: bpq_device_event,
+ bpq_device_event,
+ 0
};
-
#define MAXBPQDEV 100
static struct bpqdev {
struct bpqdev *next;
char ethname[14]; /* ether device name */
- struct net_device *ethdev; /* link to ethernet device */
- struct net_device axdev; /* bpq device (bpq#) */
+ struct net_device *ethdev; /* link to ethernet device */
+ struct net_device axdev; /* bpq device (bpq#) */
struct net_device_stats stats; /* some statistics */
char dest_addr[6]; /* ether destination address */
char acpt_addr[6]; /* accept ether frames from this address only */
-} *bpq_devices;
-
+ struct ax25_dev ax25dev; /* AX.25 device control structure */
+} *bpq_devices = NULL;
/* ------------------------------------------------------------------------ */
-
/*
* Get the ethernet device for a BPQ device
*/
-static inline struct net_device *bpq_get_ether_dev(struct net_device *dev)
+static __inline__ struct net_device *bpq_get_ether_dev(struct net_device *dev)
{
- struct bpqdev *bpq = (struct bpqdev *) dev->priv;
+ struct bpqdev *bpq;
- return bpq ? bpq->ethdev : NULL;
+ bpq = (struct bpqdev *)dev->priv;
+ return (bpq != NULL) ? bpq->ethdev : NULL;
}
+/* ------------------------------------------------------------------------ */
+
/*
* Get the BPQ device for the ethernet device
*/
-static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev)
+static __inline__ struct net_device *bpq_get_ax25_dev(struct net_device *dev)
{
struct bpqdev *bpq;
for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next)
if (bpq->ethdev == dev)
return &bpq->axdev;
-
return NULL;
}
-static inline int dev_is_ethdev(struct net_device *dev)
+/* ------------------------------------------------------------------------ */
+
+static __inline__ int dev_is_ethdev(struct net_device *dev)
{
return (
dev->type == ARPHRD_ETHER
@@ -160,6 +156,8 @@ static inline int dev_is_ethdev(struct net_device *dev)
);
}
+/* ------------------------------------------------------------------------ */
+
/*
* Sanity check: remove all devices that ceased to exists and
* return '1' if the given BPQ device was affected.
@@ -174,7 +172,6 @@ static int bpq_check_devices(struct net_device *dev)
cli();
bpq_prev = NULL;
-
for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) {
if (!dev_get(bpq->ethname)) {
if (bpq_prev)
@@ -195,23 +192,18 @@ static int bpq_check_devices(struct net_device *dev)
bpq_prev = bpq;
}
-
restore_flags(flags);
-
return result;
}
-
/* ------------------------------------------------------------------------ */
-
/*
* Receive an AX.25 frame via an ethernet interface.
*/
static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
{
int len;
- char * ptr;
struct ethhdr *eth = (struct ethhdr *)skb->mac.raw;
struct bpqdev *bpq;
@@ -242,11 +234,8 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty
skb_pull(skb, 2); /* Remove the length bytes */
skb_trim(skb, len); /* Set the length of the data */
- bpq->stats.rx_packets++;
- bpq->stats.rx_bytes += len;
-
- ptr = skb_push(skb, 1);
- *ptr = 0;
+ ((struct bpqdev *)dev->priv)->stats.rx_packets++;
+ ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len;
skb->dev = dev;
skb->protocol = htons(ETH_P_AX25);
@@ -254,10 +243,11 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty
skb->pkt_type = PACKET_HOST;
netif_rx(skb);
-
return 0;
}
+/* ------------------------------------------------------------------------ */
+
/*
* Send an AX.25 frame via an ethernet interface
*/
@@ -265,7 +255,8 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct sk_buff *newskb;
unsigned char *ptr;
- struct bpqdev *bpq;
+ struct sk_buff *skb_cp;
+ struct bpqdev *bpq = (struct bpqdev *) dev->priv;
int size;
/*
@@ -278,63 +269,72 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev)
return -ENODEV;
}
- skb_pull(skb, 1);
- size = skb->len;
+ /* NOTE non-obvious race condition prevention here */
+ skb_cp = skb_copy(skb, GFP_ATOMIC);
+ kfree_skb(skb);
+ if (skb_cp == NULL) { /* out of memory */
+ bpq->stats.tx_dropped++;
+ return 0;
+ }
+
+ size = skb_cp->len;
/*
* The AX.25 code leaves enough room for the ethernet header, but
* sendto() does not.
*/
- if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) { /* Ough! */
- if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) {
+ if (skb_headroom(skb_cp) < AX25_BPQ_HEADER_LEN) { /* Ough! */
+ if ((newskb = skb_realloc_headroom(skb_cp, AX25_BPQ_HEADER_LEN)) == NULL) {
printk(KERN_WARNING "bpqether: out of memory\n");
- kfree_skb(skb);
+ kfree_skb(skb_cp);
return -ENOMEM;
}
- if (skb->sk != NULL)
- skb_set_owner_w(newskb, skb->sk);
+ if (skb_cp->sk != NULL)
+ skb_set_owner_w(newskb, skb_cp->sk);
- kfree_skb(skb);
- skb = newskb;
+ kfree_skb(skb_cp);
+ skb_cp = newskb;
}
- skb->protocol = htons(ETH_P_AX25);
+ skb_cp->protocol = htons(ETH_P_AX25);
- ptr = skb_push(skb, 2);
+ ptr = skb_push(skb_cp, 2);
*ptr++ = (size + 5) % 256;
*ptr++ = (size + 5) / 256;
- bpq = (struct bpqdev *)dev->priv;
-
if ((dev = bpq_get_ether_dev(dev)) == NULL) {
bpq->stats.tx_dropped++;
- kfree_skb(skb);
+ kfree_skb(skb_cp);
return -ENODEV;
}
- skb->dev = dev;
- skb->nh.raw = skb->data;
- dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
+ skb_cp->dev = dev;
+ skb_cp->nh.raw = skb_cp->data;
+ dev->hard_header(skb_cp, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0);
bpq->stats.tx_packets++;
- bpq->stats.tx_bytes+=skb->len;
+ bpq->stats.tx_bytes+=skb_cp->len;
- dev_queue_xmit(skb);
- netif_wake_queue(dev);
+ dev_queue_xmit(skb_cp);
return 0;
}
+/* ------------------------------------------------------------------------ */
+
/*
* Statistics
*/
static struct net_device_stats *bpq_get_stats(struct net_device *dev)
{
- struct bpqdev *bpq = (struct bpqdev *) dev->priv;
+ struct bpqdev *bpq;
+ bpq = (struct bpqdev *)dev->priv;
return &bpq->stats;
}
+/* ------------------------------------------------------------------------ */
+
/*
* Set AX.25 callsign
*/
@@ -343,10 +343,11 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr)
struct sockaddr *sa = (struct sockaddr *)addr;
memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
-
return 0;
}
+/* ------------------------------------------------------------------------ */
+
/* Ioctl commands
*
* SIOCSBPQETHOPT reserved for enhancements
@@ -356,11 +357,12 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr)
*/
static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
+ int err;
struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data;
struct bpqdev *bpq = dev->priv;
struct bpq_req req;
- if (!capable(CAP_NET_ADMIN))
+ if (!suser())
return -EPERM;
if (bpq == NULL) /* woops! */
@@ -368,8 +370,9 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch (cmd) {
case SIOCSBPQETHOPT:
- if (copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req)))
- return -EFAULT;
+ if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0)
+ return err;
+ copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req));
switch (req.cmd) {
case SIOCGBPQETHPARAM:
case SIOCSBPQETHPARAM:
@@ -380,19 +383,37 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
break;
case SIOCSBPQETHADDR:
- if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN))
- return -EFAULT;
- if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN))
- return -EFAULT;
+ if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0)
+ return err;
+ copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN);
+ copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN);
break;
default:
return -EINVAL;
}
-
return 0;
}
+/* ------------------------------------------------------------------------ */
+
+void bpq_param_notify(struct net_device *dev, int value, int old, int new)
+{
+ /* we reject some parameters because they are not implemented (yet) */
+ switch (value) {
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_DUPLEX:
+ ax25_dev_set_value(dev, value, old);
+ default: break;
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------------ */
+
/*
* open/close a device
*/
@@ -400,32 +421,60 @@ static int bpq_open(struct net_device *dev)
{
if (bpq_check_devices(dev))
return -ENODEV; /* oops, it's gone */
-
+
MOD_INC_USE_COUNT;
netif_start_queue(dev);
return 0;
}
+/* ------------------------------------------------------------------------ */
+
static int bpq_close(struct net_device *dev)
{
netif_stop_queue(dev);
+
MOD_DEC_USE_COUNT;
return 0;
}
+/* ------------------------------------------------------------------------ */
+
/*
* currently unused
*/
static int bpq_dev_init(struct net_device *dev)
{
+ struct bpqdev *bpqdev = (struct bpqdev *) dev->priv;
+
+ dev->hard_start_xmit = bpq_xmit;
+ dev->open = bpq_open;
+ dev->stop = bpq_close;
+ dev->set_mac_address = bpq_set_mac_address;
+ dev->get_stats = bpq_get_stats;
+ dev->do_ioctl = bpq_ioctl;
+ dev->flags = 0;
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ AX25_PTR(dev) = &bpqdev->ax25dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+ AX25_PTR(dev)->hw.fast = 1;
+ AX25_PTR(dev)->hw.parameter_change_notify = bpq_param_notify;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 1);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, 10000000);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, 10000000);
+
+ dev_init_buffers(dev);
return 0;
}
-
/* ------------------------------------------------------------------------ */
-
/*
* Proc filesystem
*/
@@ -435,10 +484,11 @@ static char * bpq_print_ethaddr(unsigned char *e)
sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
e[0], e[1], e[2], e[3], e[4], e[5]);
-
return buf;
}
+/* ------------------------------------------------------------------------ */
+
static int bpq_get_info(char *buffer, char **start, off_t offset, int length)
{
struct bpqdev *bpqdev;
@@ -475,14 +525,11 @@ static int bpq_get_info(char *buffer, char **start, off_t offset, int length)
len -= (offset - begin);
if (len > length) len = length;
-
return len;
}
-
/* ------------------------------------------------------------------------ */
-
/*
* Setup a new device.
*/
@@ -524,36 +571,11 @@ static int bpq_new_device(struct net_device *dev)
dev->init = bpq_dev_init;
/* We should be locked, call register_netdevice() directly. */
-
if (register_netdevice(dev) != 0) {
kfree(bpq);
return -EIO;
}
- dev_init_buffers(dev);
-
- dev->hard_start_xmit = bpq_xmit;
- dev->open = bpq_open;
- dev->stop = bpq_close;
- dev->set_mac_address = bpq_set_mac_address;
- dev->get_stats = bpq_get_stats;
- dev->do_ioctl = bpq_ioctl;
-
- memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
- memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN);
-
- dev->flags = 0;
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
- dev->hard_header = ax25_encapsulate;
- dev->rebuild_header = ax25_rebuild_header;
-#endif
-
- dev->type = ARPHRD_AX25;
- dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
- dev->mtu = AX25_DEF_PACLEN;
- dev->addr_len = AX25_ADDR_LEN;
-
cli();
if (bpq_devices == NULL) {
@@ -564,10 +586,10 @@ static int bpq_new_device(struct net_device *dev)
}
sti();
-
return 0;
}
+/* ------------------------------------------------------------------------ */
/*
* Handle device status changes.
@@ -595,32 +617,30 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi
default:
break;
}
-
return NOTIFY_DONE;
}
-
/* ------------------------------------------------------------------------ */
/*
* Initialize driver. To be called from af_ax25 if not compiled as a
* module
*/
-static int __init bpq_init_driver(void)
+int __init bpq_init_driver(void)
{
struct net_device *dev;
+ bpq_packet_type.type = htons(ETH_P_BPQ);
dev_add_pack(&bpq_packet_type);
-
register_netdevice_notifier(&bpq_dev_notifier);
-
- printk(banner);
+ printk(KERN_INFO "AX.25: bqpether driver version 0.02\n");
proc_net_create("bpqether", 0, bpq_get_info);
read_lock_bh(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
- if (dev_is_ethdev(dev)) {
+ if (dev_is_ethdev(dev))
+ {
read_unlock_bh(&dev_base_lock);
bpq_new_device(dev);
read_lock_bh(&dev_base_lock);
@@ -630,12 +650,13 @@ static int __init bpq_init_driver(void)
return 0;
}
-static void __exit bpq_cleanup_driver(void)
+/* ------------------------------------------------------------------------ */
+
+void __exit bpq_cleanup_driver(void)
{
struct bpqdev *bpq;
dev_remove_pack(&bpq_packet_type);
-
unregister_netdevice_notifier(&bpq_dev_notifier);
proc_net_remove("bpqether");
diff --git a/drivers/net/hamradio/dmascc.h b/drivers/net/hamradio/dmascc.h
new file mode 100644
index 000000000..bd156e665
--- /dev/null
+++ b/drivers/net/hamradio/dmascc.h
@@ -0,0 +1,191 @@
+/*
+ * $Id: dmascc.h,v 1.0
+ *
+ * Driver for high-speed SCC boards (those with DMA support)
+ *
+ * These are #define, type declarations and prototypes.
+ *
+ * Cleaned up on 03/11/2000 Jens David, <dg1kjd@afthd.tu-darmstadt.de>
+ *
+ * Copyright (C) 1997 Klaus Kudielka
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* ------------------------------------------------------------------------ */
+
+/* Number of buffers per channel */
+#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */
+#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */
+#define BUF_SIZE 2016
+
+/* Cards supported */
+#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 1843200, 3686400 }
+#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \
+ 0, 8, 3686400, 7372800 }
+#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \
+ 0, 4, 6144000, 6144000 }
+
+#define HARDWARE { HW_PI, HW_PI2, HW_TWIN }
+
+#define TYPE_PI 0
+#define TYPE_PI2 1
+#define TYPE_TWIN 2
+#define NUM_TYPES 3
+#define MAX_NUM_DEVS 32
+
+/* SCC chips supported */
+#define Z8530 0
+#define Z85C30 1
+#define Z85230 2
+
+#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" }
+
+/* I/O registers */
+/* 8530 registers relative to card base */
+#define SCCB_CMD 0x00
+#define SCCB_DATA 0x01
+#define SCCA_CMD 0x02
+#define SCCA_DATA 0x03
+
+/* 8253/8254 registers relative to card base */
+#define TMR_CNT0 0x00
+#define TMR_CNT1 0x01
+#define TMR_CNT2 0x02
+#define TMR_CTRL 0x03
+
+/* Additional PI/PI2 registers relative to card base */
+#define PI_DREQ_MASK 0x04
+
+/* Additional PackeTwin registers relative to card base */
+#define TWIN_INT_REG 0x08
+#define TWIN_CLR_TMR1 0x09
+#define TWIN_CLR_TMR2 0x0a
+#define TWIN_SPARE_1 0x0b
+#define TWIN_DMA_CFG 0x08
+#define TWIN_SERIAL_CFG 0x09
+#define TWIN_DMA_CLR_FF 0x0a
+#define TWIN_SPARE_2 0x0b
+
+/* PackeTwin I/O register values */
+/* INT_REG */
+#define TWIN_SCC_MSK 0x01
+#define TWIN_TMR1_MSK 0x02
+#define TWIN_TMR2_MSK 0x04
+#define TWIN_INT_MSK 0x07
+
+/* SERIAL_CFG */
+#define TWIN_DTRA_ON 0x01
+#define TWIN_DTRB_ON 0x02
+#define TWIN_EXTCLKA 0x04
+#define TWIN_EXTCLKB 0x08
+#define TWIN_LOOPA_ON 0x10
+#define TWIN_LOOPB_ON 0x20
+#define TWIN_EI 0x80
+
+/* DMA_CFG */
+#define TWIN_DMA_HDX_T1 0x08
+#define TWIN_DMA_HDX_R1 0x0a
+#define TWIN_DMA_HDX_T3 0x14
+#define TWIN_DMA_HDX_R3 0x16
+#define TWIN_DMA_FDX_T3R1 0x1b
+#define TWIN_DMA_FDX_T1R3 0x1d
+
+/* Status values */
+/* tx_state */
+#define TX_IDLE 0
+#define TX_OFF 1
+#define TX_TXDELAY 2
+#define TX_ACTIVE 3
+#define TX_SQDELAY 4
+
+/* ------------------------------------------------------------------------ */
+
+/* Data types */
+struct scc_hardware {
+ char *name;
+ int io_region;
+ int io_delta;
+ int io_size;
+ int num_devs;
+ int scc_offset;
+ int tmr_offset;
+ int tmr_hz;
+ int pclk_hz;
+};
+
+struct scc_priv {
+ struct ax25_dev ax25dev;
+ struct enet_statistics stats;
+ struct scc_info *info;
+ int channel;
+ int cmd, data, tmr;
+ struct scc_param param;
+ char rx_buf[NUM_RX_BUF][BUF_SIZE];
+ int rx_len[NUM_RX_BUF];
+ int rx_ptr;
+ struct tq_struct rx_task;
+ int rx_head, rx_tail, rx_count;
+ int rx_over;
+ char tx_buf[NUM_TX_BUF][BUF_SIZE];
+ int tx_len[NUM_TX_BUF];
+ int tx_ptr;
+ int tx_head, tx_tail, tx_count;
+ int tx_state;
+ unsigned long tx_start;
+ int status;
+};
+
+struct scc_info {
+ int type;
+ int chip;
+ int open;
+ int scc_base;
+ int tmr_base;
+ int twin_serial_cfg;
+ struct net_device dev[2];
+ struct scc_priv priv[2];
+ struct scc_info *next;
+};
+
+/* ------------------------------------------------------------------------ */
+
+/* Prototypes */
+int dmascc_init(void) __init;
+static int setup_adapter(int io, int h, int n) __init;
+static inline void write_scc(int ctl, int reg, int val);
+static inline int read_scc(int ctl, int reg);
+static int scc_open(struct net_device *dev);
+static int scc_close(struct net_device *dev);
+static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int scc_send_packet(struct sk_buff *skb, struct net_device *dev);
+static struct enet_statistics *scc_get_stats(struct net_device *dev);
+static int scc_set_mac_address(struct net_device *dev, void *sa);
+static void scc_isr(int irq, void *dev_id, struct pt_regs * regs);
+static inline void z8530_isr(struct scc_info *info);
+static void rx_isr(struct net_device *dev);
+static void special_condition(struct net_device *dev, int rc);
+static void rx_bh(void *arg);
+static void tx_isr(struct net_device *dev);
+static void es_isr(struct net_device *dev);
+static void tm_isr(struct net_device *dev);
+static inline void delay(struct net_device *dev, int t);
+static unsigned int report_dcd(struct net_device *dev);
+static unsigned int report_ptt(struct net_device *dev);
+static void parameter_change_notify(struct net_device *dev, int valueno,
+ int old, int new);
+
+/* ------------------------------------------------------------------------ */
diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c
index e1af65373..1726c8ed3 100644
--- a/drivers/net/hamradio/hdlcdrv.c
+++ b/drivers/net/hamradio/hdlcdrv.c
@@ -287,7 +287,7 @@ void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s)
/* ---------------------------------------------------------------------- */
-static inline void do_kiss_params(struct hdlcdrv_state *s,
+static void inline do_kiss_params(struct hdlcdrv_state *s,
unsigned char *data, unsigned long len)
{
@@ -889,7 +889,14 @@ EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv);
/* --------------------------------------------------------------------- */
-static int __init hdlcdrv_init_driver(void)
+#ifdef MODULE
+
+MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
+MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
+
+/* --------------------------------------------------------------------- */
+
+int __init init_module(void)
{
printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n");
printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n");
@@ -898,16 +905,10 @@ static int __init hdlcdrv_init_driver(void)
/* --------------------------------------------------------------------- */
-static void __exit hdlcdrv_cleanup_driver(void)
+void cleanup_module(void)
{
printk(KERN_INFO "hdlcdrv: cleanup\n");
}
-/* --------------------------------------------------------------------- */
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder");
-module_init(hdlcdrv_init_driver);
-module_exit(hdlcdrv_cleanup_driver);
-
+#endif /* MODULE */
/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/kiss.c b/drivers/net/hamradio/kiss.c
new file mode 100644
index 000000000..f3cce6429
--- /dev/null
+++ b/drivers/net/hamradio/kiss.c
@@ -0,0 +1,1276 @@
+/*
+ * kiss.c This module implements the KISS protocol for kernel-based
+ * devices like TTY. It interfaces between a raw TTY, and the
+ * kernel's AX.25 protocol layers.
+ *
+ * Shamelessly copied from slip.c on 99/01/04:
+ * Matthias Welwarsky <dg2fef@afthd.tu-darmstadt.de>
+ *
+ * Original Authors:
+ * Laurence Culhane, <loz@holmes.demon.co.uk>
+ * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ *
+ * See slip.c for additional credits and fixes.
+ *
+ * 2000-09-21 adapted for kernel 2.4.0, it however still crashes
+ * badly when killing kissattach and trying to re-
+ * start the interface. Does the original SLIP driver
+ * do the same?
+ *
+ */
+
+#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/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#ifdef CONFIG_INET
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#endif
+#include "kiss.h"
+
+
+#define KISS_VERSION "KISS-NET3.019-NEWTTY"
+
+static int maxdev = KISS_NRUNIT;
+static kiss_ctrl_t **kiss_ctrls = NULL;
+static struct tty_ldisc kiss_ldisc;
+
+static int kiss_esc(unsigned char *p, unsigned char *d, int len);
+static void kiss_unesc(struct kiss *ks, unsigned char c);
+static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len);
+static int kiss_init(struct net_device *dev);
+static int kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg);
+static int kiss_dev_ioctl(struct net_device *dev,struct ifreq *rq,int cmd);
+static int kiss_dev_set_mac_address(struct net_device *dev, void *addr);
+static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new);
+static void kiss_update_parameters(struct net_device *dev);
+static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value);
+
+static spinlock_t ks_ctrl_lock = SPIN_LOCK_UNLOCKED;
+
+/*---------------------------------------------------------------------------*/
+
+static const unsigned short Crc_16_table[] = {
+ 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
+ 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
+ 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
+ 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
+ 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
+ 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
+ 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
+ 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
+ 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
+ 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
+ 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
+ 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
+ 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
+ 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
+ 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
+ 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
+ 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
+ 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
+ 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
+ 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
+ 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
+ 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
+ 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
+ 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
+ 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
+ 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
+ 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
+ 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
+ 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
+ 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
+ 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
+ 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
+};
+
+/*---------------------------------------------------------------------------*/
+
+static unsigned short
+calc_crc_16(unsigned char *cp, int size)
+{
+ unsigned short crc = 0;
+
+ while (size--)
+ crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff];
+
+ return crc;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int
+check_crc_16(unsigned char *cp, int size)
+{
+ unsigned short crc = 0xffff;
+
+ if (size < 3)
+ return -1;
+
+ while (size--)
+ crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff];
+
+ if (crc != 0)
+ return -1;
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static const unsigned short Crc_flex_table[] = {
+ 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38,
+ 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770,
+ 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9,
+ 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1,
+ 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a,
+ 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672,
+ 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb,
+ 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3,
+ 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c,
+ 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574,
+ 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd,
+ 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5,
+ 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e,
+ 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476,
+ 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf,
+ 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7,
+ 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30,
+ 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378,
+ 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1,
+ 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9,
+ 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32,
+ 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a,
+ 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3,
+ 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb,
+ 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34,
+ 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c,
+ 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5,
+ 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd,
+ 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36,
+ 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e,
+ 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7,
+ 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff
+};
+
+/*---------------------------------------------------------------------------*/
+
+static unsigned short
+calc_crc_flex(unsigned char *cp, int size)
+{
+ unsigned short crc = 0xffff;
+
+ while (size--)
+ crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
+
+ return crc;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static int
+check_crc_flex(unsigned char *cp, int size)
+{
+ unsigned short crc = 0xffff;
+
+ if (size < 3)
+ return -1;
+
+ while (size--)
+ crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
+
+ if ((crc & 0xffff) != 0x7070)
+ return -1;
+
+ return 0;
+}
+
+/********************************
+* Buffer administration routines:
+* kiss_alloc_bufs()
+* kiss_free_bufs()
+* kiss_realloc_bufs()
+*
+* NOTE: kiss_realloc_bufs != kiss_free_bufs + kiss_alloc_bufs, because
+* kiss_realloc_bufs provides strong atomicity and reallocation
+* on actively running device.
+*********************************/
+
+/*
+ Allocate channel buffers.
+ */
+
+static int
+kiss_alloc_bufs(struct kiss *ks, int mtu)
+{
+ int err = -ENOBUFS;
+ unsigned long len;
+ char * rbuff = NULL;
+ char * xbuff = NULL;
+
+ /*
+ * Allocate the KISS frame buffers:
+ *
+ * rbuff Receive buffer.
+ * xbuff Transmit buffer.
+ */
+ len = mtu * 2;
+
+ /*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2)
+ len = 576 * 2;
+ rbuff = kmalloc(len + 4, GFP_KERNEL);
+ if (rbuff == NULL)
+ goto err_exit;
+ xbuff = kmalloc(len + 4, GFP_KERNEL);
+ if (xbuff == NULL)
+ goto err_exit;
+
+ spin_lock_bh(&ks->lock);
+ if (ks->tty == NULL) {
+ err = -ENODEV;
+ goto err_exit;
+ }
+ ks->mtu = mtu;
+ ks->buffsize = len;
+ ks->rcount = 0;
+ ks->xleft = 0;
+ rbuff = xchg(&ks->rbuff, rbuff);
+ xbuff = xchg(&ks->xbuff, xbuff);
+ err = 0;
+
+ /* Cleanup */
+err_exit:
+ spin_unlock_bh(&ks->lock);
+ if (xbuff)
+ kfree(xbuff);
+ if (rbuff)
+ kfree(rbuff);
+ return err;
+}
+
+/* Free a KISS channel buffers. */
+static void
+kiss_free_bufs(struct kiss *ks)
+{
+ void * tmp;
+
+ /* Free all KISS frame buffers. */
+ if ((tmp = xchg(&ks->rbuff, NULL)) != NULL)
+ kfree(tmp);
+ if ((tmp = xchg(&ks->xbuff, NULL)) != NULL)
+ kfree(tmp);
+}
+
+/*
+ Reallocate kiss channel buffers.
+ */
+
+static int kiss_realloc_bufs(struct kiss *ks, int mtu)
+{
+ int err = 0;
+ struct net_device *dev = ks->dev;
+ unsigned char *xbuff, *rbuff;
+ int len = (mtu + MAX_HEADER) * 2;
+
+/*
+ * allow for arrival of larger UDP packets, even if we say not to
+ * also fixes a bug in which SunOS sends 512-byte packets even with
+ * an MSS of 128
+ */
+ if (len < 576 * 2)
+ len = 576 * 2;
+
+ xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+ rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC);
+
+ if (xbuff == NULL || rbuff == NULL) {
+ if (mtu >= dev->mtu) {
+ printk("%s: unable to grow kiss buffers, MTU change cancelled.\n",
+ dev->name);
+ err = -ENOBUFS;
+ }
+ goto done;
+ }
+
+ spin_lock_bh(&ks->lock);
+
+ err = -ENODEV;
+ if (ks->tty == NULL)
+ goto done_on_bh;
+
+ xbuff = xchg(&ks->xbuff, xbuff);
+ rbuff = xchg(&ks->rbuff, rbuff);
+ if (ks->xleft) {
+ if (ks->xleft <= len) {
+ memcpy(ks->xbuff, ks->xhead, ks->xleft);
+ } else {
+ ks->xleft = 0;
+ ks->stats.tx_dropped++;
+ }
+ }
+ ks->xhead = ks->xbuff;
+
+ if (ks->rcount) {
+ if (ks->rcount <= len) {
+ memcpy(ks->rbuff, rbuff, ks->rcount);
+ } else {
+ ks->rcount = 0;
+ ks->stats.rx_over_errors++;
+ ks->flags.error=1;
+ }
+ }
+ ks->mtu = mtu + MAX_HEADER;;
+ dev->mtu = mtu;
+ ks->buffsize = len;
+ err = 0;
+
+done_on_bh:
+ spin_unlock_bh(&ks->lock);
+
+done:
+ if (xbuff)
+ kfree(xbuff);
+ if (rbuff)
+ kfree(rbuff);
+ return err;
+}
+
+/* Send one completely decapsulated IP datagram to the IP layer. */
+static void
+kiss_bump(struct kiss *ks)
+{
+ struct sk_buff *skb;
+ int count;
+
+ if (ks->mode == KISS_MODE_ADAPTIVE) {
+ if (ks->rbuff[0] & 0x20) {
+ ks->mode = KISS_MODE_FLEX;
+ } else
+ if (ks->rbuff[0] & 0x80) {
+ ks->mode = KISS_MODE_SMACK;
+ }
+ }
+
+ if (ks->mode == KISS_MODE_FLEX) {
+ if (check_crc_flex(ks->rbuff, ks->rcount) < 0) {
+ ks->stats.rx_errors++;
+ return;
+ }
+ ks->rcount -= 2;
+ } else if (ks->mode == KISS_MODE_SMACK) {
+ if (check_crc_16(ks->rbuff, ks->rcount) < 0) {
+ ks->stats.rx_errors++;
+ return;
+ }
+ ks->rcount -= 2;
+ }
+
+ count = ks->rcount-1;
+ ks->stats.rx_bytes += count;
+
+ skb = dev_alloc_skb(count+MAX_HEADER);
+ if (skb == NULL) {
+ printk("%s: memory squeeze, dropping packet.\n", ks->dev->name);
+ ks->stats.rx_dropped++;
+ return;
+ }
+ skb_reserve(skb, MAX_HEADER);
+ skb->dev = ks->dev;
+ memcpy(skb_put(skb,count), ks->rbuff+1, count);
+ skb->mac.raw = skb->data;
+ skb->protocol = __constant_htons(ETH_P_AX25);
+ netif_rx(skb);
+ ks->stats.rx_packets++;
+}
+
+/* Encapsulate one IP datagram and stuff into a TTY queue. */
+static void
+kiss_encaps(struct kiss *ks, unsigned char *icp, int len)
+{
+ unsigned char *p;
+ int actual, count;
+
+ if (len > ks->mtu) { /* Sigh, shouldn't occur BUT ... */
+ printk ("%s: dropping oversized transmit packet!\n", ks->dev->name);
+ ks->stats.tx_dropped++;
+ netif_wake_queue(ks->dev);
+ return;
+ }
+
+ p = icp;
+
+ if (*p == 0) {
+ /* KISS data */
+ switch (ks->mode) {
+ unsigned short crc;
+
+ case KISS_MODE_FLEX:
+ *p |= 0x20;
+ crc = calc_crc_flex(p, len);
+ count = kiss_esc_crc(p, ks->xbuff, crc, len+2);
+ break;
+ case KISS_MODE_SMACK:
+ *p |= 0x80;
+ crc = calc_crc_16(p, len);
+ count = kiss_esc_crc(p, ks->xbuff, crc, len+2);
+ break;
+ default:
+ count = kiss_esc(p, ks->xbuff, len);
+ break;
+ }
+ } else {
+ /* KISS parameter */
+ ks->xbuff[0] = END;
+ memcpy(&ks->xbuff[1], p, len);
+ ks->xbuff[len+1] = END;
+ count = len+2;
+ }
+
+ /* Order of next two lines is *very* important.
+ * When we are sending a little amount of data,
+ * the transfer may be completed inside driver.write()
+ * routine, because it's running with interrupts enabled.
+ * In this case we *never* got WRITE_WAKEUP event,
+ * if we did not request it before write operation.
+ * 14 Oct 1994 Dmitry Gorodchanin.
+ */
+ ks->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
+ actual = ks->tty->driver.write(ks->tty, 0, ks->xbuff, count);
+ ks->dev->trans_start = jiffies;
+ ks->xleft = count - actual;
+ ks->xhead = ks->xbuff + actual;
+}
+
+/*
+ * Called by the driver when there's room for more data. If we have
+ * more packets to send, we send them here.
+ */
+static void kiss_write_wakeup(struct tty_struct *tty)
+{
+ int actual;
+ struct kiss *ks = (struct kiss *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!ks || !netif_running(ks->dev)) {
+ return;
+ }
+ if (ks->xleft <= 0) {
+ /* Now serial buffer is almost free & we can start
+ * transmission of another packet */
+ ks->stats.tx_packets++;
+ tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ netif_wake_queue(ks->dev);
+ return;
+ }
+
+ actual = tty->driver.write(tty, 0, ks->xhead, ks->xleft);
+ ks->xleft -= actual;
+ ks->xhead += actual;
+}
+
+/* Encapsulate an IP datagram and kick it into a TTY queue. */
+static int
+kiss_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ spin_lock(&ks->lock);
+
+ /* ldisc disappeared under our feet, ouch! */
+ if (ks->tty == NULL)
+ goto done;
+
+ /* rant by dl1bke:
+ * There used to be a completely useless copy of
+ * the skb here. If there really _is_ a race condition
+ * fix the upper protocols, dammit! Masquearading these
+ * problems doesn't help at all, they'll rear their ugly
+ * head somewhere else again anyway!
+ */
+
+ /* We were not busy, so we are now... :-) */
+ if (skb_headroom(skb) > 0)
+ {
+ *skb_push(skb, 1) = 0; /* prepend KISS header */
+ netif_stop_queue(dev);
+ ks->stats.tx_bytes+=skb->len;
+ kiss_encaps(ks, skb->data, skb->len);
+ } else {
+ ks->stats.tx_dropped++;
+ }
+
+done:
+ dev_kfree_skb(skb);
+ spin_unlock(&ks->lock);
+ return 0;
+}
+
+
+/******************************************
+ * Routines looking at netdevice side.
+ ******************************************/
+
+/* Netdevice UP -> DOWN routine */
+
+static int
+kiss_dev_close(struct net_device *dev)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ spin_lock_bh(&ks->lock);
+ if (ks->tty) {
+ /* TTY discipline is running. */
+ ks->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+ }
+
+ ks->rcount = 0;
+ ks->xleft = 0;
+ netif_stop_queue(dev);
+ spin_unlock_bh(&ks->lock);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Netdevice DOWN -> UP routine */
+
+static int kiss_dev_open(struct net_device *dev)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ if (ks->tty==NULL)
+ return -ENODEV;
+
+ ks->flags.in_use = 1;
+ ks->flags.error = 0;
+ ks->flags.escape = 0;
+
+ MOD_INC_USE_COUNT;
+
+ netif_start_queue(dev);
+ return 0;
+}
+
+/* Netdevice change MTU request */
+
+static int kiss_change_mtu(struct net_device *dev, int new_mtu)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ if (new_mtu < 68 || new_mtu > 65534)
+ return -EINVAL;
+
+ if (new_mtu != dev->mtu)
+ return kiss_realloc_bufs(ks, new_mtu);
+ return 0;
+}
+
+/* Netdevice get statistics request */
+
+static struct net_device_stats *
+kiss_get_stats(struct net_device *dev)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ return (&ks->stats);
+}
+
+/* Netdevice register callback */
+
+static int kiss_init(struct net_device *dev)
+{
+ struct kiss *ks = (struct kiss*)(dev->priv);
+
+ /*
+ * Finish setting up the DEVICE info.
+ */
+
+ dev->mtu = ks->mtu - MAX_HEADER;
+ dev->hard_start_xmit = kiss_xmit;
+ dev->open = kiss_dev_open;
+ dev->stop = kiss_dev_close;
+ dev->get_stats = kiss_get_stats;
+ dev->change_mtu = kiss_change_mtu;
+ dev->do_ioctl = kiss_dev_ioctl;
+ dev->set_mac_address = kiss_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_init_buffers(dev);
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+
+ /* initialise DDI interface values */
+ AX25_PTR(dev) = &ks->ax25dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+ AX25_PTR(dev)->hw.fast = 1;
+ AX25_PTR(dev)->hw.dcd = NULL;
+ AX25_PTR(dev)->hw.ptt = NULL;
+ AX25_PTR(dev)->hw.cts = NULL;
+ AX25_PTR(dev)->hw.rts = NULL;
+ AX25_PTR(dev)->hw.parameter_change_notify = kiss_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_SLOTTIME, 10);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE, 64);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 0);
+ return 0;
+}
+
+static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value)
+{
+ struct kiss *ks = (struct kiss *) dev->priv;
+ unsigned char buf[2] = {param, value};
+
+ kiss_encaps(ks, buf, 2);
+ return;
+}
+
+static void kiss_update_parameters(struct net_device *dev)
+{
+ kiss_send_parameter(dev, KISS_PARAM_TXDELAY,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY)/10);
+ kiss_send_parameter(dev, KISS_PARAM_PPERS,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE));
+ kiss_send_parameter(dev, KISS_PARAM_SLOT,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME)/10);
+ kiss_send_parameter(dev, KISS_PARAM_DUPLEX,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX));
+ kiss_send_parameter(dev, KISS_PARAM_TXTAIL,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)/10);
+ return;
+}
+
+static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ struct kiss *ks = (struct kiss *) 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(ks->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_DUPLEX:
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ case AX25_VALUES_MEDIA_SLOTTIME:
+ case AX25_VALUES_MEDIA_PPERSISTENCE:
+ kiss_update_parameters(dev);
+ case AX25_VALUES_MEDIA_AUTO_ADJUST:
+ default:
+ break;
+ }
+ return;
+}
+
+/******************************************
+ Routines looking at TTY side.
+ ******************************************/
+
+
+static int kiss_receive_room(struct tty_struct *tty)
+{
+ return 65536; /* We can handle an infinite amount of data. :-) */
+}
+
+/*
+ * Handle the 'receiver data ready' interrupt.
+ * This function is called by the 'tty_io' module in the kernel when
+ * a block of KISS data has been received, which can now be decapsulated
+ * and sent on to some IP layer for further processing.
+ */
+
+static void kiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
+{
+ struct kiss *ks = (struct kiss *) tty->disc_data;
+
+ if (!ks || !netif_running(ks->dev))
+ return;
+
+ /* Read the characters out of the buffer */
+ while (count--) {
+ if (fp && *fp++) {
+ if (!ks->flags.error)
+ {
+ ks->flags.error = 1;
+ ks->stats.rx_errors++;
+ }
+ cp++;
+ continue;
+ }
+ kiss_unesc(ks, *cp++);
+ }
+}
+
+/************************************
+ * kiss_open helper routines.
+ ************************************/
+
+/* Collect hanged up channels */
+
+static void kiss_sync(void)
+{
+ int i;
+
+ spin_lock_bh(&ks_ctrl_lock);
+ for (i = 0; i < maxdev; i++) {
+ kiss_ctrl_t *ksp = kiss_ctrls[i];
+ if (ksp == NULL)
+ break;
+ if (ksp->ctrl.tty)
+ continue;
+ if (ksp->dev.flags & IFF_UP)
+ dev_close(&ksp->dev);
+ }
+ spin_unlock_bh(&ks_ctrl_lock);
+}
+
+/* Find a free KISS channel, and link in this `tty' line. */
+static struct kiss *
+kiss_alloc(kdev_t line)
+{
+ struct kiss *ks;
+ kiss_ctrl_t *ksp = NULL;
+ int i;
+ int sel = -1;
+ int score = -1;
+
+ if (kiss_ctrls == NULL)
+ return NULL; /* Master array missing ! */
+
+ spin_lock_bh(&ks_ctrl_lock);
+
+ for (i = 0; i < maxdev; i++) {
+ ksp = kiss_ctrls[i];
+ if (ksp == NULL)
+ break;
+
+ if (ksp->ctrl.tty)
+ continue;
+
+ if (current->pid == ksp->ctrl.pid) {
+ if (ksp->ctrl.line == line && score < 3) {
+ sel = i;
+ score = 3;
+ continue;
+ }
+ if (score < 2) {
+ sel = i;
+ score = 2;
+ }
+ continue;
+ }
+ if (ksp->ctrl.line == line && score < 1) {
+ sel = i;
+ score = 1;
+ continue;
+ }
+ if (score < 0) {
+ sel = i;
+ score = 0;
+ }
+ }
+
+ if (sel >= 0) {
+ i = sel;
+ ksp = kiss_ctrls[i];
+ if (score > 1) {
+ ksp->ctrl.flags.in_use = 1;
+ ksp->ctrl.flags.error = 0;
+ ksp->ctrl.flags.escape = 0;
+ goto done;
+ }
+ }
+
+ /* Sorry, too many, all slots in use */
+ if (i >= maxdev)
+ goto outahere;
+
+ if (ksp) {
+ if (ksp->ctrl.flags.in_use) {
+ unregister_netdevice(&ksp->dev);
+ kiss_free_bufs(&ksp->ctrl);
+ }
+ } else if ((ksp = (kiss_ctrl_t *)kmalloc(sizeof(kiss_ctrl_t),GFP_KERNEL)) == NULL)
+ goto outahere;
+
+ memset(ksp, 0, sizeof(kiss_ctrl_t));
+
+ ks = &ksp->ctrl;
+ /* Initialize channel control data */
+ ks->dev = &ksp->dev;
+ ks->mode = KISS_MODE_ADAPTIVE;
+ sprintf(ksp->dev.name, "ax%d", i);
+ ksp->dev.base_addr = i;
+ ksp->dev.priv = (void*)ks;
+ ksp->dev.init = kiss_init;
+ kiss_ctrls[i] = ksp;
+
+done:
+ spin_lock_init(&ksp->ctrl.lock);
+ spin_unlock_bh(&ks_ctrl_lock);
+ return &ksp->ctrl;
+
+outahere:
+ spin_unlock_bh(&ks_ctrl_lock);
+ return NULL;
+}
+
+/*
+ * Open the high-level part of the KISS channel.
+ * This function is called by the TTY module when the
+ * KISS line discipline is called for. Because we are
+ * sure the tty line exists, we only have to link it to
+ * a free KISS channel...
+ */
+static int
+kiss_open(struct tty_struct *tty)
+{
+ struct kiss *ks;
+ int err;
+
+ MOD_INC_USE_COUNT;
+
+ /* Collect hanged up channels. */
+ kiss_sync();
+
+ ks = (struct kiss *) tty->disc_data;
+ err = -EEXIST;
+ /* First make sure we're not already connected. */
+ if (ks) goto err_exit;
+
+ /* OK. Find a free KISS channel to use. */
+ err = -ENFILE;
+ if ((ks = kiss_alloc(tty->device)) == NULL)
+ goto err_exit;
+
+ ks->tty = tty;
+ tty->disc_data = ks;
+ ks->line = tty->device;
+ ks->pid = current->pid;
+ if (tty->driver.flush_buffer)
+ tty->driver.flush_buffer(tty);
+ if (tty->ldisc.flush_buffer)
+ tty->ldisc.flush_buffer(tty);
+
+ if (! ks->flags.in_use) {
+ /* Perform the low-level KISS initialization. */
+ if ((err = kiss_alloc_bufs(ks, KISS_MTU)) != 0)
+ goto err_free_chan;
+ if (register_netdevice(ks->dev)) {
+ kiss_free_bufs(ks);
+ goto err_free_chan;
+ }
+
+ ks->flags.in_use = 1;
+ }
+
+ return ks->dev->base_addr;
+
+err_free_chan:
+
+ ks->tty = NULL;
+ tty->disc_data = NULL;
+ ks->flags.in_use = 0;
+
+err_exit:
+ printk(KERN_DEBUG "kiss_open: err:%d\n", err);
+
+ /* Count references from TTY module */
+ MOD_DEC_USE_COUNT;
+ return err;
+}
+
+/*
+ Let me to blame a bit.
+ 1. TTY module calls this funstion on soft interrupt.
+ 2. TTY module calls this function WITH MASKED INTERRUPTS!
+ 3. TTY module does not notify us about line discipline
+ shutdown,
+
+ Seems, now it is clean. The solution is to consider netdevice and
+ line discipline sides as two independent threads.
+
+ By-product (not desired): sl? does not feel hangups and remains open.
+ It is supposed, that user level program (dip, diald, slattach...)
+ will catch SIGHUP and make the rest of work.
+
+ I see no way to make more with current tty code. --ANK
+ */
+
+/*
+ * Close down a KISS channel.
+ * This means flushing out any pending queues, and then restoring the
+ * TTY line discipline to what it was before it got hooked to KISS
+ * (which usually is TTY again).
+ */
+static void
+kiss_close(struct tty_struct *tty)
+{
+ struct kiss *ks = (struct kiss *) tty->disc_data;
+
+ /* First make sure we're connected. */
+ if (!ks || ks->tty != tty)
+ return;
+
+ tty->disc_data = 0;
+ ks->tty = NULL;
+ ks->line = 0;
+
+ /* Count references from TTY module */
+ MOD_DEC_USE_COUNT;
+}
+
+ /************************************************************************
+ * STANDARD KISS ENCAPSULATION *
+ ************************************************************************/
+
+int
+kiss_esc(unsigned char *s, unsigned char *d, int len)
+{
+ unsigned char *ptr = d;
+ unsigned char c;
+
+ /*
+ * Send an initial END character to flush out any
+ * data that may have accumulated in the receiver
+ * due to line noise.
+ */
+
+ *ptr++ = END;
+
+ /*
+ * For each byte in the packet, send the appropriate
+ * character sequence, according to the KISS protocol.
+ */
+
+ while (len-- > 0) {
+ switch(c = *s++) {
+ case END:
+ *ptr++ = ESC;
+ *ptr++ = ESC_END;
+ break;
+ case ESC:
+ *ptr++ = ESC;
+ *ptr++ = ESC_ESC;
+ break;
+ default:
+ *ptr++ = c;
+ break;
+ }
+ }
+ *ptr++ = END;
+ return (ptr - d);
+}
+
+/*
+ * MW:
+ * OK its ugly, but tell me a better solution without copying the
+ * packet to a temporary buffer :-)
+ */
+static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len)
+{
+ unsigned char *ptr = d;
+ unsigned char c = 0;
+
+ *ptr++ = END;
+ while (len > 0) {
+ if (len > 2)
+ c = *s++;
+ else if (len > 1)
+ c = crc >> 8;
+ else if (len > 0)
+ c = crc & 0xff;
+
+ len--;
+
+ switch (c) {
+ case END:
+ *ptr++ = ESC;
+ *ptr++ = ESC_END;
+ break;
+ case ESC:
+ *ptr++ = ESC;
+ *ptr++ = ESC_ESC;
+ break;
+ default:
+ *ptr++ = c;
+ break;
+ }
+ }
+ *ptr++ = END;
+ return ptr - d;
+}
+
+static void kiss_unesc(struct kiss *ks, unsigned char s)
+{
+
+ switch(s) {
+ case END:
+ if (! ks->flags.error && (ks->rcount > 2))
+ kiss_bump(ks);
+
+ ks->flags.error = 0;
+ ks->flags.escape = 0;
+ ks->rcount = 0;
+ return;
+
+ case ESC:
+ ks->flags.escape = 1;
+ return;
+ case ESC_ESC:
+ if (ks->flags.escape)
+ {
+ s = ESC;
+ ks->flags.escape = 0;
+ }
+ break;
+ case ESC_END:
+ if (ks->flags.escape) {
+ s = END;
+ ks->flags.escape = 0;
+ }
+ break;
+ }
+ if (!ks->flags.error) {
+ if (ks->rcount < ks->buffsize) {
+ ks->rbuff[ks->rcount++] = s;
+ } else {
+ ks->stats.rx_over_errors++;
+ ks->flags.error = 1;
+ }
+ }
+}
+
+
+static int kiss_set_mac_address(struct net_device *dev, void *addr)
+{
+ if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN))
+ return -EFAULT;
+ return 0;
+}
+
+static int kiss_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 KISS channel. */
+static int
+kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
+{
+ struct kiss *ks = (struct kiss *) tty->disc_data;
+ unsigned int tmp;
+
+ /* First make sure we're connected. */
+ if (!ks) return -EINVAL;
+
+ switch(cmd) {
+ case SIOCGIFNAME:
+ tmp = strlen(ks->dev->name) + 1;
+ if (copy_to_user(arg, ks->dev->name, tmp))
+ return -EFAULT;
+ return 0;
+
+ case SIOCSIFHWADDR:
+ return kiss_set_mac_address(ks->dev, arg);
+
+ case SIOCGIFENCAP:
+ if (put_user(ks->mode, (int *) arg))
+ return -EFAULT;
+ return 0;
+ case SIOCSIFENCAP:
+ if (get_user(tmp, (int *) arg))
+ return -EFAULT;
+ if (tmp < 0 || tmp > KISS_MODE_TNN_TOKENRING)
+ return -EINVAL;
+ if (tmp == KISS_MODE_COMPAT) tmp = KISS_MODE_ADAPTIVE;
+ ks->mode = tmp;
+ return 0;
+
+ /* 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 kiss_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+ int retval = -ENOIOCTLCMD;
+ struct kiss *ks = (struct kiss *)dev->priv;
+
+ if (ks == NULL)
+ return -ENODEV;
+
+ spin_lock_bh(&ks->lock);
+
+ if (!ks->tty) {
+ retval = -ENODEV;
+ goto done;
+ }
+
+ printk(KERN_DEBUG "ioctl %d called for dev %s\n", cmd, dev->name);
+
+ switch(cmd) {
+ default:
+ retval = 0;
+ break;
+ }
+
+done:
+ spin_unlock_bh(&ks->lock);
+ return retval;
+}
+
+/* Initialize KISS control device -- register KISS line discipline */
+
+int __init kiss_init_driver(void)
+{
+ int status;
+
+ if (maxdev < KISS_NRUNIT) maxdev = KISS_NRUNIT; /* Sanity */
+
+ printk(KERN_INFO "KISS: version %s (dynamic channels, max=%d)\n",
+ KISS_VERSION, maxdev);
+
+ kiss_ctrls = (kiss_ctrl_t **) kmalloc(sizeof(void*)*maxdev, GFP_KERNEL);
+ if (kiss_ctrls == NULL)
+ {
+ printk("KISS: Can't allocate kiss_ctrls[] array! Uaargh! (-> No KISS available)\n");
+ return -ENOMEM;
+ }
+
+ /* Clear the pointer array, we allocate devices when we need them */
+ memset(kiss_ctrls, 0, sizeof(void*)*maxdev); /* Pointers */
+
+ /* Fill in our line protocol discipline, and register it */
+ memset(&kiss_ldisc, 0, sizeof(kiss_ldisc));
+ kiss_ldisc.magic = TTY_LDISC_MAGIC;
+ kiss_ldisc.name = "kiss";
+ kiss_ldisc.flags = 0;
+ kiss_ldisc.open = kiss_open;
+ kiss_ldisc.close = kiss_close;
+ kiss_ldisc.read = NULL;
+ kiss_ldisc.write = NULL;
+ kiss_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
+ unsigned int, unsigned long)) kiss_ioctl;
+ kiss_ldisc.poll = NULL;
+ kiss_ldisc.receive_buf = kiss_receive_buf;
+ kiss_ldisc.receive_room = kiss_receive_room;
+ kiss_ldisc.write_wakeup = kiss_write_wakeup;
+ if ((status = tty_register_ldisc(N_KISS, &kiss_ldisc)) != 0) {
+ printk("KISS: can't register line discipline (err = %d)\n", status);
+ }
+
+
+ return status;
+
+ /* Return "not found", so that dev_init() will unlink
+ * the placeholder device entry for us.
+ */
+ return -ENODEV;
+ }
+
+
+void __exit kiss_exit_driver(void)
+{
+ int i;
+
+ if (kiss_ctrls != NULL) {
+ unsigned long start = jiffies;
+ int busy = 0;
+
+ /* First of all: check for active disciplines and hangup them.
+ */
+ do {
+ if (busy) {
+ current->counter = 0;
+ schedule();
+ }
+
+ busy = 0;
+ spin_lock_bh(&ks_ctrl_lock);
+ for (i = 0; i < maxdev; i++) {
+ struct kiss_ctrl *ksc = kiss_ctrls[i];
+ if (ksc && ksc->ctrl.tty) {
+ busy++;
+ tty_hangup(ksc->ctrl.tty);
+ }
+ }
+ spin_unlock_bh(&ks_ctrl_lock);
+ } while (busy && jiffies - start < 1*HZ);
+
+ busy = 0;
+ for (i = 0; i < maxdev; i++) {
+ struct kiss_ctrl *ksc = kiss_ctrls[i];
+ if (ksc) {
+ unregister_netdev(&ksc->dev);
+ if (ksc->ctrl.tty) {
+ printk("%s: tty discipline is still running\n", ksc->dev.name);
+ /* Pin module forever */
+ MOD_INC_USE_COUNT;
+ busy++;
+ continue;
+ }
+ kiss_free_bufs(&ksc->ctrl);
+ kfree(ksc);
+ kiss_ctrls[i] = NULL;
+ }
+ }
+ if (!busy) {
+ kfree(kiss_ctrls);
+ kiss_ctrls = NULL;
+ }
+ }
+ if ((i = tty_register_ldisc(N_KISS, NULL)))
+ {
+ printk("KISS: can't unregister line discipline (err = %d)\n", i);
+ }
+}
+
+MODULE_PARM(maxdev, "i");
+MODULE_AUTHOR("Matthias Welwarsky <dg2fef@afthd.tu-darmstadt.de>");
+MODULE_DESCRIPTION("KISS driver for AX.25");
+module_init(kiss_init_driver);
+module_exit(kiss_exit_driver);
diff --git a/drivers/net/hamradio/kiss.h b/drivers/net/hamradio/kiss.h
new file mode 100644
index 000000000..f4df5720d
--- /dev/null
+++ b/drivers/net/hamradio/kiss.h
@@ -0,0 +1,90 @@
+/*
+ * kiss.h Define the KISS device driver interface and constants.
+ *
+ * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY
+ * AS SOON AS POSSIBLE!
+ *
+ * Version: @(#)kiss.h 1.2.0 03/28/93
+ *
+ * Fixes:
+ * Alan Cox : Added slip mtu field.
+ * Matt Dillon : Printable slip (borrowed from net2e)
+ * Alan Cox : Added SL_SLIP_LOTS
+ * Dmitry Gorodchanin : A lot of changes in the 'struct slip'
+ * Dmitry Gorodchanin : Added CSLIP statistics.
+ * Stanislav Voronyi : Make line checking as created by
+ * Igor Chechik, RELCOM Corp.
+ * Craig Schlenter : Fixed #define bug that caused
+ * CSLIP telnets to hang in 1.3.61-6
+ *
+ * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org>
+ */
+#ifndef _LINUX_KISS_H
+#define _LINUX_KISS_H
+
+#include <linux/config.h>
+#include <linux/netdevice.h>
+
+#define KISS_NRUNIT 16
+#define KISS_MTU (256+MAX_HEADER)
+
+/* KISS protocol characters. */
+#define END 0300 /* indicates end of frame */
+#define ESC 0333 /* indicates byte stuffing */
+#define ESC_END 0334 /* ESC ESC_END means END 'data' */
+#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
+
+typedef enum {
+ KISS_MODE_NORMAL,
+ KISS_MODE_ADAPTIVE,
+ KISS_MODE_FLEX,
+ KISS_MODE_SMACK,
+ KISS_MODE_COMPAT,
+ KISS_MODE_TNN_TOKENRING
+} kiss_crcmode_t;
+
+typedef enum {
+ KISS_PARAM_TXDELAY=1,
+ KISS_PARAM_PPERS,
+ KISS_PARAM_SLOT,
+ KISS_PARAM_TXTAIL,
+ KISS_PARAM_DUPLEX
+} kiss_command_t;
+
+struct kiss {
+ struct tty_struct *tty; /* ptr to TTY structure */
+ struct net_device *dev; /* easy for intr handling */
+
+ spinlock_t lock;
+
+ /* 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 */
+ int mtu; /* Our mtu (to spot changes!) */
+ int buffsize; /* Max buffers sizes */
+
+ struct {
+ int in_use:1;
+ int escape:1;
+ int error:1;
+ } flags;
+
+ kiss_crcmode_t mode; /* KISS mode */
+
+ kdev_t line;
+ pid_t pid;
+
+ struct net_device_stats stats; /* statistics */
+ struct ax25_dev ax25dev;
+};
+
+typedef struct kiss_ctrl {
+ struct kiss ctrl; /* KISS things */
+ struct net_device dev; /* the device */
+} kiss_ctrl_t;
+
+
+#endif /* _LINUX_KISS.H */
diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c
deleted file mode 100644
index 3ec09be5a..000000000
--- a/drivers/net/hamradio/mkiss.c
+++ /dev/null
@@ -1,1016 +0,0 @@
-/*
- * MKISS Driver
- *
- * 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.
- * This method clears the way to implement other kiss protocols
- * like mkiss smack g8bpq ..... so far only mkiss is implemented.
- *
- * Hans Alblas <hans@esrac.ele.tue.nl>
- *
- * History
- * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15.
- * Matthias (DG2FEF) Added support for FlexNet CRC (on special request)
- * Fixed bug in ax25_close(): dev_lock_wait() was
- * called twice, causing a deadlock.
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <asm/system.h>
-#include <asm/segment.h>
-#include <asm/bitops.h>
-#include <asm/uaccess.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/in.h>
-#include <linux/inet.h>
-#include <linux/tty.h>
-#include <linux/errno.h>
-#include <linux/netdevice.h>
-#include <linux/major.h>
-#include <linux/init.h>
-#include <linux/rtnetlink.h>
-#include <linux/etherdevice.h>
-#include <linux/skbuff.h>
-#include <linux/if_arp.h>
-
-#include <net/ax25.h>
-
-#include "mkiss.h"
-
-#ifdef CONFIG_INET
-#include <linux/ip.h>
-#include <linux/tcp.h>
-#endif
-
-static const char banner[] __initdata = KERN_INFO "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n";
-
-#define NR_MKISS 4
-#define MKISS_SERIAL_TYPE_NORMAL 1
-
-struct mkiss_channel {
- int magic; /* magic word */
- int init; /* channel exists? */
- struct tty_struct *tty; /* link to tty control structure */
-};
-
-typedef struct ax25_ctrl {
- struct ax_disp ctrl; /* */
- struct net_device dev; /* the device */
-} ax25_ctrl_t;
-
-static ax25_ctrl_t **ax25_ctrls;
-
-int ax25_maxdev = AX25_MAXDEV; /* Can be overridden with insmod! */
-
-static struct tty_ldisc ax_ldisc;
-
-static int ax25_init(struct net_device *);
-static int kiss_esc(unsigned char *, unsigned char *, int);
-static int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, int);
-static void kiss_unesc(struct ax_disp *, unsigned char);
-
-/*---------------------------------------------------------------------------*/
-
-static const unsigned short Crc_flex_table[] = {
- 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38,
- 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770,
- 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9,
- 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1,
- 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a,
- 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672,
- 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb,
- 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3,
- 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c,
- 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574,
- 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd,
- 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5,
- 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e,
- 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476,
- 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf,
- 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7,
- 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30,
- 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378,
- 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1,
- 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9,
- 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32,
- 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a,
- 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3,
- 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb,
- 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34,
- 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c,
- 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5,
- 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd,
- 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36,
- 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e,
- 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7,
- 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff
-};
-
-/*---------------------------------------------------------------------------*/
-
-static unsigned short calc_crc_flex(unsigned char *cp, int size)
-{
- unsigned short crc = 0xffff;
-
- while (size--)
- crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
-
- return crc;
-}
-
-/*---------------------------------------------------------------------------*/
-
-static int check_crc_flex(unsigned char *cp, int size)
-{
- unsigned short crc = 0xffff;
-
- if (size < 3)
- return -1;
-
- while (size--)
- crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff];
-
- if ((crc & 0xffff) != 0x7070)
- return -1;
-
- return 0;
-}
-
-/*---------------------------------------------------------------------------*/
-
-/* Find a free channel, and link in this `tty' line. */
-static inline struct ax_disp *ax_alloc(void)
-{
- ax25_ctrl_t *axp=NULL;
- int i;
-
- for (i = 0; i < ax25_maxdev; i++) {
- axp = ax25_ctrls[i];
-
- /* Not allocated ? */
- if (axp == NULL)
- break;
-
- /* Not in use ? */
- if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags))
- break;
- }
-
- /* Sorry, too many, all slots in use */
- if (i >= ax25_maxdev)
- return NULL;
-
- /* If no channels are available, allocate one */
- if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) {
- axp = ax25_ctrls[i];
- memset(axp, 0, sizeof(ax25_ctrl_t));
-
- /* Initialize channel control data */
- set_bit(AXF_INUSE, &axp->ctrl.flags);
- sprintf(axp->dev.name, "ax%d", i++);
- axp->ctrl.tty = NULL;
- axp->dev.base_addr = i;
- axp->dev.priv = (void *)&axp->ctrl;
- axp->dev.next = NULL;
- axp->dev.init = ax25_init;
- }
-
- if (axp != NULL) {
- /*
- * register device so that it can be ifconfig'ed
- * ax25_init() will be called as a side-effect
- * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl !
- */
- if (register_netdev(&axp->dev) == 0) {
- /* (Re-)Set the INUSE bit. Very Important! */
- set_bit(AXF_INUSE, &axp->ctrl.flags);
- axp->ctrl.dev = &axp->dev;
- axp->dev.priv = (void *) &axp->ctrl;
-
- return &axp->ctrl;
- } else {
- clear_bit(AXF_INUSE,&axp->ctrl.flags);
- printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n");
- }
- }
-
- return NULL;
-}
-
-/* Free an AX25 channel. */
-static inline void ax_free(struct ax_disp *ax)
-{
- /* Free all AX25 frame buffers. */
- if (ax->rbuff)
- kfree(ax->rbuff);
- ax->rbuff = NULL;
- if (ax->xbuff)
- kfree(ax->xbuff);
- ax->xbuff = NULL;
- if (!test_and_clear_bit(AXF_INUSE, &ax->flags))
- printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name);
-}
-
-static void ax_changedmtu(struct ax_disp *ax)
-{
- struct net_device *dev = ax->dev;
- unsigned char *xbuff, *rbuff, *oxbuff, *orbuff;
- int len;
- unsigned long flags;
-
- len = dev->mtu * 2;
-
- /*
- * allow for arrival of larger UDP packets, even if we say not to
- * also fixes a bug in which SunOS sends 512-byte packets even with
- * an MSS of 128
- */
- if (len < 576 * 2)
- len = 576 * 2;
-
- xbuff = kmalloc(len + 4, GFP_ATOMIC);
- rbuff = kmalloc(len + 4, GFP_ATOMIC);
-
- if (xbuff == NULL || rbuff == NULL) {
- printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n",
- ax->dev->name);
- dev->mtu = ax->mtu;
- if (xbuff != NULL)
- kfree(xbuff);
- if (rbuff != NULL)
- kfree(rbuff);
- return;
- }
-
- save_flags(flags);
- cli();
-
- oxbuff = ax->xbuff;
- ax->xbuff = xbuff;
- orbuff = ax->rbuff;
- ax->rbuff = rbuff;
-
- if (ax->xleft) {
- if (ax->xleft <= len) {
- memcpy(ax->xbuff, ax->xhead, ax->xleft);
- } else {
- ax->xleft = 0;
- ax->tx_dropped++;
- }
- }
-
- ax->xhead = ax->xbuff;
-
- if (ax->rcount) {
- if (ax->rcount <= len) {
- memcpy(ax->rbuff, orbuff, ax->rcount);
- } else {
- ax->rcount = 0;
- ax->rx_over_errors++;
- set_bit(AXF_ERROR, &ax->flags);
- }
- }
-
- ax->mtu = dev->mtu + 73;
- ax->buffsize = len;
-
- restore_flags(flags);
-
- if (oxbuff != NULL)
- kfree(oxbuff);
- if (orbuff != NULL)
- kfree(orbuff);
-}
-
-
-/* Set the "sending" flag. This must be atomic. */
-static inline void ax_lock(struct ax_disp *ax)
-{
- netif_stop_queue(ax->dev);
-}
-
-
-/* Clear the "sending" flag. This must be atomic. */
-static inline void ax_unlock(struct ax_disp *ax)
-{
- netif_start_queue(ax->dev);
-}
-
-/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
-static void ax_bump(struct ax_disp *ax)
-{
- struct ax_disp *tmp_ax;
- struct sk_buff *skb;
- struct mkiss_channel *mkiss;
- int count;
-
- tmp_ax = ax;
-
- if (ax->rbuff[0] > 0x0f) {
- if (ax->mkiss != NULL) {
- mkiss= ax->mkiss->tty->driver_data;
- if (mkiss->magic == MKISS_DRIVER_MAGIC)
- tmp_ax = ax->mkiss;
- } else if (ax->rbuff[0] & 0x20) {
- ax->crcmode = CRC_MODE_FLEX;
- if (check_crc_flex(ax->rbuff, ax->rcount) < 0) {
- ax->rx_errors++;
- return;
- }
- ax->rcount -= 2;
- }
- }
-
- count = ax->rcount;
-
- if ((skb = dev_alloc_skb(count)) == NULL) {
- printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name);
- ax->rx_dropped++;
- return;
- }
-
- skb->dev = tmp_ax->dev;
- memcpy(skb_put(skb,count), ax->rbuff, count);
- skb->mac.raw = skb->data;
- skb->protocol = htons(ETH_P_AX25);
- netif_rx(skb);
- tmp_ax->rx_packets++;
-}
-
-/* Encapsulate one AX.25 packet and stuff into a TTY queue. */
-static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len)
-{
- unsigned char *p;
- int actual, count;
- struct mkiss_channel *mkiss = ax->tty->driver_data;
-
- if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */
- ax_changedmtu(ax);
-
- if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */
- len = ax->mtu;
- printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name);
- ax->tx_dropped++;
- ax_unlock(ax);
- return;
- }
-
- p = icp;
-
- if (mkiss->magic != MKISS_DRIVER_MAGIC) {
- switch (ax->crcmode) {
- unsigned short crc;
-
- case CRC_MODE_FLEX:
- *p |= 0x20;
- crc = calc_crc_flex(p, len);
- count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2);
- break;
-
- default:
- count = kiss_esc(p, (unsigned char *)ax->xbuff, len);
- break;
- }
- ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count);
- ax->tx_packets++;
- ax->dev->trans_start = jiffies;
- ax->xleft = count - actual;
- ax->xhead = ax->xbuff + actual;
- } else {
- count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len);
- ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
- actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count);
- ax->tx_packets++;
- ax->mkiss->dev->trans_start = jiffies;
- ax->mkiss->xleft = count - actual;
- ax->mkiss->xhead = ax->mkiss->xbuff + actual;
- }
-}
-
-/*
- * Called by the driver when there's room for more data. If we have
- * more packets to send, we send them here.
- */
-static void ax25_write_wakeup(struct tty_struct *tty)
-{
- int actual;
- struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
- struct mkiss_channel *mkiss;
-
- /* First make sure we're connected. */
- if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev))
- return;
- if (ax->xleft <= 0) {
- /* Now serial buffer is almost free & we can start
- * transmission of another packet
- */
- tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
- if (ax->mkiss != NULL) {
- mkiss= ax->mkiss->tty->driver_data;
- if (mkiss->magic == MKISS_DRIVER_MAGIC)
- ax_unlock(ax->mkiss);
- }
-
- netif_wake_queue(ax->dev);
- return;
- }
-
- actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft);
- ax->xleft -= actual;
- ax->xhead += actual;
-}
-
-/* Encapsulate an AX.25 packet and kick it into a TTY queue. */
-static int ax_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- struct ax_disp *ax = (struct ax_disp *) dev->priv;
- struct mkiss_channel *mkiss = ax->tty->driver_data;
- struct ax_disp *tmp_ax;
-
- tmp_ax = NULL;
-
- if (mkiss->magic == MKISS_DRIVER_MAGIC) {
- if (skb->data[0] < 0x10)
- skb->data[0] = skb->data[0] + 0x10;
- tmp_ax = ax->mkiss;
- }
-
- if (!netif_running(dev)) {
- printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name);
- return 1;
- }
-
- if (tmp_ax != NULL)
- if (netif_queue_stopped(tmp_ax->dev))
- return 1;
-
- if (tmp_ax != NULL)
- if (netif_queue_stopped(dev)) {
- printk(KERN_ERR "mkiss: dev busy while serial dev is free\n");
- ax_unlock(ax);
- }
-
- if (netif_queue_stopped(dev)) {
- /*
- * May be we must check transmitter timeout here ?
- * 14 Oct 1994 Dmitry Gorodchanin.
- */
- if (jiffies - dev->trans_start < 20 * HZ) {
- /* 20 sec timeout not reached */
- return 1;
- }
-
- printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name,
- (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ?
- "bad line quality" : "driver error");
-
- ax->xleft = 0;
- ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
- ax_unlock(ax);
- }
-
- /* We were not busy, so we are now... :-) */
- if (skb != NULL) {
- ax_lock(ax);
- if (tmp_ax != NULL)
- ax_lock(tmp_ax);
- ax_encaps(ax, skb->data, skb->len);
- kfree_skb(skb);
- }
-
- return 0;
-}
-
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-
-/* Return the frame type ID */
-static int ax_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 ax_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 AX25 channel. Easy! */
-static int ax_open(struct net_device *dev)
-{
- struct ax_disp *ax = (struct ax_disp *) dev->priv;
- unsigned long len;
-
- if (ax->tty == NULL)
- return -ENODEV;
-
- /*
- * Allocate the frame buffers:
- *
- * rbuff Receive buffer.
- * xbuff Transmit buffer.
- */
- len = dev->mtu * 2;
-
- /*
- * allow for arrival of larger UDP packets, even if we say not to
- * also fixes a bug in which SunOS sends 512-byte packets even with
- * an MSS of 128
- */
- if (len < 576 * 2)
- len = 576 * 2;
-
- if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
- goto norbuff;
-
- if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL)
- goto noxbuff;
-
- ax->mtu = dev->mtu + 73;
- ax->buffsize = len;
- ax->rcount = 0;
- ax->xleft = 0;
-
- ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */
-
- netif_start_queue(dev);
- return 0;
-
-noxbuff:
- kfree(ax->rbuff);
-
-norbuff:
- return -ENOMEM;
-}
-
-
-/* Close the low-level part of the AX25 channel. Easy! */
-static int ax_close(struct net_device *dev)
-{
- struct ax_disp *ax = (struct ax_disp *) dev->priv;
-
- if (ax->tty == NULL)
- return -EBUSY;
-
- ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
-
- netif_stop_queue(dev);
-
- return 0;
-}
-
-static int ax25_receive_room(struct tty_struct *tty)
-{
- return 65536; /* We can handle an infinite amount of data. :-) */
-}
-
-/*
- * Handle the 'receiver data ready' interrupt.
- * This function is called by the 'tty_io' module in the kernel when
- * a block of data has been received, which can now be decapsulated
- * and sent on to the AX.25 layer for further processing.
- */
-static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count)
-{
- struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
-
- if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev))
- return;
-
- /*
- * Argh! mtu change time! - costs us the packet part received
- * at the change
- */
- if (ax->mtu != ax->dev->mtu + 73)
- ax_changedmtu(ax);
-
- /* Read the characters out of the buffer */
- while (count--) {
- if (fp != NULL && *fp++) {
- if (!test_and_set_bit(AXF_ERROR, &ax->flags))
- ax->rx_errors++;
- cp++;
- continue;
- }
-
- kiss_unesc(ax, *cp++);
- }
-}
-
-static int ax25_open(struct tty_struct *tty)
-{
- struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
- struct ax_disp *tmp_ax;
- struct mkiss_channel *mkiss;
- int err, cnt;
-
- /* First make sure we're not already connected. */
- if (ax && ax->magic == AX25_MAGIC)
- return -EEXIST;
-
- /* OK. Find a free AX25 channel to use. */
- if ((ax = ax_alloc()) == NULL)
- return -ENFILE;
-
- ax->tty = tty;
- tty->disc_data = ax;
-
- ax->mkiss = NULL;
- tmp_ax = NULL;
-
- if (tty->driver.flush_buffer)
- tty->driver.flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
-
- /* Restore default settings */
- ax->dev->type = ARPHRD_AX25;
-
- /* Perform the low-level AX25 initialization. */
- if ((err = ax_open(ax->dev)))
- return err;
-
- mkiss = ax->tty->driver_data;
-
- if (mkiss->magic == MKISS_DRIVER_MAGIC) {
- for (cnt = 1; cnt < ax25_maxdev; cnt++) {
- if (ax25_ctrls[cnt]) {
- if (netif_running(&ax25_ctrls[cnt]->dev)) {
- if (ax == &ax25_ctrls[cnt]->ctrl) {
- cnt--;
- tmp_ax = &ax25_ctrls[cnt]->ctrl;
- break;
- }
- }
- }
- }
- }
-
- if (tmp_ax != NULL) {
- ax->mkiss = tmp_ax;
- tmp_ax->mkiss = ax;
- }
-
- MOD_INC_USE_COUNT;
-
- /* Done. We have linked the TTY line to a channel. */
- return ax->dev->base_addr;
-}
-
-static void ax25_close(struct tty_struct *tty)
-{
- struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
-
- /* First make sure we're connected. */
- if (ax == NULL || ax->magic != AX25_MAGIC)
- return;
-
- unregister_netdev(ax->dev);
-
- tty->disc_data = 0;
- ax->tty = NULL;
-
- ax_free(ax);
- MOD_DEC_USE_COUNT;
-}
-
-
-static struct net_device_stats *ax_get_stats(struct net_device *dev)
-{
- static struct net_device_stats stats;
- struct ax_disp *ax = (struct ax_disp *) dev->priv;
-
- memset(&stats, 0, sizeof(struct net_device_stats));
-
- stats.rx_packets = ax->rx_packets;
- stats.tx_packets = ax->tx_packets;
- stats.rx_dropped = ax->rx_dropped;
- stats.tx_dropped = ax->tx_dropped;
- stats.tx_errors = ax->tx_errors;
- stats.rx_errors = ax->rx_errors;
- stats.rx_over_errors = ax->rx_over_errors;
-
- return &stats;
-}
-
-
-/************************************************************************
- * STANDARD ENCAPSULATION *
- ************************************************************************/
-
-static int kiss_esc(unsigned char *s, unsigned char *d, int len)
-{
- unsigned char *ptr = d;
- unsigned char c;
-
- /*
- * Send an initial END character to flush out any
- * data that may have accumulated in the receiver
- * due to line noise.
- */
-
- *ptr++ = END;
-
- while (len-- > 0) {
- switch (c = *s++) {
- case END:
- *ptr++ = ESC;
- *ptr++ = ESC_END;
- break;
- case ESC:
- *ptr++ = ESC;
- *ptr++ = ESC_ESC;
- break;
- default:
- *ptr++ = c;
- break;
- }
- }
-
- *ptr++ = END;
-
- return ptr - d;
-}
-
-/*
- * MW:
- * OK its ugly, but tell me a better solution without copying the
- * packet to a temporary buffer :-)
- */
-static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len)
-{
- unsigned char *ptr = d;
- unsigned char c=0;
-
- *ptr++ = END;
- while (len > 0) {
- if (len > 2)
- c = *s++;
- else if (len > 1)
- c = crc >> 8;
- else if (len > 0)
- c = crc & 0xff;
-
- len--;
-
- switch (c) {
- case END:
- *ptr++ = ESC;
- *ptr++ = ESC_END;
- break;
- case ESC:
- *ptr++ = ESC;
- *ptr++ = ESC_ESC;
- break;
- default:
- *ptr++ = c;
- break;
- }
- }
- *ptr++ = END;
- return ptr - d;
-}
-
-static void kiss_unesc(struct ax_disp *ax, unsigned char s)
-{
- switch (s) {
- case END:
- /* drop keeptest bit = VSV */
- if (test_bit(AXF_KEEPTEST, &ax->flags))
- clear_bit(AXF_KEEPTEST, &ax->flags);
-
- if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2))
- ax_bump(ax);
-
- clear_bit(AXF_ESCAPE, &ax->flags);
- ax->rcount = 0;
- return;
-
- case ESC:
- set_bit(AXF_ESCAPE, &ax->flags);
- return;
- case ESC_ESC:
- if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
- s = ESC;
- break;
- case ESC_END:
- if (test_and_clear_bit(AXF_ESCAPE, &ax->flags))
- s = END;
- break;
- }
-
- if (!test_bit(AXF_ERROR, &ax->flags)) {
- if (ax->rcount < ax->buffsize) {
- ax->rbuff[ax->rcount++] = s;
- return;
- }
-
- ax->rx_over_errors++;
- set_bit(AXF_ERROR, &ax->flags);
- }
-}
-
-
-static int ax_set_mac_address(struct net_device *dev, void *addr)
-{
- if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN))
- return -EFAULT;
- return 0;
-}
-
-static int ax_set_dev_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 ax25 channel. */
-static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg)
-{
- struct ax_disp *ax = (struct ax_disp *) tty->disc_data;
- unsigned int tmp;
-
- /* First make sure we're connected. */
- if (ax == NULL || ax->magic != AX25_MAGIC)
- return -EINVAL;
-
- switch (cmd) {
- case SIOCGIFNAME:
- if (copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1))
- return -EFAULT;
- return 0;
-
- case SIOCGIFENCAP:
- return put_user(4, (int *)arg);
-
- case SIOCSIFENCAP:
- if (get_user(tmp, (int *)arg))
- return -EFAULT;
- ax->mode = tmp;
- ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */
- ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3;
- ax->dev->type = ARPHRD_AX25;
- return 0;
-
- case SIOCSIFHWADDR:
- return ax_set_mac_address(ax->dev, arg);
-
- default:
- return -ENOIOCTLCMD;
- }
-}
-
-static int ax_open_dev(struct net_device *dev)
-{
- struct ax_disp *ax = (struct ax_disp *) dev->priv;
-
- if (ax->tty == NULL)
- return -ENODEV;
-
- return 0;
-}
-
-
-/* Initialize the driver. Called by network startup. */
-static int ax25_init(struct net_device *dev)
-{
- struct ax_disp *ax = (struct ax_disp *) 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 (ax == NULL) /* Allocation failed ?? */
- return -ENODEV;
-
- /* Set up the "AX25 Control Block". (And clear statistics) */
- memset(ax, 0, sizeof (struct ax_disp));
- ax->magic = AX25_MAGIC;
- ax->dev = dev;
-
- /* Finish setting up the DEVICE info. */
- dev->mtu = AX_MTU;
- dev->hard_start_xmit = ax_xmit;
- dev->open = ax_open_dev;
- dev->stop = ax_close;
- dev->get_stats = ax_get_stats;
- dev->set_mac_address = ax_set_dev_mac_address;
- dev->hard_header_len = 0;
- dev->addr_len = 0;
- dev->type = ARPHRD_AX25;
- dev->tx_queue_len = 10;
- dev->hard_header = ax_header;
- dev->rebuild_header = ax_rebuild_header;
-
- memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
- memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN);
-
- dev_init_buffers(dev);
-
- /* New-style flags. */
- dev->flags = 0;
-
- return 0;
-}
-
-
-/* ******************************************************************** */
-/* * Init MKISS driver * */
-/* ******************************************************************** */
-
-static int __init mkiss_init_driver(void)
-{
- int status;
-
- printk(banner);
-
- if (ax25_maxdev < 4)
- ax25_maxdev = 4; /* Sanity */
-
- if ((ax25_ctrls = kmalloc(sizeof(void *) * ax25_maxdev, GFP_KERNEL)) == NULL) {
- printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array!\n");
- return -ENOMEM;
- }
-
- /* Clear the pointer array, we allocate devices when we need them */
- memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */
-
- /* Fill in our line protocol discipline, and register it */
- ax_ldisc.magic = TTY_LDISC_MAGIC;
- ax_ldisc.name = "mkiss";
- ax_ldisc.open = ax25_open;
- ax_ldisc.close = ax25_close;
- ax_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *,
- unsigned int, unsigned long))ax25_disp_ioctl;
- ax_ldisc.receive_buf = ax25_receive_buf;
- ax_ldisc.receive_room = ax25_receive_room;
- ax_ldisc.write_wakeup = ax25_write_wakeup;
-
- if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0) {
- printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status);
- kfree(ax25_ctrls);
- }
- return status;
-}
-
-static void __exit mkiss_exit_driver(void)
-{
- int i;
-
- for (i = 0; i < ax25_maxdev; i++) {
- if (ax25_ctrls[i]) {
- /*
- * VSV = if dev->start==0, then device
- * unregistered while close proc.
- */
- if (netif_running(&ax25_ctrls[i]->dev))
- unregister_netdev(&ax25_ctrls[i]->dev);
- kfree(ax25_ctrls[i]);
- }
- }
-
- kfree(ax25_ctrls);
- ax25_ctrls = NULL;
-
- if ((i = tty_register_ldisc(N_AX25, NULL)))
- printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i);
-}
-
-MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>");
-MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs");
-MODULE_PARM(ax25_maxdev, "i");
-MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices");
-
-module_init(mkiss_init_driver);
-module_exit(mkiss_exit_driver);
-
diff --git a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h
deleted file mode 100644
index 4a3d700cb..000000000
--- a/drivers/net/hamradio/mkiss.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/****************************************************************************
- * Defines for the Multi-KISS driver.
- ****************************************************************************/
-
-#define AX25_MAXDEV 16 /* MAX number of AX25 channels;
- This can be overridden with
- insmod -oax25_maxdev=nnn */
-#define AX_MTU 236
-
-/* SLIP/KISS protocol characters. */
-#define END 0300 /* indicates end of frame */
-#define ESC 0333 /* indicates byte stuffing */
-#define ESC_END 0334 /* ESC ESC_END means END 'data' */
-#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */
-
-struct ax_disp {
- int magic;
-
- /* Various fields. */
- struct tty_struct *tty; /* ptr to TTY structure */
- struct net_device *dev; /* easy for intr handling */
- struct ax_disp *mkiss; /* 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 long flags; /* Flag values/ mode etc */
- /* long req'd: used by set_bit --RR */
-#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;
- int crcmode; /* MW: for FlexNet, SMACK etc. */
-#define CRC_MODE_NONE 0
-#define CRC_MODE_FLEX 1
-#define CRC_MODE_SMACK 2
-};
-
-#define AX25_MAGIC 0x5316
-#define MKISS_DRIVER_MAGIC 1215
diff --git a/drivers/net/hamradio/pciscc4.c b/drivers/net/hamradio/pciscc4.c
new file mode 100644
index 000000000..c167bebb7
--- /dev/null
+++ b/drivers/net/hamradio/pciscc4.c
@@ -0,0 +1,2494 @@
+/*****************************************************************************
+ *
+ * pciscc4.c This is the device driver for the PCISCC-4 card or any other
+ * board based on the Siemens PEB-20534H (DSCC-4) communication
+ * controller. The PCISCC-4 is a four-channel medium-speed (up
+ * to 10 respectively 52 Mbps/channel) synchronous serial
+ * interface controller with HDLC protocol processor and
+ * busmaster-DMA facilities.
+ *
+ * Info: http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4
+ *
+ * Authors: (c) 1999 Jens David <dg1kjd@afthd.tu-darmstadt.de>
+ *
+ * Policy: Please contact me before making structural changes.
+ * Before applying changes to the communication with
+ * the DSCC-4 please read:
+ * - Data Sheet 09/98 PEB-20534 Version 2.0
+ * - Delta Sheet Chip Rev. 2.0-2.1
+ * - Addendum/Corrections to Data Sheet 09/98 as of 01/99
+ * - DSCC-4 Errata Sheet (Book?) 03/99 Chip Rev. 2.1
+ * - Sample driver source code as of 07/27/99
+ * All these documents are available from Infineon on
+ * request or from http://www.infineon.de/... .
+ * At least the current version of this beast likes to be
+ * treated _very_ carefully. If you don't do this, it crashes
+ * itself or the system. I have made comments on these common
+ * traps where appropriate. No, there isn't such thing as a
+ * "master reset".
+ *
+ * CVS: $Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 dg1kjd Exp $
+ *
+ * Changelog: Please log any changes here.
+ * | 08/23/99 Initial version Jens
+ * | 08/25/99 Reworked buffer concept to use last-mode Jens
+ * | policy and implemented Siemens' workarounds
+ * | 08/27/99 Reworked transmitter to use internal timers Jens
+ * | for better resolution at txdelay/txtail
+ * | 09/01/99 Ioctl()s implemented Jens
+ * | 09/10/99 Descriptor chain reworked. RX hold condition Jens
+ * | can't occur any more. TX descriptors are not Jens
+ * | re-initialized after transmit any more.
+ * | 09/12/99 TX reworked. TX-Timeout added. Jens
+ * | 09/13/99 TX timeout fixed Jens
+ * | 10/09/99 Cosmetic fixes and comments added Jens
+ * | 10/16/99 Cosmetic stuff and non-module mode fixed Jens
+ * | 10/21/99 TX-skbs are not freed in xmit()-statemachine Jens
+ * | 10/25/99 Default configuration more sensible now Jens
+ * | 02/13/00 Converted to new driver interface Jens
+ *
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *----------------------------------------------------------------------------
+ *
+ * | Please note that the GPL allows you to use the driver, NOT the radio. |
+ * | In order to use the radio, you need a license from the communications |
+ * | authority of your country. |
+ *
+ *----------------------------------------------------------------------------
+ *
+ *****************************************************************************
+ *
+ * Concept of Operation
+ * --------------------
+ *
+ * I. SCCs
+ * We use all SCC cores in HDLC mode. Asyncronous and BiSync operation is not
+ * supported and probably never will. We do not make use of the built-in
+ * LAPB/LAPD protocol processor features (Auto Mode). Moreover can't use
+ * automatic address recognition either because it is implemented in a way
+ * which allows only operation with fixed header sizes and address fields on
+ * static positions. Thus we use HDLC address mode 0. As far as the clock modes
+ * are concerned we make use of mode 0a (for DF9IC-like modems, RX and TX clock
+ * from pin header), 0b (G3RUH-"like", external RX clock, internal TX clock
+ * from BRG but unfornately without oversampling), 6b (for TCM-3105-like simple
+ * modems, using on-chip DPLL for RX clock recovery. Internal TX clock from BRG
+ * which can optionaly be provided on TxClk and/or RTS pins. No oversampling.)
+ * and 4 (external RX and TX clock like DF9IC, but with clock gapping
+ * function, see Data Book 09/98 pp. 141 ff). Channel coding is user-selectable
+ * on a per-channel basis. DF9IC-type modems like NRZ (conversion NRZ->NRZI
+ * done internally), TCM-3105-like modems are usually used with NRZI coding.
+ * Moreover manchester, FM0 and FM1 can be selected (untested).
+ * The internal SCC-DMAC interface seem to obey the KISS-concept. The drawback
+ * of this fact is, that the chip fills our data buffers in memory completely
+ * sequential. If at the end of a frame the SCC realizes, that the FCS failed,
+ * it does not "discard" the frame. That is, it requests an interrupt and
+ * uses up a packet buffer as if the frame was valid. The frame is, however,
+ * marked invalid, but of cause the host processor still needs to clean the
+ * mess up, which costs time. Now consider that every six ones in series on a
+ * empty channel will cause an interrupt and work to the handler. The only
+ * way around this is to gate the receive data with the DCD signal. Of cause
+ * the modem's DCD needs to be very fast to accomplish this. The standard-DCD
+ * on DF9IC-type modems currently isn't. As far as modem handshake is concerned
+ * we program the DCD input of each channel as general purpose input and read
+ * its state whenever L2 requests us to do so. TX handshake can be used in two
+ * modes I called "hard" and "soft". Hard mode is reserved for future
+ * (smart) modems which tell the controller when they are ready to transmit
+ * using the CTS (clear to send) signal. In soft mode we use each channel's
+ * internal timer to generate txdelay and txtail. The advantage of this concept
+ * is, that we have a resolution of one bit since the timers are clocked with
+ * the effective TxClk, which also allows us to probe the TX-bitrate in external
+ * clock modes (L2 likes this information). The SCC cores have some other
+ * goodies, as preample transmission, one insertion after 7 consecutive zeros
+ * and stuff like this which we make user selectable.
+ *
+ * II. DMA Controller and IRQs
+ * For maximum performance and least host processor load, the design of the
+ * DMA controller is descriptor orientated. For both, RX and TX channels
+ * descriptor "queues" are set up on device initialization. Each descriptor
+ * contains a link to its subsequent desriptor, a pointer to the buffer
+ * associated with it and the buffer's size. The buffer itself is _not_ part
+ * of the descriptor, but can be located anywhere else in address space.
+ * Thus, in TX case all we have to do when a packet to be sent arrives from
+ * L2, is painting out a descriptor (pointer to the sk_buf's data buffer,
+ * length of the frame and so on) and telling the DMAC to process it. We do
+ * not have to move the data around in memory. When the descriptor is finished
+ * (i.e. packet sent out completely or at least data completely in FIFO), the
+ * DMAC sets a flag (C) in the descriptor and issues an IRQ. We check the flag
+ * and if it is set, we can skb_free up the packet. Both descriptor queues (RX
+ * and TX) are organized circular with a user setable size and allocated
+ * statically at device initialization. As far as the RX queue ("ring") is
+ * concerned we also already allocate the sk_buffs associated with them.
+ * Whenever the DMAC processed a RX descriptor (thus "filled" the buffer
+ * associated with it) we release the buffer to L2 and allocate a new one.
+ * No copying. The structure of the RX descriptor chain never changes either.
+ * It stays the same since inititalization on device initialization and
+ * descriptor memory itself is only freed when the device destructor is called.
+ * The fact that both descriptor queues are kept statically has two advantages:
+ * It is save, because the DMAC can not "escape" due to a race condition and
+ * mess up our memory and it works around a hardware bug in the DSCC-4.
+ * A few words on linux mm:
+ * When a device driver allocates memory using functions like malloc() or
+ * alloc_skb(), the returned address pointers are pointers to virtual memory.
+ * In case of access to this memory, the MMU, as part of the CPU translates
+ * the virtual addresses to physical ones, which are e.g. used to drive the
+ * RAM address bus lines. If a PCI bus master accesses the same memory, it
+ * needs to know the right address vom _its_ point of view, the so-called
+ * "bus" address. On most architectures this is the same as the physical
+ * address. We use the funktion virt_to_bus() and bus_to_virt() to translate
+ * them. The descriptor structures contain both types, just to make the code
+ * faster and more readable. The symbol names for "bus"-pointers end on
+ * "addr", for example rx_desc_t.next --(virt-to-bus)--> rx_desc_t.nextptr.
+ * When we accessed "common" memory (i.e. descriptor or buffer memory) we
+ * issue a flush_cache_all() due to the fact that some architectures (not PC)
+ * don't keep memory caches consistent on DMAs. Where it isn't apropriate gcc
+ * will optimize it away for us.
+ * Another word on IRQ management:
+ * The DMAC is not only responsible for moving around network data from/to
+ * the SCC cores, but also maintains 10 so-called "interrupt queues" (IQs).
+ * These are intended to help speeding up operation and minimize load on the
+ * host CPU. There is the configuration queue (IQCFG) which is responsible
+ * for answers to DMAC configuration commands, the peripheral queue (IQPER),
+ * which cares about interrupt sources on the local bus, SSC (not SCC!) or GPP
+ * if enabled, and one TX and one RX queue per channel (IQRX and IQTX
+ * respectively), which are responsible for RX and TX paths and not only
+ * indicate DMAC exceptions (packet finished etc.) but also SCC exceptions
+ * (FIFO over/underrun, frame length exceeded etc.). Each element in the
+ * queues is a dword. The queues are "naturally" organized circular as well.
+ * Naturally means, that there is no such thing as a "next pointer" as in
+ * the frame descriptors, but that we tell the DMAC the length of each queue
+ * (user configurable in 32 dword-steps) and it does the wrap-around
+ * automagically. Whenever an element is added to a queue an IRQ is issued.
+ * The IRQ handler acks all interrupts by writing back the global status
+ * register (GSTAR) and starts processing ALL queues, independent of who
+ * caused the IRQ.
+ *
+ * III. General Purpose Port (GPP)
+ * The general purpose port is used for status LED connection. We support
+ * only 2 LEDs per port. These can be controlled with an ioctl(). We do not
+ * care about it, this ought to be done by a user-space daemon. The SSC port
+ * is not used. The LBI can be configured with the global settings and
+ * controlled by an own ioctl(). We don't care any more.
+ *
+ * IV. Configuration
+ * We divide configuration into global (i.e. concerning all ports, chipwide)
+ * and local. We have one template for each, chipcfg_default and devcfg_default
+ * which is hardcoded and never changes. On module load it is copied for each
+ * chip and each device respectively (chipctl_t.cfg and devctl_t.cfg). The
+ * silicon is initialized with these values only in chip_open() and
+ * device_open() and the structures themselves can only be changed when the
+ * corresponding interface (or all interfaces for global config) is down.
+ * Changes take effect when the interface is brought up the next time.
+ *
+ * V. Initialization
+ * When module_init is called, the PCI driver already took care of assigning
+ * two pieces of memory space and an IRQ to each board. On module load we do
+ * nothing else than building up our internal structures (devctl_t and
+ * chipctl_t), grabbing the interface names and registering them with the
+ * network subsystem. Chip_open() and device_open() are called only upon uping
+ * a device and perform IRQ grabbing, memory mapping and allocation and
+ * hardware initialization.
+ *
+ * VI. RX Handling
+ * When a frame is received completely, the C flag in the corresponding
+ * descriptor is set by the DSCC-4, an interrupt vector transfered to the
+ * channel's RX IQ, and the IRQ line asserted. The IRQ handler takes control
+ * of the system. The first step it performs is reading the global status
+ * register and writing it back, thus ack'ing the IRQ. Then it is analyzed
+ * bit-by-bit to find out where it originated from. The channel's RX IQ
+ * is examined and the function pciscc_isr_receiver() called for the
+ * corresponding port. This functions processes the rx descriptor queue
+ * starting with the element (devctl_t.dq_rx_next) following the last element
+ * processed the last time the function was called. All descriptors with the
+ * C-flag ("C"omplete) set are processed. And at the end the dq_rx_next pointer
+ * is updated to the next to last element processed. During "processing" at
+ * first two pieces of information are examined: The status field of the
+ * descriptor, mainly containing the length of the received frame and a flag
+ * telling us wether the frame reception was aborted or not (RA), which
+ * was written by the DMAC and the so-called Receive Data Section Status Byte
+ * (RSTA) which was appended to the end of the data buffer by the channel's
+ * SCC core. Both are checked and if they yield a valid frame and we success-
+ * fully allocated a new skb we remove the old one from the descriptor and
+ * hand it off to pciscc_rx_skb() which paints out some of the skb's members
+ * and fires it up to the MAC layer. The descriptor's fields are re-initialized
+ * anyway to prepare it for the next reception.
+ * After all complete descriptors were processed we must tell the DMAC that the
+ * last ready-to-fill descriptor (LRDA, Last Receive Descriptor Address) is the
+ * one pre-previous to the last one processed. In fact experiments show that
+ * normally this is not neccessary since we get an interrupt for _every_
+ * received frame so we can re-prepare the descriptor then. This is just to
+ * prevent the DMAC from "running around" uncontrolled in the circular
+ * structure, eventually losing sync with the devctl_t.dq_rx_next pointer in
+ * case of _very_ high bus/interrupt latency on heavy system load conditions.
+ *
+ * VII. TX Handling
+ * We assume we are in half duplex mode with software txdelay since everything
+ * else is a lot easier. The current TX state is kept track of in the
+ * devctl_t.txstate variable. When L2 hands us a frame, the first thing we
+ * do is check wether there is a free TX descriptor ready in the device's
+ * ring. The element dq_tx_last is a pointer to the last descriptor which
+ * is currently to be sent or already is sent. Thus, the element next to this
+ * element is the one we would like to fill. The variable dq_tx_cleanup
+ * of the device control structure tells us the next element to be "cleaned
+ * up" (free skb etc.) after transmission. If it points not to the element
+ * we like to fill, the element we like to fill is free. If it does, we must
+ * discard the new frame due to the full descriptor queue. Now that we are
+ * sure to have found our descriptor candidate will can paint it out, but
+ * we can't start transmission yet. We check what state the TX is in. If
+ * it is idle, we load the timer with the txdelay value and start it. Of cause
+ * we also need to manually assert the RTS line. If we were already
+ * transmitting, or sending trailing flags we immediately schedule the
+ * descriptor for process by the DMAC by pointing LTDA (Last Transmit
+ * Descriptor Address) to it. In the first case, the txdelay-timer will run
+ * out after the txdelay period is over, causing an IRQ. The interrupt handler
+ * can now schedule the transmission. During transmission we use the timer
+ * as some kind of software watchdog. When transmission finishes, we again
+ * program and start the timer, this time with the txtail value. The txstate
+ * variable is set to TX_TAIL and as soon as the timer runs out we can
+ * deassert the RTS line and reset txstate to TX_IDLE. The frame(s) were
+ * (hopefully) transmitted successfully. Anyway, each transmitted frame
+ * causes a ALLS TX IRQ. The only thing we need to do then, is to call
+ * tx_cleanup() which walks through the tx descriptor ring, cleaning up
+ * all finished entries (freeing up the skbs and things like this).
+ *
+ *****************************************************************************
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <linux/pciscc4.h>
+
+/* ------------------------------------------------------------------------- */
+/* user serviceable area or module parameter */
+
+static int xtal = 29491200; /* oscillator frequency in HZ */
+static int probebit = 9600; /* number of cycles for tx_bitrate test */
+static int txtimeout= 30; /* TX watchdog - timeout in seconds */
+#define PCISCC_DEBUG /* enable debugging */
+#undef PCISCC_VDEBUG /* enable verbose buffer debugging */
+#define FULLDUP_PTT /* pull PTT on full duplex */
+
+/* ------------------------------------------------------------------------- */
+/* global variables */
+
+static const char PCISCC_VERSION[] = "$Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 dg1kjd Exp $";
+
+static int chipcnt;
+static struct devctl_t *devctl[64];
+static struct chipctl_t *chipctl[16];
+static struct tq_struct txto_task;
+static unsigned char *dummybuf;
+
+/* template for global configuration, copied on driver init */
+static struct chipcfg_t chipcfg_default = {
+ 0, /* LBI mode */
+ 1, /* oscillator power */
+ 16, /* number of RX descriptors and buffers per channel */
+ 128, /* number of TX descriptors per channel */
+ 32, /* interrupt queue length */
+ -1, /* priority channel */
+ 16, /* RX main fifo DMA threshold */
+ 0 /* endian swap for data buffers */
+};
+
+/* template for device configuration, copied on driver init */
+static struct devcfg_t devcfg_default = {
+ CFG_CHCODE_NRZ, /* channel coding */
+ CFG_CM_DF9IC, /* clocking mode */
+ CFG_DUPLEX_HALF, /* duplex mode */
+ 0, /* DPLL */
+ 10, /* BRG "M" */
+ 0, /* BRG "N" (N+1)*2^M; M=10, N=0 => 9600 baud */
+ 0, /* clock-out enable */
+ 0, /* data inversion */
+ CFG_TXDDRIVE_TP, /* TxD driver type */
+ 0, /* carrier detect inversion */
+ 0, /* test loop */
+ 32, /* TX main fifo share */
+ 24, /* TX main fifo refill threshold in dw */
+ 20, /* TX main fifo forward */
+ 24, /* RX channel fifo forward threshold */
+ CFG_TXDEL_SOFT, /* TX-delay mode */
+ 2000, /* software TX-delay in bitclk cycles => default 250 ms @9600 baud */
+ 400, /* TX-tail, see above */
+ 1, /* shared flags */
+ 0, /* CRC mode */
+ 0, /* preamble repeatitions */
+ 0, /* preamble */
+ 0 /* HDLC extensions */
+};
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef PCISCC_DEBUG
+/* dump DMAC's register to syslog */
+static void pciscc_dmac_regdump(struct chipctl_t *cctlp)
+{
+ printk(KERN_INFO "PCISCC: ---------- begin DMAC register dump ----------\n");
+ printk(KERN_INFO "CH BRDA LRDA FRDA BTDA LTDA FTDA CFG\n");
+ printk(KERN_INFO " 0 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
+ (unsigned long) readl(cctlp->io_base+CH0BRDA),
+ (unsigned long) readl(cctlp->io_base+CH0LRDA),
+ (unsigned long) readl(cctlp->io_base+CH0FRDA),
+ (unsigned long) readl(cctlp->io_base+CH0BTDA),
+ (unsigned long) readl(cctlp->io_base+CH0LTDA),
+ (unsigned long) readl(cctlp->io_base+CH0FTDA),
+ (unsigned long) readl(cctlp->io_base+CH0CFG));
+ printk(KERN_INFO " 1 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
+ (unsigned long) readl(cctlp->io_base+CH1BRDA),
+ (unsigned long) readl(cctlp->io_base+CH1LRDA),
+ (unsigned long) readl(cctlp->io_base+CH1FRDA),
+ (unsigned long) readl(cctlp->io_base+CH1BTDA),
+ (unsigned long) readl(cctlp->io_base+CH1LTDA),
+ (unsigned long) readl(cctlp->io_base+CH1FTDA),
+ (unsigned long) readl(cctlp->io_base+CH1CFG));
+ printk(KERN_INFO " 2 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
+ (unsigned long) readl(cctlp->io_base+CH2BRDA),
+ (unsigned long) readl(cctlp->io_base+CH2LRDA),
+ (unsigned long) readl(cctlp->io_base+CH2FRDA),
+ (unsigned long) readl(cctlp->io_base+CH2BTDA),
+ (unsigned long) readl(cctlp->io_base+CH2LTDA),
+ (unsigned long) readl(cctlp->io_base+CH2FTDA),
+ (unsigned long) readl(cctlp->io_base+CH2CFG));
+ printk(KERN_INFO " 3 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
+ (unsigned long) readl(cctlp->io_base+CH3BRDA),
+ (unsigned long) readl(cctlp->io_base+CH3LRDA),
+ (unsigned long) readl(cctlp->io_base+CH3FRDA),
+ (unsigned long) readl(cctlp->io_base+CH3BTDA),
+ (unsigned long) readl(cctlp->io_base+CH3LTDA),
+ (unsigned long) readl(cctlp->io_base+CH3FTDA),
+ (unsigned long) readl(cctlp->io_base+CH3CFG));
+ printk(KERN_INFO "PCISCC: ---------- end DMAC register dump ----------\n");
+ return;
+}
+#endif /* PCISCC_DEBUG */
+
+/* ------------------------------------------------------------------------- */
+
+#ifdef PCISCC_DEBUG
+/* dump descriptor rings to syslog */
+static void pciscc_queuedump(struct devctl_t *dctlp)
+{
+ unsigned int i;
+ struct rx_desc_t *rdp;
+ struct tx_desc_t *tdp;
+
+ if (!dctlp) return;
+ printk(KERN_INFO "PCISCC: ---------- begin queue dump RX iface %s ----------\n", dctlp->name);
+ printk(KERN_INFO "# &desc &next &prev &nextptr &dataptr &skb &feptr flags result\n");
+ for (rdp=dctlp->dq_rx,i=0; ((rdp!=dctlp->dq_rx) || (i==0)) && (i<256); rdp=rdp->next,i++) {
+ if (!rdp) break;
+ printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx B0x%08lx 0x%08lx 0x%08lx\n",
+ i,
+ (unsigned long) rdp,
+ (unsigned long) rdp->next,
+ (unsigned long) rdp->prev,
+ (unsigned long) rdp->nextptr,
+ (unsigned long) rdp->dataptr,
+ (unsigned long) rdp->skb,
+ (unsigned long) rdp->feptr,
+ (unsigned long) rdp->flags,
+ (unsigned long) rdp->result);
+ }
+ printk(KERN_INFO "PCISCC: ---------- end queue dump RX iface %s ----------\n", dctlp->name);
+ printk(KERN_INFO "PCISCC: ---------- begin queue dump TX iface %s ----------\n", dctlp->name);
+ printk(KERN_INFO "%s->dq_tx=V0x%08lx %s->dq_tx_cleanup=V0x%08lx %s->dq_tx_last=V0x%08lx.\n",
+ dctlp->name, (unsigned long) dctlp->dq_tx,
+ dctlp->name, (unsigned long) dctlp->dq_tx_cleanup,
+ dctlp->name, (unsigned long) dctlp->dq_tx_last);
+ printk(KERN_INFO "# &desc &next &prev &nextptr &dataptr &skb flags result\n");
+ for (tdp=dctlp->dq_tx,i=0; ((tdp!=dctlp->dq_tx) || (i==0)) && (i<256); tdp=tdp->next,i++) {
+ if (!tdp) break;
+ printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx 0x%08lx 0x%08lx\n",
+ i,
+ (unsigned long) tdp,
+ (unsigned long) tdp->next,
+ (unsigned long) tdp->prev,
+ (unsigned long) tdp->nextptr,
+ (unsigned long) tdp->dataptr,
+ (unsigned long) tdp->skb,
+ (unsigned long) tdp->flags,
+ (unsigned long) tdp->result);
+ }
+ printk(KERN_INFO "PCISCC: ---------- end queue dump TX iface %s ----------\n", dctlp->name);
+ return;
+}
+#endif /* PCISCC_DEBUG */
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * initialize chip, called when first interface of a chip is brought up
+ * action sequency was carefully chosen, don't mess with it
+ */
+static int pciscc_chip_open(struct chipctl_t *cctlp)
+{
+ unsigned long start_time;
+ volatile unsigned long gcc_optimizer_safe;
+ int i;
+ int fifo_sum;
+ char pci_latency;
+
+ if (cctlp->initialized) return 0;
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: chip_open().\n");
+#endif
+ /*
+ * make sure FIFO space requested by all devices is not larger than
+ * what's available on the silicon.
+ */
+ cli();
+ for (i=0, fifo_sum=0; i<4; i++) {
+ fifo_sum += cctlp->device[i]->cfg.mfifo_tx_p;
+ }
+ sti();
+ if (fifo_sum > 128) {
+ printk(KERN_ERR "PCISCC: chip_open(): TX main_fifo misconfiguration.\n");
+ return -EAGAIN;
+ }
+ /* map control and LBI memory */
+ cctlp->io_base=ioremap_nocache(cctlp->pcidev->base_address[0], 2*1024);
+ cctlp->lbi_base=ioremap_nocache(cctlp->pcidev->base_address[1], 64*1024);
+ /* enable bus mastering */
+ pci_set_master(cctlp->pcidev);
+ /* tweak latency */
+ pcibios_read_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, &pci_latency);
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: chip_open(): Old PCI latency timer: %u.\n", (unsigned int) pci_latency);
+#endif
+ pcibios_write_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, 255);
+ /* request IRQ */
+ if (request_irq(cctlp->pcidev->irq, pciscc_isr, SA_SHIRQ, "pciscc", (void *) cctlp)) {
+ printk(KERN_ERR "PCISCC: chip_open(): Could not get IRQ #%u.\n", cctlp->pcidev->irq);
+ pciscc_chip_close(cctlp);
+ return -EAGAIN;
+ }
+ cctlp->irq=cctlp->pcidev->irq;
+ /* allocate and initialize peripheral queue */
+ if (!(cctlp->iq_per = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating peripheral interrupt queue.\n");
+ return -ENOMEM;
+ }
+ memset(cctlp->iq_per, 0, cctlp->cfg.iqlen*4);
+ cctlp->iq_per_next = cctlp->iq_per;
+ /* configuration interrupt queue */
+ if (!(cctlp->iq_cfg = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating configuration interrupt queue.\n");
+ return -ENOMEM;
+ }
+ memset(cctlp->iq_cfg, 0, cctlp->cfg.iqlen*4);
+ cctlp->iq_cfg_next = cctlp->iq_cfg;
+ /* global hardware initialization */
+ cli();
+ writel((cctlp->cfg.endianswap ? ENDIAN : 0)
+ | (cctlp->cfg.prichan != -1 ? (SPRI | (cctlp->cfg.prichan * CHN)) : 0)
+ | (4 * PERCFG)
+ | (3 * LCD)
+ | CMODE
+ | (cctlp->cfg.oscpwr ? 0 : OSCPD), cctlp->io_base+GMODE);
+ writel(virt_to_bus(cctlp->iq_per), cctlp->io_base+IQPBAR);
+ writel(virt_to_bus(cctlp->iq_cfg), cctlp->io_base+IQCFGBAR);
+ writel((((cctlp->cfg.iqlen/32)-1)*IQCFGLEN) | (((cctlp->cfg.iqlen/32)-1)*IQPLEN), cctlp->io_base+IQLENR2);
+ writel(((cctlp->device[0]->cfg.mfifo_tx_p/4)*TFSIZE0)
+ | ((cctlp->device[1]->cfg.mfifo_tx_p/4)*TFSIZE1)
+ | ((cctlp->device[2]->cfg.mfifo_tx_p/4)*TFSIZE2)
+ | ((cctlp->device[3]->cfg.mfifo_tx_p/4)*TFSIZE3), cctlp->io_base+FIFOCR1);
+ writel(((cctlp->device[0]->cfg.mfifo_tx_r/4)*TFRTHRES0)
+ | ((cctlp->device[1]->cfg.mfifo_tx_r/4)*TFRTHRES1)
+ | ((cctlp->device[2]->cfg.mfifo_tx_r/4)*TFRTHRES2)
+ | ((cctlp->device[3]->cfg.mfifo_tx_r/4)*TFRTHRES3)
+ | M4_0 | M4_1 | M4_2 | M4_3, cctlp->io_base+FIFOCR2);
+ writel(cctlp->cfg.mfifo_rx_t, cctlp->io_base+FIFOCR3);
+ writel(((cctlp->device[0]->cfg.mfifo_tx_f)*TFFTHRES0)
+ | ((cctlp->device[1]->cfg.mfifo_tx_f)*TFFTHRES1)
+ | ((cctlp->device[2]->cfg.mfifo_tx_f)*TFFTHRES2)
+ | ((cctlp->device[3]->cfg.mfifo_tx_f)*TFFTHRES3), cctlp->io_base+FIFOCR4);
+ /* mask out all DMAC interrupts */
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG);
+ /* all SCC cores in reset state */
+ writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0);
+ /* mask out all SCC interrupts */
+ writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR);
+ /* peripheral configuration */
+ writel((BTYP*3), cctlp->io_base+LCONF);
+ writel(0x00000000, cctlp->io_base+SSCCON);
+ writel(0x00000000, cctlp->io_base+SSCIM);
+ writel(0x000000ff, cctlp->io_base+GPDIR);
+ writel(0x00000000, cctlp->io_base+GPDATA);
+ writel(0x00000000, cctlp->io_base+GPIM);
+ sti();
+ /* initialize configuration and peripheral IQs */
+ start_time = jiffies;
+ cctlp->mailbox = MAILBOX_NONE;
+ writel(CFGIQCFG | CFGIQP | AR, cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: chip_open(): Success on IQ config request.\n");
+#endif
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: chip_open(): Timeout on IQ config request. Sync HDDs and hardware-reset NOW!\n");
+ pciscc_chip_close(cctlp);
+ return -EIO;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: chip_open(): Failure on IQ config request. Sync HDDs and hardware-reset NOW!\n");
+ pciscc_chip_close(cctlp);
+ return -EIO;
+ }
+ cctlp->initialized=1;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * close chip, called when last device (channel) of a chip was closed.
+ * don't mess up.
+ */
+static int pciscc_chip_close(struct chipctl_t *cctlp)
+{
+ if (cctlp->usecnt) {
+ printk(KERN_ERR "PCISCC: chip_close() called while channels active.\n");
+ return -EBUSY;
+ }
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: chip_close().\n");
+#endif
+ /* global configuration to reset state */
+ cli();
+ writel((cctlp->cfg.endianswap ? ENDIAN : 0)
+ | (4 * PERCFG)
+ | (3 * LCD)
+ | CMODE
+ | OSCPD, cctlp->io_base+GMODE);
+ /* mask all DMAC interrupts */
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG);
+ writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG);
+ /* SCC cores to reset state */
+ writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0);
+ writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0);
+ /* mask all SCC interrupts */
+ writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR);
+ writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR);
+ sti();
+ /* free IQs, free IRQ, unmap control address space */
+ if (cctlp->iq_per) {
+ kfree(cctlp->iq_per);
+ cctlp->iq_per=0;
+ cctlp->iq_per_next=0;
+ }
+ if (cctlp->iq_cfg) {
+ kfree(cctlp->iq_cfg);
+ cctlp->iq_cfg=0;
+ cctlp->iq_cfg_next=0;
+ }
+ if (cctlp->irq) {
+ free_irq(cctlp->irq, (void *) cctlp);
+ cctlp->irq=0;
+ }
+ if (cctlp->io_base) {
+ iounmap(cctlp->io_base);
+ cctlp->io_base=0;
+ }
+ if (cctlp->lbi_base) {
+ iounmap(cctlp->lbi_base);
+ cctlp->lbi_base=0;
+ }
+ cctlp->initialized=0;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * open one channel, chip must have been initialized by chip_open() before.
+ * the sequence of actions done here was carefully chosen, don't mess with
+ * it unless you know exactly what you are doing...
+ */
+static int pciscc_channel_open(struct devctl_t *dctlp)
+{
+ struct chipctl_t *cctlp = dctlp->chip;
+ struct net_device *dev = &dctlp->dev;
+ int channel = dctlp->channel;
+ struct rx_desc_t *rdp, *last_rdp;
+ struct tx_desc_t *tdp, *last_tdp;
+ unsigned long l;
+ unsigned long start_time;
+ volatile unsigned long gcc_optimizer_safe;
+ int i;
+ unsigned char *data;
+
+ if (dctlp->dev.start) return 0;
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open().\n");
+#endif
+ /* allocate and initialize RX and TX IQs */
+ if (!(dctlp->iq_rx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx interrupt queue.\n");
+ return -ENOMEM;
+ }
+ memset(dctlp->iq_rx, 0, cctlp->cfg.iqlen*4);
+ dctlp->iq_rx_next = dctlp->iq_rx;
+ if (!(dctlp->iq_tx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx interrupt queue.\n");
+ return -ENOMEM;
+ }
+ memset(dctlp->iq_tx, 0, cctlp->cfg.iqlen*4);
+ dctlp->iq_tx_next = dctlp->iq_tx;
+ cli();
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR1); /* stop SCC */
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR2);
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR0);
+ writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
+ /* set IQ lengths and base addresses */
+ l = readl(cctlp->io_base+IQLENR1);
+ switch (channel) {
+ case 0: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
+ writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC0RXBAR);
+ writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC0TXBAR);
+ l &= 0x0fff0fff;
+ l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC0RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC0TXLEN);
+ break;
+ case 1: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
+ writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC1RXBAR);
+ writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC1TXBAR);
+ l &= 0xf0fff0ff;
+ l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC1RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC1TXLEN);
+ break;
+ case 2: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
+ writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC2RXBAR);
+ writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC2TXBAR);
+ l &= 0xff0fff0f;
+ l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC2RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC2TXLEN);
+ break;
+ case 3: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
+ writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC3RXBAR);
+ writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC3TXBAR);
+ l &= 0xfff0fff0;
+ l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC3RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC3TXLEN);
+ break;
+ }
+ writel(l, cctlp->io_base+IQLENR1);
+ sti();
+ start_time = jiffies;
+ cctlp->mailbox = MAILBOX_NONE;
+ writel(AR /* Action Request */
+ | (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0)
+ | (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0)
+ | (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0)
+ | (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open(): Success on IQSCC config request.\n");
+#endif
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
+ pciscc_channel_close(dctlp);
+ return -EIO;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: channel_open(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
+ pciscc_channel_close(dctlp);
+ return -EIO;
+ }
+ /* initialize channel's SCC core */
+ cli();
+ l = PU
+ | ((dctlp->cfg.coding == CFG_CHCODE_NRZ) ? 0*SC : 0)
+ | ((dctlp->cfg.coding == CFG_CHCODE_NRZI) ? 2*SC : 0)
+ | ((dctlp->cfg.coding == CFG_CHCODE_FM0) ? 4*SC : 0)
+ | ((dctlp->cfg.coding == CFG_CHCODE_FM1) ? 5*SC : 0)
+ | ((dctlp->cfg.coding == CFG_CHCODE_MANCH) ? 6*SC : 0)
+ | VIS
+ | ((dctlp->cfg.dpll & CFG_DPLL_PS) ? 0 : PSD)
+ | ((dctlp->cfg.clkout & CFG_TXTXCLK) ? TOE : 0)
+ | ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? SSEL : 0)
+ | ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? SSEL : 0)
+ | ((dctlp->cfg.clockmode == CFG_CM_HS) ? HS : 0)
+ | ((dctlp->cfg.clockmode == CFG_CM_DF9IC) ? 0*CM : 0) /* clockmode 0a */
+ | ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? 0*CM : 0) /* clockmode 0b */
+ | ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? 6*CM : 0) /* clockmode 6b */
+ | ((dctlp->cfg.clockmode == CFG_CM_HS) ? 4*CM : 0); /* clockmode 4 */
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR0);
+ l = (dctlp->cfg.datainv ? DIV : 0)
+ | ((dctlp->cfg.txddrive & CFG_TXDDRIVE_TP) ? ODS : 0)
+ | (dctlp->cfg.cdinv ? 0 : ICD)
+ | ((dctlp->cfg.clkout & CFG_TXRTS) ? TCLKO : 0)
+ | ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) ? 0 : FCTS)
+ | MDS1
+ | (dctlp->cfg.testloop ? TLP : 0)
+ | (dctlp->cfg.sharedflg ? SFLAG : 0)
+ | ((dctlp->cfg.crcmode & CFG_CRCMODE_RESET_0000) ? CRL : 0)
+ | ((dctlp->cfg.crcmode & CFG_CRCMODE_CRC32) ? C32 : 0);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ l = RAC
+ | ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCD) ? DRCRC : 0)
+ | ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCRCFWD) ? RCRC : 0)
+ | ((dctlp->cfg.crcmode & CFG_CRCMODE_TXNOCRC) ? XCRC : 0)
+ /* 1 and 2 dwords somehow don't work */
+ | ((dctlp->cfg.cfifo_rx_t == 1) ? 1*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 2) ? 1*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 4) ? 1*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 16) ? 2*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 24) ? 3*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 32) ? 4*RFTH : 0)
+ | ((dctlp->cfg.cfifo_rx_t == 60) ? 5*RFTH : 0)
+ | (dctlp->cfg.preamble*PRE)
+ | (dctlp->cfg.preamb_rpt ? EPT : 0)
+ | ((dctlp->cfg.preamb_rpt == 2) ? PRE0 : 0)
+ | ((dctlp->cfg.preamb_rpt == 4) ? PRE1 : 0)
+ | ((dctlp->cfg.preamb_rpt == 8) ? PRE0|PRE1 : 0)
+ | ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEFILL) ? 0 : ITF)
+ | ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEINS) ? OIN : 0);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR2);
+ writel((dctlp->cfg.brate_m*BRM) | (dctlp->cfg.brate_n*BRN), cctlp->io_base+SCCBASE[channel]+BRR);
+ writel(RCE | (dctlp->dev.mtu*RL), cctlp->io_base+SCCBASE[channel]+RLCR);
+ /*
+ * all sent | tx device underrun | timer int | tx message repeat |
+ * tx pool ready | rx device overflow | receive FIFO overflow |
+ * frame length exceeded => interrupt mask register
+ */
+ writel(~(ALLS | XDU | TIN | XMR | XPR | RDO | RFO | FLEX), cctlp->io_base+SCCBASE[channel]+IMR);
+ sti();
+ /* wait until command_executing (CEC) is clear */
+ start_time=jiffies;
+ do {
+ l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
+ } while (gcc_optimizer_safe);
+ if (l & CEC) {
+ /* not ready, but we will execute reset anyway */
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for reset.\n");
+ }
+ /* execute channel's SCC core RX and TX reset */
+ writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
+ start_time = jiffies;
+ dctlp->tx_mailbox = 0xffffffff;
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */
+ case 0x02001000: /* SCC XPR interrupt received */
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open(): Success on SCC reset.\n");
+#endif
+ break;
+ case 0xffffffff:
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC reset. Clocking problem?\n");
+ break;
+ default:
+ printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
+ break;
+ }
+ if (!(dctlp->tx_bitrate=pciscc_probe_txrate(dctlp))) dctlp->tx_bitrate=9600;
+ /*
+ * Prepare circular RX and TX descriptor queues ("FIFO" rings)
+ * Attention:
+ * This beast gets _very_ angry if you try to hand it a
+ * descriptor with a data length of 0. In fact it crashes
+ * the system by asserting /SERR or something.
+ */
+ cli();
+ rdp = last_rdp = NULL;
+ for (i=0; i<cctlp->cfg.rxbufcnt; i++, last_rdp=rdp) {
+ if (!(rdp=kmalloc(sizeof(struct rx_desc_t), GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx descriptor chain.\n");
+ sti();
+ pciscc_channel_close(dctlp);
+ return -ENOMEM;
+ }
+ memset(rdp, 0, sizeof(struct rx_desc_t));
+ if (i==0) {
+ dctlp->dq_rx=rdp; /* queue (ring) "head" */
+ } else {
+ rdp->prev=last_rdp;
+ last_rdp->next=rdp;
+ last_rdp->nextptr=(void *) virt_to_bus(rdp);
+ }
+ if (!(rdp->skb=alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating socket buffers.\n");
+ sti();
+ pciscc_channel_close(dctlp);
+ return -ENOMEM;
+ }
+ skb_reserve(rdp->skb, SKB_HEADROOM);
+ rdp->dataptr=(void *) virt_to_bus(data=skb_put(rdp->skb, dctlp->dev.mtu+10)); /* we will skb_trim() it after */
+ rdp->flags=(dctlp->dev.mtu*NO); /* reception when we know frame length */
+ }
+ rdp->next=dctlp->dq_rx; /* close ring structure */
+ rdp->nextptr=(void *) virt_to_bus(dctlp->dq_rx);
+ dctlp->dq_rx->prev=rdp;
+ dctlp->dq_rx_next=dctlp->dq_rx; /* first descriptor to be processed = "first" descriptor in chain */
+ /* TX queue */
+ tdp = last_tdp = NULL;
+ for (i=0; i<cctlp->cfg.txbufcnt; i++, last_tdp=tdp) {
+ if (!(tdp=kmalloc(sizeof(struct tx_desc_t), GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx descriptor chain.\n");
+ sti();
+ pciscc_channel_close(dctlp);
+ return -ENOMEM;
+ }
+ memset(tdp, 0, sizeof(struct tx_desc_t));
+ if (i==0) {
+ dctlp->dq_tx=tdp;
+ } else {
+ tdp->prev=last_tdp;
+ last_tdp->next=tdp;
+ last_tdp->nextptr=(void *) virt_to_bus(tdp);
+ }
+ tdp->skb=NULL;
+ tdp->dataptr=(void *) virt_to_bus(dummybuf);
+ tdp->flags=(8*NO) | FE;
+ }
+ tdp->next=dctlp->dq_tx; /* close ring structure */
+ tdp->nextptr=(void *) virt_to_bus(dctlp->dq_tx);
+ dctlp->dq_tx->prev=tdp;
+ dctlp->dq_tx_last=dctlp->dq_tx; /* last descriptor to be transmitted */
+ dctlp->dq_tx_cleanup=dctlp->dq_tx; /* first descriptor to be cleaned up after transmission */
+ flush_cache_all();
+ /* initialize DMAC channel's RX */
+ switch (channel) {
+ case 0: writel(IDR, cctlp->io_base+CH0CFG);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0BRDA);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0FRDA);
+ writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH0LRDA);
+ break;
+ case 1: writel(IDR, cctlp->io_base+CH1CFG);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1BRDA);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1FRDA);
+ writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH1LRDA);
+ break;
+ case 2: writel(IDR, cctlp->io_base+CH2CFG);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2BRDA);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2FRDA);
+ writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH2LRDA);
+ break;
+ case 3: writel(IDR, cctlp->io_base+CH3CFG);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3BRDA);
+ writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3FRDA);
+ writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH3LRDA);
+ break;
+ }
+ sti();
+ start_time=jiffies;
+ cctlp->mailbox=MAILBOX_NONE;
+ writel(AR, cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe);
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-RX config request.\n");
+#endif
+ dctlp->dmac_rx=DMAC_RX_INIT;
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ }
+ /* mask all DMAC interrupts (needed) */
+ switch (channel) {
+ case 0: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
+ break;
+ case 1: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
+ break;
+ case 2: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
+ break;
+ case 3: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
+ break;
+ }
+ /* SCC core TX reset (again) */
+ start_time=jiffies;
+ do {
+ l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
+ } while (gcc_optimizer_safe);
+ if (l & CEC) {
+ /* not ready, but we will execute reset anyway */
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for TX-reset.\n");
+ }
+ writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
+ start_time = jiffies;
+ dctlp->tx_mailbox = 0xffffffff;
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */
+ case 0x02001000: /* SCC XPR interrupt received */
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open(): Success on SCC TX-reset.\n");
+#endif
+ break;
+ case 0xffffffff:
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC TX-reset. Clocking problem?\n");
+ break;
+ default:
+ printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC TX-reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
+ break;
+ }
+ /*
+ * initialize DMAC's TX channel, FI must stay masked all the time
+ * even during operation, see device errata 03/99
+ */
+ switch (channel) {
+ case 0: writel(IDT | MTFI, cctlp->io_base+CH0CFG);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0BTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0FTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(IDT | MTFI, cctlp->io_base+CH1CFG);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1BTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1FTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(IDT | MTFI, cctlp->io_base+CH2CFG);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2BTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2FTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(IDT | MTFI, cctlp->io_base+CH3CFG);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3BTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3FTDA);
+ writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ start_time=jiffies;
+ cctlp->mailbox=MAILBOX_NONE;
+ writel(AR, cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe);
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-TX config request.\n");
+#endif
+ dctlp->txstate=TX_IDLE;
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ }
+#ifdef FULLDUP_PTT
+ if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) {
+ l = readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l |= RTS;
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ }
+#endif
+ flush_cache_all();
+#ifdef PCISCC_DEBUG
+ pciscc_dmac_regdump(cctlp);
+ pciscc_queuedump(dctlp);
+#endif
+ dctlp->chip->usecnt++;
+ dctlp->dev.start=1;
+ /* clear statistics */
+ mdelay(10);
+ memset(&dctlp->stats, 0, sizeof(struct net_device_stats));
+ /* some housekeeping */
+ pciscc_update_values(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* close one channel - don't mess with it either */
+static void pciscc_channel_close(struct devctl_t *dctlp)
+{
+ struct chipctl_t *cctlp = dctlp->chip;
+ int channel = dctlp->channel;
+ struct rx_desc_t *rdp, *last_rdp;
+ struct tx_desc_t *tdp, *last_tdp;
+ unsigned long l;
+ unsigned long start_time;
+ volatile unsigned long gcc_optimizer_safe;
+
+#ifdef PCISCC_DEBUG
+ pciscc_dmac_regdump(cctlp);
+ pciscc_queuedump(dctlp);
+#endif
+ /* at first stop timer */
+ writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
+ /* wait until command_executing (CEC) is clear */
+ start_time=jiffies;
+ do {
+ l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
+ } while (gcc_optimizer_safe);
+ if (l & CEC) {
+ /* not ready, but we will execute reset anyway */
+ printk(KERN_ERR "PCISCC: channel_close(): Timeout waiting for SCC being ready for reset.\n");
+ }
+ /* RX and TX SCC reset */
+ writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
+ start_time = jiffies;
+ dctlp->tx_mailbox = 0xffffffff;
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */
+ case 0x02001000: /* SCC XPR interrupt received */
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_close(): Success on SCC reset.\n");
+#endif
+ break;
+ case 0xffffffff:
+ printk(KERN_ERR "PCISCC: channel_close(): Timeout on SCC reset.\n");
+ break;
+ default:
+ printk(KERN_ERR "PCISCC: channel_close(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
+ break;
+ }
+ /* stop SCC core */
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR2);
+ writel(0, cctlp->io_base+SCCBASE[channel]+CCR0);
+ /*
+ * Give the isr some time to "refill" the rx-dq
+ * we _MUST_ guarantee that the DMAC-RX is _NOT_ in
+ * hold state when issuing the RESET command, otherwise the DMAC
+ * will crash. (DSCC-4 Rev. <= 2.1)
+ * In addition to that we may only issue a RESET if channel is
+ * currently really initialized, otherwise something horrible will
+ * result.
+ */
+ start_time=jiffies;
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<5;
+ } while (gcc_optimizer_safe);
+ /* OK, now we should be ready to put the DMAC into reset state */
+ switch (channel) {
+ case 0: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
+ break;
+ case 1: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
+ break;
+ case 2: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
+ break;
+ case 3: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
+ break;
+ }
+ start_time = jiffies;
+ cctlp->mailbox = MAILBOX_NONE;
+ writel(AR, cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_close(): Success on DMAC reset channel %u.\n", channel);
+#endif
+ dctlp->dmac_rx=DMAC_RX_RESET;
+ dctlp->txstate=TX_RESET;
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: channel_close(): Timeout on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel);
+ break;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: channel_close(): Failure on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel);
+ break;
+ }
+ /* clear IQs */
+ l = readl(cctlp->io_base+IQLENR1);
+ switch (channel) {
+ case 0: l &= 0x0fff0fff;
+ writel(l, cctlp->io_base+IQLENR1);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0RXBAR);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0TXBAR);
+ break;
+ case 1: l &= 0xf0fff0ff;
+ writel(l, cctlp->io_base+IQLENR1);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1RXBAR);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1TXBAR);
+ break;
+ case 2: l &= 0xff0fff0f;
+ writel(l, cctlp->io_base+IQLENR1);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2RXBAR);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2TXBAR);
+ break;
+ case 3: l &= 0xfff0fff0;
+ writel(l, cctlp->io_base+IQLENR1);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3RXBAR);
+ writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3TXBAR);
+ break;
+ }
+ start_time = jiffies;
+ cctlp->mailbox = MAILBOX_NONE;
+ writel(AR
+ | (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0)
+ | (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0)
+ | (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0)
+ | (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR);
+ do {
+ gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
+ } while (gcc_optimizer_safe); /* timeout 20 jiffies */
+ switch (cctlp->mailbox) { /* mailbox was written by isr */
+ case MAILBOX_OK:
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: channel_close(): Success on IQSCC config request.\n");
+#endif
+ break;
+ case MAILBOX_NONE:
+ printk(KERN_ERR "PCISCC: channel_close(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ case MAILBOX_FAILURE:
+ printk(KERN_ERR "PCISCC: channel_close(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
+ break;
+ }
+ if (dctlp->dq_rx) {
+ rdp=dctlp->dq_rx; /* free descriptor chains and buffers */
+ do {
+ if (rdp->skb) {
+ kfree_skb(rdp->skb);
+ rdp->skb=NULL;
+ }
+ last_rdp=rdp;
+ rdp=rdp->next;
+ kfree(last_rdp);
+ } while (rdp!=dctlp->dq_rx);
+ dctlp->dq_rx=NULL;
+ }
+ dctlp->dq_rx_next=NULL;
+ if (dctlp->dq_tx) {
+ tdp=dctlp->dq_tx;
+ do {
+ if (tdp->skb) {
+ kfree_skb(tdp->skb);
+ tdp->skb=NULL;
+ }
+ last_tdp=tdp;
+ tdp=tdp->next;
+ kfree(last_tdp);
+ } while (tdp!=dctlp->dq_tx);
+ dctlp->dq_tx=NULL;
+ }
+ dctlp->dq_tx_cleanup=NULL;
+ dctlp->dq_tx_last=NULL;
+ if (dctlp->iq_rx) { /* free IQs */
+ kfree(dctlp->iq_rx);
+ dctlp->iq_rx=NULL;
+ }
+ if (dctlp->iq_tx) {
+ kfree(dctlp->iq_tx);
+ dctlp->iq_tx=NULL;
+ }
+ dctlp->dev.start=0;
+ dctlp->chip->usecnt--;
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* interrupt handler root */
+static void pciscc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct chipctl_t *cctlp = (struct chipctl_t *) dev_id;
+ struct devctl_t *dctlp;
+ unsigned long flags;
+ unsigned long status;
+ unsigned long l;
+ int channel;
+ unsigned long *iqp;
+ int processed;
+
+ status = readl(cctlp->io_base+GSTAR);
+ writel(status, cctlp->io_base+GSTAR); /* ack' irq */
+ if (!status) return;
+ /* do not disturb... */
+ save_flags(flags);
+ cli();
+ if (status & (IIPGPP | IIPLBI | IIPSSC)) {
+ /* process peripheral queue */
+ processed = 0;
+ for (iqp=cctlp->iq_per_next; *iqp!=0; iqp=((iqp==(cctlp->iq_per+cctlp->cfg.iqlen-1)) ? cctlp->iq_per : iqp+1)) { /* note wrap-arround */
+ /* I just love raping for-statements */
+ printk(KERN_INFO "PCISCC: isr: IQPER vector: 0x%0lx.\n", *iqp);
+ processed++;
+ *iqp=0;
+ }
+ cctlp->iq_per_next=iqp;
+ }
+ if (status & IICFG) {
+ /* process configuration queue */
+ cctlp->mailbox = MAILBOX_NONE;
+ processed = 0;
+ /* I love raping for-statements... */
+ for (iqp=cctlp->iq_cfg_next; *iqp!=0; iqp=((iqp==(cctlp->iq_cfg+cctlp->cfg.iqlen-1)) ? cctlp->iq_cfg : iqp+1)) { /* note wrap-arround */
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: isr: IQCFG vector: 0x%0lx.\n", *iqp);
+#endif
+ if ((*iqp) & ARACK) {
+ cctlp->mailbox = MAILBOX_OK;
+ processed++;
+ }
+ if ((*iqp) & ARF) {
+ cctlp->mailbox = MAILBOX_FAILURE;
+ processed++;
+ }
+ *iqp=0;
+ }
+ cctlp->iq_cfg_next=iqp;
+ if (processed != 1) {
+ printk(KERN_ERR "PCISCC: isr: Something weird going on... IICFG:processed=%u.\n", processed);
+ }
+ }
+ for (channel=0; channel<4; channel++) if (status & (1<<(24+channel))) {
+ /* process TX queue */
+ dctlp=cctlp->device[channel];
+ if (!dctlp->iq_tx || !dctlp->iq_tx_next) continue;
+ processed = 0;
+ for (iqp=dctlp->iq_tx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1)) { /* note wrap-arround */
+ if (*iqp & TIN) {
+ /* timer interrupt */
+ writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
+ /* now, which state are we in? */
+ switch (dctlp->txstate) {
+ case TX_DELAY:
+ /* data transmit */
+ dctlp->txstate=TX_XMIT;
+ switch (channel) {
+ case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ break;
+ case TX_TAIL:
+ /* transmitting tail */
+ dctlp->txstate=TX_IDLE;
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ break;
+ case TX_PROBE:
+ /* tx bitrate test going on */
+ do_gettimeofday(&dctlp->tv);
+ dctlp->txstate=TX_RESET;
+ dctlp->tx_mailbox=1;
+ break;
+ case TX_CAL:
+ /* we are (i.e. were) calibrating */
+ if (dctlp->dq_tx_last != dctlp->dq_tx_cleanup) {
+ /* we have something in the tx queue */
+ dctlp->txstate = TX_XMIT;
+ switch (channel) {
+ case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ } else {
+ dctlp->txstate=TX_IDLE;
+#ifdef FULLDUP_PTT
+ if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) {
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ }
+#else
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+#endif
+ }
+ break;
+ case TX_XMIT:
+ /* watchdog just ran out */
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ dctlp->txstate=TX_IDLE;
+ txto_task.routine=pciscc_bh_txto;
+ txto_task.data=(void *) dctlp;
+ queue_task(&txto_task, &tq_scheduler);
+ break;
+ default:
+ printk(KERN_ERR "PCISCC: isr: Timer interrupt while txstate=%u.\n", dctlp->txstate);
+ dctlp->txstate=TX_IDLE;
+ }
+ }
+ if (*iqp & ALLS) {
+ /* a TX frame was just completed */
+ pciscc_isr_txcleanup(dctlp);
+ if (dctlp->dq_tx_cleanup == dctlp->dq_tx_last) {
+ /* complete TX-queue sent out */
+ if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) {
+ /* just update txstate */
+ dctlp->txstate=TX_IDLE;
+#ifndef FULLDUP_PTT
+ /* release PTT */
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+#endif
+ } else if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) {
+ /* start txtail */
+ dctlp->txstate=TX_TAIL;
+ writel(dctlp->cfg.txtailval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ } else if (dctlp->cfg.txdelmode == CFG_TXDEL_HARD) {
+ /* deassert RTS immediately */
+ dctlp->txstate=TX_IDLE;
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ }
+ }
+ }
+ if (*iqp & XDU) {
+ /*
+ * TX stall - now we are _really_ in trouble.
+ * We must reset the SCC core and re-init DMAC-TX.
+ * This includes delay loops and we are in interrupt
+ * context, with all interrupts disabled...
+ */
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: isr: TX data underrun occured iface=%s.\n", dctlp->name);
+#endif
+ dctlp->stats.tx_fifo_errors++;
+ /* reset TX-DMAC */
+ switch (channel) {
+ case 0: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH0CFG);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
+ case 1: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH1CFG);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
+ case 2: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH2CFG);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
+ case 3: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH3CFG);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
+ }
+ writel(AR, cctlp->io_base+GCMDR);
+ mdelay(10);
+ /* reset SCC core TX */
+ writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
+ mdelay(30);
+ /* re-init TX-DMAC */
+ switch (channel) {
+ case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
+ writel(MTFI | IDT, cctlp->io_base+CH0CFG);
+ case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
+ writel(MTFI | IDT, cctlp->io_base+CH1CFG);
+ case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
+ writel(MTFI | IDT, cctlp->io_base+CH2CFG);
+ case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA);
+ writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
+ writel(MTFI | IDT, cctlp->io_base+CH3CFG);
+ }
+ writel(AR, cctlp->io_base+GCMDR);
+ mdelay(10);
+ /* We can't do anything more since we are still
+ * in the interrupt handler. We can only hope that
+ * the reset succeeded. */
+ }
+ if (*iqp & XMR) {
+ /*
+ * TX message repeat - not critical, since
+ * resolved automagically by abort sequence
+ * and retransmit.
+ */
+#ifdef PCISCC_VDEBUG
+ printk(KERN_INFO "PCISCC: isr: TX message repeat interrupt iface=%s.\n", dctlp->name);
+#endif
+ }
+ dctlp->tx_mailbox=*iqp;
+ *iqp=0;
+ processed++;
+ }
+ dctlp->iq_tx_next=iqp;
+ }
+ for (channel=0; channel<4; channel++) if (status & (1<<(28+channel))) {
+ dctlp=cctlp->device[channel];
+ /* process RX queue */
+ if (!dctlp->iq_rx || !dctlp->iq_rx_next) {
+ printk(KERN_ERR "PCISCC: isr: IQCHAN%uRX interrupt for non-initialized queue!\n", channel);
+ continue;
+ }
+ processed = 0;
+ for (iqp=dctlp->iq_rx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_rx+cctlp->cfg.iqlen-1)) ? dctlp->iq_rx : iqp+1)) { /* note wrap-arround */
+ /* statistics only */
+ if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RDO)) {
+ dctlp->stats.rx_fifo_errors++;
+ }
+ if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RFO)) {
+ dctlp->stats.rx_over_errors++;
+ }
+ if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_FLEX)) {
+ dctlp->stats.rx_length_errors++;
+ }
+ if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_ERR)) {
+ dctlp->stats.rx_errors++;
+ }
+ if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_HI)) {
+ printk(KERN_ERR "PCISCC: isr: Weird... received HI interrupt.\n");
+ }
+ if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_FI)) {
+ }
+ dctlp->rx_mailbox=*iqp;
+ *iqp=0;
+ processed++;
+ }
+ /* in any case check RX descriptor queue for received frames */
+ if (dctlp->dev.start) pciscc_isr_receiver(dctlp);
+ dctlp->iq_rx_next=iqp;
+ }
+ restore_flags(flags);
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* called by interrupt handler root when RX interrupt occurred */
+static __inline__ void pciscc_isr_receiver(struct devctl_t *dctlp)
+{
+ struct chipctl_t *cctlp = dctlp->chip;
+ int channel = dctlp->channel;
+ struct rx_desc_t *rdp;
+ long status;
+ unsigned char rdsb; /* receive data status byte, generated by DMAC at buffer end */
+ int bno;
+ int valid;
+ struct sk_buff *new_skb;
+ int processed;
+
+#ifdef PCISCC_DEBUG
+ if (!dctlp->dev.start) {
+ printk(KERN_INFO "PCISCC: isr_receiver: frame received while !dev->start.\n");
+ }
+#endif
+ for (rdp=dctlp->dq_rx_next, processed=0; (rdp->result & C); rdp=rdp->next, processed++) {
+#ifdef PCISCC_DEBUG
+ if ((rdp->nextptr != (void *) virt_to_bus(rdp->next)) || (rdp->dataptr != (void *) virt_to_bus(rdp->skb->data))) {
+ panic("PCISCC: isr_receiver(): mm fucked with our buffers");
+ }
+#endif
+ status = rdp->result;
+ bno = (status >> 16) & 0x1fff;
+ valid = 1; /* we assume frame valid */
+ if ((status & RA) || (bno <= 0) || (bno > dctlp->dev.mtu) || !(status & FE) || (rdp->feptr != (void *) virt_to_bus(rdp))) {
+ /* aborted or invalid length */
+ valid = 0;
+ } else {
+ rdsb = rdp->skb->data[bno-1];
+ if (!(rdsb & SB_VFR)) { /* incorrect bit length */
+ valid = 0;
+ dctlp->stats.rx_frame_errors++;
+ }
+ if (rdsb & SB_RDO) { /* data overflow */
+ valid = 0; /* areadly counted */
+ }
+ if (!(rdsb & SB_CRC) && !(dctlp->cfg.crcmode & CFG_CRCMODE_RXCD)) {
+ /* CRC error */
+ valid = 0;
+ dctlp->stats.rx_crc_errors++;
+ }
+ if (rdsb & SB_RAB) { /* receive message aborted */
+ valid = 0;
+ }
+ }
+ /* OK, this is a little bit tricky. We have to make sure
+ * that every descriptor has a buffer assigned. Thus we
+ * can only release a buffer to the link layer if we get
+ * a new one in turn from mm before. */
+ if (valid) {
+ if ((new_skb = alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_ATOMIC))) {
+ skb_reserve(new_skb, SKB_HEADROOM);
+ skb_trim(rdp->skb, bno-1);
+ pciscc_rx_skb(rdp->skb, dctlp);
+ rdp->skb = new_skb;
+ rdp->dataptr=(void *) virt_to_bus(skb_put(rdp->skb, dctlp->dev.mtu+10));
+ } else {
+#ifdef PCISCC_DEBUG
+ printk(KERN_INFO "PCISCC: isr_receiver: Out of memory allocating new skb!\n");
+#endif
+ }
+ }
+ rdp->flags=dctlp->dev.mtu*NO; /* prepare descriptor for next time */
+ rdp->result=0;
+ rdp->feptr=0;
+ flush_cache_all();
+ }
+#ifdef PCISCC_VDEBUG
+ printk(KERN_INFO "PCISCC: isr_receiver: Processed %u frames at once.\n", processed);
+#endif
+ dctlp->dq_rx_next = rdp;
+ /*
+ * tell DMAC last available descriptor - keep up one
+ * descriptor space for security (paranoia) (...->prev->prev)
+ */
+ switch (channel) {
+ case 0: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH0LRDA);
+ break;
+ case 1: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH1LRDA);
+ break;
+ case 2: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH2LRDA);
+ break;
+ case 3: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH3LRDA);
+ break;
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* called by IRQ handler root when a TX descriptor was completed */
+static __inline__ void pciscc_isr_txcleanup(struct devctl_t *dctlp)
+{
+ struct tx_desc_t *tdp;
+ int processed;
+
+ processed=0;
+ tdp=dctlp->dq_tx_cleanup;
+ while ((tdp != dctlp->dq_tx_last) && (tdp->result & C)) {
+ /* clean up all (C)omplete descriptors */
+ if (tdp->skb) {
+#ifdef PCISCC_DEBUG
+ if ((tdp->nextptr != (void *) virt_to_bus(tdp->next)) || (tdp->dataptr != (void *) virt_to_bus(tdp->skb->data))) {
+ /*
+ * paranoia check -
+ * this should _never_ever_occur_ .
+ * if it does, the memory subsystem moved around
+ * our buffers in address space, and it's the
+ * last you will see.
+ */
+ printk(KERN_ERR "PCISCC: isr_txcleanup(): mm fucked with our buffers");
+ }
+#endif
+ dctlp->stats.tx_packets++;
+ dctlp->stats.tx_bytes += tdp->skb->len;
+ kfree_skb(tdp->skb);
+ tdp->skb = NULL;
+ }
+ tdp->flags = (FE | (NO*8)); /* dummy */
+ tdp->dataptr = (void *) virt_to_bus(dummybuf); /* paranoia */
+ tdp->result = 0;
+ tdp = tdp->next;
+ processed++;
+#ifdef PCISCC_DEBUG
+ if (processed>100) {
+ printk(KERN_ERR "PCISCC: trouble in isr_txcleanup or please reduce bit rate by 20 dB.\n");
+ dctlp->dev.start=0;
+ break;
+ }
+#endif
+ }
+ dctlp->dq_tx_cleanup = tdp;
+ flush_cache_all();
+#ifdef PCISCC_VDEBUG
+ printk(KERN_INFO "PCISCC: isr_txcleanup: Processed %u frames.\n", processed);
+#endif
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* called by TIN ISR as bh when TX timeout has occured (watchdog) */
+static void pciscc_bh_txto(void *arg)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) arg;
+
+ printk(KERN_ERR "PCISCC: Taking interface %s down due to TX hang. Clocking problem?\n", dctlp->name);
+ dev_close(&dctlp->dev);
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* *REALLY* ugly work-around for timer race */
+static __inline__ void pciscc_clear_timer(struct devctl_t *dctlp)
+{
+ struct chipctl_t *cctlp = dctlp->chip;
+ unsigned long *iqp;
+
+ /* walk through TX queue eliminating TINs FIXME */
+ if (!dctlp->iq_tx || !dctlp->iq_tx_next) return;
+ for (iqp=dctlp->iq_tx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1)) { /* note wrap-arround */
+ if (*iqp & TIN) *iqp = SCCIV_IGNORE;
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * probe TX bitrate of a channel, called from device_open()
+ * idea behind it:
+ * load the channels timer with a known value and measure how long it
+ * takes to reach zero, using the system timer
+ */
+static long pciscc_probe_txrate(struct devctl_t *dctlp)
+{
+ struct chipctl_t *cctlp = dctlp->chip;
+ int channel = dctlp->channel;
+ struct timeval tv_start;
+ volatile unsigned long gcc_optimizer_safe;
+ unsigned long start_time;
+ long delta_us;
+ unsigned long long tx_bitrate;
+ unsigned long l;
+
+ dctlp->txstate = TX_PROBE;
+ dctlp->tx_mailbox = 0;
+ start_time = jiffies;
+ /* assert RTS line */
+ l=readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1);
+ l |= RTS;
+ writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1);
+ writel((probebit*TVALUE), cctlp->io_base+SCCBASE[dctlp->channel]+TIMR);
+ do_gettimeofday(&tv_start);
+ writel(STI, cctlp->io_base+SCCBASE[dctlp->channel]+CMDR);
+ do {
+ gcc_optimizer_safe = !dctlp->tx_mailbox && ((jiffies-start_time)<1000);
+ } while (gcc_optimizer_safe);
+ dctlp->txstate = TX_RESET;
+ if (!dctlp->tx_mailbox) {
+ printk(KERN_ERR "PCISCC: probe_txrate(): Timeout probing %s-TxClk. Clocking problem?\n", dctlp->dev.name);
+ return 0;
+
+ } else {
+ delta_us = (dctlp->tv.tv_sec - tv_start.tv_sec)*1000000+(dctlp->tv.tv_usec - tv_start.tv_usec);
+ }
+ tx_bitrate = 10000*probebit/(delta_us/100);
+ printk(KERN_INFO "PCISCC: probe_txrate(): tx_bitrate=%ld.\n", (long) tx_bitrate);
+ if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) {
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ } else {
+#ifndef FULLDUP_PTT
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l=l & (~RTS);
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+#endif
+ }
+ return tx_bitrate;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* DDI support: immediately assert RTS, downcall from MAC layer */
+static void pciscc_setptt(struct net_device *dev)
+{
+#ifdef AX25_ARBITER_NOT_BUGGY
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = dctlp->chip;
+ unsigned long l;
+
+ l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1);
+ l |= RTS;
+ writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1);
+#endif
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* report DCD state to DDI layer */
+static unsigned int pciscc_getdcd(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = dctlp->chip;
+ unsigned long l;
+ unsigned int dcd;
+
+ l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR);
+ dcd = dctlp->cfg.cdinv ? !!(l & CD) : !(l & CD);
+ return dcd;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* return CTS state to DDI layer */
+static unsigned int pciscc_getcts(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = dctlp->chip;
+ unsigned int cts;
+
+ cts = !!(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS);
+ return cts;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* report PTT state to DDI layer */
+static unsigned int pciscc_getptt(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = dctlp->chip;
+ unsigned int ptt;
+
+ ptt = !(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS);
+ ptt = (dctlp->cfg.cdinv ? ptt : !ptt);
+ return ptt;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/*
+ * this function is called by DDI layer whenever a media parameter change
+ * was suggested by either proc/sysctl or MAC/LAP internal cause
+ */
+static void pciscc_parameter_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ int m,n;
+ int br, bits;
+
+ printk(KERN_ERR "PCISCC: pciscc_parameter_notify(%s, %u).\n", dev->name, valueno);
+ switch (valueno) {
+ case AX25_VALUES_MEDIA_DUPLEX:
+ if (dev->start) goto reject;
+ dctlp->cfg.duplex = new;
+ break;
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ if (dev->start) goto reject;
+ pciscc_rate2brg(new, &m, &n);
+ dctlp->cfg.brate_m = m;
+ dctlp->cfg.brate_n = n;
+ break;
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ if (br == 0) goto reject;
+ bits = (br*new)/1000;
+ if (bits == 0) bits=16; /* two flags */
+ if (valueno == AX25_VALUES_MEDIA_TXDELAY) {
+ dctlp->cfg.txdelval = bits;
+ } else {
+ dctlp->cfg.txtailval = bits;
+ }
+ break;
+ case AX25_VALUES_MEDIA_SLOTTIME:
+ case AX25_VALUES_MEDIA_PPERSISTENCE:
+ case AX25_VALUES_MEDIA_AUTO_ADJUST:
+ default:
+ /*
+ * We do not care about changes of
+ * those - they are somebody else's problem (now).
+ */
+ return;
+ }
+
+reject:
+ /*
+ * (Re)store values from our (changed)
+ * configuration information
+ */
+ pciscc_update_values(dev);
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* convert BRG N and M into bitrate in bps */
+static int pciscc_brg2rate(int m, int n)
+{
+ int br = xtal / ((n+1) * (1<<m));
+
+ if (br == 0) br=1;
+ return br;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* find best BRG N and M match for given bitrate */
+static void pciscc_rate2brg(int rate, int *m, int *n)
+{
+ int ratio = xtal/rate;
+ int brg_best_n = 0;
+ int brg_best_m = 0;
+ int brg_tmp_n = 0;
+ int brg_tmp = 0;
+ int brg_best = 0;
+ int i;
+
+ *m = *n = 0;
+ if (ratio > 2097152) return;
+ for (i=0; i<16; i++) {
+ brg_tmp_n = (ratio/(1<<i))-1;
+ if (brg_tmp_n > 63 || brg_tmp_n < 0) continue;
+ brg_tmp = (brg_tmp_n+1)*(1<<i);
+ if (abs(brg_best-ratio) < abs(brg_tmp-ratio)) continue;
+ brg_best = brg_tmp;
+ brg_best_n = brg_tmp_n;
+ brg_best_m = i;
+ }
+ *m = brg_best_m;
+ *n = brg_best_n;
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void pciscc_update_values(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ int rx_br, tx_br;
+ int clk_ext;
+
+ AX25_PTR(dev)->hw.fast = 0;
+ tx_br = rx_br = 0;
+ clk_ext = (dctlp->cfg.clockmode == CFG_CM_G3RUH
+ || dctlp->cfg.clockmode == CFG_CM_TCM3105);
+ if (clk_ext) {
+ tx_br = rx_br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n);
+ }
+ if (dev->start) {
+ tx_br = dctlp->tx_bitrate;
+ if (clk_ext) rx_br = tx_br; /* assumption; we have no way to find out */
+ }
+ if (tx_br > 0) {
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, dctlp->cfg.duplex);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, tx_br);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, rx_br);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, dctlp->cfg.txdelval/tx_br);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, dctlp->cfg.txtailval/tx_br);
+ }
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* netdevice UP -> DOWN routine */
+static int pciscc_dev_close(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+
+ if (dctlp->dev.start) {
+ pciscc_channel_close(dctlp);
+ }
+ dctlp->dev.start=0;
+ pciscc_update_values(dev);
+ if (dctlp->chip->initialized && !dctlp->chip->usecnt) {
+ pciscc_chip_close(dctlp->chip);
+ }
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* netdevice DOWN -> UP routine */
+static int pciscc_dev_open(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ int res;
+
+ if (!dctlp->chip->initialized) {
+ if ((res=pciscc_chip_open(dctlp->chip))) return res;
+ }
+ if (!dctlp->dev.start) {
+ if ((res=pciscc_channel_open(dctlp))) return res;
+ pciscc_update_values(dev);
+ }
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* netdevice change MTU request */
+static int pciscc_change_mtu(struct net_device *dev, int new_mtu)
+{
+ dev->mtu=new_mtu;
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* netdevice get statistics request */
+static struct net_device_stats *pciscc_get_stats(struct net_device *dev)
+{
+ struct devctl_t *dctlp;
+
+ if (!dev || !dev->priv) return NULL; /* paranoia */
+ dctlp = (struct devctl_t *) dev->priv;
+ return &dctlp->stats;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* netdevice register - finish painting netdev structure */
+static int pciscc_dev_init(struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ int br;
+
+ dev->mtu = 1500;
+ dev->hard_start_xmit = pciscc_xmit;
+ dev->open = pciscc_dev_open;
+ dev->stop = pciscc_dev_close;
+ dev->get_stats = pciscc_get_stats;
+ dev->change_mtu = pciscc_change_mtu;
+ dev->do_ioctl = pciscc_dev_ioctl;
+ dev->set_mac_address = pciscc_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->flags = (IFF_BROADCAST | IFF_MULTICAST);
+ AX25_PTR(dev) = &((struct devctl_t *) dev->priv)->ax25dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+ AX25_PTR(dev)->hw.dcd = pciscc_getdcd;
+ AX25_PTR(dev)->hw.ptt = pciscc_getptt;
+ AX25_PTR(dev)->hw.rts = pciscc_setptt;
+ AX25_PTR(dev)->hw.cts = pciscc_getcts;
+ AX25_PTR(dev)->hw.parameter_change_notify = pciscc_parameter_notify;
+ dev_init_buffers(dev);
+ memcpy(&dctlp->cfg, &devcfg_default, sizeof(struct devcfg_t));
+ br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n);
+ pciscc_update_values(dev);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* set device's L2 address */
+static int pciscc_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;
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * IOCTLs:
+ *
+ * SIOCPCISCCGCCFG PCISCC Get Chip ConFiG
+ * SIOCPCISCCSCCFG PCISCC Set Chip ConFiG
+ * SIOCPCISCCGDCFG PCISCC Get Device ConFiG
+ * SIOCPCISCCSDCFG PCISCC Set Device ConFiG
+ * SIOCPCISCCSLED PCISCC Set LEDs
+ * SIOCPCISCCGDSTAT PCISCC Get Device STATus
+ * SIOCPCISCCDCAL PCISCC Device CALibrate
+ * SIOCPCISCCLBI PCISCC DSCC-4 Local Bus Interface transaction
+ */
+
+static int pciscc_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = dctlp->chip;
+ struct devcfg_t dcfg;
+ struct chipcfg_t ccfg;
+ struct lbi_xfer lbi;
+ int channel = dctlp->channel;
+ int i;
+ unsigned long l;
+ unsigned long status;
+ unsigned long time;
+
+ switch (cmd) {
+ case SIOCPCISCCGCCFG:
+ /* return cctlp->cfg structure in user-provided buffer */
+ if (copy_to_user(ifr->ifr_data, &cctlp->cfg, sizeof(struct chipcfg_t))) {
+ return -EFAULT;
+ }
+ return 0;
+ case SIOCPCISCCSCCFG:
+ /* copy user-provided data buffer to cctlp->cfg */
+ if (!suser()) return -EPERM;
+ for (i=0; i<4; i++) {
+ if (cctlp->device[i]->dev.start) return -EBUSY;
+ }
+ if (copy_from_user(&ccfg, ifr->ifr_data, sizeof(struct chipcfg_t))) {
+ return -EFAULT;
+ }
+ if ((ccfg.rxbufcnt < 4) || (ccfg.rxbufcnt > 128)) return -EINVAL;
+ if ((ccfg.txbufcnt < 4) || (ccfg.txbufcnt > 256)) return -EINVAL;
+ if ((ccfg.iqlen < 32) || (ccfg.iqlen > 512) || (ccfg.iqlen % 32 != 0)) return -EINVAL;
+ if ((ccfg.prichan > 3) || (ccfg.prichan < -1)) return -EINVAL;
+ if ((ccfg.mfifo_rx_t > 124) || (ccfg.mfifo_rx_t < 4)) return -EINVAL;
+ memcpy((unsigned char *) &cctlp->cfg, (unsigned char *) &ccfg, sizeof(struct chipcfg_t));
+ return 0;
+ case SIOCPCISCCGDCFG:
+ /* return dctlp->cfg structure in user-provided buffer */
+ if (copy_to_user(ifr->ifr_data, &dctlp->cfg, sizeof(struct devcfg_t))) {
+ return -EFAULT;
+ }
+ return 0;
+ case SIOCPCISCCSDCFG:
+ /* copy user-provided data buffer to dctlp->cfg */
+ if (!suser()) return -EPERM;
+ for (i=0; i<4; i++) {
+ if (cctlp->device[i]->dev.start) return -EBUSY;
+ }
+ if (copy_from_user(&dcfg, ifr->ifr_data, sizeof(struct devcfg_t))) {
+ return -EFAULT;
+ }
+ if ((dcfg.coding < CFG_CHCODE_MIN) || (dcfg.coding > CFG_CHCODE_MAX)) return -EINVAL;
+ if ((dcfg.clockmode < CFG_CM_MIN) || (dcfg.clockmode > CFG_CM_MAX)) return -EINVAL;
+ if ((dcfg.duplex < CFG_DUPLEX_MIN) || (dcfg.duplex > CFG_DUPLEX_MAX)) return -EINVAL;
+ if (dcfg.brate_m > CFG_BRATEM_MAX) return -EINVAL;
+ if (dcfg.brate_n > CFG_BRATEN_MAX) return -EINVAL;
+ if ((dcfg.txddrive < CFG_TXDDRIVE_MIN) || (dcfg.txddrive > CFG_TXDDRIVE_MAX)) return -EINVAL;
+ if ((dcfg.mfifo_tx_p < 4) || (dcfg.mfifo_tx_p > 124) || (dcfg.mfifo_tx_p % 4 != 0)) return -EINVAL;
+ if ((dcfg.mfifo_tx_r < 1) || (dcfg.mfifo_tx_r > dcfg.mfifo_tx_p) || (dcfg.mfifo_tx_r % 4 != 0)) return -EINVAL;
+ if (dcfg.mfifo_tx_f > (dcfg.mfifo_tx_p-1)) return -EINVAL;
+ if ((dcfg.cfifo_rx_t != 1) && (dcfg.cfifo_rx_t != 2) && (dcfg.cfifo_rx_t != 4)
+ && (dcfg.cfifo_rx_t != 16) && (dcfg.cfifo_rx_t != 24) && (dcfg.cfifo_rx_t != 32)
+ && (dcfg.cfifo_rx_t != 60)) return -EINVAL;
+ if ((dcfg.txdelmode < CFG_TXDEL_MIN) || (dcfg.txdelmode > CFG_TXDEL_MAX)) return -EINVAL;
+ if ((dcfg.preamb_rpt!=0) && (dcfg.preamb_rpt!=1) && (dcfg.preamb_rpt!=2) && (dcfg.preamb_rpt!=4) && (dcfg.preamb_rpt!=8)) return -EINVAL;
+ memcpy((unsigned char *) &dctlp->cfg, (unsigned char *) &dcfg, sizeof(struct devcfg_t));
+ pciscc_update_values(dev);
+ return 0;
+ case SIOCPCISCCSLED:
+ /* set channel LEDs */
+ if (!suser()) return -EPERM;
+ l = readl(cctlp->io_base+GPDATA);
+ switch (channel) {
+ case 0: l &= ~((1<<0) | (1<<1));
+ l |= (((unsigned long) ifr->ifr_data & 3) << 0);
+ break;
+ case 1: l &= ~((1<<2) | (1<<3));
+ l |= (((unsigned long) ifr->ifr_data & 3) << 2);
+ break;
+ case 2: l &= ~((1<<4) | (1<<5));
+ l |= (((unsigned long) ifr->ifr_data & 3) << 4);
+ break;
+ case 3: l &= ~((1<<6) | (1<<7));
+ l |= (((unsigned long) ifr->ifr_data & 3) << 6);
+ break;
+ }
+ writel(l, cctlp->io_base+GPDATA);
+ return 0;
+ case SIOCPCISCCGDSTAT:
+ /* get channel status */
+ status = (dctlp->txstate & 0x0f);
+ l = readl(cctlp->io_base+SCCBASE[channel]+STAR);
+ if (l & DPLA) status |= STATUS_DPLA;
+ if (l & RLI) status |= STATUS_RLI;
+ if (dctlp->cfg.cdinv) {
+ if (l & CD) status |= STATUS_CD;
+ } else {
+ if (!(l & CD)) status |= STATUS_CD;
+ }
+ if (l & CTS) status |= STATUS_CTS;
+ if (readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1) & RTS) status |= STATUS_RTS;
+ ifr->ifr_data = (void *) status;
+ return 0;
+ case SIOCPCISCCDCAL:
+ /* calibrate */
+ if (!suser()) return -EPERM;
+ if (!dev->start) return -EAGAIN;
+ if ((dctlp->txstate != TX_IDLE) && (dctlp->txstate != TX_IDLE)) return -EAGAIN;
+ time = (unsigned long) ifr->ifr_data;
+ if (time > 0xffffff) return -EINVAL;
+ writel((time*TVALUE), cctlp->io_base+SCCBASE[channel]+TIMR);
+ if (time == 0) {
+ dctlp->txstate = TX_IDLE;
+ if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) {
+ l = readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l &= ~RTS;
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+ } else {
+ /* duplex */
+#ifndef FULLDUP_PTT
+ l = readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ l &= ~RTS;
+ writel(l, cctlp->io_base+SCCBASE[channel]+CCR1);
+#endif
+ }
+ } else {
+ dctlp->txstate = TX_CAL;
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ }
+ return 0;
+ case SIOCPCISCCLBI:
+ /* local bus transaction */
+ if (!suser()) return -EPERM;
+ if (copy_from_user(&lbi, ifr->ifr_data, sizeof(struct lbi_xfer))) {
+ return -EFAULT;
+ }
+ if (lbi.mode == LBI_WRITE) {
+ writew(lbi.data, cctlp->lbi_base+lbi.addr);
+ } else {
+ lbi.data = readl(cctlp->lbi_base+lbi.addr);
+ if (copy_to_user(ifr->ifr_data, &lbi, sizeof(struct lbi_xfer)))
+ return -EFAULT;
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* transmit frame, downcall from MAC layer */
+static int pciscc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
+ struct chipctl_t *cctlp = (struct chipctl_t *) dctlp->chip;
+ int channel = dctlp->channel;
+ unsigned long flags;
+ struct tx_desc_t *txdp;
+ unsigned long l;
+
+ if (!dev->start) {
+ printk(KERN_ERR "PCISCC: xmit(): Call when iface %s is down\n", dev->name);
+ kfree_skb(skb);
+ return 0;
+ }
+ if (!skb) {
+ printk(KERN_ERR "PCISCC: xmit(): L2 handed us a NULL skb!\n");
+ return 0;
+ }
+ if (!skb->len) {
+ printk(KERN_ERR "PCISCC: xmit(): L2 tried to trick us into sending a skb of len 0!\n");
+ kfree_skb(skb);
+ return 0;
+ }
+ save_flags(flags);
+ cli();
+ txdp=dctlp->dq_tx_last->next;
+ if ((txdp == dctlp->dq_tx_cleanup) || (txdp->next == dctlp->dq_tx_cleanup) || (txdp->result & C) || (txdp->next->result & C)) {
+ /* desriptor chain "full" */
+#ifdef PCISCC_VDEBUG
+ printk(KERN_INFO "PCISCC: xmit(): Dropping frame due to full TX queue interface %s.\n", dev->name);
+#endif
+ dctlp->stats.tx_dropped++;
+ kfree_skb(skb);
+ restore_flags(flags);
+ return 0;
+ }
+ /* prepare TX descriptor */
+ txdp->result=0;
+ txdp->skb=skb;
+ txdp->flags=FE | (BNO*skb->len);
+ txdp->dataptr=(void *) virt_to_bus(skb->data);
+ dctlp->dq_tx_last=txdp;
+ flush_cache_all();
+ if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) {
+ /* in full duplex mode we can start frame transmit at once */
+ dctlp->txstate=TX_XMIT;
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1);
+ switch (channel) {
+ case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ } else if ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) || !dctlp->cfg.txdelval) {
+ /* Hardware TX-delay control using RTS/CTS or zero TX-delay */
+ if (dctlp->txstate == TX_IDLE) {
+ writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ }
+ dctlp->txstate=TX_XMIT;
+ if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) {
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1);
+ }
+ switch (channel) {
+ case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ } else {
+ /* half duplex, software txdelay */
+ switch (dctlp->txstate) {
+ case TX_RESET:
+ /* TX not initialized */
+ printk(KERN_INFO "PCISCC: xmit(): %s: Cannot transmit frame since TX is not inititalized!\n", dev->name);
+ case TX_IDLE:
+ /* TX is idle, key up and start txdelay */
+ dctlp->txstate=TX_DELAY;
+ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1);
+ writel(dctlp->cfg.txdelval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ break;
+ case TX_DELAY:
+ /* tx is already keyed but not yet ready */
+ break;
+ case TX_TAIL:
+ /* tx is currently transmitting closing txtail sequence */
+ writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
+ pciscc_clear_timer(dctlp);
+ writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
+ case TX_XMIT: /* note fall-through */
+ /* tx is already transmitting preamble or data */
+ dctlp->txstate=TX_XMIT;
+ switch (channel) {
+ case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
+ break;
+ case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
+ break;
+ case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
+ break;
+ case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
+ break;
+ }
+ break;
+ case TX_PROBE:
+ case TX_CAL:
+ /* we are busy with diagnostic stuff */
+ break;
+ default:
+ /* should not occur */
+ printk(KERN_ERR "PCISCC: Unhandled txstate in xmit() iface=%s.\n", dev->name);
+ }
+ }
+ /* skb will be kfree()d by isr_txcleanup after transmission */
+ restore_flags(flags);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* called by receiver function - prepare received skb and fire up to L2 */
+static __inline__ void pciscc_rx_skb(struct sk_buff *skb, struct devctl_t *dctlp)
+{
+ if (!skb) {
+ printk(KERN_ERR "PCISCC: rx_skb(): Received NULL skb iface=%s.\n", dctlp->name);
+ return;
+ }
+ dctlp->stats.rx_packets++;
+ dctlp->stats.rx_bytes += skb->len;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->dev = &dctlp->dev;
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+ netif_rx(skb);
+ return;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Initialize pciscc control device */
+#ifdef MODULE
+int pciscc_init(void)
+#else /* !MODULE */
+__initfunc(int pciscc_init(struct net_device *dummy))
+#endif /* !MODULE */
+{
+ int i,j;
+ int devnum;
+ struct pci_dev *pcidev = NULL;
+
+ printk(KERN_ERR "PCISCC: version %s\n", PCISCC_VERSION);
+ if (!(dummybuf = kmalloc(256, GFP_DMA | GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: init: Could not get memory for dummybuf.\n");
+ return -ENOMEM;
+ }
+ chipcnt=0;
+ j=0;
+ while ((pcidev = pci_find_device(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_PEB20534H, pcidev))) {
+ if (!(chipctl[chipcnt]=kmalloc(sizeof(struct chipctl_t), GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: Out of memory allocating chipctl-structure\n");
+#ifdef MODULE
+ cleanup_module();
+#endif
+ return -ENOMEM;
+ }
+ memset(chipctl[chipcnt], 0, sizeof(struct chipctl_t));
+ chipctl[chipcnt]->pcidev=pcidev;
+ memcpy(&chipctl[chipcnt]->cfg, &chipcfg_default, sizeof(struct chipcfg_t));
+ for (i=0;i<4;i++) {
+ devnum = chipcnt*4+i;
+ if (!(devctl[devnum]=kmalloc(sizeof(struct devctl_t), GFP_KERNEL))) {
+ printk(KERN_ERR "PCISCC: Out of memory allocating devctl-structure.\n");
+#ifdef MODULE
+ cleanup_module();
+#endif
+ return -ENOMEM;
+ }
+ memset(devctl[devnum], 0, sizeof(struct devctl_t));
+ do {
+ sprintf(devctl[devnum]->name, "dscc%u", devnum);
+ j++;
+ } while (dev_get(devctl[devnum]->name) && j<60); /* find free device name */
+ if (j>=60) { /* none left */
+ printk(KERN_ERR "PCISCC: Could not find free netdev name.\n");
+#ifdef MODULE
+ cleanup_module();
+#endif
+ return -EEXIST;
+ }
+ devctl[devnum]->dev.priv = (void *) devctl[devnum];
+ devctl[devnum]->dev.name = devctl[devnum]->name;
+ devctl[devnum]->dev.init = pciscc_dev_init;
+ devctl[devnum]->chip = chipctl[chipcnt];
+ devctl[devnum]->channel = i;
+ chipctl[chipcnt]->device[i] = devctl[devnum];
+ register_netdev(&devctl[devnum]->dev);
+ }
+ chipcnt++;
+ }
+ printk(KERN_ERR "PCISCC: %u controller(s) found.\n", chipcnt);
+ return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+#ifndef MODULE
+__initfunc(void pciscc_setup(char *str, int *ints))
+{
+ return;
+}
+#endif
+
+/* ------------------------------------------------------------------------- */
+
+
+/*****************************************************************************
+ * Module stuff. *
+ *****************************************************************************/
+
+#ifdef MODULE
+MODULE_AUTHOR("Jens David, DG1KJD <dg1kjd@afthd.tu-darmstadt.de>");
+MODULE_DESCRIPTION("AX.25 Device Driver for Siemens PEB-20534H (DSCC-4) based SCC cards");
+MODULE_SUPPORTED_DEVICE("pciscc");
+MODULE_PARM(xtal, "i");
+MODULE_PARM(probebit, "i");
+MODULE_PARM(txtimeout, "i");
+
+int init_module(void)
+{
+ return pciscc_init();
+}
+
+void cleanup_module(void)
+{
+ int i;
+ struct chipctl_t *cctlp;
+ struct devctl_t *dctlp;
+
+ for (i=0; i<4*chipcnt; i++) {
+ dctlp=devctl[i];
+ pciscc_dev_close(&dctlp->dev);
+ if (dctlp) {
+ unregister_netdev(&dctlp->dev);
+ kfree(dctlp);
+ devctl[i]=NULL;
+ }
+ }
+ for (i=0; i<chipcnt; i++) {
+ cctlp=chipctl[i];
+ if (cctlp) {
+ if (cctlp->irq) {
+ free_irq(cctlp->irq, (void *) cctlp);
+ cctlp->irq=0;
+ }
+ if (cctlp->io_base) {
+ iounmap(cctlp->io_base);
+ cctlp->io_base=0;
+ }
+ if (cctlp->lbi_base) {
+ iounmap(cctlp->lbi_base);
+ cctlp->lbi_base=0;
+ }
+ kfree(cctlp);
+ chipctl[i]=NULL;
+ }
+ }
+ if (dummybuf) {
+ kfree(dummybuf);
+ }
+ return;
+}
+#endif /* MODULE */
diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c
index d256ef600..4cf69753c 100644
--- a/drivers/net/hamradio/scc.c
+++ b/drivers/net/hamradio/scc.c
@@ -1,9 +1,10 @@
#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
-#define VERSION "3.0"
+#define VERSION "4.0"
+#define BANNER "Z8530 SCC driver version "VERSION".dl1bke by DL1BKE\n"
/*
- * Please use z8530drv-utils-3.0 with this version.
+ * Please use z8530drv-utils-4.0 with this version.
* ------------------
*
* You can find a subset of the documentation in
@@ -103,9 +104,8 @@
flags that aren't... Restarting the DPLL does not help
either, it resynchronizes too slow and the first received
frame gets lost.
- 2000-02-13 Fixed for new network driver interface changes, still
- does TX timeouts itself since it uses its own queue
- scheme.
+ 1999-02-21 Started to implement the new AX.25 device interface
+ 2000-07-18 Ported to 2.4.x
Thanks to all who contributed to this driver with ideas and bug
reports!
@@ -113,7 +113,7 @@
NB -- if you find errors, change something, please let me know
first before you distribute it... And please don't touch
the version number. Just replace my callsign in
- "v3.0.dl1bke" with your own. Just to avoid confusion...
+ "v4.0.dl1bke" with your own. Just to avoid confusion...
If you want to add your modification to the linux distribution
please (!) contact me first.
@@ -131,21 +131,23 @@
Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU
Internet: jreuter@yaina.de
- www : http://yaina.de/jreuter
+ www : http://yaina.de/jreuter/
*/
/* ----------------------------------------------------------------------- */
-#undef SCC_LDELAY /* slow it even a bit more down */
#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */
-#define SCC_MAXCHIPS 4 /* number of max. supported chips */
#define SCC_BUFSIZE 384 /* must not exceed 4096 */
#undef SCC_DEBUG
#define SCC_DEFAULT_CLOCK 4915200
/* default pclock if nothing is specified */
+#define SCC_SIMPLE_MAC /* no rts/cts control by DDI layer */
+#define SCC_WATCHDOG_TIMEOUT 10
+ /* ten seconds */
+
/* ----------------------------------------------------------------------- */
#include <linux/config.h>
@@ -162,6 +164,7 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/spinlock.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
@@ -170,196 +173,199 @@
#include <linux/socket.h>
#include <linux/init.h>
-#include <linux/scc.h>
-#include "z8530.h"
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
#include <net/ax25.h>
+#include <net/ax25dev.h>
+
#include <asm/irq.h>
+
#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
-#include <linux/ctype.h>
-#include <linux/kernel.h>
-#include <linux/proc_fs.h>
+#include <linux/scc.h>
+#include "z8530.h"
-static const char banner[] __initdata = KERN_INFO "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n";
+int scc_init(void);
-static void t_dwait(unsigned long);
-static void t_txdelay(unsigned long);
-static void t_tail(unsigned long);
-static void t_busy(unsigned long);
-static void t_maxkeyup(unsigned long);
-static void t_idle(unsigned long);
static void scc_tx_done(struct scc_channel *);
-static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long);
-static void scc_start_maxkeyup(struct scc_channel *);
-static void scc_start_defer(struct scc_channel *);
-
-static void z8530_init(void);
+static void scc_kick_tx(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(struct scc_channel *), unsigned long);
+static void scc_tail(struct scc_channel *scc);
+#ifndef SCC_SIMPLE_MAC
+static void scc_tx_forced(struct scc_channel *scc);
+static void scc_set_rts(struct scc_channel *scc);
+#endif
static void init_channel(struct scc_channel *scc);
static void scc_key_trx (struct scc_channel *scc, char tx);
static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
static void scc_init_timer(struct scc_channel *scc);
-static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev);
+static unsigned int scc_ddi_report_dcd(struct net_device *);
+static unsigned int scc_ddi_report_ptt(struct net_device *);
+#ifndef SCC_SIMPLE_MAC
+static unsigned int scc_ddi_report_cts(struct net_device *);
+static void scc_ddi_set_rts(struct net_device *);
+#endif
+static void scc_ddi_set_bitrate(struct net_device *, unsigned int);
+static void scc_ddi_param_update(struct net_device *);
+static void scc_ddi_param_notify(struct net_device *, int, int, int);
+
+static int scc_net_setup(struct scc_channel *scc);
static int scc_net_init(struct net_device *dev);
static int scc_net_open(struct net_device *dev);
static int scc_net_close(struct net_device *dev);
static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
static int scc_net_tx(struct sk_buff *skb, struct net_device *dev);
+static void scc_net_timeout(struct net_device *dev);
static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
static int scc_net_set_mac_address(struct net_device *dev, void *addr);
static struct net_device_stats * scc_net_get_stats(struct net_device *dev);
-static unsigned char *SCC_DriverName = "scc";
+static int scc_proc_intvec_nchips(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **);
+static int scc_proc_intvec_modem(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **);
+static int scc_proc_intvec_port(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **);
+static int scc_proc_intvec_chip(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **);
-static struct irqflags { unsigned char used : 1; } Ivec[16];
-
-static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */
+struct scc_ctrl_proc_tables {
+ ctl_table parent[2]; /* /proc/sys/net/dev/ */
+ ctl_table dir[2]; /* /.../dev/z8530drv/ */
+ ctl_table chip[10]; /* /.../dev/z8530drv/chip<n> */
+};
+
+static int scc_irq_used[16];
static struct scc_ctrl {
- io_port chan_A;
- io_port chan_B;
- int irq;
-} SCC_ctrl[SCC_MAXCHIPS+1];
+ struct scc_channel channel_a;
+ struct scc_channel channel_b;
+ struct scc_ctrl_proc_tables proc_tables;
+
+ struct ctl_table_header * proc_table_head;
+} **scc_ctrl;
+
+
+static struct ctl_table_header *scc_proc_table_header;
+static int maxchips = 4;
+static int Nchips = 0;
+static long IO_Delay = 0;
+static spinlock_t IO_Spinlock = SPIN_LOCK_UNLOCKED;
+
+static ctl_table scc_proc_parent_table[];
+static ctl_table scc_proc_nchips_table[];
-static unsigned char Driver_Initialized;
-static int Nchips;
-static io_port Vector_Latch;
/* ******************************************************************** */
/* * Port Access Functions * */
/* ******************************************************************** */
+static inline unsigned char
+Inb(io_port port)
+{
+ int r;
+
+ r = inb(port);
+ if (IO_Delay) udelay(IO_Delay);
+ return r;
+}
+
+static inline void
+Outb(io_port port, unsigned char val)
+{
+ outb(val, port);
+ if (IO_Delay) udelay(IO_Delay);
+}
+
/* These provide interrupt save 2-step access to the Z8530 registers */
-static inline unsigned char InReg(io_port port, unsigned char reg)
+static unsigned char
+InReg(io_port port, unsigned char reg)
{
unsigned long flags;
unsigned char r;
-
- save_flags(flags);
- cli();
-#ifdef SCC_LDELAY
- Outb(port, reg);
- udelay(SCC_LDELAY);
- r=Inb(port);
- udelay(SCC_LDELAY);
-#else
- Outb(port, reg);
- r=Inb(port);
-#endif
- restore_flags(flags);
+
+ spin_lock_irqsave(&IO_Spinlock, flags);
+ if (IO_Delay)
+ {
+ outb(reg, port);
+ udelay(IO_Delay);
+ r=inb(port);
+ udelay(IO_Delay);
+ } else {
+ outb(reg, port);
+ r=inb(port);
+ }
+ spin_unlock_irqrestore(&IO_Spinlock, flags);
return r;
}
-static inline void OutReg(io_port port, unsigned char reg, unsigned char val)
+static void
+OutReg(io_port port, unsigned char reg, unsigned char val)
{
unsigned long flags;
-
- save_flags(flags);
- cli();
-#ifdef SCC_LDELAY
- Outb(port, reg); udelay(SCC_LDELAY);
- Outb(port, val); udelay(SCC_LDELAY);
-#else
- Outb(port, reg);
- Outb(port, val);
-#endif
- restore_flags(flags);
+
+ spin_lock_irqsave(&IO_Spinlock, flags);
+ if (IO_Delay)
+ {
+ outb(reg, port);
+ udelay(IO_Delay);
+ outb(val, port);
+ udelay(IO_Delay);
+ } else {
+ outb(reg, port);
+ outb(val, port);
+ }
+ spin_unlock_irqrestore(&IO_Spinlock, flags);
}
-static inline void wr(struct scc_channel *scc, unsigned char reg,
- unsigned char val)
+static inline
+void wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
}
-static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+static inline void
+or(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
}
-static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+static inline void
+cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
{
OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
}
/* ******************************************************************** */
-/* * Some useful macros * */
-/* ******************************************************************** */
-
-static inline void scc_discard_buffers(struct scc_channel *scc)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- if (scc->tx_buff != NULL)
- {
- dev_kfree_skb(scc->tx_buff);
- scc->tx_buff = NULL;
- }
-
- while (skb_queue_len(&scc->tx_queue))
- dev_kfree_skb(skb_dequeue(&scc->tx_queue));
-
- restore_flags(flags);
-}
-
-
-
-/* ******************************************************************** */
/* * Interrupt Service Routines * */
/* ******************************************************************** */
-/* ----> subroutines for the interrupt handlers <---- */
-
-static inline void scc_notify(struct scc_channel *scc, int event)
-{
- struct sk_buff *skb;
- char *bp;
-
- if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA)
- return;
-
- skb = dev_alloc_skb(2);
- if (skb != NULL)
- {
- bp = skb_put(skb, 2);
- *bp++ = PARAM_HWEVENT;
- *bp++ = event;
- scc_net_rx(scc, skb);
- } else
- scc->stat.nospace++;
-}
-
static inline void flush_rx_FIFO(struct scc_channel *scc)
{
int k;
- for (k=0; k<3; k++)
+ for (k=0; k<(scc->enhanced? 8:4); k++)
Inb(scc->data);
if(scc->rx_buff != NULL) /* did we receive something? */
{
- scc->stat.rxerrs++; /* then count it as an error */
- dev_kfree_skb_irq(scc->rx_buff);
+ scc->stat.rxerrs++; /* then count it as an error */
+ kfree_skb(scc->rx_buff);
scc->rx_buff = NULL;
}
}
static void start_hunt(struct scc_channel *scc)
{
- if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL))
OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
- or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
}
/* ----> four different interrupt handlers for Tx, Rx, changing of */
@@ -377,36 +383,9 @@ static inline void scc_txint(struct scc_channel *scc)
if (skb == NULL)
{
- skb = skb_dequeue(&scc->tx_queue);
- scc->tx_buff = skb;
netif_wake_queue(scc->dev);
-
- if (skb == NULL)
- {
- scc_tx_done(scc);
- Outb(scc->ctrl, RES_Tx_P);
- return;
- }
-
- if (skb->len == 0) /* Paranoia... */
- {
- dev_kfree_skb_irq(skb);
- scc->tx_buff = NULL;
- scc_tx_done(scc);
- Outb(scc->ctrl, RES_Tx_P);
- return;
- }
-
- scc->stat.tx_state = TXS_ACTIVE;
-
- OutReg(scc->ctrl, R0, RES_Tx_CRC);
- /* reset CRC generator */
- or(scc,R10,ABUNDER); /* re-install underrun protection */
- Outb(scc->data,*skb->data); /* send byte */
- skb_pull(skb, 1);
-
- if (!scc->enhanced) /* reset EOM latch */
- Outb(scc->ctrl,RES_EOM_L);
+ scc_tx_done(scc);
+ Outb(scc->ctrl, RES_Tx_P);
return;
}
@@ -416,11 +395,11 @@ static inline void scc_txint(struct scc_channel *scc)
{
Outb(scc->ctrl, RES_Tx_P); /* reset pending int */
cl(scc, R10, ABUNDER); /* send CRC */
- dev_kfree_skb_irq(skb);
+ dev_kfree_skb(skb);
scc->tx_buff = NULL;
- scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */
+ scc_kick_tx(scc); /* next frame */
return;
- }
+ }
/* send octet */
@@ -447,25 +426,23 @@ static inline void scc_exint(struct scc_channel *scc)
/* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
- if ((changes & SYNC_HUNT) && scc->kiss.softdcd)
+ if ((changes & SYNC_HUNT) && scc->modem.softdcd)
{
if (status & SYNC_HUNT)
{
scc->dcd = 0;
flush_rx_FIFO(scc);
- if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL))
OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
} else {
scc->dcd = 1;
}
-
- scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON);
}
/* DCD: on = start to receive packet, off = ABORT condition */
/* (a successfully received packet generates a special condition int) */
- if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */
+ if((changes & DCD) && !scc->modem.softdcd) /* DCD input changed state */
{
if(status & DCD) /* DCD is now ON */
{
@@ -476,8 +453,6 @@ static inline void scc_exint(struct scc_channel *scc)
flush_rx_FIFO(scc);
scc->dcd = 0;
}
-
- scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
}
#ifdef notdef
@@ -489,24 +464,24 @@ static inline void scc_exint(struct scc_channel *scc)
if (chg_and_stat & CTS) /* CTS is now ON */
{
- if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */
- scc_start_tx_timer(scc, t_txdelay, 0);
+ if (scc->modem.tx_delay == 0) /* zero TXDELAY = wait for CTS */
+ scc_start_tx_timer(scc, t_tx_delay, 0);
}
#endif
if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
{
- scc->stat.tx_under++; /* oops, an underrun! count 'em */
+ scc->stat.tx_under++; /* oops, an underrun! count 'em */
Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */
if (scc->tx_buff != NULL)
{
- dev_kfree_skb_irq(scc->tx_buff);
+ dev_kfree_skb(scc->tx_buff);
scc->tx_buff = NULL;
}
or(scc,R10,ABUNDER);
- scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */
+ scc_tx_done(scc);
}
scc->status = status;
@@ -517,11 +492,12 @@ static inline void scc_exint(struct scc_channel *scc)
/* Receiver interrupt handler */
static inline void scc_rxint(struct scc_channel *scc)
{
+ unsigned char status;
struct sk_buff *skb;
scc->stat.rxints++;
- if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ if((scc->wreg[5] & RTS) && (scc->modem.fullduplex == 0))
{
Inb(scc->data); /* discard char */
or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
@@ -529,7 +505,6 @@ static inline void scc_rxint(struct scc_channel *scc)
}
skb = scc->rx_buff;
-
if (skb == NULL)
{
skb = dev_alloc_skb(scc->stat.bufsize);
@@ -541,167 +516,116 @@ static inline void scc_rxint(struct scc_channel *scc)
or(scc, R3, ENT_HM);
return;
}
-
- scc->rx_buff = skb;
- *(skb_put(skb, 1)) = 0; /* KISS data */
- }
- if (skb->len >= scc->stat.bufsize)
- {
-#ifdef notdef
- printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
-#endif
- dev_kfree_skb_irq(skb);
- scc->rx_buff = NULL;
- Inb(scc->data);
- or(scc, R3, ENT_HM);
- return;
+ scc->rx_buff = skb;
}
- *(skb_put(skb, 1)) = Inb(scc->data);
-}
-
-
-/* Receive Special Condition interrupt handler */
-static inline void scc_spint(struct scc_channel *scc)
-{
- unsigned char status;
- struct sk_buff *skb;
- scc->stat.spints++;
-
- status = InReg(scc->ctrl,R1); /* read receiver status */
-
- Inb(scc->data); /* throw away Rx byte */
- skb = scc->rx_buff;
-
- if(status & Rx_OVR) /* receiver overrun */
+ while(InReg(scc->ctrl, R0) & Rx_CH_AV)
{
- scc->stat.rx_over++; /* count them */
- or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
-
- if (skb != NULL)
- dev_kfree_skb_irq(skb);
- scc->rx_buff = skb = NULL;
- }
+ status = InReg(scc->ctrl, R1);
- if(status & END_FR && skb != NULL) /* end of frame */
- {
- /* CRC okay, frame ends on 8 bit boundary and received something ? */
-
- if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+ if (skb->len > scc->stat.bufsize)
{
- /* ignore last received byte (first of the CRC bytes) */
- skb_trim(skb, skb->len-1);
- scc_net_rx(scc, skb);
- scc->rx_buff = NULL;
- scc->stat.rxframes++;
- } else { /* a bad frame */
- dev_kfree_skb_irq(skb);
+#ifdef notdef
+ printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
+#endif
+ kfree_skb(skb);
scc->rx_buff = NULL;
- scc->stat.rxerrs++;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
}
- }
- Outb(scc->ctrl,ERR_RES);
-}
+ *(skb_put(skb, 1)) = Inb(scc->data);
+ if (status & Rx_OVR)
+ {
+ scc->stat.rx_over++;
+ or(scc, R3, ENT_HM);
+ if (skb != NULL) kfree_skb(skb);
+ scc->rx_buff = NULL;
+ Outb(scc->ctrl,ERR_RES);
+ }
-/* ----> interrupt service routine for the Z8530 <---- */
+ if (status & END_FR && skb != NULL)
+ {
+ /* CRC okay, frame ends on 8 bit boundary and received something ? */
-static void scc_isr_dispatch(struct scc_channel *scc, int vector)
-{
- switch (vector & VECTOR_MASK)
- {
- case TXINT: scc_txint(scc); break;
- case EXINT: scc_exint(scc); break;
- case RXINT: scc_rxint(scc); break;
- case SPINT: scc_spint(scc); break;
+ if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+ {
+ /* ignore last received byte (first of the CRC bytes) */
+ skb_trim(skb, skb->len-1);
+ scc_net_rx(scc, skb);
+ scc->rx_buff = NULL;
+ scc->stat.rxframes++;
+ } else { /* a bad frame */
+ kfree_skb(skb);
+ scc->rx_buff = NULL;
+ scc->stat.rxerrs++;
+ Outb(scc->ctrl,ERR_RES);
+ }
+ }
}
}
-/* If the card has a latch for the interrupt vector (like the PA0HZP card)
- use it to get the number of the chip that generated the int.
- If not: poll all defined chips.
- */
-#define SCC_IRQTIMEOUT 30000
+#define SCC_IRQTIMEOUT 5000
static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
{
- unsigned char vector;
+ unsigned char istat;
struct scc_channel *scc;
- struct scc_ctrl *ctrl;
+ struct scc_ctrl **ctrl_p, *ctrl;
int k;
- if (Vector_Latch)
- {
- for(k=0; k < SCC_IRQTIMEOUT; k++)
- {
- Outb(Vector_Latch, 0); /* Generate INTACK */
-
- /* Read the vector */
- if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
- if (vector & 0x01) break;
-
- scc=&SCC_Info[vector >> 3 ^ 0x01];
- if (!scc->dev) break;
-
- scc_isr_dispatch(scc, vector);
-
- OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */
- }
-
- if (k == SCC_IRQTIMEOUT)
- printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
-
- return;
- }
-
/* Find the SCC generating the interrupt by polling all attached SCCs
* reading RR3A (the interrupt pending register)
*/
- ctrl = SCC_ctrl;
- while (ctrl->chan_A)
+ ctrl = *scc_ctrl;
+ for (ctrl_p=scc_ctrl; *ctrl_p; ctrl_p++)
{
- if (ctrl->irq != irq)
- {
- ctrl++;
- continue;
- }
+ ctrl = *ctrl_p;
+ if (ctrl->channel_a.irq != irq) continue;
- scc = NULL;
- for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+ for (k = 0; k < SCC_IRQTIMEOUT; k++)
{
- vector=InReg(ctrl->chan_B,R2); /* Read the vector */
- if (vector & 0x01) break;
-
- scc = &SCC_Info[vector >> 3 ^ 0x01];
- if (!scc->dev) break;
+ istat = InReg(ctrl->channel_a.ctrl, R3);
+ if (!(istat & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT)))
+ break;
+
+ scc = &ctrl->channel_a;
+ if ((istat & (CHARxIP|CHATxIP|CHAEXT)) && scc->dev)
+ {
+ scc = &ctrl->channel_a;
+ if (istat & CHARxIP)
+ scc_rxint(scc);
+ if (istat & CHATxIP)
+ scc_txint(scc);
+ if (istat & CHAEXT)
+ scc_exint(scc);
+ }
- scc_isr_dispatch(scc, vector);
+ scc = &ctrl->channel_b;
+ if ((istat & (CHBRxIP|CHBTxIP|CHBEXT)) && scc->dev)
+ {
+ scc = &ctrl->channel_a;
+ if (istat & CHBRxIP)
+ scc_rxint(scc);
+ if (istat & CHBTxIP)
+ scc_txint(scc);
+ if (istat & CHBEXT)
+ scc_exint(scc);
+ }
}
+
if (k == SCC_IRQTIMEOUT)
{
printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
break;
}
-
- /* This looks weird and it is. At least the BayCom USCC doesn't
- * use the Interrupt Daisy Chain, thus we'll have to start
- * all over again to be sure not to miss an interrupt from
- * (any of) the other chip(s)...
- * Honestly, the situation *is* braindamaged...
- */
-
- if (scc != NULL)
- {
- OutReg(scc->ctrl,R0,RES_H_IUS);
- ctrl = SCC_ctrl;
- } else
- ctrl++;
}
}
@@ -724,24 +648,131 @@ static inline void set_brg(struct scc_channel *scc, unsigned int tc)
static inline void set_speed(struct scc_channel *scc)
{
- disable_irq(scc->irq);
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
- if (scc->modem.speed > 0) /* paranoia... */
- set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+ if (scc->modem.rx_speed > 0) /* paranoia... */
+ set_brg(scc, (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2);
- enable_irq(scc->irq);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
}
+/*
+ Construct value for clock mode register (R11)
+
+ Here are some common settings:
+ ------------------------------
+
+ normal half duplex operation:
+ tx_clock_source = CLOCK_SOURCE_DPLL;
+ rx_clock_source = CLOCK_SOURCE_DPLL;
+ trxc_pin_mode = TRXCP_MODE_DPLL_OUT;
+
+ note: this seems bogus, we used to switch to
+ tx_clock_source = CLOCK_SOURCE_BRG;
+ rx_clock_source = CLOCK_SOURCE_DPLL;
+ trxc_pin_mode = TRXCP_MODE_BRG_OUT;
+ on transmit which appears to be correct for Rx as well.
+
+ normal operation for modems w/ own clock (re)generation on BayCom cards
+ tx_clock_source = CLOCK_SOURCE_RTxC
+ rx_clock_source = CLOCK_SOURCE_TRxC
+ trxc_pin_mode = TRXCP_MODE_IN
+
+ normal operation for modems w/ own clock (re)generation on elswhere
+ tx_clock_source = CLOCK_SOURCE_TRxC
+ rx_clock_source = CLOCK_SOURCE_RTxC
+ trxc_pin_mode = TRXCP_MODE_IN
+
+ fullduplex mode for modems w/o own clock generator
+ (aka "divider mode") - BayCom style:
+ tx_clock_source = CLOCK_SOURCE_RTxC
+ rx_clock_source = CLOCK_SOURCE_DPLL
+ trxc_pin_mode = TRXCP_MODE_DPLL_OUT;
+ (divider divides by 2)
+
+ fullduplex mode for modems w/o own clock generator
+ (aka "divider mode") - usual style:
+ tx_clock_source = CLOCK_SOURCE_RTxC
+ rx_clock_source = CLOCK_SOURCE_DPLL
+ trxc_pin_mode = TRXCP_MODE_BRG_OUT
+ (BRG clock = 32 * DPLL clock, divider divides by 16)
+
+ Note that only TRxC can be programmed to output, RTxC
+ is _always_ input
+*/
-/* ----> initialize a SCC channel <---- */
+static int scc_calc_r11(int rx_clock_source, int tx_clock_source, int trxc_pin_mode)
+{
+ int clockmode = 0;
-static inline void init_brg(struct scc_channel *scc)
+ switch(tx_clock_source)
+ {
+ case CLOCK_SOURCE_RTxC:
+ break;
+ case CLOCK_SOURCE_TRxC:
+ clockmode = TCTRxCP;
+ break;
+ case CLOCK_SOURCE_BRG:
+ clockmode = TCBR;
+ break;
+ case CLOCK_SOURCE_DPLL:
+ clockmode = TCDPLL;
+ }
+
+
+ switch(rx_clock_source)
+ {
+ case CLOCK_SOURCE_RTxC:
+ break;
+ case CLOCK_SOURCE_TRxC:
+ clockmode |= RCTRxCP;
+ break;
+ case CLOCK_SOURCE_BRG:
+ clockmode |= RCBR;
+ break;
+ case CLOCK_SOURCE_DPLL:
+ clockmode |= RCDPLL;
+ }
+
+
+ switch(trxc_pin_mode)
+ {
+ case TRXCP_MODE_IN:
+ break;
+ case TRXCP_MODE_TXC_OUT:
+ clockmode |= TRxCTC|TRxCOI;
+ break;
+ case TRXCP_MODE_BRG_OUT:
+ clockmode |= TRxCBR|TRxCOI;
+ break;
+ case TRXCP_MODE_DPLL_OUT:
+ clockmode |= TRxCDP|TRxCOI;
+ break;
+ }
+
+ return clockmode;
+}
+
+
+static void scc_init_brg_and_dpll(struct scc_channel *scc)
{
wr(scc, R14, BRSRC); /* BRG source = PCLK */
- OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
- OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
+ if (scc->modem.tx_clock_source == CLOCK_SOURCE_DPLL ||
+ scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)
+ {
+ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
+ OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
+ } else {
+ OutReg(scc->ctrl, R14, DISDPLL|scc->wreg[R14]);
+ }
}
+
+/* ----> initialize an SCC channel <---- */
+
+
/*
* Initialization according to the Z8530 manual (SGS-Thomson's version):
*
@@ -789,10 +820,10 @@ static inline void init_brg(struct scc_channel *scc)
static void init_channel(struct scc_channel *scc)
{
- del_timer(&scc->tx_t);
- del_timer(&scc->tx_wdog);
+ unsigned long flags;
- disable_irq(scc->irq);
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+ del_timer(&scc->tx_timer);
wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
wr(scc,R1,0); /* no W/REQ operation */
@@ -801,56 +832,11 @@ static void init_channel(struct scc_channel *scc)
wr(scc,R6,0); /* SDLC address zero (not used) */
wr(scc,R7,FLAG); /* SDLC flag value */
wr(scc,R9,VIS); /* vector includes status */
- wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+ wr(scc,R10,(scc->modem.nrz_mode? NRZI : NRZ)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
wr(scc,R14, 0);
-
-/* set clock sources:
-
- CLK_DPLL: normal halfduplex operation
-
- RxClk: use DPLL
- TxClk: use DPLL
- TRxC mode DPLL output
-
- CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
-
- BayCom: others:
-
- TxClk = pin RTxC TxClk = pin TRxC
- RxClk = pin TRxC RxClk = pin RTxC
-
-
- CLK_DIVIDER:
- RxClk = use DPLL
- TxClk = pin RTxC
-
- BayCom: others:
- pin TRxC = DPLL pin TRxC = BRG
- (RxClk * 1) (RxClk * 32)
-*/
-
-
- switch(scc->modem.clocksrc)
- {
- case CLK_DPLL:
- wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
- init_brg(scc);
- break;
-
- case CLK_DIVIDER:
- wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
- init_brg(scc);
- break;
-
- case CLK_EXTERNAL:
- wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
- OutReg(scc->ctrl, R14, DISDPLL);
- break;
-
- }
-
- set_speed(scc); /* set baudrate */
+ wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode));
+ scc_init_brg_and_dpll(scc);
if(scc->enhanced)
{
@@ -858,7 +844,7 @@ static void init_channel(struct scc_channel *scc)
wr(scc,R7,AUTOEOM);
}
- if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD))
+ if(scc->modem.softdcd || (InReg(scc->ctrl,R0) & DCD))
/* DCD is now ON */
{
start_hunt(scc);
@@ -866,7 +852,7 @@ static void init_channel(struct scc_channel *scc)
/* enable ABORT, DCD & SYNC/HUNT interrupts */
- wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE));
+ wr(scc,R15, BRKIE|TxUIE|(scc->modem.softdcd? SYNCIE:DCDIE));
Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
@@ -875,14 +861,13 @@ static void init_channel(struct scc_channel *scc)
scc->status = InReg(scc->ctrl,R0); /* read initial status */
- or(scc,R9,MIE); /* master interrupt enable */
-
- scc_init_timer(scc);
-
- enable_irq(scc->irq);
-}
+ or(scc,R9,MIE); /* master interrupt enable */
+ scc_init_timer(scc);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+ set_speed(scc); /* set baudrate */
+}
/* ******************************************************************** */
@@ -895,17 +880,20 @@ static void init_channel(struct scc_channel *scc)
static void scc_key_trx(struct scc_channel *scc, char tx)
{
+ unsigned long flags;
unsigned int time_const;
if (scc->brand & PRIMUS)
- Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
+ Outb(scc->ctrl + 4, scc->special_option | (tx? 0x80 : 0));
- if (scc->modem.speed < 300)
- scc->modem.speed = 1200;
- time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
+ if (tx)
+ time_const = (unsigned) (scc->clock / (scc->modem.tx_speed * 2)) - 2;
+ else
+ time_const = (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2;
- disable_irq(scc->irq);
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
if (tx)
{
@@ -913,7 +901,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx)
or(scc, R15, TxUIE);
}
- if (scc->modem.clocksrc == CLK_DPLL)
+ if (scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)
{ /* force simplex operation */
if (tx)
{
@@ -927,7 +915,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx)
wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
/* By popular demand: tx_inhibit */
- if (scc->kiss.tx_inhibit)
+ if (scc->modem.tx_inhibit)
{
or(scc,R5, TxENAB);
scc->wreg[R5] |= RTS;
@@ -943,10 +931,10 @@ static void scc_key_trx(struct scc_channel *scc, char tx)
wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
#ifndef CONFIG_SCC_TRXECHO
- if (scc->kiss.softdcd)
+ if (scc->modem.softdcd)
#endif
{
- or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+ or(scc,R15, scc->modem.softdcd? SYNCIE:DCDIE);
start_hunt(scc);
}
}
@@ -954,14 +942,14 @@ static void scc_key_trx(struct scc_channel *scc, char tx)
if (tx)
{
#ifdef CONFIG_SCC_TRXECHO
- if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ if (scc->modem.fullduplex == 0)
{
cl(scc, R3, RxENABLE);
cl(scc, R15, DCDIE|SYNCIE);
}
#endif
- if (scc->kiss.tx_inhibit)
+ if (scc->modem.tx_inhibit)
{
or(scc,R5, TxENAB);
scc->wreg[R5] |= RTS;
@@ -971,87 +959,84 @@ static void scc_key_trx(struct scc_channel *scc, char tx)
} else {
cl(scc,R5,RTS|TxENAB); /* disable tx */
- if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) &&
+ if ((scc->modem.fullduplex == 0) &&
#ifndef CONFIG_SCC_TRXECHO
- scc->kiss.softdcd)
+ scc->modem.softdcd)
#else
1)
#endif
{
- or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE);
+ or(scc, R15, scc->modem.softdcd? SYNCIE:DCDIE);
start_hunt(scc);
}
}
}
- enable_irq(scc->irq);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
}
-
-/* ----> SCC timer interrupt handler and friends. <---- */
-
-static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when)
+static void scc_kick_tx(struct scc_channel *scc)
{
+ struct sk_buff *skb;
unsigned long flags;
-
-
- save_flags(flags);
- cli();
- del_timer(&scc->tx_t);
+ spin_lock_irqsave(&scc->spinlocks.kick_tx, flags);
- if (when == 0)
- {
- handler((unsigned long) scc);
- } else
- if (when != TIMER_OFF)
- {
- scc->tx_t.data = (unsigned long) scc;
- scc->tx_t.function = handler;
- scc->tx_t.expires = jiffies + (when*HZ)/100;
- add_timer(&scc->tx_t);
- }
-
- restore_flags(flags);
-}
+ skb = scc->tx_new;
+ scc->tx_new = NULL;
+ netif_wake_queue(scc->dev);
-static void scc_start_defer(struct scc_channel *scc)
-{
- unsigned long flags;
-
- save_flags(flags);
- cli();
+ if (skb == NULL) goto nada;
- del_timer(&scc->tx_wdog);
-
- if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF)
+ if (skb->len == 0) /* Paranoia... */
{
- scc->tx_wdog.data = (unsigned long) scc;
- scc->tx_wdog.function = t_busy;
- scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer;
- add_timer(&scc->tx_wdog);
+ dev_kfree_skb(skb);
+ scc->tx_buff = NULL;
+ Outb(scc->ctrl, RES_Tx_P);
+ goto nada;
}
- restore_flags(flags);
+
+ scc->tx_buff = skb;
+ scc->stat.tx_state = TXS_ACTIVE;
+ OutReg(scc->ctrl, R0, RES_Tx_CRC);
+ /* reset CRC generator */
+ or(scc,R10,ABUNDER); /* re-install underrun protection */
+ Outb(scc->data,*skb->data); /* send byte */
+ skb_pull(skb, 1);
+
+ if (!scc->enhanced) /* reset EOM latch */
+ Outb(scc->ctrl,RES_EOM_L);
+
+ spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags);
+ return;
+
+nada:
+ scc_tx_done(scc);
+ spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags);
+ return;
}
-static void scc_start_maxkeyup(struct scc_channel *scc)
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(struct scc_channel *), unsigned long when)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&scc->spinlocks.timer, flags);
- del_timer(&scc->tx_wdog);
-
- if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF)
+ del_timer(&scc->tx_timer);
+
+ if (when != 0)
{
- scc->tx_wdog.data = (unsigned long) scc;
- scc->tx_wdog.function = t_maxkeyup;
- scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup;
- add_timer(&scc->tx_wdog);
+ scc->tx_timer.data = (unsigned long) scc;
+ scc->tx_timer.function = (void (*)(unsigned long)) handler;
+ scc->tx_timer.expires = jiffies + (when*HZ)/1000;
+ add_timer(&scc->tx_timer);
+ } else {
+ handler(scc);
}
-
- restore_flags(flags);
+
+ spin_unlock_irqrestore(&scc->spinlocks.timer, flags);
}
/*
@@ -1061,243 +1046,96 @@ static void scc_start_maxkeyup(struct scc_channel *scc)
static void scc_tx_done(struct scc_channel *scc)
{
- /*
- * trx remains keyed in fulldup mode 2 until t_idle expires.
- */
-
- switch (scc->kiss.fulldup)
- {
- case KISS_DUPLEX_LINK:
- scc->stat.tx_state = TXS_IDLE2;
- if (scc->kiss.idletime != TIMER_OFF)
- scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100);
- break;
- case KISS_DUPLEX_OPTIMA:
- scc_notify(scc, HWEV_ALL_SENT);
- break;
- default:
- scc->stat.tx_state = TXS_BUSY;
- scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
- }
+ scc->stat.tx_state = TXS_TAIL;
- netif_wake_queue(scc->dev);
+ if (scc->modem.tx_tail != 0)
+ scc_start_tx_timer(scc, scc_tail, scc->modem.tx_tail);
+ else
+ scc_tail(scc);
}
-
-static unsigned char Rand = 17;
-
-static inline int is_grouped(struct scc_channel *scc)
+#ifndef SCC_SIMPLE_MAC
+static void scc_tx_forced(struct scc_channel *scc)
{
- int k;
- struct scc_channel *scc2;
- unsigned char grp1, grp2;
-
- grp1 = scc->kiss.group;
-
- for (k = 0; k < (Nchips * 2); k++)
- {
- scc2 = &SCC_Info[k];
- grp2 = scc2->kiss.group;
-
- if (scc2 == scc || !(scc2->dev && grp2))
- continue;
-
- if ((grp1 & 0x3f) == (grp2 & 0x3f))
- {
- if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) )
- return 1;
-
- if ( (grp1 & RXGROUP) && scc2->dcd )
- return 1;
- }
- }
- return 0;
+ scc->stat.tx_state = TXS_TAIL;
+
+ if (scc->tx_new)
+ scc_kick_tx(scc);
+ else
+ // remain key-up'ed for the time of tx_delay...
+ // what's the timeout in 6pack?
+ scc_start_tx_timer(scc, scc_tail, scc->modem.tx_delay);
}
+#endif
-/* DWAIT and SLOTTIME expired
- *
- * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer
- * else key trx and start txdelay
- * fulldup == 1: key trx and start txdelay
- * fulldup == 2: mintime expired, reset status or key trx and start txdelay
- */
-
-static void t_dwait(unsigned long channel)
+static void scc_tx_start(struct scc_channel *scc, struct sk_buff *skb)
{
- struct scc_channel *scc = (struct scc_channel *) channel;
+ scc->tx_new = skb;
- if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */
- {
- if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */
- {
- scc->stat.tx_state = TXS_IDLE;
- netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */
- return;
- }
-
- scc->stat.tx_state = TXS_BUSY;
- }
+ /*
+ * scc_set_rts may also start a tx delay wait time, if we
+ * get a frame to transmit within this time RTS would be set,
+ * shorten the tx delay time...
+ */
- if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
+ if (scc->stat.tx_state != TXS_TXDELAY)
{
- Rand = Rand * 17 + 31;
-
- if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) )
+ scc->stat.tx_state = TXS_TXDELAY;
+
+ if ( !(scc->wreg[R5] & RTS) )
{
- scc_start_defer(scc);
- scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime);
- return ;
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, scc_kick_tx, scc->modem.tx_delay);
+ } else {
+ scc_start_tx_timer(scc, scc_kick_tx, 0);
}
}
+}
+
+#ifndef SCC_SIMPLE_MAC
+static void scc_set_rts(struct scc_channel *scc)
+{
+ scc->stat.tx_state = TXS_TXDELAY;
if ( !(scc->wreg[R5] & RTS) )
{
scc_key_trx(scc, TX_ON);
- scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
+ scc_start_tx_timer(scc, scc_tx_forced, scc->modem.tx_delay);
} else {
- scc_start_tx_timer(scc, t_txdelay, 0);
+ scc_start_tx_timer(scc, scc_tx_forced, 0);
}
}
+#endif
-
-/* TXDELAY expired
- *
- * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog.
+/*
+ * TAILTIME expired
*/
-static void t_txdelay(unsigned long channel)
+static void scc_tail(struct scc_channel *scc)
{
- struct scc_channel *scc = (struct scc_channel *) channel;
-
- scc_start_maxkeyup(scc);
+ if (scc->tx_buff != NULL)
+ return;
- if (scc->tx_buff == NULL)
+ if (scc->tx_new != NULL)
{
- disable_irq(scc->irq);
- scc_txint(scc);
- enable_irq(scc->irq);
+ scc_kick_tx(scc);
+ return;
}
-}
-
-/* TAILTIME expired
- *
- * switch off transmitter. If we were stopped by Maxkeyup restart
- * transmission after 'mintime' seconds
- */
-
-static void t_tail(unsigned long channel)
-{
- struct scc_channel *scc = (struct scc_channel *) channel;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- del_timer(&scc->tx_wdog);
scc_key_trx(scc, TX_OFF);
-
- restore_flags(flags);
-
- if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */
- {
- scc->stat.tx_state = TXS_WAIT;
-
- if (scc->kiss.mintime != TIMER_OFF) /* try it again */
- scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
- else
- scc_start_tx_timer(scc, t_dwait, 0);
- return;
- }
-
scc->stat.tx_state = TXS_IDLE;
- netif_wake_queue(scc->dev);
-}
-
-
-/* BUSY timeout
- *
- * throw away send buffers if DCD remains active too long.
- */
-
-static void t_busy(unsigned long channel)
-{
- struct scc_channel *scc = (struct scc_channel *) channel;
-
- del_timer(&scc->tx_t);
- netif_stop_queue(scc->dev); /* don't pile on the wabbit! */
-
- scc_discard_buffers(scc);
- scc->stat.txerrs++;
- scc->stat.tx_state = TXS_IDLE;
-
- netif_wake_queue(scc->dev);
-}
-
-/* MAXKEYUP timeout
- *
- * this is our watchdog.
- */
-
-static void t_maxkeyup(unsigned long channel)
-{
- struct scc_channel *scc = (struct scc_channel *) channel;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- /*
- * let things settle down before we start to
- * accept new data.
- */
-
- netif_stop_queue(scc->dev);
- scc_discard_buffers(scc);
-
- del_timer(&scc->tx_t);
-
- cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */
- cl(scc, R15, TxUIE); /* count it. */
- OutReg(scc->ctrl, R0, RES_Tx_P);
-
- restore_flags(flags);
-
- scc->stat.txerrs++;
- scc->stat.tx_state = TXS_TIMEOUT;
- scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
-}
-
-/* IDLE timeout
- *
- * in fulldup mode 2 it keys down the transmitter after 'idle' seconds
- * of inactivity. We will not restart transmission before 'mintime'
- * expires.
- */
-
-static void t_idle(unsigned long channel)
-{
- struct scc_channel *scc = (struct scc_channel *) channel;
-
- del_timer(&scc->tx_wdog);
-
- scc_key_trx(scc, TX_OFF);
-
- if (scc->kiss.mintime != TIMER_OFF)
- scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100);
- scc->stat.tx_state = TXS_WAIT;
}
static void scc_init_timer(struct scc_channel *scc)
{
unsigned long flags;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&scc->spinlocks.timer, flags);
scc->stat.tx_state = TXS_IDLE;
+ // FIXME: this can't be all, can it...?
- restore_flags(flags);
+ spin_unlock_irqrestore(&scc->spinlocks.timer, flags);
}
@@ -1307,256 +1145,155 @@ static void scc_init_timer(struct scc_channel *scc)
/*
- * this will set the "hardware" parameters through KISS commands or ioctl()
+ * this will set the (some, anyway...) MODEM parameters
*/
-#define CAST(x) (unsigned long)(x)
-
-static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg)
+static unsigned int scc_set_param(struct scc_channel *scc, struct scc_modem *modem)
{
- switch (cmd)
- {
- case PARAM_TXDELAY: scc->kiss.txdelay=arg; break;
- case PARAM_PERSIST: scc->kiss.persist=arg; break;
- case PARAM_SLOTTIME: scc->kiss.slottime=arg; break;
- case PARAM_TXTAIL: scc->kiss.tailtime=arg; break;
- case PARAM_FULLDUP: scc->kiss.fulldup=arg; break;
- case PARAM_DTR: break; /* does someone need this? */
- case PARAM_GROUP: scc->kiss.group=arg; break;
- case PARAM_IDLE: scc->kiss.idletime=arg; break;
- case PARAM_MIN: scc->kiss.mintime=arg; break;
- case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break;
- case PARAM_WAIT: scc->kiss.waittime=arg; break;
- case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break;
- case PARAM_TX: scc->kiss.tx_inhibit=arg; break;
-
- case PARAM_SOFTDCD:
- scc->kiss.softdcd=arg;
- if (arg)
- {
- or(scc, R15, SYNCIE);
- cl(scc, R15, DCDIE);
- start_hunt(scc);
- } else {
- or(scc, R15, DCDIE);
- cl(scc, R15, SYNCIE);
- }
- break;
-
- case PARAM_SPEED:
- if (arg < 256)
- scc->modem.speed=arg*100;
- else
- scc->modem.speed=arg;
+ scc->modem.tx_delay = modem->tx_delay;
+ scc->modem.tx_tail = modem->tx_tail;
+ scc->modem.fullduplex = modem->fullduplex;
+ scc->modem.tx_inhibit = modem->tx_inhibit;
+ scc->modem.softdcd = modem->softdcd;
- if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */
- set_speed(scc);
- break;
-
- case PARAM_RTS:
- if ( !(scc->wreg[R5] & RTS) )
- {
- if (arg != TX_OFF)
- scc_key_trx(scc, TX_ON);
- scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay);
- } else {
- if (arg == TX_OFF)
- {
- scc->stat.tx_state = TXS_BUSY;
- scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime);
- }
- }
- break;
-
- case PARAM_HWEVENT:
- scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF);
- break;
- default: return -EINVAL;
+ if (modem->softdcd)
+ {
+ or(scc, R15, SYNCIE);
+ cl(scc, R15, DCDIE);
+ start_hunt(scc);
+ } else {
+ or(scc, R15, DCDIE);
+ cl(scc, R15, SYNCIE);
}
return 0;
}
-
-
-static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd)
+static unsigned int scc_ddi_report_dcd(struct net_device *dev)
{
- switch (cmd)
- {
- case PARAM_TXDELAY: return CAST(scc->kiss.txdelay);
- case PARAM_PERSIST: return CAST(scc->kiss.persist);
- case PARAM_SLOTTIME: return CAST(scc->kiss.slottime);
- case PARAM_TXTAIL: return CAST(scc->kiss.tailtime);
- case PARAM_FULLDUP: return CAST(scc->kiss.fulldup);
- case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd);
- case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0);
- case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0);
- case PARAM_SPEED: return CAST(scc->modem.speed);
- case PARAM_GROUP: return CAST(scc->kiss.group);
- case PARAM_IDLE: return CAST(scc->kiss.idletime);
- case PARAM_MIN: return CAST(scc->kiss.mintime);
- case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup);
- case PARAM_WAIT: return CAST(scc->kiss.waittime);
- case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer);
- case PARAM_TX: return CAST(scc->kiss.tx_inhibit);
- default: return NO_SUCH_PARAM;
- }
-
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "dcd=%d\n", scc->dcd); */
+ return scc->dcd;
}
-#undef CAST
-
-/* ******************************************************************* */
-/* * Send calibration pattern * */
-/* ******************************************************************* */
-
-static void scc_stop_calibrate(unsigned long channel)
+static unsigned int scc_ddi_report_ptt(struct net_device *dev)
{
- struct scc_channel *scc = (struct scc_channel *) channel;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- del_timer(&scc->tx_wdog);
- scc_key_trx(scc, TX_OFF);
- wr(scc, R6, 0);
- wr(scc, R7, FLAG);
- Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
- Outb(scc->ctrl,RES_EXT_INT);
-
- netif_wake_queue(scc->dev);
- restore_flags(flags);
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "rts=%d\n", (scc->wreg[R5] & RTS)? 1:0); */
+ return (scc->wreg[R5] & RTS)? 1:0;
}
-
-static void
-scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern)
+#ifndef SCC_SIMPLE_MAC
+static unsigned int scc_ddi_report_cts(struct net_device *dev)
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ int txs = scc->stat.tx_state;
- netif_stop_queue(scc->dev);
- scc_discard_buffers(scc);
+/* printk(KERN_INFO "cts=%d\n", ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0); */
+ return ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0;
+}
- del_timer(&scc->tx_wdog);
+static void scc_ddi_set_rts(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "setrts\n"); */
+ scc_set_rts(scc);
+}
+#endif
- scc->tx_wdog.data = (unsigned long) scc;
- scc->tx_wdog.function = scc_stop_calibrate;
- scc->tx_wdog.expires = jiffies + HZ*duration;
- add_timer(&scc->tx_wdog);
+static void scc_ddi_set_bitrate(struct net_device *dev, unsigned int speed)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ scc->modem.rx_speed = speed;
+ scc->modem.tx_speed = speed;
+ if (scc->stat.tx_state != TXS_ACTIVE)
+ set_speed(scc);
+}
- /* This doesn't seem to work. Why not? */
- wr(scc, R6, 0);
- wr(scc, R7, pattern);
+/* Update general parameters so that they reflect our internal settings */
+static void scc_ddi_param_update(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
- /*
- * Don't know if this works.
- * Damn, where is my Z8530 programming manual...?
- */
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, scc->modem.fullduplex);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, scc->modem.rx_speed);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, scc->modem.tx_speed);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, scc->modem.tx_delay);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, scc->modem.tx_tail);
+ return;
+}
- Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
- Outb(scc->ctrl,RES_EXT_INT);
+/* Called from upper layers when parameter was changed */
+static void scc_ddi_param_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
- scc_key_trx(scc, TX_ON);
- restore_flags(flags);
+ switch (valueno)
+ {
+ case AX25_VALUES_MEDIA_DUPLEX:
+ if (!netif_running(dev)) scc->modem.fullduplex = new;
+ break;
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ scc_ddi_set_bitrate(dev, new);
+ break;
+ case AX25_VALUES_MEDIA_TXDELAY:
+ scc->modem.tx_delay = new;
+ break;
+ case AX25_VALUES_MEDIA_TXTAIL:
+ scc->modem.tx_tail = new;
+ break;
+ default:
+ break;
+ }
+ scc_ddi_param_update(dev);
+ return;
}
/* ******************************************************************* */
/* * Init channel structures, special HW, etc... * */
/* ******************************************************************* */
-/*
- * Reset the Z8530s and setup special hardware
- */
-
-static void z8530_init(void)
-{
- struct scc_channel *scc;
- int chip, k;
- unsigned long flags;
- char *flag;
-
-
- printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
-
- flag=" ";
- for (k = 0; k < 16; k++)
- if (Ivec[k].used)
- {
- printk("%s%d", flag, k);
- flag=",";
- }
- printk("\n");
-
-
- /* reset and pre-init all chips in the system */
- for (chip = 0; chip < Nchips; chip++)
- {
- scc=&SCC_Info[2*chip];
- if (!scc->ctrl) continue;
-
- /* Special SCC cards */
- if(scc->brand & EAGLE) /* this is an EAGLE card */
- Outb(scc->special,0x08); /* enable interrupt on the board */
-
- if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */
- Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */
-
-
- /* Reset and pre-init Z8530 */
-
- save_flags(flags);
- cli();
-
- Outb(scc->ctrl, 0);
- OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */
- udelay(100); /* give it 'a bit' more time than required */
- wr(scc, R2, chip*16); /* interrupt vector */
- wr(scc, R9, VIS); /* vector includes status */
-
- restore_flags(flags);
- }
-
-
- Driver_Initialized = 1;
-}
/*
* Allocate device structure, err, instance, and register driver
*/
-static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev)
+static int scc_net_setup(struct scc_channel *scc)
{
+ int k = 0;
struct net_device *dev;
- if (dev_get(name))
- {
- printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
- return -EEXIST;
- }
-
if ((scc->dev = (struct net_device *) kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL)
return -ENOMEM;
dev = scc->dev;
memset(dev, 0, sizeof(struct net_device));
- strcpy(dev->name, name);
+ do {
+ sprintf(dev->name, "scc%d", k);
+ if (++k > 256) goto bail_out; /* avoid endless loop */
+ } while (dev_get_by_name(dev->name) == NULL);
+
dev->priv = (void *) scc;
dev->init = scc_net_init;
- if ((addev? register_netdevice(dev) : register_netdev(dev)) != 0) {
- kfree(dev);
- return -EIO;
- }
+ if (register_netdev(dev) != 0) goto bail_out;
+
+ scc->proc_dev_name = dev->name;
+ scc->tx_buff = NULL;
+ scc->tx_new = NULL;
+ scc->init = 1;
+ scc_ddi_param_update(dev);
- SET_MODULE_OWNER(dev);
return 0;
+
+bail_out:
+ kfree(dev);
+ scc->dev = NULL;
+ return -EINVAL;
}
@@ -1565,15 +1302,12 @@ static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev
/* * Network driver methods * */
/* ******************************************************************** */
-static unsigned char ax25_bcast[AX25_ADDR_LEN] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static unsigned char ax25_nocall[AX25_ADDR_LEN] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
-
/* ----> Initialize device <----- */
static int scc_net_init(struct net_device *dev)
{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct ax25_dev *ax25dev;
dev_init_buffers(dev);
dev->tx_queue_len = 16; /* should be enough... */
@@ -1582,23 +1316,28 @@ static int scc_net_init(struct net_device *dev)
dev->stop = scc_net_close;
dev->hard_start_xmit = scc_net_tx;
- dev->hard_header = ax25_encapsulate;
- dev->rebuild_header = ax25_rebuild_header;
dev->set_mac_address = scc_net_set_mac_address;
dev->get_stats = scc_net_get_stats;
dev->do_ioctl = scc_net_ioctl;
- dev->tx_timeout = NULL;
-
- memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN);
- memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN);
-
- dev->flags = 0;
+ dev->tx_timeout = scc_net_timeout;
+ dev->watchdog_timeo = SCC_WATCHDOG_TIMEOUT;
dev->type = ARPHRD_AX25;
dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
dev->mtu = AX25_DEF_PACLEN;
dev->addr_len = AX25_ADDR_LEN;
+ AX25_PTR(dev) = ax25dev = &scc->ax25dev;
+ memset(ax25dev, 0, sizeof(struct ax25_dev));
+ ax25dev->hw.ptt = scc_ddi_report_ptt;
+ ax25dev->hw.dcd = scc_ddi_report_dcd;
+#ifndef SCC_SIMPLE_MAC
+ ax25dev->hw.cts = scc_ddi_report_cts;
+ ax25dev->hw.rts = scc_ddi_set_rts;
+#endif
+ ax25dev->hw.parameter_change_notify = scc_ddi_param_notify;
+ ax25dev->hw.fast = 0;
+
return 0;
}
@@ -1608,15 +1347,17 @@ static int scc_net_open(struct net_device *dev)
{
struct scc_channel *scc = (struct scc_channel *) dev->priv;
- if (!scc->init)
- return -EINVAL;
+ if (!scc->init) return -EINVAL;
+
+ MOD_INC_USE_COUNT;
scc->tx_buff = NULL;
- skb_queue_head_init(&scc->tx_queue);
init_channel(scc);
+ scc_ddi_param_update(dev);
netif_start_queue(dev);
+
return 0;
}
@@ -1628,21 +1369,21 @@ static int scc_net_close(struct net_device *dev)
unsigned long flags;
netif_stop_queue(dev);
-
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
- Outb(scc->ctrl,0); /* Make sure pointer is written */
- wr(scc,R1,0); /* disable interrupts */
+ Outb(scc->ctrl,0); /* Make sure pointer is written */
+ wr(scc,R1,0); /* disable interrupts */
wr(scc,R3,0);
- del_timer(&scc->tx_t);
- del_timer(&scc->tx_wdog);
+ del_timer(&scc->tx_timer);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
- restore_flags(flags);
-
- scc_discard_buffers(scc);
+ if (scc->tx_buff)
+ kfree_skb(scc->tx_buff);
+ if (scc->tx_new)
+ kfree_skb(scc->tx_new);
+ MOD_DEC_USE_COUNT;
return 0;
}
@@ -1650,8 +1391,9 @@ static int scc_net_close(struct net_device *dev)
static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
{
- if (skb->len == 0) {
- dev_kfree_skb_irq(skb);
+ if (skb->len == 0)
+ {
+ kfree_skb(skb);
return;
}
@@ -1671,299 +1413,186 @@ static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
static int scc_net_tx(struct sk_buff *skb, struct net_device *dev)
{
struct scc_channel *scc = (struct scc_channel *) dev->priv;
- unsigned long flags;
- char kisscmd;
- if (skb->len > scc->stat.bufsize || skb->len < 2) {
+ if (skb->len > scc->stat.bufsize || skb->len < 2)
+ {
scc->dev_stat.tx_dropped++; /* bogus frame */
dev_kfree_skb(skb);
return 0;
}
-
+
+ netif_stop_queue(dev);
+
scc->dev_stat.tx_packets++;
scc->stat.txframes++;
-
- kisscmd = *skb->data & 0x1f;
- skb_pull(skb, 1);
- if (kisscmd) {
- scc_set_param(scc, kisscmd, *skb->data);
- dev_kfree_skb(skb);
+#ifdef notdef
+ /**** The following "fix" is bogus. Fix the protocol, dammit! ****/
+
+ /* avoid race condition when skb is a cloned broadcast */
+ skb_cp = skb_copy(skb, GFP_ATOMIC);
+ dev_kfree_skb(skb);
+ if (skb_cp == NULL) {
+ scc->dev_stat.tx_dropped++; /* out of memory */
return 0;
}
+#endif
- save_flags(flags);
- cli();
+ /* transmit frame */
- if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) {
- struct sk_buff *skb_del;
- skb_del = skb_dequeue(&scc->tx_queue);
- dev_kfree_skb(skb_del);
- }
- skb_queue_tail(&scc->tx_queue, skb);
dev->trans_start = jiffies;
-
+ scc_tx_start(scc, skb);
- /*
- * Start transmission if the trx state is idle or
- * t_idle hasn't expired yet. Use dwait/persistance/slottime
- * algorithm for normal halfduplex operation.
- */
+ return 0;
+}
- if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) {
- scc->stat.tx_state = TXS_BUSY;
- if (scc->kiss.fulldup == KISS_DUPLEX_HALF)
- scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime);
- else
- scc_start_tx_timer(scc, t_dwait, 0);
- }
- restore_flags(flags);
- return 0;
+/* -----> Watchdog <----- */
+
+static void scc_net_timeout(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+
+ del_timer(&scc->tx_timer);
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+ cl(scc, R1, TxINT_ENAB);
+ cl(scc, R15, TxUIE);
+ OutReg(scc->ctrl, R0, RES_Tx_P);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+
+ scc_start_tx_timer(scc, init_channel, 100);
}
-/* ----> ioctl functions <---- */
-/*
- * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg
- * SIOCSCCINI - initialize driver arg: ---
- * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg
- * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg
- * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg
- * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg
- * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg
- * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg
- */
+
+/* ----> ioctl functions <---- */
static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct scc_kiss_cmd kiss_cmd;
- struct scc_mem_config memcfg;
- struct scc_hw_config hwcfg;
- struct scc_calibrate cal;
- int chan;
- unsigned char device_name[10];
- void *arg;
- struct scc_channel *scc;
-
- scc = (struct scc_channel *) dev->priv;
- arg = (void *) ifr->ifr_data;
-
- if (!Driver_Initialized)
- {
- if (cmd == SIOCSCCCFG)
- {
- int found = 1;
+ return -ENOIOCTLCMD;
+}
- if (!capable(CAP_SYS_RAWIO)) return -EPERM;
- if (!arg) return -EFAULT;
+static int scc_check_and_init(struct scc_ctrl *ctrl)
+{
+ ctl_table *t_port[2];
+ unsigned long flags;
+ int found_a = 1;
+ int found_b = 1;
+ int port, entry;
- if (Nchips >= SCC_MAXCHIPS)
- return -EINVAL;
+ /* are we still missing vital information? */
+ if (ctrl->channel_a.ctrl <= 0 || ctrl->channel_a.data <= 0 ||
+ ctrl->channel_b.ctrl <= 0 || ctrl->channel_b.ctrl <= 0 ||
+ ctrl->channel_a.irq <= 0)
+ return 1;
- if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
- return -EFAULT;
+ check_region(ctrl->channel_a.ctrl, 1);
+ check_region(ctrl->channel_b.ctrl, 1);
- if (hwcfg.irq == 2) hwcfg.irq = 9;
+ spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags);
- if (!Ivec[hwcfg.irq].used && hwcfg.irq)
- {
- if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
- printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
- else
- Ivec[hwcfg.irq].used = 1;
- }
+ /* reset the chip */
- if (hwcfg.vector_latch) {
- if (!request_region(Vector_Latch, 1, "scc vector latch"))
- printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch);
- else
- Vector_Latch = hwcfg.vector_latch;
- }
+ Outb(ctrl->channel_a.ctrl, 0);
+ OutReg(ctrl->channel_a.ctrl, R9, FHWRES);
+ udelay(100);
- if (hwcfg.clock == 0)
- hwcfg.clock = SCC_DEFAULT_CLOCK;
+ /* check if it's really there... */
#ifndef SCC_DONT_CHECK
- disable_irq(hwcfg.irq);
+ OutReg(ctrl->channel_a.ctrl, R13, 0x55);
+ udelay(5);
+ if (InReg(ctrl->channel_a.ctrl, R13) != 0x55)
+ found_a = 0;
+
+ OutReg(ctrl->channel_b.ctrl, R13, 0xaa);
+ udelay(5);
+ if (InReg(ctrl->channel_b.ctrl, R13) != 0xaa)
+ found_b = 0;
+#endif
+ spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags);
- check_region(scc->ctrl, 1);
- Outb(hwcfg.ctrl_a, 0);
- OutReg(hwcfg.ctrl_a, R9, FHWRES);
- udelay(100);
- OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */
- udelay(5);
+ printk(KERN_INFO "z8530drv: chip %d, channel A (0x%3.3x/0x%3.3x) %sfound, "
+ "channel B (0x%3.3x/0x%3.3x) %sfound, irq %d\n",
+ Nchips,
+ (int) ctrl->channel_a.data, (int) ctrl->channel_a.ctrl, found_a? "":"not ",
+ (int) ctrl->channel_b.data, (int) ctrl->channel_b.ctrl, found_b? "":"not ",
+ ctrl->channel_a.irq);
- if (InReg(hwcfg.ctrl_a,R13) != 0x55)
- found = 0;
+ /* at least one channel not properly specified */
+ if (!(found_a && found_b))
+ return -EINVAL;
- enable_irq(hwcfg.irq);
-#endif
+ /* grab IRQ */
+ if (!scc_irq_used[ctrl->channel_a.irq])
+ {
+ if (request_irq(ctrl->channel_a.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
+ {
+ printk(KERN_WARNING "z8530drv: cannot get IRQ %d\n", ctrl->channel_a.irq);
+ return -EINVAL;
+ }
- if (found)
- {
- SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a;
- SCC_Info[2*Nchips ].data = hwcfg.data_a;
- SCC_Info[2*Nchips ].irq = hwcfg.irq;
- SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b;
- SCC_Info[2*Nchips+1].data = hwcfg.data_b;
- SCC_Info[2*Nchips+1].irq = hwcfg.irq;
-
- SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
- SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
- SCC_ctrl[Nchips].irq = hwcfg.irq;
- }
+ scc_irq_used[ctrl->channel_a.irq]++;
+ }
+ /* register IO ports */
+ request_region(ctrl->channel_a.ctrl, 1, "scc ctrl");
+ request_region(ctrl->channel_b.ctrl, 1, "scc ctrl");
+ request_region(ctrl->channel_a.data, 1, "scc data");
+ request_region(ctrl->channel_b.data, 1, "scc data");
- for (chan = 0; chan < 2; chan++)
- {
- sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+ /* register network devices */
+ scc_net_setup(&ctrl->channel_a);
+ scc_net_setup(&ctrl->channel_b);
- SCC_Info[2*Nchips+chan].special = hwcfg.special;
- SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
- SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
- SCC_Info[2*Nchips+chan].option = hwcfg.option;
- SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc;
-#ifdef SCC_DONT_CHECK
- printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n",
- device_name,
- SCC_Info[2*Nchips+chan].data,
- SCC_Info[2*Nchips+chan].ctrl);
+ /* initialize special hardware -- note that port & option need to be set at this point */
+ if (ctrl->channel_a.special_port > 0)
+ {
+ if (ctrl->channel_a.brand & EAGLE) /* enable interrupt on EAGLE card */
+ Outb(ctrl->channel_a.special_port, 0x08);
+ if (ctrl->channel_a.brand & (PC100 | PRIMUS)) /* set MODEM mode on PC100 or PRIMUS cards */
+ Outb(ctrl->channel_a.special_port, ctrl->channel_a.special_option);
+ }
-#else
- printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n",
- device_name,
- chan? hwcfg.data_b : hwcfg.data_a,
- chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
- found? "found" : "missing");
-#endif
- if (found)
- {
- request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
- request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
- if (Nchips+chan != 0)
- scc_net_setup(&SCC_Info[2*Nchips+chan], device_name, 1);
- }
- }
-
- if (found) Nchips++;
-
- return 0;
- }
-
- if (cmd == SIOCSCCINI)
- {
- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
-
- if (Nchips == 0)
- return -EINVAL;
+ /* inhibit change of vital parameters after init; this probably isn't entirely race free
+ * free, but we can live with this (at worst the new mode hasn't been propagated to the VFS
+ * yet and we get an EINVAL though the file seems to be writable. *shrugs* */
- z8530_init();
- return 0;
+ for (entry=0; ctrl->proc_tables.chip[entry].ctl_name; entry++)
+ {
+ switch(ctrl->proc_tables.chip[entry].ctl_name)
+ {
+ case NET_DEV_Z8530DRV_IRQ:
+ case NET_DEV_Z8530DRV_ESCC:
+ case NET_DEV_Z8530DRV_SPECIAL_PORT:
+ case NET_DEV_Z8530DRV_SPECIAL_OPT:
+ ctrl->proc_tables.chip[entry].mode = 0444;
+ break;
}
-
- return -EINVAL; /* confuse the user */
}
-
- if (!scc->init)
+
+ t_port[0] = ctrl->channel_a.proc_tables.channel;
+ t_port[1] = ctrl->channel_b.proc_tables.channel;
+
+ for (port=0; port < 2; port++)
{
- if (cmd == SIOCSCCCHANINI)
+ for (entry=0; t_port[port][entry].ctl_name; entry++)
{
- if (!capable(CAP_NET_ADMIN)) return -EPERM;
- if (!arg) return -EINVAL;
-
- scc->stat.bufsize = SCC_BUFSIZE;
-
- if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
- return -EINVAL;
-
- /* default KISS Params */
-
- if (scc->modem.speed < 4800)
+ switch(t_port[port][entry].ctl_name)
{
- scc->kiss.txdelay = 36; /* 360 ms */
- scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */
- scc->kiss.slottime = 16; /* 160 ms */
- scc->kiss.tailtime = 4; /* minimal reasonable value */
- scc->kiss.fulldup = 0; /* CSMA */
- scc->kiss.waittime = 50; /* 500 ms */
- scc->kiss.maxkeyup = 10; /* 10 s */
- scc->kiss.mintime = 3; /* 3 s */
- scc->kiss.idletime = 30; /* 30 s */
- scc->kiss.maxdefer = 120; /* 2 min */
- scc->kiss.softdcd = 0; /* hardware dcd */
- } else {
- scc->kiss.txdelay = 10; /* 100 ms */
- scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */
- scc->kiss.slottime = 8; /* 160 ms */
- scc->kiss.tailtime = 1; /* minimal reasonable value */
- scc->kiss.fulldup = 0; /* CSMA */
- scc->kiss.waittime = 50; /* 500 ms */
- scc->kiss.maxkeyup = 7; /* 7 s */
- scc->kiss.mintime = 3; /* 3 s */
- scc->kiss.idletime = 30; /* 30 s */
- scc->kiss.maxdefer = 120; /* 2 min */
- scc->kiss.softdcd = 0; /* hardware dcd */
+ case NET_DEV_Z8530DRV_DATA_PORT:
+ case NET_DEV_Z8530DRV_CTRL_PORT:
+ t_port[port][entry].mode = 0444;
+ break;
}
-
- scc->tx_buff = NULL;
- skb_queue_head_init(&scc->tx_queue);
- scc->init = 1;
-
- return 0;
}
-
- return -EINVAL;
}
-
- switch(cmd)
- {
- case SIOCSCCRESERVED:
- return -ENOIOCTLCMD;
-
- case SIOCSCCSMEM:
- if (!capable(CAP_SYS_RAWIO)) return -EPERM;
- if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
- return -EINVAL;
- scc->stat.bufsize = memcfg.bufsize;
- return 0;
-
- case SIOCSCCGSTAT:
- if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
- return -EINVAL;
- return 0;
-
- case SIOCSCCGKISS:
- if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
- return -EINVAL;
- kiss_cmd.param = scc_get_param(scc, kiss_cmd.command);
- if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd)))
- return -EINVAL;
- return 0;
-
- case SIOCSCCSKISS:
- if (!capable(CAP_NET_ADMIN)) return -EPERM;
- if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd)))
- return -EINVAL;
- return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param);
-
- case SIOCSCCCAL:
- if (!capable(CAP_SYS_RAWIO)) return -EPERM;
- if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0)
- return -EINVAL;
- scc_start_calibrate(scc, cal.time, cal.pattern);
- return 0;
-
- default:
- return -ENOIOCTLCMD;
-
- }
-
- return -EINVAL;
+ return 0;
}
/* ----> set interface callsign <---- */
@@ -1980,7 +1609,7 @@ static int scc_net_set_mac_address(struct net_device *dev, void *addr)
static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
{
struct scc_channel *scc = (struct scc_channel *) dev->priv;
-
+
scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
@@ -1990,193 +1619,635 @@ static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
}
/* ******************************************************************** */
-/* * dump statistics to /proc/net/z8530drv * */
+/* * /proc/sys/ interface * */
/* ******************************************************************** */
+/*
+ * structure:
+ * ---------
+ *
+ * /proc/sys/net/dev/z8530drv/
+ * nchips
+ * delay
+ * chip_0/
+ * vendor
+ * clock
+ * irq
+ * delay
+ * enhanced
+ * special_port
+ * special_opt
+ * port_a/
+ * data_port
+ * ctrl_port
+ * ifname
+ * modem/
+ * rx_speed
+ * tx_speed
+ * rx_clock_source
+ * tx_clock_source
+ * trx_pin_mode
+ * fullduplex
+ * trx_does_feedback
+ * nrz_mode
+ * tx_inhibit
+ * softdcd
+ * tx_delay
+ * tx_tail
+ * stats/
+ * rx_ints
+ * tx_ints
+ * ex_ints
+ * sp_ints
+ * tx_frames
+ * rx_frames
+ * tx_errors
+ * rx_errors
+ * tx_underruns
+ * rx_overruns
+ * no_space
+ * tx_state
+ * port_b/
+ * ...
+ * chip_1/
+ * ...
+ *
+ * The idea is to be able to configure the driver through SNMP, store
+ * the configuration in an LDAP directory or place it into an XML
+ * document... The structure may look overly complex for the
+ * purpose, but the old "initialize through a special ioctl()" was too
+ * complex to maintain.
+ */
+
+/* parent node /proc/sys/net/dev */
+
+static ctl_table scc_proc_root_table[] = {
+ {NET_DEV, "dev", NULL, 0, 0555, scc_proc_parent_table},
+ {0}
+};
+
+/* our directory */
-static int scc_net_get_info(char *buffer, char **start, off_t offset, int length)
+static ctl_table scc_proc_parent_table[] = {
+ {NET_DEV_Z8530DRV, "z8530drv", NULL, 0, 0555, scc_proc_nchips_table},
+ {0}
+};
+
+/* number of chips, subdirectories get added or removed dynamically */
+
+static ctl_table scc_proc_nchips_table[] = {
+ {NET_DEV_Z8530DRV_NCHIPS, "nchips",
+ &Nchips, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_nchips, NULL},
+ {NET_DEV_Z8530DRV_DELAY, "io_delay",
+ &IO_Delay, sizeof(int), 0644, NULL,
+ &proc_dointvec, &sysctl_intvec, NULL},
+ {0}
+};
+
+
+static void scc_proc_setup_channel(struct scc_ctrl *ctrl, struct scc_channel *scc)
{
- struct scc_channel *scc;
- struct scc_kiss *kiss;
- struct scc_stat *stat;
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
- int k;
+ int entry;
+ ctl_table *t_port = scc->proc_tables.channel;
+ ctl_table *t_modem = scc->proc_tables.modem;
+ ctl_table *t_stats = scc->proc_tables.stats;
+
+ /* Set /.../chip_<n>/channel_<p>/modem/ */
+
+
+ /* Praise the Lord! We can do this with GCC! */
+ entry=0;
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_RX_SPEED, "rx_speed",
+ &scc->modem.rx_speed, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TX_SPEED, "tx_speed",
+ &scc->modem.tx_speed, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_RX_CLOCK_SOURCE, "rx_clock_source",
+ &scc->modem.rx_clock_source, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TX_CLOCK_SOURCE, "tx_clock_source",
+ &scc->modem.tx_clock_source, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TRXC_PIN_MODE, "trxc_pin_mode",
+ &scc->modem.trxc_pin_mode, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_FULLDUPLEX, "fullduplex",
+ &scc->modem.fullduplex, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TRX_FEEDBACK, "trx_feedback",
+ &scc->modem.trx_feedback, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_NRZ_MODE, "nrz_mode",
+ &scc->modem.nrz_mode, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TX_INHIBIT, "tx_inhibit",
+ &scc->modem.tx_inhibit, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_SOFTDCD, "softdcd",
+ &scc->modem.softdcd, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TX_DELAY, "tx_delay",
+ &scc->modem.tx_delay, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_TX_TAIL, "tx_tail",
+ &scc->modem.tx_tail, sizeof(int), 0644, NULL,
+ proc_dointvec, scc_proc_intvec_modem, NULL,
+ (void *) scc
+ };
+ t_modem[entry] = (ctl_table) {0};
+
+ /* Set /.../chip_<n>/channel_<p>/stats/ */
+ entry = 0;
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_RX_INTS, "rx_ints",
+ &scc->stat.rxints, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_TX_INTS, "tx_ints",
+ &scc->stat.txints, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_EX_INTS, "ex_ints",
+ &scc->stat.exints, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_TX_FRAMES, "tx_frames",
+ &scc->stat.txframes, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_RX_FRAMES, "rx_frames",
+ &scc->stat.rxframes, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_TX_ERRORS, "tx_errors",
+ &scc->stat.txerrs, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_RX_ERRORS, "rx_errors",
+ &scc->stat.rxerrs, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_TX_UNDERRUNS, "tx_underruns",
+ &scc->stat.tx_under, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_RX_OVERRUNS, "rx_overruns",
+ &scc->stat.rx_over, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_NO_SPACE, "no_space",
+ &scc->stat.nospace, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT_TX_STATE, "tx_state",
+ &scc->stat.tx_state, sizeof(int), 0444, NULL,
+ &proc_dointvec, &scc_proc_intvec_modem, NULL
+ };
+ t_stats[entry] = (ctl_table) {0};
+
+ /* Set /.../chip_<n>/channel_<p>/ */
+
+ entry = 0;
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_DATA_PORT, "data_port",
+ &scc->data, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_port, NULL,
+ (void *) ctrl, (void *) scc
+ };
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_CTRL_PORT, "ctrl_port",
+ &scc->ctrl, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) ctrl, (void *) scc
+ };
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_IFNAME, "ifname",
+ &scc->proc_dev_name, IFNAMSIZ, 0444, NULL,
+ &proc_dostring, &sysctl_string, NULL,
+ (void *) ctrl, (void *) scc
+ };
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_BUFSIZE, "bufsize",
+ &scc->stat.bufsize, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_port, NULL,
+ (void *) ctrl, (void *) scc
+ };
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_MODEM, "modem",
+ NULL, 0, 0555, t_modem
+ };
+ t_port[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_STAT, "stats",
+ NULL, 0, 0555, t_stats
+ };
+ t_port[entry] = (ctl_table) {0};
+
+ memset(scc, 0, sizeof(struct scc_channel));
+ scc->clock = SCC_DEFAULT_CLOCK;
+ scc->ctrl = -1;
+ scc->data = -1;
+ scc->special_port = -1;
+ scc->irq = -1;
+ scc->modem.rx_speed = 9600;
+ scc->modem.tx_speed = 9600;
+ scc->modem.tx_clock_source = CLOCK_SOURCE_RTxC;
+ scc->modem.rx_clock_source = CLOCK_SOURCE_TRxC;
+ scc->modem.trxc_pin_mode = TRXCP_MODE_IN;
+ spin_lock_init(&scc->spinlocks.hwaccess);
+ spin_lock_init(&scc->spinlocks.timer);
+ spin_lock_init(&scc->spinlocks.kick_tx);
+}
- len += sprintf(buffer, "z8530drv-"VERSION"\n");
- if (!Driver_Initialized)
- {
- len += sprintf(buffer+len, "not initialized\n");
- goto done;
- }
+static void scc_proc_setup_chip(void)
+{
+ int entry;
+ ctl_table *t_chip = scc_ctrl[Nchips]->proc_tables.chip;
+ struct scc_channel *channel_a = &scc_ctrl[Nchips]->channel_a;
+ struct scc_channel *channel_b = &scc_ctrl[Nchips]->channel_b;
+
+ /* Set /.../chip_<n>/ */
+
+ entry=0;
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_VENDOR, "vendor",
+ &channel_a->brand, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_CLOCK, "clock",
+ &channel_a->clock, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_IRQ, "irq",
+ &channel_a->irq, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_ESCC, "enhanced",
+ &channel_a->enhanced, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_SPECIAL_PORT, "special_port",
+ &channel_a->special_port, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_SPECIAL_OPT, "special_opt",
+ &channel_a->special_option, sizeof(int), 0644, NULL,
+ &proc_dointvec, &scc_proc_intvec_chip, NULL,
+ (void *) &scc_ctrl[Nchips]
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_CHANNEL_A, "channel_a",
+ NULL, 0, 0555, channel_a->proc_tables.channel
+ };
+ t_chip[entry++] = (ctl_table) {
+ NET_DEV_Z8530DRV_CHANNEL_B, "channel_b",
+ NULL, 0, 0555, channel_b->proc_tables.channel
+ };
+ t_chip[entry] = (ctl_table) {0};
+}
+
+static void scc_proc_remove_channel(struct scc_ctrl *ctrl, struct scc_channel *scc)
+{
+ long flags;
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ /* disable interrupts */
+ cl(scc, R9, MIE);
+ udelay(50);
- if (!Nchips)
+ /* free io ports */
+ release_region(scc->ctrl, 1);
+ release_region(scc->data, 1);
+ if (scc->dev)
{
- len += sprintf(buffer+len, "chips missing\n");
- goto done;
+ unregister_netdev(scc->dev);
+ kfree(scc->dev);
}
- for (k = 0; k < Nchips*2; k++)
- {
- scc = &SCC_Info[k];
- stat = &scc->stat;
- kiss = &scc->kiss;
-
- if (!scc->init)
- continue;
-
- /* dev data ctrl irq clock brand enh vector special option
- * baud nrz clocksrc softdcd bufsize
- * rxints txints exints spints
- * rcvd rxerrs over / xmit txerrs under / nospace bufsize
- * txd pers slot tail ful wait min maxk idl defr txof grp
- * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
- * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
- */
-
- len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
- scc->dev->name,
- scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
- scc->enhanced, Vector_Latch, scc->special,
- scc->option);
- len += sprintf(buffer+len, "\t%lu %d %d %d %d\n",
- scc->modem.speed, scc->modem.nrz,
- scc->modem.clocksrc, kiss->softdcd,
- stat->bufsize);
- len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n",
- stat->rxints, stat->txints, stat->exints, stat->spints);
- len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
- stat->rxframes, stat->rxerrs, stat->rx_over,
- stat->txframes, stat->txerrs, stat->tx_under,
- stat->nospace, stat->tx_state);
-
-#define K(x) kiss->x
- len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n",
- K(txdelay), K(persist), K(slottime), K(tailtime),
- K(fulldup), K(waittime), K(mintime), K(maxkeyup),
- K(idletime), K(maxdefer), K(tx_inhibit), K(group));
-#undef K
-#ifdef SCC_DEBUG
- {
- int reg;
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+}
- len += sprintf(buffer+len, "\tW ");
- for (reg = 0; reg < 16; reg++)
- len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
- len += sprintf(buffer+len, "\n");
-
- len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
- for (reg = 3; reg < 8; reg++)
- len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
- len += sprintf(buffer+len, "XX ");
- for (reg = 9; reg < 16; reg++)
- len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
- len += sprintf(buffer+len, "\n");
- }
-#endif
- len += sprintf(buffer+len, "\n");
+static void scc_proc_remove_chip(struct scc_ctrl *ctrl)
+{
+ long flags;
+ int irq;
- pos = begin + len;
+ spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags);
- if (pos < offset) {
- len = 0;
- begin = pos;
- }
+ Outb(ctrl->channel_a.ctrl, 0);
+ OutReg(ctrl->channel_a.ctrl, R9, FHWRES);
- if (pos > offset + length)
- break;
+ irq = ctrl->channel_a.irq;
+ if (--scc_irq_used[irq] <= 0)
+ {
+ free_irq(irq, NULL);
+ scc_irq_used[irq] = 0; /* paranoia */
}
-done:
+ spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags);
- *start = buffer + (offset - begin);
- len -= (offset - begin);
+ unregister_sysctl_table(scc_ctrl[Nchips]->proc_table_head);
+ kfree(scc_ctrl[Nchips]->proc_tables.dir[0].procname);
+ kfree(scc_ctrl[Nchips]);
+}
- if (len > length) len = length;
- return len;
-}
+static int scc_proc_intvec_modem(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
+{
+ int retval = 0;
+ struct scc_channel *scc = (struct scc_channel *) table->extra1;
+ if (scc == NULL || !scc->init) return -EINVAL;
-
-/* ******************************************************************** */
-/* * Init SCC driver * */
-/* ******************************************************************** */
+ switch(table->ctl_name)
+ {
+ case NET_DEV_Z8530DRV_RX_SPEED:
+ case NET_DEV_Z8530DRV_TX_SPEED:
+ if (scc->stat.tx_state != TXS_ACTIVE)
+ set_speed(scc);
+ break;
+ case NET_DEV_Z8530DRV_RX_CLOCK_SOURCE:
+ case NET_DEV_Z8530DRV_TX_CLOCK_SOURCE:
+ case NET_DEV_Z8530DRV_TRXC_PIN_MODE:
+ wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode));
+ scc_init_brg_and_dpll(scc);
+ break;
+ case NET_DEV_Z8530DRV_FULLDUPLEX:
+ case NET_DEV_Z8530DRV_TRX_FEEDBACK:
+ case NET_DEV_Z8530DRV_TX_INHIBIT:
+ case NET_DEV_Z8530DRV_TX_DELAY:
+ case NET_DEV_Z8530DRV_TX_TAIL:
+ break;
+ case NET_DEV_Z8530DRV_NRZ_MODE:
+ case NET_DEV_Z8530DRV_SOFTDCD:
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ scc_ddi_param_update(scc->dev);
+ return retval;
+}
-static int __init scc_init_driver (void)
+static int scc_proc_intvec_port(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
{
- int result;
- char devname[10];
-
- printk(banner);
-
- sprintf(devname,"%s0", SCC_DriverName);
+ struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1;
+ struct scc_channel *scc = (struct scc_channel *) table->extra2;
+ int val;
+
+ val = *((int *) newval);
+
+ if (scc == NULL) return -EINVAL;
- result = scc_net_setup(SCC_Info, devname, 0);
- if (result)
+ switch(table->ctl_name)
{
- printk(KERN_ERR "z8530drv: cannot initialize module\n");
- return result;
+ case NET_DEV_Z8530DRV_DATA_PORT:
+ if (val > 0)
+ {
+ scc_check_and_init(ctrl);
+ return 1;
+ }
+ break;
+ case NET_DEV_Z8530DRV_CTRL_PORT:
+ if (val > 0)
+ {
+ scc_check_and_init(ctrl);
+ return 1;
+ }
+ break;
+ case NET_DEV_Z8530DRV_BUFSIZE:
+ break;
}
- proc_net_create("z8530drv", 0, scc_net_get_info);
-
return 0;
}
-static void __exit scc_cleanup_driver(void)
+static int scc_proc_intvec_chip(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
{
- long flags;
- io_port ctrl;
- int k;
- struct scc_channel *scc;
-
- save_flags(flags);
- cli();
+ struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1;
+ int val;
+
+ if (ctrl == NULL) return -EINVAL;
+ if (ctrl->channel_a.init || ctrl->channel_b.init)
+ return -EINVAL;
+
+ val = *((int *) newval);
- if (Nchips == 0)
+ switch(table->ctl_name)
{
- unregister_netdev(SCC_Info[0].dev);
- kfree(SCC_Info[0].dev);
+ case NET_DEV_Z8530DRV_VENDOR:
+ case NET_DEV_Z8530DRV_CLOCK:
+ case NET_DEV_Z8530DRV_ESCC:
+ case NET_DEV_Z8530DRV_SPECIAL_PORT:
+ case NET_DEV_Z8530DRV_SPECIAL_OPT:
+ break;
+ case NET_DEV_Z8530DRV_IRQ:
+ if (val > 0)
+ {
+ ctrl->channel_a.irq = val;
+ ctrl->channel_b.irq = val;
+ scc_check_and_init(ctrl);
+ return 1;
+ }
+ break;
}
- for (k = 0; k < Nchips; k++)
- if ( (ctrl = SCC_ctrl[k].chan_A) )
- {
- Outb(ctrl, 0);
- OutReg(ctrl,R9,FHWRES); /* force hardware reset */
- udelay(50);
- }
-
- for (k = 0; k < Nchips*2; k++)
+ return 0;
+}
+
+static int scc_proc_intvec_nchips(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
+{
+ struct scc_ctrl_proc_tables *tables = NULL;
+ struct scc_ctrl *ctrl = NULL;
+ int chip;
+ char *dirname;
+ int new_nchips, old_nchips;
+
+ new_nchips = *((int *) newval);
+ old_nchips = *((int *) oldval);
+
+ /* sanity checks...*/
+
+ if (new_nchips < 0 || new_nchips > maxchips)
+ return -EINVAL;
+
+ /* adding chips */
+ if (new_nchips > old_nchips)
{
- scc = &SCC_Info[k];
- if (scc->ctrl)
+ for (chip=0; chip < new_nchips - old_nchips; chip++)
{
- release_region(scc->ctrl, 1);
- release_region(scc->data, 1);
+ ctrl = (struct scc_ctrl *) kmalloc(sizeof(struct scc_ctrl), GFP_KERNEL);
+ if (ctrl == NULL) return -ENOMEM;
+ scc_ctrl[Nchips] = ctrl;
+ memset(ctrl, 0, sizeof(struct scc_ctrl));
+
+ tables = &ctrl->proc_tables;
+ scc_proc_setup_channel(ctrl, &ctrl->channel_a);
+ scc_proc_setup_channel(ctrl, &ctrl->channel_b);
+ scc_proc_setup_chip();
+
+ /* !!FIXME!! free memory when removing a chip */
+ dirname = kmalloc(sizeof(char)*IFNAMSIZ, GFP_KERNEL);
+ if (dirname == NULL) return -ENOMEM;
+
+ sprintf(dirname, "chip_%d", Nchips);
+ tables->dir[0] = (ctl_table) {
+ NET_DEV_Z8530DRV_CHIP_BASE+Nchips, dirname,
+ NULL, 0, 0555, tables->chip
+ };
+ tables->dir[1] = (ctl_table) {0};
+
+ tables->parent[0] = (ctl_table) {
+ NET_DEV_Z8530DRV, "z8530drv",
+ NULL, 0, 0555, tables->dir
+ };
+ tables->parent[1] = (ctl_table) {0};
+
+ /* add return value to scc structure */
+ scc_ctrl[Nchips]->proc_table_head = register_sysctl_table(tables->parent, 1);
+ Nchips++;
}
- if (scc->dev)
+
+ *((int *) newval) = Nchips;
+ }
+ else if (new_nchips < old_nchips)
+ {
+ for(chip=0; chip < old_nchips - new_nchips; chip++)
{
- unregister_netdev(scc->dev);
- kfree(scc->dev);
+ ctrl = scc_ctrl[Nchips-1];
+
+ /* !!FIXME!! possible race condition here */
+
+ if (netif_running(ctrl->channel_a.dev) ||
+ netif_running(ctrl->channel_b.dev))
+ break;
+
+ scc_proc_remove_channel(ctrl, &ctrl->channel_a);
+ scc_proc_remove_channel(ctrl, &ctrl->channel_b);
+ scc_proc_remove_chip(ctrl);
+
+ Nchips--;
}
+
+ *((int *) newval) = Nchips;
}
- for (k=0; k < 16 ; k++)
- if (Ivec[k].used) free_irq(k, NULL);
-
- if (Vector_Latch)
- release_region(Vector_Latch, 1);
+ return 0;
+}
+
+
+
+/* ******************************************************************** */
+/* * Init SCC driver * */
+/* ******************************************************************** */
+
+
+int __init scc_init_driver(void)
+{
+ int k;
+ size_t size;
+
+ printk(KERN_INFO BANNER);
- restore_flags(flags);
+ size=sizeof(struct scc_ctrl)*(maxchips+1);
+ scc_ctrl = kmalloc(size, GFP_KERNEL);
+ if (scc_ctrl == NULL) return -ENOMEM;
+ memset(scc_ctrl, 0, size);
+
+ for (k = 0; k < 16; k++) scc_irq_used[k] = 0;
+
+#ifdef CONFIG_PROC_FS
+ scc_proc_table_header = register_sysctl_table(scc_proc_root_table, 1);
+#endif
+ return 0;
+}
+
+/* ******************************************************************** */
+/* * Module support * */
+/* ******************************************************************** */
+
+
+void __exit scc_cleanup_driver(void)
+{
+ int k;
+ for (k = 0; k < Nchips; k++)
+ {
+ scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_a);
+ scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_b);
+ scc_proc_remove_chip(scc_ctrl[k]);
+ }
+
+#ifdef CONFIG_PROC_FS
+ unregister_sysctl_table(scc_proc_table_header);
proc_net_remove("z8530drv");
+#endif
+
+ kfree(scc_ctrl);
}
MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
-MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards");
-MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards without DMA support");
+MODULE_PARM(maxchips, "i");
module_init(scc_init_driver);
module_exit(scc_cleanup_driver);
diff --git a/drivers/net/hamradio/scc.c-noprocfs b/drivers/net/hamradio/scc.c-noprocfs
new file mode 100644
index 000000000..96ba211a3
--- /dev/null
+++ b/drivers/net/hamradio/scc.c-noprocfs
@@ -0,0 +1,1926 @@
+#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $"
+
+#define VERSION "4.0"
+#define BANNER "Z8530 SCC driver version "VERSION".dl1bke by DL1BKE\n"
+
+/*
+ * Please use z8530drv-utils-4.0 with this version.
+ * ------------------
+ *
+ * You can find a subset of the documentation in
+ * linux/Documentation/networking/z8530drv.txt.
+ */
+
+/*
+ ********************************************************************
+ * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 *
+ ********************************************************************
+
+
+ ********************************************************************
+
+ Copyright (c) 1993, 2000 Joerg Reuter DL1BKE
+
+ portions (c) 1993 Guido ten Dolle PE1NNZ
+
+ ********************************************************************
+
+ The driver and the programs in the archive are UNDER CONSTRUCTION.
+ The code is likely to fail, and so your kernel could --- even
+ a whole network.
+
+ This driver is intended for Amateur Radio use. If you are running it
+ for commercial purposes, please drop me a note. I am nosy...
+
+ ...BUT:
+
+ ! You m u s t recognize the appropriate legislations of your country !
+ ! before you connect a radio to the SCC board and start to transmit or !
+ ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! !
+
+ For non-Amateur-Radio use please note that you might need a special
+ allowance/licence from the designer of the SCC Board and/or the
+ MODEM.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the (modified) GNU General Public License
+ delivered with the Linux kernel source.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should find a copy of the GNU General Public License in
+ /usr/src/linux/COPYING;
+
+ ********************************************************************
+
+
+ Incomplete history of z8530drv:
+ -------------------------------
+
+ 1994-09-13 started to write the driver, rescued most of my own
+ code (and Hans Alblas' memory buffer pool concept) from
+ an earlier project "sccdrv" which was initiated by
+ Guido ten Dolle. Not much of the old driver survived,
+ though. The first version I put my hands on was sccdrv1.3
+ from August 1993. The memory buffer pool concept
+ appeared in an unauthorized sccdrv version (1.5) from
+ August 1994.
+
+ 1995-01-31 changed copyright notice to GPL without limitations.
+
+ .
+ . <SNIP>
+ .
+
+ 1996-10-05 New semester, new driver...
+
+ * KISS TNC emulator removed (TTY driver)
+ * Source moved to drivers/net/
+ * Includes Z8530 defines from drivers/net/z8530.h
+ * Uses sk_buffer memory management
+ * Reduced overhead of /proc/net/z8530drv output
+ * Streamlined quite a lot things
+ * Invents brand new bugs... ;-)
+
+ The move to version number 3.0 reflects theses changes.
+ You can use 'kissbridge' if you need a KISS TNC emulator.
+
+ 1996-12-13 Fixed for Linux networking changes. (G4KLX)
+ 1997-01-08 Fixed the remaining problems.
+ 1997-04-02 Hopefully fixed the problems with the new *_timer()
+ routines, added calibration code.
+ 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO
+ 1998-01-29 Small fix to avoid lock-up on initialization
+ 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again,
+ using dev->tx_queue_len now instead of MAXQUEUE now.
+ 1998-10-21 Postponed the spinlock changes, would need a lot of
+ testing I currently don't have the time to. Softdcd doesn't
+ work.
+ 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it
+ never did. The DPLL locks on noise, the SYNC unit sees
+ flags that aren't... Restarting the DPLL does not help
+ either, it resynchronizes too slow and the first received
+ frame gets lost.
+ 1999-02-21 Started to implement the new AX.25 device interface
+ 2000-07-18 Ported to 2.4.x
+
+ Thanks to all who contributed to this driver with ideas and bug
+ reports!
+
+ NB -- if you find errors, change something, please let me know
+ first before you distribute it... And please don't touch
+ the version number. Just replace my callsign in
+ "v4.0.dl1bke" with your own. Just to avoid confusion...
+
+ If you want to add your modification to the linux distribution
+ please (!) contact me first.
+
+ New versions of the driver will be announced on the linux-hams
+ mailing list on vger.kernel.org. To subscribe send an e-mail
+ to majordomo@vger.kernel.org with the following line in
+ the body of the mail:
+
+ subscribe linux-hams
+
+ The content of the "Subject" field will be ignored.
+
+ vy 73,
+ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org
+ AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU
+ Internet: jreuter@yaina.de
+ www : http://yaina.de/jreuter/
+*/
+
+/* ----------------------------------------------------------------------- */
+
+#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */
+
+#define SCC_MAXCHIPS 4 /* number of max. supported chips */
+#define SCC_BUFSIZE 384 /* must not exceed 4096 */
+#undef SCC_DEBUG
+
+#define SCC_DEFAULT_CLOCK 4915200
+ /* default pclock if nothing is specified */
+
+#define SCC_SIMPLE_MAC /* no rts/cts control by DDI layer */
+#define SCC_WATCHDOG_TIMEOUT 10
+ /* ten seconds */
+
+/* ----------------------------------------------------------------------- */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/socket.h>
+#include <linux/init.h>
+
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include <asm/irq.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+
+#include <linux/scc.h>
+#include "z8530.h"
+
+int scc_init(void);
+
+static void scc_tx_done(struct scc_channel *);
+static void scc_kick_tx(struct scc_channel *);
+static void scc_start_tx_timer(struct scc_channel *, void (*)(struct scc_channel *), unsigned long);
+static void scc_tail(struct scc_channel *scc);
+#ifndef SCC_SIMPLE_MAC
+static void scc_tx_forced(struct scc_channel *scc);
+static void scc_set_rts(struct scc_channel *scc);
+#endif
+
+static void z8530_init(void);
+
+static void init_channel(struct scc_channel *scc);
+static void scc_key_trx (struct scc_channel *scc, char tx);
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs);
+static void scc_init_timer(struct scc_channel *scc);
+
+static unsigned int scc_set_param(struct scc_channel *, struct scc_modem *);
+
+static unsigned int scc_ddi_report_dcd(struct net_device *);
+static unsigned int scc_ddi_report_ptt(struct net_device *);
+#ifndef SCC_SIMPLE_MAC
+static unsigned int scc_ddi_report_cts(struct net_device *);
+static void scc_ddi_set_rts(struct net_device *);
+#endif
+static void scc_ddi_set_bitrate(struct net_device *, unsigned int);
+static void scc_ddi_param_update(struct net_device *);
+static void scc_ddi_param_notify(struct net_device *, int, int, int);
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev);
+static int scc_net_init(struct net_device *dev);
+static int scc_net_open(struct net_device *dev);
+static int scc_net_close(struct net_device *dev);
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb);
+static int scc_net_tx(struct sk_buff *skb, struct net_device *dev);
+static void scc_net_timeout(struct net_device *dev);
+static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int scc_net_set_mac_address(struct net_device *dev, void *addr);
+static struct net_device_stats * scc_net_get_stats(struct net_device *dev);
+
+static unsigned char *SCC_DriverName = "scc";
+
+static struct irqflags { unsigned char used : 1; } Ivec[16];
+
+static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */
+
+static struct scc_ctrl {
+ io_port chan_A;
+ io_port chan_B;
+ int irq;
+} SCC_ctrl[SCC_MAXCHIPS+1];
+
+static unsigned char Driver_Initialized = 0;
+static int Nchips = 0;
+static io_port Vector_Latch = 0;
+static long IO_Delay = 0;
+static spinlock_t IO_Spinlock = SPIN_LOCK_UNLOCKED;
+
+
+/* ******************************************************************** */
+/* * Port Access Functions * */
+/* ******************************************************************** */
+
+static inline unsigned char
+Inb(io_port port)
+{
+ int r;
+
+ r = inb(port);
+ if (IO_Delay) udelay(IO_Delay);
+ return r;
+}
+
+static inline void
+Outb(io_port port, unsigned char val)
+{
+ outb(val, port);
+ if (IO_Delay) udelay(IO_Delay);
+}
+
+/* These provide interrupt save 2-step access to the Z8530 registers */
+
+static unsigned char
+InReg(io_port port, unsigned char reg)
+{
+ unsigned long flags;
+ unsigned char r;
+
+ spin_lock_irqsave(&IO_Spinlock, flags);
+ if (IO_Delay)
+ {
+ outb(reg, port);
+ udelay(IO_Delay);
+ r=inb(port);
+ udelay(IO_Delay);
+ } else {
+ outb(reg, port);
+ r=inb(port);
+ }
+ spin_unlock_irqrestore(&IO_Spinlock, flags);
+ return r;
+}
+
+static void
+OutReg(io_port port, unsigned char reg, unsigned char val)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&IO_Spinlock, flags);
+ if (IO_Delay)
+ {
+ outb(reg, port);
+ udelay(IO_Delay);
+ outb(val, port);
+ udelay(IO_Delay);
+ } else {
+ outb(reg, port);
+ outb(val, port);
+ }
+ spin_unlock_irqrestore(&IO_Spinlock, flags);
+}
+
+static inline
+void wr(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] = val));
+}
+
+static inline void
+or(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val));
+}
+
+static inline void
+cl(struct scc_channel *scc, unsigned char reg, unsigned char val)
+{
+ OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val));
+}
+
+/* ******************************************************************** */
+/* * Interrupt Service Routines * */
+/* ******************************************************************** */
+
+
+static inline void flush_rx_FIFO(struct scc_channel *scc)
+{
+ int k;
+
+ for (k=0; k<3; k++)
+ Inb(scc->data);
+
+ if(scc->rx_buff != NULL) /* did we receive something? */
+ {
+ scc->stat.rxerrs++; /* then count it as an error */
+ kfree_skb(scc->rx_buff);
+ scc->rx_buff = NULL;
+ }
+}
+
+static void start_hunt(struct scc_channel *scc)
+{
+ if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+ or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */
+}
+
+/* ----> four different interrupt handlers for Tx, Rx, changing of */
+/* DCD/CTS and Rx/Tx errors */
+
+/* Transmitter interrupt handler */
+static inline void scc_txint(struct scc_channel *scc)
+{
+ struct sk_buff *skb;
+
+ scc->stat.txints++;
+ skb = scc->tx_buff;
+
+ /* send first octet */
+
+ if (skb == NULL)
+ {
+ netif_wake_queue(scc->dev);
+ scc_tx_done(scc);
+ Outb(scc->ctrl, RES_Tx_P);
+ return;
+ }
+
+ /* End Of Frame... */
+
+ if (skb->len == 0)
+ {
+ Outb(scc->ctrl, RES_Tx_P); /* reset pending int */
+ cl(scc, R10, ABUNDER); /* send CRC */
+ dev_kfree_skb(skb);
+ scc->tx_buff = NULL;
+ scc_kick_tx(scc); /* next frame */
+ return;
+ }
+
+ /* send octet */
+
+ Outb(scc->data,*skb->data);
+ skb_pull(skb, 1);
+}
+
+
+/* External/Status interrupt handler */
+static inline void scc_exint(struct scc_channel *scc)
+{
+ unsigned char status,changes,chg_and_stat;
+
+ scc->stat.exints++;
+
+ status = InReg(scc->ctrl,R0);
+ changes = status ^ scc->status;
+ chg_and_stat = changes & status;
+
+ /* ABORT: generated whenever DCD drops while receiving */
+
+ if (chg_and_stat & BRK_ABRT) /* Received an ABORT */
+ flush_rx_FIFO(scc);
+
+ /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */
+
+ if ((changes & SYNC_HUNT) && scc->modem.softdcd)
+ {
+ if (status & SYNC_HUNT)
+ {
+ scc->dcd = 0;
+ flush_rx_FIFO(scc);
+ if ((scc->modem.clocksrc != CLK_EXTERNAL))
+ OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */
+ } else {
+ scc->dcd = 1;
+ }
+ }
+
+ /* DCD: on = start to receive packet, off = ABORT condition */
+ /* (a successfully received packet generates a special condition int) */
+
+ if((changes & DCD) && !scc->modem.softdcd) /* DCD input changed state */
+ {
+ if(status & DCD) /* DCD is now ON */
+ {
+ start_hunt(scc);
+ scc->dcd = 1;
+ } else { /* DCD is now OFF */
+ cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */
+ flush_rx_FIFO(scc);
+ scc->dcd = 0;
+ }
+ }
+
+#ifdef notdef
+ /* CTS: use external TxDelay (what's that good for?!)
+ * Anyway: If we _could_ use it (BayCom USCC uses CTS for
+ * own purposes) we _should_ use the "autoenable" feature
+ * of the Z8530 and not this interrupt...
+ */
+
+ if (chg_and_stat & CTS) /* CTS is now ON */
+ {
+ if (scc->modem.txdelay == 0) /* zero TXDELAY = wait for CTS */
+ scc_start_tx_timer(scc, t_txdelay, 0);
+ }
+#endif
+
+ if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM))
+ {
+ scc->stat.tx_under++; /* oops, an underrun! count 'em */
+ Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */
+
+ if (scc->tx_buff != NULL)
+ {
+ dev_kfree_skb(scc->tx_buff);
+ scc->tx_buff = NULL;
+ }
+
+ or(scc,R10,ABUNDER);
+ scc_tx_done(scc);
+ }
+
+ scc->status = status;
+ Outb(scc->ctrl,RES_EXT_INT);
+}
+
+
+/* Receiver interrupt handler */
+static inline void scc_rxint(struct scc_channel *scc)
+{
+ struct sk_buff *skb;
+
+ scc->stat.rxints++;
+
+ if((scc->wreg[5] & RTS) && (scc->modem.fullduplex == 0))
+ {
+ Inb(scc->data); /* discard char */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+ return;
+ }
+
+ skb = scc->rx_buff;
+
+ if (skb == NULL)
+ {
+ skb = dev_alloc_skb(scc->stat.bufsize);
+ if (skb == NULL)
+ {
+ scc->dev_stat.rx_dropped++;
+ scc->stat.nospace++;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
+ }
+
+ scc->rx_buff = skb;
+ }
+
+ if (skb->len > scc->stat.bufsize)
+ {
+#ifdef notdef
+ printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n");
+#endif
+ kfree_skb(skb);
+ scc->rx_buff = NULL;
+ Inb(scc->data);
+ or(scc, R3, ENT_HM);
+ return;
+ }
+
+ *(skb_put(skb, 1)) = Inb(scc->data);
+}
+
+
+/* Receive Special Condition interrupt handler */
+static inline void scc_spint(struct scc_channel *scc)
+{
+ unsigned char status;
+ struct sk_buff *skb;
+
+ scc->stat.spints++;
+
+ status = InReg(scc->ctrl,R1); /* read receiver status */
+
+ Inb(scc->data); /* throw away Rx byte */
+ skb = scc->rx_buff;
+
+ if(status & Rx_OVR) /* receiver overrun */
+ {
+ scc->stat.rx_over++; /* count them */
+ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */
+
+ if (skb != NULL)
+ kfree_skb(skb);
+ scc->rx_buff = NULL;
+ }
+
+ if(status & END_FR && skb != NULL) /* end of frame */
+ {
+ /* CRC okay, frame ends on 8 bit boundary and received something ? */
+
+ if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0)
+ {
+ /* ignore last received byte (first of the CRC bytes) */
+ skb_trim(skb, skb->len-1);
+ scc_net_rx(scc, skb);
+ scc->rx_buff = NULL;
+ scc->stat.rxframes++;
+ } else { /* a bad frame */
+ kfree_skb(skb);
+ scc->rx_buff = NULL;
+ scc->stat.rxerrs++;
+ }
+ }
+
+ Outb(scc->ctrl,ERR_RES);
+}
+
+
+/* ----> interrupt service routine for the Z8530 <---- */
+
+static void scc_isr_dispatch(struct scc_channel *scc, int vector)
+{
+ switch (vector & VECTOR_MASK)
+ {
+ case TXINT: scc_txint(scc); break;
+ case EXINT: scc_exint(scc); break;
+ case RXINT: scc_rxint(scc); break;
+ case SPINT: scc_spint(scc); break;
+ }
+}
+
+/* If the card has a latch for the interrupt vector (like the PA0HZP card)
+ use it to get the number of the chip that generated the int.
+ If not: poll all defined chips.
+ */
+
+#define SCC_IRQTIMEOUT 30000
+
+static void scc_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned char vector;
+ struct scc_channel *scc;
+ struct scc_ctrl *ctrl;
+ int k;
+
+ if (Vector_Latch)
+ {
+ for(k=0; k < SCC_IRQTIMEOUT; k++)
+ {
+ Outb(Vector_Latch, 0); /* Generate INTACK */
+
+ /* Read the vector */
+ if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break;
+ if (vector & 0x01) break;
+
+ scc=&SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+
+ OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */
+ }
+
+ if (k == SCC_IRQTIMEOUT)
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n");
+
+ return;
+ }
+
+ /* Find the SCC generating the interrupt by polling all attached SCCs
+ * reading RR3A (the interrupt pending register)
+ */
+
+ ctrl = SCC_ctrl;
+ while (ctrl->chan_A)
+ {
+ if (ctrl->irq != irq)
+ {
+ ctrl++;
+ continue;
+ }
+
+ scc = NULL;
+ for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++)
+ {
+ vector=InReg(ctrl->chan_B,R2); /* Read the vector */
+ if (vector & 0x01) break;
+
+ scc = &SCC_Info[vector >> 3 ^ 0x01];
+ if (!scc->dev) break;
+
+ scc_isr_dispatch(scc, vector);
+ }
+
+ if (k == SCC_IRQTIMEOUT)
+ {
+ printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n");
+ break;
+ }
+
+ /* This looks weird and it is. At least the BayCom USCC doesn't
+ * use the Interrupt Daisy Chain, thus we'll have to start
+ * all over again to be sure not to miss an interrupt from
+ * (any of) the other chip(s)...
+ * Honestly, the situation *is* braindamaged...
+ */
+
+ if (scc != NULL)
+ {
+ OutReg(scc->ctrl,R0,RES_H_IUS);
+ ctrl = SCC_ctrl;
+ } else
+ ctrl++;
+ }
+}
+
+
+
+/* ******************************************************************** */
+/* * Init Channel */
+/* ******************************************************************** */
+
+
+/* ----> set SCC channel speed <---- */
+
+static inline void set_brg(struct scc_channel *scc, unsigned int tc)
+{
+ cl(scc,R14,BRENABL); /* disable baudrate generator */
+ wr(scc,R12,tc & 255); /* brg rate LOW */
+ wr(scc,R13,tc >> 8); /* brg rate HIGH */
+ or(scc,R14,BRENABL); /* enable baudrate generator */
+}
+
+static inline void set_speed(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ if (scc->modem.speed > 0) /* paranoia... */
+ set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2);
+
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+}
+
+
+/* ----> initialize a SCC channel <---- */
+
+static inline void init_brg(struct scc_channel *scc)
+{
+ wr(scc, R14, BRSRC); /* BRG source = PCLK */
+ OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */
+ OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */
+}
+
+/*
+ * Initialization according to the Z8530 manual (SGS-Thomson's version):
+ *
+ * 1. Modes and constants
+ *
+ * WR9 11000000 chip reset
+ * WR4 XXXXXXXX Tx/Rx control, async or sync mode
+ * WR1 0XX00X00 select W/REQ (optional)
+ * WR2 XXXXXXXX program interrupt vector
+ * WR3 XXXXXXX0 select Rx control
+ * WR5 XXXX0XXX select Tx control
+ * WR6 XXXXXXXX sync character
+ * WR7 XXXXXXXX sync character
+ * WR9 000X0XXX select interrupt control
+ * WR10 XXXXXXXX miscellaneous control (optional)
+ * WR11 XXXXXXXX clock control
+ * WR12 XXXXXXXX time constant lower byte (optional)
+ * WR13 XXXXXXXX time constant upper byte (optional)
+ * WR14 XXXXXXX0 miscellaneous control
+ * WR14 XXXSSSSS commands (optional)
+ *
+ * 2. Enables
+ *
+ * WR14 000SSSS1 baud rate enable
+ * WR3 SSSSSSS1 Rx enable
+ * WR5 SSSS1SSS Tx enable
+ * WR0 10000000 reset Tx CRG (optional)
+ * WR1 XSS00S00 DMA enable (optional)
+ *
+ * 3. Interrupt status
+ *
+ * WR15 XXXXXXXX enable external/status
+ * WR0 00010000 reset external status
+ * WR0 00010000 reset external status twice
+ * WR1 SSSXXSXX enable Rx, Tx and Ext/status
+ * WR9 000SXSSS enable master interrupt enable
+ *
+ * 1 = set to one, 0 = reset to zero
+ * X = user defined, S = same as previous init
+ *
+ *
+ * Note that the implementation differs in some points from above scheme.
+ *
+ */
+
+static void init_channel(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+ del_timer(&scc->tx_timer);
+
+ wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */
+ wr(scc,R1,0); /* no W/REQ operation */
+ wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */
+ wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */
+ wr(scc,R6,0); /* SDLC address zero (not used) */
+ wr(scc,R7,FLAG); /* SDLC flag value */
+ wr(scc,R9,VIS); /* vector includes status */
+ wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */
+ wr(scc,R14, 0);
+
+
+/* set clock sources:
+
+ CLK_DPLL: normal halfduplex operation
+
+ RxClk: use DPLL
+ TxClk: use DPLL
+ TRxC mode DPLL output
+
+ CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem)
+
+ BayCom: others:
+
+ TxClk = pin RTxC TxClk = pin TRxC
+ RxClk = pin TRxC RxClk = pin RTxC
+
+
+ CLK_DIVIDER:
+ RxClk = use DPLL
+ TxClk = pin RTxC
+
+ BayCom: others:
+ pin TRxC = DPLL pin TRxC = BRG
+ (RxClk * 1) (RxClk * 32)
+*/
+
+
+ switch(scc->modem.clocksrc)
+ {
+ case CLK_DPLL:
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+ init_brg(scc);
+ break;
+
+ case CLK_DIVIDER:
+ wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI);
+ init_brg(scc);
+ break;
+
+ case CLK_EXTERNAL:
+ wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP);
+ OutReg(scc->ctrl, R14, DISDPLL);
+ break;
+
+ }
+
+
+ if(scc->enhanced)
+ {
+ or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */
+ wr(scc,R7,AUTOEOM);
+ }
+
+ if(scc->modem.softdcd || (InReg(scc->ctrl,R0) & DCD))
+ /* DCD is now ON */
+ {
+ start_hunt(scc);
+ }
+
+ /* enable ABORT, DCD & SYNC/HUNT interrupts */
+
+ wr(scc,R15, BRKIE|TxUIE|(scc->modem.softdcd? SYNCIE:DCDIE));
+
+ Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */
+ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */
+
+ or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */
+
+ scc->status = InReg(scc->ctrl,R0); /* read initial status */
+
+ or(scc,R9,MIE); /* master interrupt enable */
+
+ scc_init_timer(scc);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+
+ set_speed(scc); /* set baudrate */
+}
+
+
+/* ******************************************************************** */
+/* * SCC timer functions * */
+/* ******************************************************************** */
+
+
+/* ----> scc_key_trx sets the time constant for the baudrate
+ generator and keys the transmitter <---- */
+
+static void scc_key_trx(struct scc_channel *scc, char tx)
+{
+ unsigned long flags;
+ unsigned int time_const;
+
+ if (scc->brand & PRIMUS)
+ Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0));
+
+ if (scc->modem.speed < 300)
+ scc->modem.speed = 1200;
+
+ time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2;
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ if (tx)
+ {
+ or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */
+ or(scc, R15, TxUIE);
+ }
+
+ if (scc->modem.clocksrc == CLK_DPLL)
+ { /* force simplex operation */
+ if (tx)
+ {
+#ifdef CONFIG_SCC_TRXECHO
+ cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */
+ cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */
+#endif
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */
+ wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR);
+
+ /* By popular demand: tx_inhibit */
+ if (scc->modem.tx_inhibit)
+ {
+ or(scc,R5, TxENAB);
+ scc->wreg[R5] |= RTS;
+ } else {
+ or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */
+ }
+ } else {
+ cl(scc,R5,RTS|TxENAB);
+
+ set_brg(scc, time_const); /* reprogram baudrate generator */
+
+ /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */
+ wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP);
+
+#ifndef CONFIG_SCC_TRXECHO
+ if (scc->modem.softdcd)
+#endif
+ {
+ or(scc,R15, scc->modem.softdcd? SYNCIE:DCDIE);
+ start_hunt(scc);
+ }
+ }
+ } else {
+ if (tx)
+ {
+#ifdef CONFIG_SCC_TRXECHO
+ if (scc->modem.fullduplex == 0)
+ {
+ cl(scc, R3, RxENABLE);
+ cl(scc, R15, DCDIE|SYNCIE);
+ }
+#endif
+
+ if (scc->modem.tx_inhibit)
+ {
+ or(scc,R5, TxENAB);
+ scc->wreg[R5] |= RTS;
+ } else {
+ or(scc,R5,RTS|TxENAB); /* enable tx */
+ }
+ } else {
+ cl(scc,R5,RTS|TxENAB); /* disable tx */
+
+ if ((scc->modem.fullduplex == 0) &&
+#ifndef CONFIG_SCC_TRXECHO
+ scc->modem.softdcd)
+#else
+ 1)
+#endif
+ {
+ or(scc, R15, scc->modem.softdcd? SYNCIE:DCDIE);
+ start_hunt(scc);
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+}
+
+static void scc_kick_tx(struct scc_channel *scc)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.kick_tx, flags);
+
+ skb = scc->tx_new;
+ scc->tx_new = NULL;
+ netif_wake_queue(scc->dev);
+
+ if (skb == NULL) goto nada;
+
+ if (skb->len == 0) /* Paranoia... */
+ {
+ dev_kfree_skb(skb);
+ scc->tx_buff = NULL;
+ Outb(scc->ctrl, RES_Tx_P);
+ goto nada;
+ }
+
+ scc->tx_buff = skb;
+ scc->stat.tx_state = TXS_ACTIVE;
+ OutReg(scc->ctrl, R0, RES_Tx_CRC);
+ /* reset CRC generator */
+ or(scc,R10,ABUNDER); /* re-install underrun protection */
+ Outb(scc->data,*skb->data); /* send byte */
+ skb_pull(skb, 1);
+
+ if (!scc->enhanced) /* reset EOM latch */
+ Outb(scc->ctrl,RES_EOM_L);
+
+ spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags);
+ return;
+
+nada:
+ scc_tx_done(scc);
+ spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags);
+ return;
+}
+
+/* ----> SCC timer interrupt handler and friends. <---- */
+
+static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(struct scc_channel *), unsigned long when)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.timer, flags);
+
+ del_timer(&scc->tx_timer);
+
+ if (when != 0)
+ {
+ scc->tx_timer.data = (unsigned long) scc;
+ scc->tx_timer.function = (void (*)(unsigned long)) handler;
+ scc->tx_timer.expires = jiffies + (when*HZ)/1000;
+ add_timer(&scc->tx_timer);
+ } else {
+ handler(scc);
+ }
+
+ spin_unlock_irqrestore(&scc->spinlocks.timer, flags);
+}
+
+/*
+ * This is called from scc_txint() when there are no more frames to send.
+ * Not exactly a timer function, but it is a close friend of the family...
+ */
+
+static void scc_tx_done(struct scc_channel *scc)
+{
+ scc->stat.tx_state = TXS_TAIL;
+
+ if (scc->modem.tailtime != 0)
+ scc_start_tx_timer(scc, scc_tail, scc->modem.tailtime);
+ else
+ scc_tail(scc);
+}
+
+#ifndef SCC_SIMPLE_MAC
+static void scc_tx_forced(struct scc_channel *scc)
+{
+ scc->stat.tx_state = TXS_TAIL;
+
+ if (scc->tx_new)
+ scc_kick_tx(scc);
+ else
+ // remain key-up'ed for the time of txdelay...
+ // what's the timeout in 6pack?
+ scc_start_tx_timer(scc, scc_tail, scc->modem.txdelay);
+}
+#endif
+
+static void scc_tx_start(struct scc_channel *scc, struct sk_buff *skb)
+{
+ scc->tx_new = skb;
+
+ /*
+ * scc_set_rts may also start a tx delay wait time, if we
+ * get a frame to transmit within this time RTS would be set,
+ * shorten the tx delay time...
+ */
+
+ if (scc->stat.tx_state != TXS_TXDELAY)
+ {
+ scc->stat.tx_state = TXS_TXDELAY;
+
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, scc_kick_tx, scc->modem.txdelay);
+ } else {
+ scc_start_tx_timer(scc, scc_kick_tx, 0);
+ }
+ }
+}
+
+#ifndef SCC_SIMPLE_MAC
+static void scc_set_rts(struct scc_channel *scc)
+{
+ scc->stat.tx_state = TXS_TXDELAY;
+
+ if ( !(scc->wreg[R5] & RTS) )
+ {
+ scc_key_trx(scc, TX_ON);
+ scc_start_tx_timer(scc, scc_tx_forced, scc->modem.txdelay);
+ } else {
+ scc_start_tx_timer(scc, scc_tx_forced, 0);
+ }
+}
+#endif
+
+/*
+ * TAILTIME expired
+ */
+
+static void scc_tail(struct scc_channel *scc)
+{
+ if (scc->tx_buff != NULL)
+ return;
+
+ if (scc->tx_new != NULL)
+ {
+ scc_kick_tx(scc);
+ return;
+ }
+
+ scc_key_trx(scc, TX_OFF);
+ scc->stat.tx_state = TXS_IDLE;
+}
+
+static void scc_init_timer(struct scc_channel *scc)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&scc->spinlocks.timer, flags);
+
+ scc->stat.tx_state = TXS_IDLE;
+ // FIXME: this can't be all, can it...?
+
+ spin_unlock_irqrestore(&scc->spinlocks.timer, flags);
+}
+
+
+/* ******************************************************************** */
+/* * Set/get L1 parameters * */
+/* ******************************************************************** */
+
+
+/*
+ * this will set the (some, anyway...) MODEM parameters
+ */
+
+static unsigned int scc_set_param(struct scc_channel *scc, struct scc_modem *modem)
+{
+ scc->modem.txdelay = modem->txdelay;
+ scc->modem.tailtime = modem->tailtime;
+ scc->modem.fullduplex = modem->fullduplex;
+ scc->modem.tx_inhibit = modem->tx_inhibit;
+ scc->modem.softdcd = modem->softdcd;
+
+
+ if (modem->softdcd)
+ {
+ or(scc, R15, SYNCIE);
+ cl(scc, R15, DCDIE);
+ start_hunt(scc);
+ } else {
+ or(scc, R15, DCDIE);
+ cl(scc, R15, SYNCIE);
+ }
+
+ return 0;
+}
+
+static unsigned int scc_ddi_report_dcd(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "dcd=%d\n", scc->dcd); */
+ return scc->dcd;
+}
+
+static unsigned int scc_ddi_report_ptt(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "rts=%d\n", (scc->wreg[R5] & RTS)? 1:0); */
+ return (scc->wreg[R5] & RTS)? 1:0;
+}
+
+#ifndef SCC_SIMPLE_MAC
+static unsigned int scc_ddi_report_cts(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ int txs = scc->stat.tx_state;
+
+/* printk(KERN_INFO "cts=%d\n", ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0); */
+ return ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0;
+}
+
+static void scc_ddi_set_rts(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+/* printk(KERN_INFO "setrts\n"); */
+ scc_set_rts(scc);
+}
+#endif
+
+static void scc_ddi_set_bitrate(struct net_device *dev, unsigned int speed)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ scc->modem.speed = speed;
+ if (scc->stat.tx_state != TXS_ACTIVE)
+ set_speed(scc);
+}
+
+/* Update general parameters so that they reflect our internal settings */
+static void scc_ddi_param_update(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, scc->modem.fullduplex);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, scc->modem.speed);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, scc->modem.speed);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, scc->modem.txdelay);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, scc->modem.tailtime);
+ return;
+}
+
+/* Called from upper layers when parameter was changed */
+static void scc_ddi_param_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ switch (valueno)
+ {
+ case AX25_VALUES_MEDIA_DUPLEX:
+ if (!netif_running(dev)) scc->modem.fullduplex = new;
+ break;
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ scc_ddi_set_bitrate(dev, new);
+ break;
+ case AX25_VALUES_MEDIA_TXDELAY:
+ scc->modem.txdelay = new;
+ break;
+ case AX25_VALUES_MEDIA_TXTAIL:
+ scc->modem.tailtime = new;
+ break;
+ default:
+ break;
+ }
+ scc_ddi_param_update(dev);
+ return;
+}
+
+/* ******************************************************************* */
+/* * Init channel structures, special HW, etc... * */
+/* ******************************************************************* */
+
+/*
+ * Reset the Z8530s and setup special hardware
+ */
+
+static void z8530_init(void)
+{
+ struct scc_channel *scc;
+ int chip, k;
+ unsigned long flags;
+ char *flag;
+
+
+ printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2);
+
+ flag=" ";
+ for (k = 0; k < 16; k++)
+ if (Ivec[k].used)
+ {
+ printk("%s%d", flag, k);
+ flag=",";
+ }
+ printk("\n");
+
+
+ /* reset and pre-init all chips in the system */
+ for (chip = 0; chip < Nchips; chip++)
+ {
+ scc=&SCC_Info[2*chip];
+ if (!scc->ctrl) continue;
+
+ /* Special SCC cards */
+
+ if(scc->brand & EAGLE) /* this is an EAGLE card */
+ Outb(scc->special,0x08); /* enable interrupt on the board */
+
+ if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */
+ Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */
+
+
+ /* Reset and pre-init Z8530 */
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ Outb(scc->ctrl,0);
+ OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */
+ udelay(100); /* give it 'a bit' more time than required */
+ wr(scc, R2, chip*16); /* interrupt vector */
+ wr(scc, R9, VIS); /* vector includes status */
+
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+ }
+
+
+ Driver_Initialized = 1;
+}
+
+/*
+ * Allocate device structure, err, instance, and register driver
+ */
+
+static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev)
+{
+ struct net_device *dev;
+
+ if (dev_get_by_name(name) != NULL)
+ {
+ printk(KERN_INFO "Z8530drv: device %s already exists.\n", name);
+ return -EEXIST;
+ }
+
+ if ((scc->dev = (struct net_device *) kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+
+ dev = scc->dev;
+ memset(dev, 0, sizeof(struct net_device));
+
+ dev->priv = (void *) scc;
+ strcpy(dev->name, name);
+ dev->init = scc_net_init;
+
+ if ((addev? register_netdevice(dev) : register_netdev(dev)) != 0)
+ {
+ kfree(dev);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+
+/* ******************************************************************** */
+/* * Network driver methods * */
+/* ******************************************************************** */
+
+/* ----> Initialize device <----- */
+
+static int scc_net_init(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ struct ax25_dev *ax25dev;
+ dev_init_buffers(dev);
+
+ dev->tx_queue_len = 16; /* should be enough... */
+
+ dev->open = scc_net_open;
+ dev->stop = scc_net_close;
+
+ dev->hard_start_xmit = scc_net_tx;
+ dev->set_mac_address = scc_net_set_mac_address;
+ dev->get_stats = scc_net_get_stats;
+ dev->do_ioctl = scc_net_ioctl;
+ dev->tx_timeout = scc_net_timeout;
+ dev->watchdog_timeo = SCC_WATCHDOG_TIMEOUT;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN;
+ dev->mtu = AX25_DEF_PACLEN;
+ dev->addr_len = AX25_ADDR_LEN;
+
+ AX25_PTR(dev) = ax25dev = &scc->ax25dev;
+ memset(ax25dev, 0, sizeof(struct ax25_dev));
+ ax25dev->hw.ptt = scc_ddi_report_ptt;
+ ax25dev->hw.dcd = scc_ddi_report_dcd;
+#ifndef SCC_SIMPLE_MAC
+ ax25dev->hw.cts = scc_ddi_report_cts;
+ ax25dev->hw.rts = scc_ddi_set_rts;
+#endif
+ ax25dev->hw.parameter_change_notify = scc_ddi_param_notify;
+ ax25dev->hw.fast = 0;
+
+ return 0;
+}
+
+/* ----> open network device <---- */
+
+static int scc_net_open(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (!scc->init) return -EINVAL;
+
+ MOD_INC_USE_COUNT;
+
+ scc->tx_buff = NULL;
+
+ init_channel(scc);
+
+ scc_ddi_param_update(dev);
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/* ----> close network device <---- */
+
+static int scc_net_close(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+
+ netif_stop_queue(dev);
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ Outb(scc->ctrl,0); /* Make sure pointer is written */
+ wr(scc,R1,0); /* disable interrupts */
+ wr(scc,R3,0);
+
+ del_timer(&scc->tx_timer);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+
+ if (scc->tx_buff)
+ kfree_skb(scc->tx_buff);
+ if (scc->tx_new)
+ kfree_skb(scc->tx_new);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* ----> receive frame, called from scc_rxint() <---- */
+
+static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb)
+{
+ if (skb->len == 0)
+ {
+ kfree_skb(skb);
+ return;
+ }
+
+ scc->dev_stat.rx_packets++;
+
+ skb->dev = scc->dev;
+ skb->protocol = htons(ETH_P_AX25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+ return;
+}
+
+/* ----> transmit frame <---- */
+
+static int scc_net_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ if (skb->len > scc->stat.bufsize || skb->len < 2)
+ {
+ scc->dev_stat.tx_dropped++; /* bogus frame */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ netif_stop_queue(dev);
+
+ scc->dev_stat.tx_packets++;
+ scc->stat.txframes++;
+
+#ifdef notdef
+ /**** The following "fix" is bogus. Fix the protocol, dammit! ****/
+
+ /* avoid race condition when skb is a cloned broadcast */
+ skb_cp = skb_copy(skb, GFP_ATOMIC);
+ dev_kfree_skb(skb);
+ if (skb_cp == NULL) {
+ scc->dev_stat.tx_dropped++; /* out of memory */
+ return 0;
+ }
+#endif
+
+ /* transmit frame */
+
+ dev->trans_start = jiffies;
+ scc_tx_start(scc, skb);
+
+ return 0;
+}
+
+
+/* -----> Watchdog <----- */
+
+static void scc_net_timeout(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+ unsigned long flags;
+
+ del_timer(&scc->tx_timer);
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+ cl(scc, R1, TxINT_ENAB);
+ cl(scc, R15, TxUIE);
+ OutReg(scc->ctrl, R0, RES_Tx_P);
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+
+ scc_start_tx_timer(scc, init_channel, 100);
+}
+
+
+
+/* ----> ioctl functions <---- */
+
+/*
+ * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg
+ * SIOCSCCINI - initialize driver arg: ---
+ * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg
+ * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg
+ * SIOCSCCGMODEM - get level 1 parameter arg: (struct scc_modem *) arg
+ * SIOCSCCSMODEM - set level 1 parameter arg: (struct scc_modem *) arg
+ * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg
+ */
+
+static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct scc_modem modem;
+ struct scc_mem_config memcfg;
+ struct scc_hw_config hwcfg;
+ unsigned long flags;
+ int chan;
+ unsigned char device_name[10];
+ void *arg;
+ struct scc_channel *scc;
+ unsigned int ret;
+
+ scc = (struct scc_channel *) dev->priv;
+ arg = (void *) ifr->ifr_data;
+
+ if (!Driver_Initialized)
+ {
+ if (cmd == SIOCSCCCFG)
+ {
+ int found = 1;
+
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+ if (!arg) return -EFAULT;
+
+ if (Nchips >= SCC_MAXCHIPS)
+ return -EINVAL;
+
+ if (copy_from_user(&hwcfg, arg, sizeof(hwcfg)))
+ return -EFAULT;
+
+ IO_Spinlock = SPIN_LOCK_UNLOCKED;
+ IO_Delay = hwcfg.delay;
+
+ if (hwcfg.irq == 2) hwcfg.irq = 9;
+ if (!Ivec[hwcfg.irq].used && hwcfg.irq)
+ {
+ if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL))
+ printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq);
+ else
+ Ivec[hwcfg.irq].used = 1;
+ }
+
+ if (hwcfg.vector_latch)
+ Vector_Latch = hwcfg.vector_latch;
+
+ if (hwcfg.clock == 0)
+ hwcfg.clock = SCC_DEFAULT_CLOCK;
+
+
+#ifndef SCC_DONT_CHECK
+ save_flags(flags);
+ cli();
+
+ check_region(scc->ctrl, 1);
+ Outb(hwcfg.ctrl_a, 0);
+ OutReg(hwcfg.ctrl_a, R9, FHWRES);
+ udelay(100);
+ OutReg(hwcfg.ctrl_a, R13, 0x55);
+ udelay(5);
+ if (InReg(hwcfg.ctrl_a, R13) != 0x55)
+ found = 0;
+
+ restore_flags(flags);
+#endif
+
+ if (found)
+ {
+ SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a;
+ SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b;
+ SCC_ctrl[Nchips].irq = hwcfg.irq;
+ }
+
+
+ for (chan = 0; chan < 2; chan++)
+ {
+ sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan);
+
+ SCC_Info[2*Nchips+chan].ctrl = chan? hwcfg.ctrl_b:hwcfg.ctrl_a;
+ SCC_Info[2*Nchips+chan].data = chan? hwcfg.data_b:hwcfg.data_a;
+ SCC_Info[2*Nchips+chan].irq = hwcfg.irq;
+ SCC_Info[2*Nchips+chan].special = hwcfg.special;
+ SCC_Info[2*Nchips+chan].clock = hwcfg.clock;
+ SCC_Info[2*Nchips+chan].brand = hwcfg.brand;
+ SCC_Info[2*Nchips+chan].option = hwcfg.option;
+ SCC_Info[2*Nchips+chan].enhanced= hwcfg.escc;
+
+#ifdef SCC_DONT_CHECK
+ printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n",
+ device_name,
+ SCC_Info[2*Nchips+chan].data,
+ SCC_Info[2*Nchips+chan].ctrl);
+
+#else
+ printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n",
+ device_name,
+ chan? hwcfg.data_b : hwcfg.data_a,
+ chan? hwcfg.ctrl_b : hwcfg.ctrl_a,
+ found? "found" : "missing");
+#endif
+
+ if (found)
+ {
+ request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl");
+ request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data");
+ if (Nchips+chan != 0)
+ scc_net_setup(&SCC_Info[2*Nchips+chan], device_name, 1);
+ }
+ }
+
+ if (found) Nchips++;
+
+ return 0;
+ }
+
+ if (cmd == SIOCSCCINI)
+ {
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ if (Nchips == 0)
+ return -EINVAL;
+
+ z8530_init();
+ scc_ddi_param_update(dev);
+ return 0;
+ }
+
+ return -EINVAL; /* confuse the user */
+ }
+
+ if (!scc->init)
+ {
+ if (cmd == SIOCSCCCHANINI)
+ {
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (!arg) return -EINVAL;
+
+ scc->stat.bufsize = SCC_BUFSIZE;
+
+ if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem)))
+ return -EINVAL;
+
+ scc->tx_buff = NULL;
+ scc->tx_new = NULL;
+ scc->init = 1;
+
+ scc_ddi_param_update(dev);
+ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+ switch(cmd)
+ {
+ case SIOCSCCRESERVED:
+ return -ENOIOCTLCMD;
+
+ case SIOCSCCSMEM:
+ if (!capable(CAP_SYS_RAWIO)) return -EPERM;
+ if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg)))
+ return -EINVAL;
+ scc->stat.bufsize = memcfg.bufsize;
+ return 0;
+
+ case SIOCSCCGSTAT:
+ if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat)))
+ return -EINVAL;
+ return 0;
+
+ case SIOCSCCGMODEM:
+ if (!arg || copy_to_user(arg, &scc->modem, sizeof(struct scc_modem)))
+ return -EINVAL;
+ return 0;
+
+ case SIOCSCCSMODEM:
+ if (!capable(CAP_NET_ADMIN)) return -EPERM;
+ if (!arg || copy_from_user(&modem, arg, sizeof(struct scc_modem)))
+ return -EINVAL;
+ ret = scc_set_param(scc, &modem);
+ scc_ddi_param_update(dev);
+ return ret;
+
+ default:
+ return -ENOIOCTLCMD;
+
+ }
+
+ return -EINVAL;
+}
+
+/* ----> set interface callsign <---- */
+
+static int scc_net_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *) addr;
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ return 0;
+}
+
+/* ----> get statistics <---- */
+
+static struct net_device_stats *scc_net_get_stats(struct net_device *dev)
+{
+ struct scc_channel *scc = (struct scc_channel *) dev->priv;
+
+ scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over;
+ scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under;
+ scc->dev_stat.rx_fifo_errors = scc->stat.rx_over;
+ scc->dev_stat.tx_fifo_errors = scc->stat.tx_under;
+
+ return &scc->dev_stat;
+}
+
+/* ******************************************************************** */
+/* * dump statistics to /proc/net/z8530drv * */
+/* ******************************************************************** */
+
+
+static int scc_net_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ struct scc_channel *scc;
+ struct scc_stat *stat;
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+ int k;
+
+ len += sprintf(buffer, "z8530drv-"VERSION"\n");
+
+ if (!Driver_Initialized)
+ {
+ len += sprintf(buffer+len, "not initialized\n");
+ goto done;
+ }
+
+ if (!Nchips)
+ {
+ len += sprintf(buffer+len, "chips missing\n");
+ goto done;
+ }
+
+ for (k = 0; k < Nchips*2; k++)
+ {
+ scc = &SCC_Info[k];
+ stat = &scc->stat;
+
+ if (!scc->init)
+ continue;
+
+ /* dev data ctrl irq clock brand enh vector special option
+ * baud nrz clksrc fulldplx txoff softdcd txd tail bufsize
+ * rxints txints exints spints
+ * rcvd rxerrs over / xmit txerrs under / nospace bufsize
+ * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ##
+ * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ##
+ */
+
+ len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n",
+ scc->dev->name,
+ scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand,
+ scc->enhanced, Vector_Latch, scc->special,
+ scc->option);
+ len += sprintf(buffer+len, "\t%lu %d %d %d %d %d %d %d %d\n",
+ scc->modem.speed, scc->modem.nrz,
+ scc->modem.clocksrc, scc->modem.fullduplex,
+ scc->modem.tx_inhibit, scc->modem.softdcd,
+ scc->modem.txdelay, scc->modem.tailtime,
+ stat->bufsize);
+ len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n",
+ stat->rxints, stat->txints, stat->exints, stat->spints);
+ len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n",
+ stat->rxframes, stat->rxerrs, stat->rx_over,
+ stat->txframes, stat->txerrs, stat->tx_under,
+ stat->nospace, stat->tx_state);
+
+#ifdef SCC_DEBUG
+ {
+ int reg;
+
+ len += sprintf(buffer+len, "\tW ");
+ for (reg = 0; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]);
+ len += sprintf(buffer+len, "\n");
+
+ len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1));
+ for (reg = 3; reg < 8; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "XX ");
+ for (reg = 9; reg < 16; reg++)
+ len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg));
+ len += sprintf(buffer+len, "\n");
+ }
+#endif
+ len += sprintf(buffer+len, "\n");
+
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+
+done:
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+
+
+/* ******************************************************************** */
+/* * Init SCC driver * */
+/* ******************************************************************** */
+
+int __init scc_init_driver(void)
+{
+ int chip, chan, k, result;
+ char devname[10];
+
+ printk(KERN_INFO BANNER);
+
+ memset(&SCC_ctrl, 0, sizeof(SCC_ctrl));
+
+ /* pre-init channel information */
+
+ for (chip = 0; chip < SCC_MAXCHIPS; chip++)
+ {
+ for (chan = 0; chan < 2; chan++)
+ {
+ struct scc_channel *scc = &SCC_Info[2*chip+chan];
+ memset((char *) scc, 0, sizeof(struct scc_channel));
+
+ scc->spinlocks.hwaccess = SPIN_LOCK_UNLOCKED;
+ scc->spinlocks.timer = SPIN_LOCK_UNLOCKED;
+ scc->spinlocks.kick_tx = SPIN_LOCK_UNLOCKED;
+ }
+ }
+
+ for (k = 0; k < 16; k++) Ivec[k].used = 0;
+
+ sprintf(devname,"%s0", SCC_DriverName);
+
+ result = scc_net_setup(SCC_Info, devname, 0);
+ if (result)
+ {
+ printk(KERN_ERR "z8530drv: cannot initialize module\n");
+ return result;
+ }
+
+#ifdef CONFIG_PROC_FS
+ proc_net_create("z8530drv", 0, scc_net_get_info);
+#endif
+
+ return 0;
+}
+
+/* ******************************************************************** */
+/* * Module support * */
+/* ******************************************************************** */
+
+
+void __exit scc_cleanup_driver(void)
+{
+ long flags;
+ io_port ctrl;
+ int k;
+ struct scc_channel *scc = NULL;
+
+ spin_lock_irqsave(&scc->spinlocks.hwaccess, flags);
+
+ if (Nchips == 0)
+ unregister_netdev(SCC_Info[0].dev);
+
+ for (k = 0; k < Nchips; k++)
+ if ( (ctrl = SCC_ctrl[k].chan_A) )
+ {
+ Outb(ctrl,0);
+ OutReg(ctrl,R9,FHWRES); /* force hardware reset */
+ udelay(50);
+ }
+
+ for (k = 0; k < Nchips*2; k++)
+ {
+ scc = &SCC_Info[k];
+ if (scc)
+ {
+ release_region(scc->ctrl, 1);
+ release_region(scc->data, 1);
+ if (scc->dev)
+ {
+ unregister_netdev(scc->dev);
+ kfree(scc->dev);
+ }
+ }
+ }
+
+ for (k=0; k < 16 ; k++)
+ if (Ivec[k].used) free_irq(k, NULL);
+
+ spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags);
+
+#ifdef CONFIG_PROC_FS
+ proc_net_remove("z8530drv");
+#endif
+}
+
+MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>");
+MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio");
+MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards without DMA support");
+module_init(scc_init_driver);
+module_exit(scc_cleanup_driver);
diff --git a/drivers/net/hamradio/soundmodem/Makefile b/drivers/net/hamradio/soundmodem/Makefile
deleted file mode 100644
index 959701ab1..000000000
--- a/drivers/net/hamradio/soundmodem/Makefile
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# Makefile for the soundmodem device driver.
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes..
-#
-
-O_TARGET := soundmodem.o
-
-obj-y := sm.o
-obj-$(CONFIG_SOUNDMODEM_SBC) += sm_sbc.o
-obj-$(CONFIG_SOUNDMODEM_WSS) += sm_wss.o
-obj-$(CONFIG_SOUNDMODEM_AFSK1200) += sm_afsk1200.o
-obj-$(CONFIG_SOUNDMODEM_AFSK2400_7) += sm_afsk2400_7.o
-obj-$(CONFIG_SOUNDMODEM_AFSK2400_8) += sm_afsk2400_8.o
-obj-$(CONFIG_SOUNDMODEM_AFSK2666) += sm_afsk2666.o
-obj-$(CONFIG_SOUNDMODEM_HAPN4800) += sm_hapn4800.o
-obj-$(CONFIG_SOUNDMODEM_PSK4800) += sm_psk4800.o
-obj-$(CONFIG_SOUNDMODEM_FSK9600) += sm_fsk9600.o
-
-obj-m := $(O_TARGET)
-
-all: all_targets
-.PHONY: all
-
-gentbl: gentbl.c
- $(HOSTCC) $(HOSTCFLAGS) $< -o $@ -lm
-
-TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h
-TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h
-TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h
-
-$(TBLHDR): gentbl
- ./gentbl
-
-fastdep: $(TBLHDR)
-
-include $(TOPDIR)/Rules.make
diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c
deleted file mode 100644
index c5a52b8ab..000000000
--- a/drivers/net/hamradio/soundmodem/sm.c
+++ /dev/null
@@ -1,759 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm.c -- soundcard radio modem driver.
- *
- * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- *
- * Command line options (insmod command line)
- *
- * mode mode string; eg. "wss:afsk1200"
- * iobase base address of the soundcard; common values are 0x220 for sbc,
- * 0x530 for wss
- * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss
- * dma dma number; common values are 0 or 1
- *
- *
- * History:
- * 0.1 21.09.1996 Started
- * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user)
- * 0.4 21.01.1997 Separately compileable soundcard/modem modules
- * 0.5 03.03.1997 fixed LPT probing (check_lpt result was interpreted the wrong way round)
- * 0.6 16.04.1997 init code/data tagged
- * 0.7 30.07.1997 fixed halfduplex interrupt handlers/hotfix for CS423X
- * 0.8 14.04.1998 cleanups
- * 0.9 03.08.1999 adapt to Linus' new __setup/__initcall
- * use parport lowlevel drivers instead of directly writing to a parallel port
- * removed some pre-2.2 kernel compatibility cruft
- * 0.10 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts
- * 0.11 12.02.2000 adapted to softnet driver interface
- * 0.12 03.07.2000 fix interface name handling
- */
-
-/*****************************************************************************/
-
-#include <linux/config.h>
-#include <linux/version.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/parport.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include "sm.h"
-
-/* --------------------------------------------------------------------- */
-
-/*static*/ const char sm_drvname[] = "soundmodem";
-static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n"
-KERN_INFO "soundmodem: version 0.12 compiled " __TIME__ " " __DATE__ "\n";
-
-/* --------------------------------------------------------------------- */
-
-/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = {
-#ifdef CONFIG_SOUNDMODEM_AFSK1200
- &sm_afsk1200_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
- &sm_afsk2400_7_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
- &sm_afsk2400_8_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2666
- &sm_afsk2666_tx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
-#ifdef CONFIG_SOUNDMODEM_PSK4800
- &sm_psk4800_tx,
-#endif /* CONFIG_SOUNDMODEM_PSK4800 */
-#ifdef CONFIG_SOUNDMODEM_HAPN4800
- &sm_hapn4800_8_tx,
- &sm_hapn4800_10_tx,
- &sm_hapn4800_pm8_tx,
- &sm_hapn4800_pm10_tx,
-#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
-#ifdef CONFIG_SOUNDMODEM_FSK9600
- &sm_fsk9600_4_tx,
- &sm_fsk9600_5_tx,
-#endif /* CONFIG_SOUNDMODEM_FSK9600 */
- NULL
-};
-
-/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = {
-#ifdef CONFIG_SOUNDMODEM_AFSK1200
- &sm_afsk1200_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK1200 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_7
- &sm_afsk2400_7_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2400_8
- &sm_afsk2400_8_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */
-#ifdef CONFIG_SOUNDMODEM_AFSK2666
- &sm_afsk2666_rx,
-#endif /* CONFIG_SOUNDMODEM_AFSK2666 */
-#ifdef CONFIG_SOUNDMODEM_PSK4800
- &sm_psk4800_rx,
-#endif /* CONFIG_SOUNDMODEM_PSK4800 */
-#ifdef CONFIG_SOUNDMODEM_HAPN4800
- &sm_hapn4800_8_rx,
- &sm_hapn4800_10_rx,
- &sm_hapn4800_pm8_rx,
- &sm_hapn4800_pm10_rx,
-#endif /* CONFIG_SOUNDMODEM_HAPN4800 */
-#ifdef CONFIG_SOUNDMODEM_FSK9600
- &sm_fsk9600_4_rx,
- &sm_fsk9600_5_rx,
-#endif /* CONFIG_SOUNDMODEM_FSK9600 */
- NULL
-};
-
-static const struct hardware_info *sm_hardware_table[] = {
-#ifdef CONFIG_SOUNDMODEM_SBC
- &sm_hw_sbc,
- &sm_hw_sbcfdx,
-#endif /* CONFIG_SOUNDMODEM_SBC */
-#ifdef CONFIG_SOUNDMODEM_WSS
- &sm_hw_wss,
- &sm_hw_wssfdx,
-#endif /* CONFIG_SOUNDMODEM_WSS */
- NULL
-};
-
-/* --------------------------------------------------------------------- */
-
-#define NR_PORTS 4
-
-static struct net_device sm_device[NR_PORTS];
-
-/* --------------------------------------------------------------------- */
-
-#define UART_RBR(iobase) (iobase+0)
-#define UART_THR(iobase) (iobase+0)
-#define UART_IER(iobase) (iobase+1)
-#define UART_IIR(iobase) (iobase+2)
-#define UART_FCR(iobase) (iobase+2)
-#define UART_LCR(iobase) (iobase+3)
-#define UART_MCR(iobase) (iobase+4)
-#define UART_LSR(iobase) (iobase+5)
-#define UART_MSR(iobase) (iobase+6)
-#define UART_SCR(iobase) (iobase+7)
-#define UART_DLL(iobase) (iobase+0)
-#define UART_DLM(iobase) (iobase+1)
-
-#define SER_EXTENT 8
-
-#define MIDI_DATA(iobase) (iobase)
-#define MIDI_STATUS(iobase) (iobase+1)
-#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */
-#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */
-
-#define MIDI_EXTENT 2
-
-/* ---------------------------------------------------------------------- */
-
-#define PARAM_TXDELAY 1
-#define PARAM_PERSIST 2
-#define PARAM_SLOTTIME 3
-#define PARAM_TXTAIL 4
-#define PARAM_FULLDUP 5
-#define PARAM_HARDWARE 6
-#define PARAM_RETURN 255
-
-#define SP_SER 1
-#define SP_PAR 2
-#define SP_MIDI 4
-
-/*
- * ===================== port checking routines ========================
- */
-
-enum uart { c_uart_unknown, c_uart_8250,
- c_uart_16450, c_uart_16550, c_uart_16550A};
-static const char *uart_str[] =
- { "unknown", "8250", "16450", "16550", "16550A" };
-
-static enum uart check_uart(unsigned int iobase)
-{
- unsigned char b1,b2,b3;
- enum uart u;
- enum uart uart_tab[] =
- { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A };
-
- if (iobase <= 0 || iobase > 0x1000-SER_EXTENT)
- return c_uart_unknown;
- if (check_region(iobase, SER_EXTENT))
- return c_uart_unknown;
- b1 = inb(UART_MCR(iobase));
- outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */
- b2 = inb(UART_MSR(iobase));
- outb(0x1a, UART_MCR(iobase));
- b3 = inb(UART_MSR(iobase)) & 0xf0;
- outb(b1, UART_MCR(iobase)); /* restore old values */
- outb(b2, UART_MSR(iobase));
- if (b3 != 0x90)
- return c_uart_unknown;
- inb(UART_RBR(iobase));
- inb(UART_RBR(iobase));
- outb(0x01, UART_FCR(iobase)); /* enable FIFOs */
- u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3];
- if (u == c_uart_16450) {
- outb(0x5a, UART_SCR(iobase));
- b1 = inb(UART_SCR(iobase));
- outb(0xa5, UART_SCR(iobase));
- b2 = inb(UART_SCR(iobase));
- if ((b1 != 0x5a) || (b2 != 0xa5))
- u = c_uart_8250;
- }
- return u;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int check_midi(unsigned int iobase)
-{
- unsigned long timeout;
- unsigned long flags;
- unsigned char b;
-
- if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT)
- return 0;
- if (check_region(iobase, MIDI_EXTENT))
- return 0;
- timeout = jiffies + (HZ / 100);
- while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
- if ((signed)(jiffies - timeout) > 0)
- return 0;
- save_flags(flags);
- cli();
- outb(0xff, MIDI_DATA(iobase));
- b = inb(MIDI_STATUS(iobase));
- restore_flags(flags);
- if (!(b & MIDI_WRITE_EMPTY))
- return 0;
- while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY)
- if ((signed)(jiffies - timeout) > 0)
- return 0;
- return 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-void sm_output_status(struct sm_state *sm)
-{
- int invert_dcd = 0;
- int invert_ptt = 0;
-
- int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt;
- int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd;
-
- if (sm->hdrv.ptt_out.flags & SP_SER) {
- outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase));
- outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase));
- }
- if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev && sm->pardev->port)
- parport_write_data(sm->pardev->port, ptt | (dcd << 1));
- if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv))
- outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sm_output_open(struct sm_state *sm, const char *ifname)
-{
- enum uart u = c_uart_unknown;
- struct parport *pp = NULL;
-
- sm->hdrv.ptt_out.flags = 0;
- if (sm->hdrv.ptt_out.seriobase > 0 &&
- sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT &&
- ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) {
- sm->hdrv.ptt_out.flags |= SP_SER;
- request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt");
- outb(0, UART_IER(sm->hdrv.ptt_out.seriobase));
- /* 5 bits, 1 stop, no parity, no break, Div latch access */
- outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase));
- outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase));
- outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */
- /* LCR and MCR set by output_status */
- }
- sm->pardev = NULL;
- if (sm->hdrv.ptt_out.pariobase > 0) {
- pp = parport_enumerate();
- while (pp && pp->base != sm->hdrv.ptt_out.pariobase)
- pp = pp->next;
- if (!pp)
- printk(KERN_WARNING "%s: parport at address 0x%x not found\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
- else if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT))
- printk(KERN_WARNING "%s: parport at address 0x%x cannot be used\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
- else {
- sm->pardev = parport_register_device(pp, ifname, NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL);
- if (!sm->pardev) {
- pp = NULL;
- printk(KERN_WARNING "%s: cannot register parport device (address 0x%x)\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
- } else {
- if (parport_claim(sm->pardev)) {
- parport_unregister_device(sm->pardev);
- sm->pardev = NULL;
- printk(KERN_WARNING "%s: cannot claim parport at address 0x%x\n", sm_drvname, sm->hdrv.ptt_out.pariobase);
- } else
- sm->hdrv.ptt_out.flags |= SP_PAR;
- }
- }
- }
- if (sm->hdrv.ptt_out.midiiobase > 0 &&
- sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT &&
- check_midi(sm->hdrv.ptt_out.midiiobase)) {
- sm->hdrv.ptt_out.flags |= SP_MIDI;
- request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT,
- "sm midi ptt");
- }
- sm_output_status(sm);
-
- printk(KERN_INFO "%s: ptt output:", sm_drvname);
- if (sm->hdrv.ptt_out.flags & SP_SER)
- printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase,
- uart_str[u]);
- if (sm->hdrv.ptt_out.flags & SP_PAR)
- printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase);
- if (sm->hdrv.ptt_out.flags & SP_MIDI)
- printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase);
- if (!sm->hdrv.ptt_out.flags)
- printk(" none");
- printk("\n");
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sm_output_close(struct sm_state *sm)
-{
- /* release regions used for PTT output */
- sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0;
- sm_output_status(sm);
- if (sm->hdrv.ptt_out.flags & SP_SER)
- release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT);
- if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev) {
- parport_release(sm->pardev);
- parport_unregister_device(sm->pardev);
- }
- if (sm->hdrv.ptt_out.flags & SP_MIDI)
- release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT);
- sm->hdrv.ptt_out.flags = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct net_device *dev);
-static int sm_close(struct net_device *dev);
-static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd);
-
-/* --------------------------------------------------------------------- */
-
-static const struct hdlcdrv_ops sm_ops = {
- sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl
-};
-
-/* --------------------------------------------------------------------- */
-
-static int sm_open(struct net_device *dev)
-{
- struct sm_state *sm;
- int err;
-
- if (!dev || !dev->priv ||
- ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
- printk(KERN_ERR "sm_open: invalid device struct\n");
- return -EINVAL;
- }
- sm = (struct sm_state *)dev->priv;
-
- if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open)
- return -ENODEV;
- sm->hdrv.par.bitrate = sm->mode_rx->bitrate;
- err = sm->hwdrv->open(dev, sm);
- if (err)
- return err;
- sm_output_open(sm, dev->name);
- MOD_INC_USE_COUNT;
- printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n",
- sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name,
- sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_close(struct net_device *dev)
-{
- struct sm_state *sm;
- int err = -ENODEV;
-
- if (!dev || !dev->priv ||
- ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
- printk(KERN_ERR "sm_close: invalid device struct\n");
- return -EINVAL;
- }
- sm = (struct sm_state *)dev->priv;
-
-
- if (sm->hwdrv && sm->hwdrv->close)
- err = sm->hwdrv && sm->hwdrv->close(dev, sm);
- sm_output_close(sm);
- MOD_DEC_USE_COUNT;
- printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n",
- sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma);
- return err;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sethw(struct net_device *dev, struct sm_state *sm, char *mode)
-{
- char *cp = strchr(mode, ':');
- const struct hardware_info **hwp = sm_hardware_table;
-
- if (!cp)
- cp = mode;
- else {
- *cp++ = '\0';
- while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode))
- hwp++;
- if (!hwp || !*hwp || !(*hwp)->hw_name)
- return -EINVAL;
- if ((*hwp)->loc_storage > sizeof(sm->hw)) {
- printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n",
- sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage);
- return -EINVAL;
- }
- sm->hwdrv = *hwp;
- }
- if (!*cp)
- return 0;
- if (sm->hwdrv && sm->hwdrv->sethw)
- return sm->hwdrv->sethw(dev, sm, cp);
- return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sm_ioctl(struct net_device *dev, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd)
-{
- struct sm_state *sm;
- struct sm_ioctl bi;
- unsigned long flags;
- unsigned int newdiagmode;
- unsigned int newdiagflags;
- char *cp;
- const struct modem_tx_info **mtp = sm_modem_tx_table;
- const struct modem_rx_info **mrp = sm_modem_rx_table;
- const struct hardware_info **hwp = sm_hardware_table;
-
- if (!dev || !dev->priv ||
- ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) {
- printk(KERN_ERR "sm_ioctl: invalid device struct\n");
- return -EINVAL;
- }
- sm = (struct sm_state *)dev->priv;
-
- if (cmd != SIOCDEVPRIVATE) {
- if (!sm->hwdrv || !sm->hwdrv->ioctl)
- return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
- return -ENOIOCTLCMD;
- }
- switch (hi->cmd) {
- default:
- if (sm->hwdrv && sm->hwdrv->ioctl)
- return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd);
- return -ENOIOCTLCMD;
-
- case HDLCDRVCTL_GETMODE:
- cp = hi->data.modename;
- if (sm->hwdrv && sm->hwdrv->hw_name)
- cp += sprintf(cp, "%s:", sm->hwdrv->hw_name);
- else
- cp += sprintf(cp, "<unspec>:");
- if (sm->mode_tx && sm->mode_tx->name)
- cp += sprintf(cp, "%s", sm->mode_tx->name);
- else
- cp += sprintf(cp, "<unspec>");
- if (!sm->mode_rx || !sm->mode_rx ||
- strcmp(sm->mode_rx->name, sm->mode_tx->name)) {
- if (sm->mode_rx && sm->mode_rx->name)
- cp += sprintf(cp, ",%s", sm->mode_rx->name);
- else
- cp += sprintf(cp, ",<unspec>");
- }
- if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
- return -EFAULT;
- return 0;
-
- case HDLCDRVCTL_SETMODE:
- if (netif_running(dev) || !capable(CAP_NET_ADMIN))
- return -EACCES;
- hi->data.modename[sizeof(hi->data.modename)-1] = '\0';
- return sethw(dev, sm, hi->data.modename);
-
- case HDLCDRVCTL_MODELIST:
- cp = hi->data.modename;
- while (*hwp) {
- if ((*hwp)->hw_name)
- cp += sprintf(cp, "%s:,", (*hwp)->hw_name);
- hwp++;
- }
- while (*mtp) {
- if ((*mtp)->name)
- cp += sprintf(cp, ">%s,", (*mtp)->name);
- mtp++;
- }
- while (*mrp) {
- if ((*mrp)->name)
- cp += sprintf(cp, "<%s,", (*mrp)->name);
- mrp++;
- }
- cp[-1] = '\0';
- if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi)))
- return -EFAULT;
- return 0;
-
-#ifdef SM_DEBUG
- case SMCTL_GETDEBUG:
- if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
- return -EFAULT;
- bi.data.dbg.int_rate = sm->debug_vals.last_intcnt;
- bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc;
- bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc;
- bi.data.dbg.dma_residue = sm->debug_vals.dma_residue;
- sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc =
- sm->debug_vals.dma_residue = 0;
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
-#endif /* SM_DEBUG */
-
- case SMCTL_DIAGNOSE:
- if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
- return -EFAULT;
- newdiagmode = bi.data.diag.mode;
- newdiagflags = bi.data.diag.flags;
- if (newdiagmode > SM_DIAGMODE_CONSTELLATION)
- return -EINVAL;
- bi.data.diag.mode = sm->diag.mode;
- bi.data.diag.flags = sm->diag.flags;
- bi.data.diag.samplesperbit = sm->mode_rx->sperbit;
- if (sm->diag.mode != newdiagmode) {
- save_flags(flags);
- cli();
- sm->diag.ptr = -1;
- sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
- sm->diag.mode = newdiagmode;
- restore_flags(flags);
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
- }
- if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) {
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
- }
- if (bi.data.diag.datalen > DIAGDATALEN)
- bi.data.diag.datalen = DIAGDATALEN;
- if (sm->diag.ptr < bi.data.diag.datalen) {
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
- }
- if (copy_to_user(bi.data.diag.data, sm->diag.data,
- bi.data.diag.datalen * sizeof(short)))
- return -EFAULT;
- bi.data.diag.flags |= SM_DIAGFLAG_VALID;
- save_flags(flags);
- cli();
- sm->diag.ptr = -1;
- sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID;
- sm->diag.mode = newdiagmode;
- restore_flags(flags);
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-/*
- * command line settable parameters
- */
-static char *mode[NR_PORTS] = { [0 ... NR_PORTS-1] = NULL };
-static int iobase[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
-static int irq[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
-static int dma[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
-static int dma2[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 };
-static int serio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
-static int pario[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
-static int midiio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 };
-
-MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s");
-MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600");
-MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(iobase, "soundmodem base address");
-MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(irq, "soundmodem interrupt");
-MODULE_PARM(dma, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(dma, "soundmodem dma channel");
-MODULE_PARM(dma2, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only");
-MODULE_PARM(serio, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port");
-MODULE_PARM(pario, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port");
-MODULE_PARM(midiio, "1-" __MODULE_STRING(NR_PORTS) "i");
-MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port");
-
-MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu");
-MODULE_DESCRIPTION("Soundcard amateur radio modem driver");
-
-/* --------------------------------------------------------------------- */
-
-static int __init init_soundmodem(void)
-{
- int i, j, found = 0;
- char set_hw = 1;
- struct sm_state *sm;
-
- printk(sm_drvinfo);
- /*
- * register net devices
- */
- for (i = 0; i < NR_PORTS; i++) {
- struct net_device *dev = sm_device+i;
- char ifname[IFNAMSIZ];
-
- sprintf(ifname, "sm%d", i);
- if (!mode[i])
- set_hw = 0;
- else {
- if (!strncmp(mode[i], "sbc", 3)) {
- if (iobase[i] == -1)
- iobase[i] = 0x220;
- if (irq[i] == -1)
- irq[i] = 5;
- if (dma[i] == -1)
- dma[i] = 1;
- } else {
- if (iobase[i] == -1)
- iobase[i] = 0x530;
- if (irq[i] == -1)
- irq[i] = 11;
- if (dma[i] == -1)
- dma[i] = 1;
- }
- }
- if (!set_hw)
- iobase[i] = irq[i] = 0;
- j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), ifname, iobase[i], irq[i], dma[i]);
- if (!j) {
- sm = (struct sm_state *)dev->priv;
- sm->hdrv.ptt_out.dma2 = dma2[i];
- sm->hdrv.ptt_out.seriobase = serio[i];
- sm->hdrv.ptt_out.pariobase = pario[i];
- sm->hdrv.ptt_out.midiiobase = midiio[i];
- if (set_hw && sethw(dev, sm, mode[i]))
- set_hw = 0;
- found++;
- } else {
- printk(KERN_WARNING "%s: cannot register net device\n", sm_drvname);
- }
- }
- if (!found)
- return -ENXIO;
- return 0;
-}
-
-static void __exit cleanup_soundmodem(void)
-{
- int i;
-
- printk(KERN_INFO "sm: cleanup_module called\n");
-
- for(i = 0; i < NR_PORTS; i++) {
- struct net_device *dev = sm_device+i;
- struct sm_state *sm = (struct sm_state *)dev->priv;
-
- if (sm) {
- if (sm->hdrv.magic != HDLCDRV_MAGIC)
- printk(KERN_ERR "sm: invalid magic in "
- "cleanup_module\n");
- else
- hdlcdrv_unregister_hdlcdrv(dev);
- }
- }
-}
-
-module_init(init_soundmodem);
-module_exit(cleanup_soundmodem);
-
-/* --------------------------------------------------------------------- */
-
-#ifndef MODULE
-
-/*
- * format: soundmodem=io,irq,dma[,dma2[,serio[,pario]]],mode
- * mode: hw:modem
- * hw: sbc, wss, wssfdx
- * modem: afsk1200, fsk9600
- */
-
-static int __init sm_setup(char *str)
-{
- static unsigned nr_dev = 0;
- int ints[8];
-
- if (nr_dev >= NR_PORTS)
- return 0;
- str = get_options(str, 8, ints);
- mode[nr_dev] = str;
- if (ints[0] >= 1)
- iobase[nr_dev] = ints[1];
- if (ints[0] >= 2)
- irq[nr_dev] = ints[2];
- if (ints[0] >= 3)
- dma[nr_dev] = ints[3];
- if (ints[0] >= 4)
- dma2[nr_dev] = ints[4];
- if (ints[0] >= 5)
- serio[nr_dev] = ints[5];
- if (ints[0] >= 6)
- pario[nr_dev] = ints[6];
- if (ints[0] >= 7)
- midiio[nr_dev] = ints[7];
- nr_dev++;
- return 1;
-}
-
-__setup("soundmodem=", sm_setup);
-
-#endif /* MODULE */
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm.h b/drivers/net/hamradio/soundmodem/sm.h
deleted file mode 100644
index 24a586740..000000000
--- a/drivers/net/hamradio/soundmodem/sm.h
+++ /dev/null
@@ -1,386 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm.h -- soundcard radio modem driver internal header.
- *
- * Copyright (C) 1996-1999 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#ifndef _SM_H
-#define _SM_H
-
-/* ---------------------------------------------------------------------- */
-
-#include <linux/hdlcdrv.h>
-#include <linux/soundmodem.h>
-#include <asm/processor.h>
-#include <linux/bitops.h>
-#include <linux/parport.h>
-
-#define SM_DEBUG
-
-/* ---------------------------------------------------------------------- */
-/*
- * Information that need to be kept for each board.
- */
-
-struct sm_state {
- struct hdlcdrv_state hdrv;
-
- const struct modem_tx_info *mode_tx;
- const struct modem_rx_info *mode_rx;
-
- const struct hardware_info *hwdrv;
-
- struct pardevice *pardev;
-
- /*
- * Hardware (soundcard) access routines state
- */
- struct {
- void *ibuf;
- unsigned int ifragsz;
- unsigned int ifragptr;
- unsigned int i16bit;
- void *obuf;
- unsigned int ofragsz;
- unsigned int ofragptr;
- unsigned int o16bit;
- int ptt_cnt;
- } dma;
-
- union {
- long hw[32/sizeof(long)];
- } hw;
-
- /*
- * state of the modem code
- */
- union {
- long m[48/sizeof(long)];
- } m;
- union {
- long d[256/sizeof(long)];
- } d;
-
-#define DIAGDATALEN 64
- struct diag_data {
- unsigned int mode;
- unsigned int flags;
- volatile int ptr;
- short data[DIAGDATALEN];
- } diag;
-
-
-#ifdef SM_DEBUG
- struct debug_vals {
- unsigned long last_jiffies;
- unsigned cur_intcnt;
- unsigned last_intcnt;
- unsigned mod_cyc;
- unsigned demod_cyc;
- unsigned dma_residue;
- } debug_vals;
-#endif /* SM_DEBUG */
-};
-
-/* ---------------------------------------------------------------------- */
-/*
- * Mode definition structure
- */
-
-struct modem_tx_info {
- const char *name;
- unsigned int loc_storage;
- int srate;
- int bitrate;
- void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int);
- void (*modulator_s16)(struct sm_state *, short *, unsigned int);
- void (*init)(struct sm_state *);
-};
-
-struct modem_rx_info {
- const char *name;
- unsigned int loc_storage;
- int srate;
- int bitrate;
- unsigned int overlap;
- unsigned int sperbit;
- void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int);
- void (*demodulator_s16)(struct sm_state *, const short *, unsigned int);
- void (*init)(struct sm_state *);
-};
-
-/* ---------------------------------------------------------------------- */
-/*
- * Soundcard driver definition structure
- */
-
-struct hardware_info {
- char *hw_name; /* used for request_{region,irq,dma} */
- unsigned int loc_storage;
- /*
- * mode specific open/close
- */
- int (*open)(struct net_device *, struct sm_state *);
- int (*close)(struct net_device *, struct sm_state *);
- int (*ioctl)(struct net_device *, struct sm_state *, struct ifreq *,
- struct hdlcdrv_ioctl *, int);
- int (*sethw)(struct net_device *, struct sm_state *, char *);
-};
-
-/* --------------------------------------------------------------------- */
-
-#define min(a, b) (((a) < (b)) ? (a) : (b))
-#define max(a, b) (((a) > (b)) ? (a) : (b))
-
-/* --------------------------------------------------------------------- */
-
-extern const char sm_drvname[];
-extern const char sm_drvinfo[];
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== diagnostics stuff ===============================
- */
-
-extern inline void diag_trigger(struct sm_state *sm)
-{
- if (sm->diag.ptr < 0)
- if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd)
- sm->diag.ptr = 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1))
-#define SHRT_MIN (-SHRT_MAX-1)
-
-extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod)
-{
- int val;
-
- if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
- sm->diag.mode != SM_DIAGMODE_DEMOD) ||
- sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
- return;
- val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp;
- /* clip */
- if (val > SHRT_MAX)
- val = SHRT_MAX;
- if (val < SHRT_MIN)
- val = SHRT_MIN;
- sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-extern inline void diag_add_one(struct sm_state *sm, int val)
-{
- if ((sm->diag.mode != SM_DIAGMODE_INPUT &&
- sm->diag.mode != SM_DIAGMODE_DEMOD) ||
- sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0)
- return;
- /* clip */
- if (val > SHRT_MAX)
- val = SHRT_MAX;
- if (val < SHRT_MIN)
- val = SHRT_MIN;
- sm->diag.data[sm->diag.ptr++] = val;
-}
-
-/* --------------------------------------------------------------------- */
-
-static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq)
-{
- if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) ||
- sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0)
- return;
- /* clip */
- if (vali > SHRT_MAX)
- vali = SHRT_MAX;
- if (vali < SHRT_MIN)
- vali = SHRT_MIN;
- if (valq > SHRT_MAX)
- valq = SHRT_MAX;
- if (valq < SHRT_MIN)
- valq = SHRT_MIN;
- sm->diag.data[sm->diag.ptr++] = vali;
- sm->diag.data[sm->diag.ptr++] = valq;
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== utility functions ===============================
- */
-
-#if 0
-extern inline unsigned int hweight32(unsigned int w)
- __attribute__ ((unused));
-extern inline unsigned int hweight16(unsigned short w)
- __attribute__ ((unused));
-extern inline unsigned int hweight8(unsigned char w)
- __attribute__ ((unused));
-
-extern inline unsigned int hweight32(unsigned int w)
-{
- unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555);
- res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
- res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F);
- res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF);
- return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF);
-}
-
-extern inline unsigned int hweight16(unsigned short w)
-{
- unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555);
- res = (res & 0x3333) + ((res >> 2) & 0x3333);
- res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F);
- return (res & 0x00FF) + ((res >> 8) & 0x00FF);
-}
-
-extern inline unsigned int hweight8(unsigned char w)
-{
- unsigned short res = (w & 0x55) + ((w >> 1) & 0x55);
- res = (res & 0x33) + ((res >> 2) & 0x33);
- return (res & 0x0F) + ((res >> 4) & 0x0F);
-}
-
-#endif
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
- __attribute__ ((unused));
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
- __attribute__ ((unused));
-
-extern inline unsigned int gcd(unsigned int x, unsigned int y)
-{
- for (;;) {
- if (!x)
- return y;
- if (!y)
- return x;
- if (x > y)
- x %= y;
- else
- y %= x;
- }
-}
-
-extern inline unsigned int lcm(unsigned int x, unsigned int y)
-{
- return x * y / gcd(x, y);
-}
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== profiling =======================================
- */
-
-
-#ifdef __i386__
-
-/*
- * only do 32bit cycle counter arithmetic; we hope we won't overflow.
- * in fact, overflowing modems would require over 2THz CPU clock speeds :-)
- */
-
-#define time_exec(var,cmd) \
-({ \
- if (cpu_has_tsc) { \
- unsigned int cnt1, cnt2, cnt3; \
- __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \
- cmd; \
- __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \
- var = cnt2-cnt1; \
- } else { \
- cmd; \
- } \
-})
-
-#else /* __i386__ */
-
-#define time_exec(var,cmd) cmd
-
-#endif /* __i386__ */
-
-/* --------------------------------------------------------------------- */
-
-extern const struct modem_tx_info sm_afsk1200_tx;
-extern const struct modem_tx_info sm_afsk2400_7_tx;
-extern const struct modem_tx_info sm_afsk2400_8_tx;
-extern const struct modem_tx_info sm_afsk2666_tx;
-extern const struct modem_tx_info sm_psk4800_tx;
-extern const struct modem_tx_info sm_hapn4800_8_tx;
-extern const struct modem_tx_info sm_hapn4800_10_tx;
-extern const struct modem_tx_info sm_hapn4800_pm8_tx;
-extern const struct modem_tx_info sm_hapn4800_pm10_tx;
-extern const struct modem_tx_info sm_fsk9600_4_tx;
-extern const struct modem_tx_info sm_fsk9600_5_tx;
-
-extern const struct modem_rx_info sm_afsk1200_rx;
-extern const struct modem_rx_info sm_afsk2400_7_rx;
-extern const struct modem_rx_info sm_afsk2400_8_rx;
-extern const struct modem_rx_info sm_afsk2666_rx;
-extern const struct modem_rx_info sm_psk4800_rx;
-extern const struct modem_rx_info sm_hapn4800_8_rx;
-extern const struct modem_rx_info sm_hapn4800_10_rx;
-extern const struct modem_rx_info sm_hapn4800_pm8_rx;
-extern const struct modem_rx_info sm_hapn4800_pm10_rx;
-extern const struct modem_rx_info sm_fsk9600_4_rx;
-extern const struct modem_rx_info sm_fsk9600_5_rx;
-
-extern const struct hardware_info sm_hw_sbc;
-extern const struct hardware_info sm_hw_sbcfdx;
-extern const struct hardware_info sm_hw_wss;
-extern const struct hardware_info sm_hw_wssfdx;
-
-extern const struct modem_tx_info *sm_modem_tx_table[];
-extern const struct modem_rx_info *sm_modem_rx_table[];
-extern const struct hardware_info *sm_hardware_table[];
-
-/* --------------------------------------------------------------------- */
-
-void sm_output_status(struct sm_state *sm);
-/*void sm_output_open(struct sm_state *sm);*/
-/*void sm_output_close(struct sm_state *sm);*/
-
-/* --------------------------------------------------------------------- */
-
-extern void inline sm_int_freq(struct sm_state *sm)
-{
-#ifdef SM_DEBUG
- unsigned long cur_jiffies = jiffies;
- /*
- * measure the interrupt frequency
- */
- sm->debug_vals.cur_intcnt++;
- if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) {
- sm->debug_vals.last_jiffies = cur_jiffies;
- sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt;
- sm->debug_vals.cur_intcnt = 0;
- }
-#endif /* SM_DEBUG */
-}
-
-/* --------------------------------------------------------------------- */
-#endif /* _SM_H */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk1200.c b/drivers/net/hamradio/soundmodem/sm_afsk1200.c
deleted file mode 100644
index 64b20a57c..000000000
--- a/drivers/net/hamradio/soundmodem/sm_afsk1200.c
+++ /dev/null
@@ -1,272 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_afsk1200.c -- soundcard radio modem driver, 1200 baud AFSK modem
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk1200.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk12 {
- unsigned int shreg;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- unsigned char last_rxbit;
-};
-
-struct mod_state_afsk12 {
- unsigned int shreg;
- unsigned char tx_bit;
- unsigned int bit_pll;
- unsigned int dds_inc;
- unsigned int txphase;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = {
- AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE,
- AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE
-};
-
-static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf,
- unsigned int buflen)
-{
- struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!((st->txphase++) & 7)) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- }
- st->dds_inc = dds_inc[st->tx_bit & 1];
- *buf++ = OFFSCOS(st->bit_pll);
- st->bit_pll += st->dds_inc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!((st->txphase++) & 7)) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- }
- st->dds_inc = dds_inc[st->tx_bit & 1];
- *buf++ = COS(st->bit_pll);
- st->bit_pll += st->dds_inc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum)
-{
- int sum = -0x80 * csum;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
-
- sum >>= 7;
- return sum * sum;
-}
-
-extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum)
-{
- int sum = 0;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
-
- sum >>= 15;
- return sum * sum;
-}
-
-extern __inline__ int do_filter_1200_u8(const unsigned char *buf)
-{
- int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
- sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
- sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
- sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
- return sum;
-}
-
-extern __inline__ int do_filter_1200_s16(const short *buf)
-{
- int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I);
- sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q);
- sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I);
- sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q);
- return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static const int pll_corr[2] = { -0x1000, 0x1000 };
-
-static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_1200_u8(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x2000;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr
- [st->bit_pll < 0x9000];
- j = 4 * hweight8(st->dcd_shreg & 0x38)
- - hweight16(st->dcd_shreg & 0x7c0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, (((int)*buf)-0x80) << 8, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_1200_s16(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x2000;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr
- [st->bit_pll < 0x9000];
- j = 4 * hweight8(st->dcd_shreg & 0x38)
- - hweight16(st->dcd_shreg & 0x7c0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, *buf, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_1200(struct sm_state *sm)
-{
- struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d);
-
- st->dcd_time = 120;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk1200_tx = {
- "afsk1200", sizeof(struct mod_state_afsk12),
- AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk1200_rx = {
- "afsk1200", sizeof(struct demod_state_afsk12),
- AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200,
- demodulator_1200_u8, demodulator_1200_s16, demod_init_1200
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c
deleted file mode 100644
index d217936ab..000000000
--- a/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-/*
- * This driver is intended to be compatible with TCM3105 modems
- * overclocked to 7.3728MHz. The mark and space frequencies therefore
- * lie at 3658 and 1996 Hz.
- * Note that I do _not_ recommend the building of such links, I provide
- * this only for the users who live in the coverage area of such
- * a "legacy" link.
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk2400_7.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk24 {
- unsigned int shreg;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- unsigned char last_rxbit;
-};
-
-struct mod_state_afsk24 {
- unsigned int shreg;
- unsigned char tx_bit;
- unsigned int bit_pll;
- unsigned int tx_seq;
- unsigned int phinc;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
- AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
-
-static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (st->tx_seq < 0x5555) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- st->phinc = dds_inc[st->tx_bit & 1];
- }
- st->tx_seq += 0x5555;
- st->tx_seq &= 0xffff;
- *buf = OFFSCOS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (st->tx_seq < 0x5555) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- st->phinc = dds_inc[st->tx_bit & 1];
- }
- st->tx_seq += 0x5555;
- st->tx_seq &= 0xffff;
- *buf = COS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
-{
- int sum = -0x80 * csum;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
- sum += (st[-12] * coeff[12]);
- sum += (st[-13] * coeff[13]);
-
- sum >>= 7;
- return sum * sum;
-}
-
-extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
-{
- int sum = 0;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
- sum += (st[-12] * coeff[12]);
- sum += (st[-13] * coeff[13]);
-
- sum >>= 15;
- return sum * sum;
-}
-
-extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
-{
- int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
- sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
- sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
- sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
- return sum;
-}
-
-extern __inline__ int do_filter_2400_s16(const short *buf)
-{
- int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
- sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
- sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
- sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
- return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_2400_u8(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += AFSK24_BITPLL_INC;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
- st->bit_pll += AFSK24_BITPLL_INC/2;
- else
- st->bit_pll -= AFSK24_BITPLL_INC/2;
- j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- - hweight16(st->dcd_shreg & 0x1e0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, (((int)*buf)-0x80) << 8, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_2400_s16(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += AFSK24_BITPLL_INC;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
- st->bit_pll += AFSK24_BITPLL_INC/2;
- else
- st->bit_pll -= AFSK24_BITPLL_INC/2;
- j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- - hweight16(st->dcd_shreg & 0x1e0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, *buf, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_2400(struct sm_state *sm)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-
- st->dcd_time = 120;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk2400_7_tx = {
- "afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
- modulator_2400_u8, modulator_2400_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk2400_7_rx = {
- "afsk2400_7", sizeof(struct demod_state_afsk24),
- AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
- demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c
deleted file mode 100644
index 23d233746..000000000
--- a/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c
+++ /dev/null
@@ -1,296 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-/*
- * This driver is intended to be compatible with TCM3105 modems
- * overclocked to 8MHz. The mark and space frequencies therefore
- * lie at 3970 and 2165 Hz.
- * Note that I do _not_ recommend the building of such links, I provide
- * this only for the users who live in the coverage area of such
- * a "legacy" link.
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk2400_8.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk24 {
- unsigned int shreg;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- unsigned char last_rxbit;
-};
-
-struct mod_state_afsk24 {
- unsigned int shreg;
- unsigned char tx_bit;
- unsigned int bit_pll;
- unsigned int tx_seq;
- unsigned int phinc;
-};
-
-/* --------------------------------------------------------------------- */
-
-static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE,
- AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE };
-
-static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (st->tx_seq < 0x5555) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- st->phinc = dds_inc[st->tx_bit & 1];
- }
- st->tx_seq += 0x5555;
- st->tx_seq &= 0xffff;
- *buf = OFFSCOS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (st->tx_seq < 0x5555) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1;
- st->shreg >>= 1;
- st->phinc = dds_inc[st->tx_bit & 1];
- }
- st->tx_seq += 0x5555;
- st->tx_seq &= 0xffff;
- *buf = COS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum)
-{
- int sum = -0x80 * csum;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
- sum += (st[-12] * coeff[12]);
- sum += (st[-13] * coeff[13]);
-
- sum >>= 7;
- return sum * sum;
-}
-
-extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum)
-{
- int sum = 0;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
- sum += (st[-12] * coeff[12]);
- sum += (st[-13] * coeff[13]);
-
- sum >>= 15;
- return sum * sum;
-}
-
-extern __inline__ int do_filter_2400_u8(const unsigned char *buf)
-{
- int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
- sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
- sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
- sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
- return sum;
-}
-
-extern __inline__ int do_filter_2400_s16(const short *buf)
-{
- int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I);
- sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q);
- sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I);
- sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q);
- return sum;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_2400_u8(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += AFSK24_BITPLL_INC;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
- st->bit_pll += AFSK24_BITPLL_INC/2;
- else
- st->bit_pll -= AFSK24_BITPLL_INC/2;
- j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- - hweight16(st->dcd_shreg & 0x1e0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, (((int)*buf)-0x80) << 8, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
- int j;
- int sum;
- unsigned char newsample;
-
- for (; buflen > 0; buflen--, buf++) {
- sum = do_filter_2400_s16(buf);
- st->dcd_shreg <<= 1;
- st->bit_pll += AFSK24_BITPLL_INC;
- newsample = (sum > 0);
- if (st->last_sample ^ newsample) {
- st->last_sample = newsample;
- st->dcd_shreg |= 1;
- if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2))
- st->bit_pll += AFSK24_BITPLL_INC/2;
- else
- st->bit_pll -= AFSK24_BITPLL_INC/2;
- j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c)
- - hweight16(st->dcd_shreg & 0x1e0);
- st->dcd_sum0 += j;
- }
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 120;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->shreg >>= 1;
- st->shreg |= (!(st->last_rxbit ^
- st->last_sample)) << 16;
- st->last_rxbit = st->last_sample;
- diag_trigger(sm);
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
- diag_add(sm, *buf, sum);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_2400(struct sm_state *sm)
-{
- struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d);
-
- st->dcd_time = 120;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk2400_8_tx = {
- "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400,
- modulator_2400_u8, modulator_2400_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk2400_8_rx = {
- "afsk2400_8", sizeof(struct demod_state_afsk24),
- AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400,
- demodulator_2400_u8, demodulator_2400_s16, demod_init_2400
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2666.c b/drivers/net/hamradio/soundmodem/sm_afsk2666.c
deleted file mode 100644
index 2aa2972e4..000000000
--- a/drivers/net/hamradio/soundmodem/sm_afsk2666.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_afsk2666.c -- soundcard radio modem driver, 2666 baud AFSK modem
- *
- * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_afsk2666.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_afsk26 {
- unsigned int shreg;
- unsigned long descram;
- int dem_sum[8];
- int dem_sum_mean;
- int dem_cnt;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
-};
-
-struct mod_state_afsk26 {
- unsigned int shreg;
- unsigned long scram;
- unsigned int bit_pll;
- unsigned int phinc;
- unsigned int tx_seq;
-};
-
-/* --------------------------------------------------------------------- */
-
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = ((st->scram << 1) | (st->scram & 1));
- st->scram ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
- }
- if (st->tx_seq >= 6)
- st->tx_seq = 0;
- *buf = OFFSCOS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = ((st->scram << 1) | (st->scram & 1));
- st->scram ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))];
- }
- if (st->tx_seq >= 6)
- st->tx_seq = 0;
- *buf = COS(st->bit_pll);
- st->bit_pll += st->phinc;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int convolution12_u8(const unsigned char *st, const int *coeff, int csum)
-{
- int sum = -0x80 * csum;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
-
- return sum;
-}
-
-extern __inline__ int convolution12_s16(const short *st, const int *coeff, int csum)
-{
- int sum = 0;
-
- sum += (st[0] * coeff[0]);
- sum += (st[-1] * coeff[1]);
- sum += (st[-2] * coeff[2]);
- sum += (st[-3] * coeff[3]);
- sum += (st[-4] * coeff[4]);
- sum += (st[-5] * coeff[5]);
- sum += (st[-6] * coeff[6]);
- sum += (st[-7] * coeff[7]);
- sum += (st[-8] * coeff[8]);
- sum += (st[-9] * coeff[9]);
- sum += (st[-10] * coeff[10]);
- sum += (st[-11] * coeff[11]);
-
- sum >>= 8;
- return sum;
-}
-
-/* ---------------------------------------------------------------------- */
-
-#if 0
-static int binexp(unsigned int i)
-{
- int ret = 31;
-
- if (!i)
- return 0;
- if (i < 0x10000LU) {
- i <<= 16;
- ret -= 16;
- }
- if (i < 0x1000000LU) {
- i <<= 8;
- ret -= 8;
- }
- if (i < 0x10000000LU) {
- i <<= 4;
- ret -= 4;
- }
- if (i < 0x40000000LU) {
- i <<= 2;
- ret -= 2;
- }
- if (i < 0x80000000LU)
- ret -= 1;
- return ret;
-}
-
-static const sqrt_tab[16] = {
- 00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348,
- 46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455
-};
-
-
-static unsigned int int_sqrt_approx(unsigned int i)
-{
- unsigned int j;
-
- if (i < 16)
- return sqrt_tab[i] >> 14;
- j = binexp(i) >> 1;
- i >>= (j * 2 - 2);
- return (sqrt_tab[i & 0xf] << j) >> 15;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-extern unsigned int est_pwr(int i, int q)
-{
- unsigned int ui = abs(i);
- unsigned int uq = abs(q);
-
- if (uq > ui) {
- unsigned int tmp;
- tmp = ui;
- ui = uq;
- uq = tmp;
- }
- if (uq > (ui >> 1))
- return 7*(ui>>3) + 9*(uq>>4);
- else
- return ui + (uq>>2);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval,
- int loi, int loq, int hii, int hiq)
-{
- static const int pll_corr[2] = { -0xa00, 0xa00 };
- unsigned char curbit;
- unsigned int descx;
- int val;
-
- /*
- * estimate power
- */
- val = est_pwr(hii, hiq) - est_pwr(loi, loq);
- /*
- * estimate center value
- */
- st->dem_sum[0] += val >> 8;
- if ((++st->dem_cnt) >= 256) {
- st->dem_cnt = 0;
- st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+
- st->dem_sum[2]+st->dem_sum[3]+
- st->dem_sum[4]+st->dem_sum[5]+
- st->dem_sum[6]+st->dem_sum[7]) >> 3;
- memmove(st->dem_sum+1, st->dem_sum,
- sizeof(st->dem_sum)-sizeof(st->dem_sum[0]));
- st->dem_sum[0] = 0;
- }
- /*
- * decision and bit clock regen
- */
- val -= st->dem_sum_mean;
- diag_add(sm, curval, val);
-
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x1555;
- curbit = (val > 0);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)];
- st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) -
- hweight16(st->dcd_shreg & 0xfe00);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, curbit);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 400;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffffu;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
-
- for (; buflen > 0; buflen--, buf++) {
- demod_one_sample(sm, st, (*buf-0x80)<<8,
- convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
- convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
- convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
- convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
- demod_one_sample(sm, st, (*buf-0x80)<<8,
- convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
- convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
- convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
- convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
-
- for (; buflen > 0; buflen--, buf++) {
- demod_one_sample(sm, st, *buf,
- convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0),
- convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0),
- convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1),
- convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1));
- demod_one_sample(sm, st, *buf,
- convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0),
- convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0),
- convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1),
- convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1));
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_2666(struct sm_state *sm)
-{
- struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d);
-
- st->dcd_time = 400;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_afsk2666_tx = {
- "afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666,
- modulator_2666_u8, modulator_2666_s16, NULL
-};
-
-const struct modem_rx_info sm_afsk2666_rx = {
- "afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6,
- demodulator_2666_u8, demodulator_2666_s16, demod_init_2666
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_fsk9600.c b/drivers/net/hamradio/soundmodem/sm_fsk9600.c
deleted file mode 100644
index bc2fb53b1..000000000
--- a/drivers/net/hamradio/soundmodem/sm_fsk9600.c
+++ /dev/null
@@ -1,391 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_fsk9600.c -- soundcard radio modem driver,
- * 9600 baud G3RUH compatible FSK modem
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_fsk9600.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_fsk96 {
- unsigned int shreg;
- unsigned long descram;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
-};
-
-struct mod_state_fsk96 {
- unsigned int shreg;
- unsigned long scram;
- unsigned char tx_bit;
- unsigned char *txtbl;
- unsigned int txphase;
-};
-
-/* --------------------------------------------------------------------- */
-
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!st->txphase++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) | (st->scram & 1);
- st->scram ^= !(st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
- st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
- }
- if (st->txphase >= 4)
- st->txphase = 0;
- *buf++ = *st->txtbl;
- st->txtbl += 0x100;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!st->txphase++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) | (st->scram & 1);
- st->scram ^= !(st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
- st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff);
- }
- if (st->txphase >= 4)
- st->txphase = 0;
- *buf++ = ((*st->txtbl)-0x80) << 8;
- st->txtbl += 0x100;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
- static const int pll_corr[2] = { -0x1000, 0x1000 };
- unsigned char curbit;
- unsigned int descx;
-
- for (; buflen > 0; buflen--, buf++) {
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x4000;
- curbit = (*buf >= 0x80);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr[st->bit_pll < 0xa000];
- st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
- !!(st->dcd_shreg & 0x10);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
- static const int pll_corr[2] = { -0x1000, 0x1000 };
- unsigned char curbit;
- unsigned int descx;
-
- for (; buflen > 0; buflen--, buf++) {
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x4000;
- curbit = (*buf >= 0);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr[st->bit_pll < 0xa000];
- st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) -
- !!(st->dcd_shreg & 0x10);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, *buf);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!st->txphase++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) | (st->scram & 1);
- st->scram ^= !(st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
- st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
- }
- if (st->txphase >= 5)
- st->txphase = 0;
- *buf++ = *st->txtbl;
- st->txtbl += 0x100;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m);
-
- for (; buflen > 0; buflen--) {
- if (!st->txphase++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) | (st->scram & 1);
- st->scram ^= !(st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & (SCRAM_TAP1 << 1))
- st->scram ^= SCRAM_TAPN << 1;
- st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2)));
- st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff);
- }
- if (st->txphase >= 5)
- st->txphase = 0;
- *buf++ = ((*st->txtbl)-0x80)<<8;
- st->txtbl += 0x100;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
- static const int pll_corr[2] = { -0x1000, 0x1000 };
- unsigned char curbit;
- unsigned int descx;
-
- for (; buflen > 0; buflen--, buf++) {
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x3333;
- curbit = (*buf >= 0x80);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr[st->bit_pll < 0x9999];
- st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
- hweight8(st->dcd_shreg & 0x70);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
- static const int pll_corr[2] = { -0x1000, 0x1000 };
- unsigned char curbit;
- unsigned int descx;
-
- for (; buflen > 0; buflen--, buf++) {
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x3333;
- curbit = (*buf >= 0);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr[st->bit_pll < 0x9999];
- st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) -
- hweight8(st->dcd_shreg & 0x70);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, *buf);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_9600(struct sm_state *sm)
-{
- struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d);
-
- st->dcd_time = 240;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_fsk9600_4_tx = {
- "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600,
- modulator_9600_4_u8, modulator_9600_4_s16, NULL
-};
-
-const struct modem_rx_info sm_fsk9600_4_rx = {
- "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4,
- demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_fsk9600_5_tx = {
- "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600,
- modulator_9600_5_u8, modulator_9600_5_s16, NULL
-};
-
-const struct modem_rx_info sm_fsk9600_5_rx = {
- "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5,
- demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_hapn4800.c b/drivers/net/hamradio/soundmodem/sm_hapn4800.c
deleted file mode 100644
index f6babcd9d..000000000
--- a/drivers/net/hamradio/soundmodem/sm_hapn4800.c
+++ /dev/null
@@ -1,560 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN modem
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- *
- * This module implements a (hopefully) HAPN (Hamilton Area Packet
- * Network) compatible 4800 baud modem.
- * The HAPN modem uses kind of "duobinary signalling" (not really,
- * duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling
- * instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see
- * Proakis, Digital Communications).
- * The code is untested. It is compatible with itself (i.e. it can decode
- * the packets it sent), but I could not test if it is compatible with
- * any "real" HAPN modem, since noone uses it in my region of the world.
- * Feedback therefore welcome.
- */
-
-#include "sm.h"
-#include "sm_tbl_hapn4800.h"
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_hapn48 {
- unsigned int shreg;
- unsigned int bit_pll;
- unsigned char last_bit;
- unsigned char last_bit2;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
- int lvlhi, lvllo;
-};
-
-struct mod_state_hapn48 {
- unsigned int shreg;
- unsigned char tx_bit;
- unsigned int tx_seq;
- const unsigned char *tbl;
-};
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = ((st->tx_bit << 1) |
- (st->tx_bit & 1));
- st->tx_bit ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 10)
- st->tx_seq = 0;
- *buf = *st->tbl;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = ((st->tx_bit << 1) |
- (st->tx_bit & 1));
- st->tx_bit ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 10)
- st->tx_seq = 0;
- *buf = ((*st->tbl)-0x80)<<8;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
- st->tx_bit ^= !(st->shreg & 1);
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 8)
- st->tx_seq = 0;
- *buf = *st->tbl;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
- st->tx_bit ^= !(st->shreg & 1);
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 8)
- st->tx_seq = 0;
- *buf = ((*st->tbl)-0x80)<<8;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = ((st->tx_bit << 1) |
- (st->tx_bit & 1));
- st->tx_bit ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 10)
- st->tx_seq = 0;
- *buf = *st->tbl;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = ((st->tx_bit << 1) |
- (st->tx_bit & 1));
- st->tx_bit ^= (!(st->shreg & 1));
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 10)
- st->tx_seq = 0;
- *buf = ((*st->tbl)-0x80)<<8;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
- st->tx_bit ^= !(st->shreg & 1);
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 8)
- st->tx_seq = 0;
- *buf = *st->tbl;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m);
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->tx_seq++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1);
- st->tx_bit ^= !(st->shreg & 1);
- st->shreg >>= 1;
- st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf);
- }
- if (st->tx_seq >= 8)
- st->tx_seq = 0;
- *buf = ((*st->tbl)-0x80)<<8;
- st->tbl += 0x10;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
- static const int pll_corr[2] = { -0x800, 0x800 };
- int curst, cursync;
- int inv;
-
- for (; buflen > 0; buflen--, buf++) {
- inv = ((int)(buf[-2])-0x80) << 8;
- st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
- st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
- if (inv > st->lvlhi)
- st->lvlhi = inv;
- if (inv < st->lvllo)
- st->lvllo = inv;
- if (buflen & 1)
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x199a;
- curst = cursync = 0;
- if (inv > st->lvlhi >> 1) {
- curst = 1;
- cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
- buf[-2] > buf[-0] && buf[-2] > buf[-4]);
- } else if (inv < st->lvllo >> 1) {
- curst = -1;
- cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
- buf[-2] < buf[-0] && buf[-2] < buf[-4]);
- }
- if (cursync) {
- st->dcd_shreg |= cursync;
- st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
- st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
- hweight32(st->dcd_shreg & 0xe739ce70);
- }
- hdlcdrv_channelbit(&sm->hdrv, cursync);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->last_bit2 = st->last_bit;
- if (curst < 0)
- st->last_bit = 0;
- else if (curst > 0)
- st->last_bit = 1;
- st->shreg >>= 1;
- st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, inv);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
- static const int pll_corr[2] = { -0x800, 0x800 };
- int curst, cursync;
- int inv;
-
- for (; buflen > 0; buflen--, buf++) {
- inv = buf[-2];
- st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
- st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
- if (inv > st->lvlhi)
- st->lvlhi = inv;
- if (inv < st->lvllo)
- st->lvllo = inv;
- if (buflen & 1)
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x199a;
- curst = cursync = 0;
- if (inv > st->lvlhi >> 1) {
- curst = 1;
- cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
- buf[-2] > buf[-0] && buf[-2] > buf[-4]);
- } else if (inv < st->lvllo >> 1) {
- curst = -1;
- cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
- buf[-2] < buf[-0] && buf[-2] < buf[-4]);
- }
- if (cursync) {
- st->dcd_shreg |= cursync;
- st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu];
- st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) -
- hweight32(st->dcd_shreg & 0xe739ce70);
- }
- hdlcdrv_channelbit(&sm->hdrv, cursync);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->last_bit2 = st->last_bit;
- if (curst < 0)
- st->last_bit = 0;
- else if (curst > 0)
- st->last_bit = 1;
- st->shreg >>= 1;
- st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, inv);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
- static const int pll_corr[2] = { -0x800, 0x800 };
- int curst, cursync;
- int inv;
-
- for (; buflen > 0; buflen--, buf++) {
- inv = ((int)(buf[-2])-0x80) << 8;
- st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
- st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
- if (inv > st->lvlhi)
- st->lvlhi = inv;
- if (inv < st->lvllo)
- st->lvllo = inv;
- if (buflen & 1)
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x2000;
- curst = cursync = 0;
- if (inv > st->lvlhi >> 1) {
- curst = 1;
- cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
- buf[-2] > buf[-0] && buf[-2] > buf[-4]);
- } else if (inv < st->lvllo >> 1) {
- curst = -1;
- cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
- buf[-2] < buf[-0] && buf[-2] < buf[-4]);
- }
- if (cursync) {
- st->dcd_shreg |= cursync;
- st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
- st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
- hweight32(st->dcd_shreg & 0xbbbbbbbb);
- }
- hdlcdrv_channelbit(&sm->hdrv, cursync);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->last_bit2 = st->last_bit;
- if (curst < 0)
- st->last_bit = 0;
- else if (curst > 0)
- st->last_bit = 1;
- st->shreg >>= 1;
- st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, inv);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
- static const int pll_corr[2] = { -0x800, 0x800 };
- int curst, cursync;
- int inv;
-
- for (; buflen > 0; buflen--, buf++) {
- inv = buf[-2];
- st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */
- st->lvllo = (st->lvllo * 65309) >> 16; /* decay */
- if (inv > st->lvlhi)
- st->lvlhi = inv;
- if (inv < st->lvllo)
- st->lvllo = inv;
- if (buflen & 1)
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x2000;
- curst = cursync = 0;
- if (inv > st->lvlhi >> 1) {
- curst = 1;
- cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] &&
- buf[-2] > buf[-0] && buf[-2] > buf[-4]);
- } else if (inv < st->lvllo >> 1) {
- curst = -1;
- cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] &&
- buf[-2] < buf[-0] && buf[-2] < buf[-4]);
- }
- if (cursync) {
- st->dcd_shreg |= cursync;
- st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u];
- st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) -
- hweight32(st->dcd_shreg & 0xbbbbbbbb);
- }
- hdlcdrv_channelbit(&sm->hdrv, cursync);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffff;
- st->last_bit2 = st->last_bit;
- if (curst < 0)
- st->last_bit = 0;
- else if (curst > 0)
- st->last_bit = 1;
- st->shreg >>= 1;
- st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, inv);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_hapn4800(struct sm_state *sm)
-{
- struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d);
-
- st->dcd_time = 120;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_8_tx = {
- "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800,
- modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_8_rx = {
- "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
- demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_10_tx = {
- "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800,
- modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_10_rx = {
- "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
- demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_pm8_tx = {
- "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800,
- modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_pm8_rx = {
- "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8,
- demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_hapn4800_pm10_tx = {
- "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800,
- modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL
-};
-
-const struct modem_rx_info sm_hapn4800_pm10_rx = {
- "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10,
- demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_psk4800.c b/drivers/net/hamradio/soundmodem/sm_psk4800.c
deleted file mode 100644
index cbb49042b..000000000
--- a/drivers/net/hamradio/soundmodem/sm_psk4800.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_psk4800.c -- soundcard radio modem driver, 4800 baud 8PSK modem
- *
- * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include "sm.h"
-#include "sm_tbl_psk4800.h"
-
-/* --------------------------------------------------------------------- */
-
-#define DESCRAM_TAP1 0x20000
-#define DESCRAM_TAP2 0x01000
-#define DESCRAM_TAP3 0x00001
-
-#define DESCRAM_TAPSH1 17
-#define DESCRAM_TAPSH2 12
-#define DESCRAM_TAPSH3 0
-
-#define SCRAM_TAP1 0x20000 /* X^17 */
-#define SCRAM_TAPN 0x00021 /* X^0+X^5 */
-
-#define SCRAM_SHIFT 17
-
-/* --------------------------------------------------------------------- */
-
-struct demod_state_psk48 {
- /*
- * input mixer and lowpass
- */
- short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2];
- unsigned int downmixer;
- int ovrphase;
- short magi, magq;
- /*
- * sampling instant recovery
- */
- int pwrhist[5];
- unsigned int s_phase;
- int cur_sync;
- /*
- * phase recovery
- */
- short cur_phase_dev;
- short last_ph_err;
- unsigned short pskph;
- unsigned int phase;
- unsigned short last_pskph;
- unsigned char cur_raw, last_raw, rawbits;
- /*
- * decoding
- */
- unsigned int shreg;
- unsigned long descram;
- unsigned int bit_pll;
- unsigned char last_sample;
- unsigned int dcd_shreg;
- int dcd_sum0, dcd_sum1, dcd_sum2;
- unsigned int dcd_time;
-};
-
-struct mod_state_psk48 {
- unsigned char txbits[PSK48_TXF_NUMSAMPLES];
- unsigned short txphase;
- unsigned int shreg;
- unsigned long scram;
- const short *tbl;
- unsigned int txseq;
-};
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen)
-{
- struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
- int i, j;
- int si, sq;
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->txseq++) {
- memmove(st->txbits+1, st->txbits,
- sizeof(st->txbits)-sizeof(st->txbits[0]));
- for (i = 0; i < 3; i++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) |
- (st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & SCRAM_TAP1)
- st->scram ^= SCRAM_TAPN;
- }
- j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
- st->txbits[0] -= (j ^ (j >> 1));
- st->txbits[0] &= 7;
- st->tbl = psk48_tx_table;
- }
- if (st->txseq >= PSK48_TXF_OVERSAMPLING)
- st->txseq = 0;
- for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
- si += st->tbl[st->txbits[j]];
- sq += st->tbl[st->txbits[j]+8];
- }
- *buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80;
- st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen)
-{
- struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
- int i, j;
- int si, sq;
-
- for (; buflen > 0; buflen--, buf++) {
- if (!st->txseq++) {
- memmove(st->txbits+1, st->txbits,
- sizeof(st->txbits)-sizeof(st->txbits[0]));
- for (i = 0; i < 3; i++) {
- if (st->shreg <= 1)
- st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000;
- st->scram = (st->scram << 1) |
- (st->shreg & 1);
- st->shreg >>= 1;
- if (st->scram & SCRAM_TAP1)
- st->scram ^= SCRAM_TAPN;
- }
- j = (st->scram >> (SCRAM_SHIFT+3)) & 7;
- st->txbits[0] -= (j ^ (j >> 1));
- st->txbits[0] &= 7;
- st->tbl = psk48_tx_table;
- }
- if (st->txseq >= PSK48_TXF_OVERSAMPLING)
- st->txseq = 0;
- for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) {
- si += st->tbl[st->txbits[j]];
- sq += st->tbl[st->txbits[j]+8];
- }
- *buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15;
- st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static __inline__ unsigned short tbl_atan(short q, short i)
-{
- short tmp;
- unsigned short argoffs = 0;
-
- if (i == 0 && q == 0)
- return 0;
- switch (((q < 0) << 1) | (i < 0)) {
- case 0:
- break;
- case 1:
- tmp = q;
- q = -i;
- i = tmp;
- argoffs = 0x4000;
- break;
- case 3:
- q = -q;
- i = -i;
- argoffs = 0x8000;
- break;
- case 2:
- tmp = -q;
- q = i;
- i = tmp;
- argoffs = 0xc000;
- break;
- }
- if (q > i) {
- tmp = i / q * ATAN_TABLEN;
- return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)])
- &0xffffu;
- }
- return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu;
-}
-
-#define ATAN(q,i) tbl_atan(q, i)
-
-/* --------------------------------------------------------------------- */
-
-static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st,
- short vali, short valq)
-{
- int i, j;
-
- st->magi = vali;
- st->magq = valq;
- memmove(st->pwrhist+1, st->pwrhist,
- sizeof(st->pwrhist)-sizeof(st->pwrhist[0]));
- st->pwrhist[0] = st->magi * st->magi +
- st->magq * st->magq;
- st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] &&
- (st->pwrhist[0] >> 2) > st->pwrhist[2] &&
- st-> pwrhist[3] > st->pwrhist[2] &&
- st->pwrhist[1] > st->pwrhist[2]);
- st->s_phase &= 0xffff;
- st->s_phase += PSK48_SPHASEINC;
- st->dcd_shreg <<= 1;
- if (st->cur_sync) {
- if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2))
- st->s_phase -= PSK48_SPHASEINC/6;
- else
- st->s_phase += PSK48_SPHASEINC/6;
- st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)-
- hweight16(st->dcd_shreg & 0x1f00);
- }
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->s_phase < 0x10000)
- return;
- /*
- * sample one constellation
- */
- st->last_pskph = st->pskph;
- st->pskph = (ATAN(st->magq, st->magi)-
- st->phase) & 0xffffu;
- st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000;
- st->phase += st->last_ph_err/16;
- st->last_raw = st->cur_raw;
- st->cur_raw = ((st->pskph >> 13) & 7);
- i = (st->cur_raw - st->last_raw) & 7;
- st->rawbits = i ^ (i >> 1) ^ (i >> 2);
- st->descram = (st->descram << 3) | (st->rawbits);
- hdlcdrv_channelbit(&sm->hdrv, st->descram & 4);
- hdlcdrv_channelbit(&sm->hdrv, st->descram & 2);
- hdlcdrv_channelbit(&sm->hdrv, st->descram & 1);
- i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^
- ((st->descram >> DESCRAM_TAPSH2) & 7) ^
- ((st->descram >> DESCRAM_TAPSH3) & 7));
- for (j = 4; j; j >>= 1) {
- st->shreg >>= 1;
- st->shreg |= (!!(i & j)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- }
-
-#if 0
- st->dcd_shreg <<= 1;
- st->bit_pll += 0x4000;
- curbit = (*buf >= 0x80);
- if (st->last_sample ^ curbit) {
- st->dcd_shreg |= 1;
- st->bit_pll += pll_corr
- [st->bit_pll < 0xa000];
- st->dcd_sum0 += 8 *
- hweight8(st->dcd_shreg & 0x0c) -
- !!(st->dcd_shreg & 0x10);
- }
- st->last_sample = curbit;
- hdlcdrv_channelbit(&sm->hdrv, st->last_sample);
- if ((--st->dcd_time) <= 0) {
- hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 +
- st->dcd_sum1 +
- st->dcd_sum2) < 0);
- st->dcd_sum2 = st->dcd_sum1;
- st->dcd_sum1 = st->dcd_sum0;
- st->dcd_sum0 = 2; /* slight bias */
- st->dcd_time = 240;
- }
- if (st->bit_pll >= 0x10000) {
- st->bit_pll &= 0xffffu;
- st->descram = (st->descram << 1) | curbit;
- descx = st->descram ^ (st->descram >> 1);
- descx ^= ((descx >> DESCRAM_TAPSH1) ^
- (descx >> DESCRAM_TAPSH2));
- st->shreg >>= 1;
- st->shreg |= (!(descx & 1)) << 16;
- if (st->shreg & 1) {
- hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1);
- st->shreg = 0x10000;
- }
- diag_trigger(sm);
- }
- diag_add_one(sm, ((short)(*buf - 0x80)) << 8);
-#endif
-
- diag_trigger(sm);
- diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13,
- (valq*COS(st->phase) - vali*SIN(st->phase)) >> 13);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen)
-{
- struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
- int i, si, sq;
- const short *coeff;
-
- for (; buflen > 0; buflen--, buf++) {
- memmove(st->infi+1, st->infi,
- sizeof(st->infi)-sizeof(st->infi[0]));
- memmove(st->infq+1, st->infq,
- sizeof(st->infq)-sizeof(st->infq[0]));
- si = *buf;
- si &= 0xff;
- si -= 128;
- diag_add_one(sm, si << 8);
- st->infi[0] = (si * COS(st->downmixer))>>7;
- st->infq[0] = (si * SIN(st->downmixer))>>7;
- st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
- for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2);
- i++, coeff += 2) {
- si += st->infi[i] * (*coeff);
- sq += st->infq[i] * (*coeff);
- }
- demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
- for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2);
- i++, coeff += 2) {
- si += st->infi[i] * (*coeff);
- sq += st->infq[i] * (*coeff);
- }
- demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen)
-{
- struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
- int i, si, sq;
- const short *coeff;
-
- for (; buflen > 0; buflen--, buf++) {
- memmove(st->infi+1, st->infi,
- sizeof(st->infi)-sizeof(st->infi[0]));
- memmove(st->infq+1, st->infq,
- sizeof(st->infq)-sizeof(st->infq[0]));
- si = *buf;
- diag_add_one(sm, si);
- st->infi[0] = (si * COS(st->downmixer))>>15;
- st->infq[0] = (si * SIN(st->downmixer))>>15;
- st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu;
- for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2);
- i++, coeff += 2) {
- si += st->infi[i] * (*coeff);
- sq += st->infq[i] * (*coeff);
- }
- demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
- for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2);
- i++, coeff += 2) {
- si += st->infi[i] * (*coeff);
- sq += st->infq[i] * (*coeff);
- }
- demod_psk48_baseband(sm, st, si >> 15, sq >> 15);
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static void mod_init_4800(struct sm_state *sm)
-{
- struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m);
-
- st->scram = 1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void demod_init_4800(struct sm_state *sm)
-{
- struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d);
-
- st->dcd_time = 120;
- st->dcd_sum0 = 2;
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct modem_tx_info sm_psk4800_tx = {
- "psk4800", sizeof(struct mod_state_psk48),
- PSK48_SAMPLERATE, 4800,
- modulator_4800_u8, modulator_4800_s16, mod_init_4800
-};
-
-const struct modem_rx_info sm_psk4800_rx = {
- "psk4800", sizeof(struct demod_state_psk48),
- PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING,
- demodulator_4800_u8, demodulator_4800_s16, demod_init_4800
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c
deleted file mode 100644
index 85ae47588..000000000
--- a/drivers/net/hamradio/soundmodem/sm_sbc.c
+++ /dev/null
@@ -1,941 +0,0 @@
-
-
-/*
- * sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include <linux/ptrace.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/ioport.h>
-#include <linux/soundmodem.h>
-#include <linux/delay.h>
-#include "sm.h"
-#include "smdma.h"
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
- int i = verify_area(VERIFY_READ, from, n);
- if (i)
- return i;
- memcpy_fromfs(to, from, n);
- return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
- int i = verify_area(VERIFY_WRITE, to, n);
- if (i)
- return i;
- memcpy_tofs(to, from, n);
- return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_sbc {
- unsigned char revhi, revlo;
- unsigned char fmt[2];
- unsigned int sr[2];
-};
-
-#define SCSTATE ((struct sc_state_sbc *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-/*
- * the sbc converter's registers
- */
-#define DSP_RESET(iobase) (iobase+0x6)
-#define DSP_READ_DATA(iobase) (iobase+0xa)
-#define DSP_WRITE_DATA(iobase) (iobase+0xc)
-#define DSP_WRITE_STATUS(iobase) (iobase+0xc)
-#define DSP_DATA_AVAIL(iobase) (iobase+0xe)
-#define DSP_MIXER_ADDR(iobase) (iobase+0x4)
-#define DSP_MIXER_DATA(iobase) (iobase+0x5)
-#define DSP_INTACK_16BIT(iobase) (iobase+0xf)
-#define SBC_EXTENT 16
-
-/* --------------------------------------------------------------------- */
-/*
- * SBC commands
- */
-#define SBC_OUTPUT 0x14
-#define SBC_INPUT 0x24
-#define SBC_BLOCKSIZE 0x48
-#define SBC_HI_OUTPUT 0x91
-#define SBC_HI_INPUT 0x99
-#define SBC_LO_OUTPUT_AUTOINIT 0x1c
-#define SBC_LO_INPUT_AUTOINIT 0x2c
-#define SBC_HI_OUTPUT_AUTOINIT 0x90
-#define SBC_HI_INPUT_AUTOINIT 0x98
-#define SBC_IMMED_INT 0xf2
-#define SBC_GET_REVISION 0xe1
-#define ESS_GET_REVISION 0xe7
-#define SBC_SPEAKER_ON 0xd1
-#define SBC_SPEAKER_OFF 0xd3
-#define SBC_DMA_ON 0xd0
-#define SBC_DMA_OFF 0xd4
-#define SBC_SAMPLE_RATE 0x40
-#define SBC_SAMPLE_RATE_OUT 0x41
-#define SBC_SAMPLE_RATE_IN 0x42
-#define SBC_MONO_8BIT 0xa0
-#define SBC_MONO_16BIT 0xa4
-#define SBC_STEREO_8BIT 0xa8
-#define SBC_STEREO_16BIT 0xac
-
-#define SBC4_OUT8_AI 0xc6
-#define SBC4_IN8_AI 0xce
-#define SBC4_MODE_UNS_MONO 0x00
-#define SBC4_MODE_SIGN_MONO 0x10
-
-#define SBC4_OUT16_AI 0xb6
-#define SBC4_IN16_AI 0xbe
-
-/* --------------------------------------------------------------------- */
-
-static int inline reset_dsp(struct net_device *dev)
-{
- int i;
-
- outb(1, DSP_RESET(dev->base_addr));
- udelay(300);
- outb(0, DSP_RESET(dev->base_addr));
- for (i = 0; i < 0xffff; i++)
- if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80)
- if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa)
- return 1;
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline write_dsp(struct net_device *dev, unsigned char data)
-{
- int i;
-
- for (i = 0; i < 0xffff; i++)
- if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) {
- outb(data, DSP_WRITE_DATA(dev->base_addr));
- return;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-static int inline read_dsp(struct net_device *dev, unsigned char *data)
-{
- int i;
-
- if (!data)
- return 0;
- for (i = 0; i < 0xffff; i++)
- if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) {
- *data = inb(DSP_READ_DATA(dev->base_addr));
- return 1;
- }
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int config_resources(struct net_device *dev, struct sm_state *sm, int fdx)
-{
- unsigned char irqreg = 0, dmareg = 0, realirq, realdma;
- unsigned long flags;
-
- switch (dev->irq) {
- case 2:
- case 9:
- irqreg |= 0x01;
- break;
-
- case 5:
- irqreg |= 0x02;
- break;
-
- case 7:
- irqreg |= 0x04;
- break;
-
- case 10:
- irqreg |= 0x08;
- break;
-
- default:
- return -ENODEV;
- }
-
- switch (dev->dma) {
- case 0:
- dmareg |= 0x01;
- break;
-
- case 1:
- dmareg |= 0x02;
- break;
-
- case 3:
- dmareg |= 0x08;
- break;
-
- default:
- return -ENODEV;
- }
-
- if (fdx) {
- switch (sm->hdrv.ptt_out.dma2) {
- case 5:
- dmareg |= 0x20;
- break;
-
- case 6:
- dmareg |= 0x40;
- break;
-
- case 7:
- dmareg |= 0x80;
- break;
-
- default:
- return -ENODEV;
- }
- }
- save_flags(flags);
- cli();
- outb(0x80, DSP_MIXER_ADDR(dev->base_addr));
- outb(irqreg, DSP_MIXER_DATA(dev->base_addr));
- realirq = inb(DSP_MIXER_DATA(dev->base_addr));
- outb(0x81, DSP_MIXER_ADDR(dev->base_addr));
- outb(dmareg, DSP_MIXER_DATA(dev->base_addr));
- realdma = inb(DSP_MIXER_DATA(dev->base_addr));
- restore_flags(flags);
- if ((~realirq) & irqreg || (~realdma) & dmareg) {
- printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device "
- "and IRQ/DMA specified wrongly?\n", sm_drvname);
- return -EINVAL;
- }
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline sbc_int_ack_8bit(struct net_device *dev)
-{
- inb(DSP_DATA_AVAIL(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void inline sbc_int_ack_16bit(struct net_device *dev)
-{
- inb(DSP_INTACK_16BIT(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_dsp(struct net_device *dev, struct sm_state *sm, int send)
-{
- unsigned long flags;
- static const unsigned char sbcmode[2][2] = {
- { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT },
- { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT }
- };
- static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI };
- static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON };
- unsigned int nsamps;
-
- send = !!send;
- if (!reset_dsp(dev)) {
- printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
- return;
- }
- save_flags(flags);
- cli();
- sbc_int_ack_8bit(dev);
- write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */
- write_dsp(dev, SCSTATE->fmt[send]);
- write_dsp(dev, sbcskr[send]);
- nsamps = dma_setup(sm, send, dev->dma) - 1;
- sbc_int_ack_8bit(dev);
- if (SCSTATE->revhi >= 4) {
- write_dsp(dev, sbc4mode[send]);
- write_dsp(dev, SBC4_MODE_UNS_MONO);
- write_dsp(dev, nsamps & 0xff);
- write_dsp(dev, nsamps >> 8);
- } else {
- write_dsp(dev, SBC_BLOCKSIZE);
- write_dsp(dev, nsamps & 0xff);
- write_dsp(dev, nsamps >> 8);
- write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]);
- /* hispeed mode if sample rate > 13kHz */
- }
- restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- struct sm_state *sm = (struct sm_state *)dev->priv;
- unsigned int curfrag;
-
- if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
- return;
- cli();
- sbc_int_ack_8bit(dev);
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag);
- enable_dma(dev->dma);
- sm_int_freq(sm);
- sti();
- if (sm->dma.ptt_cnt <= 0) {
- dma_receive(sm, curfrag);
- hdlcdrv_arbitrate(dev, &sm->hdrv);
- if (hdlcdrv_ptt(&sm->hdrv)) {
- /* starting to transmit */
- disable_dma(dev->dma);
- hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
- dma_start_transmit(sm);
- setup_dma_dsp(dev, sm, 1);
- dma_transmit(sm);
- }
- } else if (dma_end_transmit(sm, curfrag)) {
- /* stopping transmission */
- disable_dma(dev->dma);
- sti();
- dma_init_receive(sm);
- setup_dma_dsp(dev, sm, 0);
- } else
- dma_transmit(sm);
- sm_output_status(sm);
- hdlcdrv_transmitter(dev, &sm->hdrv);
- hdlcdrv_receiver(dev, &sm->hdrv);
-
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_open(struct net_device *dev, struct sm_state *sm)
-{
- int err;
- unsigned int dmasz, u;
-
- if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
- printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
- sizeof(struct sc_state_sbc), sizeof(sm->m));
- return -ENODEV;
- }
- if (!dev || !sm)
- return -ENXIO;
- if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT ||
- dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
- return -ENXIO;
- if (check_region(dev->base_addr, SBC_EXTENT))
- return -EACCES;
- /*
- * check if a card is available
- */
- if (!reset_dsp(dev)) {
- printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
- sm_drvname, dev->base_addr);
- return -ENODEV;
- }
- write_dsp(dev, SBC_GET_REVISION);
- if (!read_dsp(dev, &SCSTATE->revhi) ||
- !read_dsp(dev, &SCSTATE->revlo))
- return -ENODEV;
- printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
- SCSTATE->revhi, SCSTATE->revlo);
- if (SCSTATE->revhi < 2) {
- printk(KERN_ERR "%s: your card is an antiquity, at least DSP "
- "rev 2.00 required\n", sm_drvname);
- return -ENODEV;
- }
- if (SCSTATE->revhi < 3 &&
- (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) {
- printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too "
- "old, at least 3.00 required\n", sm_drvname,
- dev->base_addr, SCSTATE->revhi, SCSTATE->revlo);
- return -ENODEV;
- }
- if (SCSTATE->revhi >= 4 &&
- (err = config_resources(dev, sm, 0))) {
- printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
- return err;
- }
- /*
- * initialize some variables
- */
- dma_init_receive(sm);
- dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
- u = NUM_FRAGMENTS * sm->dma.ofragsz;
- if (u > dmasz)
- dmasz = u;
- if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
- return -ENOMEM;
- dma_init_transmit(sm);
- dma_init_receive(sm);
-
- memset(&sm->m, 0, sizeof(sm->m));
- memset(&sm->d, 0, sizeof(sm->d));
- if (sm->mode_tx->init)
- sm->mode_tx->init(sm);
- if (sm->mode_rx->init)
- sm->mode_rx->init(sm);
-
- if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT,
- sm->hwdrv->hw_name, dev)) {
- free_dma(dev->dma);
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
- setup_dma_dsp(dev, sm, 0);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_close(struct net_device *dev, struct sm_state *sm)
-{
- if (!dev || !sm)
- return -EINVAL;
- /*
- * disable interrupts
- */
- disable_dma(dev->dma);
- reset_dsp(dev);
- free_irq(dev->irq, dev);
- free_dma(dev->dma);
- release_region(dev->base_addr, SBC_EXTENT);
- kfree(sm->dma.obuf);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
-{
- char *cp = strchr(mode, '.');
- const struct modem_tx_info **mtp = sm_modem_tx_table;
- const struct modem_rx_info **mrp;
-
- if (!strcmp(mode, "off")) {
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return 0;
- }
- if (cp)
- *cp++ = '\0';
- else
- cp = mode;
- for (; *mtp; mtp++) {
- if ((*mtp)->loc_storage > sizeof(sm->m)) {
- printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
- sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
- continue;
- }
- if (!(*mtp)->name || strcmp((*mtp)->name, mode))
- continue;
- if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
- continue;
- if (!(*mtp)->modulator_u8)
- continue;
- for (mrp = sm_modem_rx_table; *mrp; mrp++) {
- if ((*mrp)->loc_storage > sizeof(sm->d)) {
- printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
- sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
- continue;
- }
- if (!(*mrp)->demodulator_u8)
- continue;
- if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
- (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) {
- sm->mode_tx = *mtp;
- sm->mode_rx = *mrp;
- SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/
- sm->mode_rx->srate);
- SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/
- sm->mode_tx->srate);
- sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
- sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
- if (sm->dma.ifragsz < sm->mode_rx->overlap)
- sm->dma.ifragsz = sm->mode_rx->overlap;
- sm->dma.i16bit = sm->dma.o16bit = 0;
- return 0;
- }
- }
- }
- return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd)
-{
- struct sm_ioctl bi;
- unsigned long flags;
- int i;
-
- if (cmd != SIOCDEVPRIVATE)
- return -ENOIOCTLCMD;
-
- if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
- return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
- HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
- HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
- if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
- return -EFAULT;
-
- switch (bi.cmd) {
- default:
- return -ENOIOCTLCMD;
-
- case SMCTL_GETMIXER:
- i = 0;
- bi.data.mix.sample_rate = sm->mode_rx->srate;
- bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
- bi.data.mix.mixer_type = SM_MIXER_INVALID;
- switch (SCSTATE->revhi) {
- case 2:
- bi.data.mix.mixer_type = SM_MIXER_CT1335;
- break;
- case 3:
- bi.data.mix.mixer_type = SM_MIXER_CT1345;
- break;
- case 4:
- bi.data.mix.mixer_type = SM_MIXER_CT1745;
- break;
- }
- if (bi.data.mix.mixer_type != SM_MIXER_INVALID &&
- bi.data.mix.reg < 0x80) {
- save_flags(flags);
- cli();
- outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
- bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr));
- restore_flags(flags);
- i = 1;
- }
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return i;
-
- case SMCTL_SETMIXER:
- if (!capable(CAP_SYS_RAWIO))
- return -EACCES;
- switch (SCSTATE->revhi) {
- case 2:
- if (bi.data.mix.mixer_type != SM_MIXER_CT1335)
- return -EINVAL;
- break;
- case 3:
- if (bi.data.mix.mixer_type != SM_MIXER_CT1345)
- return -EINVAL;
- break;
- case 4:
- if (bi.data.mix.mixer_type != SM_MIXER_CT1745)
- return -EINVAL;
- break;
- default:
- return -ENODEV;
- }
- if (bi.data.mix.reg >= 0x80)
- return -EACCES;
- save_flags(flags);
- cli();
- outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr));
- outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr));
- restore_flags(flags);
- return 0;
-
- }
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_sbc = {
- "sbc", sizeof(struct sc_state_sbc),
- sbc_open, sbc_close, sbc_ioctl, sbc_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_fdx_dsp(struct net_device *dev, struct sm_state *sm)
-{
- unsigned long flags;
- unsigned int isamps, osamps;
-
- if (!reset_dsp(dev)) {
- printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname);
- return;
- }
- save_flags(flags);
- cli();
- sbc_int_ack_8bit(dev);
- sbc_int_ack_16bit(dev);
- /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */
- write_dsp(dev, SBC_SAMPLE_RATE_IN);
- write_dsp(dev, SCSTATE->sr[0] >> 8);
- write_dsp(dev, SCSTATE->sr[0] & 0xff);
- write_dsp(dev, SBC_SAMPLE_RATE_OUT);
- write_dsp(dev, SCSTATE->sr[1] >> 8);
- write_dsp(dev, SCSTATE->sr[1] & 0xff);
- write_dsp(dev, SBC_SPEAKER_ON);
- if (sm->dma.o16bit) {
- /*
- * DMA channel 1 (8bit) does input (capture),
- * DMA channel 2 (16bit) does output (playback)
- */
- isamps = dma_setup(sm, 0, dev->dma) - 1;
- osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1;
- sbc_int_ack_8bit(dev);
- sbc_int_ack_16bit(dev);
- write_dsp(dev, SBC4_IN8_AI);
- write_dsp(dev, SBC4_MODE_UNS_MONO);
- write_dsp(dev, isamps & 0xff);
- write_dsp(dev, isamps >> 8);
- write_dsp(dev, SBC4_OUT16_AI);
- write_dsp(dev, SBC4_MODE_SIGN_MONO);
- write_dsp(dev, osamps & 0xff);
- write_dsp(dev, osamps >> 8);
- } else {
- /*
- * DMA channel 1 (8bit) does output (playback),
- * DMA channel 2 (16bit) does input (capture)
- */
- isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
- osamps = dma_setup(sm, 1, dev->dma) - 1;
- sbc_int_ack_8bit(dev);
- sbc_int_ack_16bit(dev);
- write_dsp(dev, SBC4_OUT8_AI);
- write_dsp(dev, SBC4_MODE_UNS_MONO);
- write_dsp(dev, osamps & 0xff);
- write_dsp(dev, osamps >> 8);
- write_dsp(dev, SBC4_IN16_AI);
- write_dsp(dev, SBC4_MODE_SIGN_MONO);
- write_dsp(dev, isamps & 0xff);
- write_dsp(dev, isamps >> 8);
- }
- dma_init_receive(sm);
- dma_init_transmit(sm);
- restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- struct sm_state *sm = (struct sm_state *)dev->priv;
- unsigned char intsrc, pbint = 0, captint = 0;
- unsigned int ocfrag, icfrag;
- unsigned long flags;
-
- if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC)
- return;
- save_flags(flags);
- cli();
- outb(0x82, DSP_MIXER_ADDR(dev->base_addr));
- intsrc = inb(DSP_MIXER_DATA(dev->base_addr));
- if (intsrc & 0x01) {
- sbc_int_ack_8bit(dev);
- if (sm->dma.o16bit) {
- captint = 1;
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- dma_ptr(sm, 0, dev->dma, &icfrag);
- enable_dma(dev->dma);
- } else {
- pbint = 1;
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- dma_ptr(sm, 1, dev->dma, &ocfrag);
- enable_dma(dev->dma);
- }
- }
- if (intsrc & 0x02) {
- sbc_int_ack_16bit(dev);
- if (sm->dma.o16bit) {
- pbint = 1;
- disable_dma(sm->hdrv.ptt_out.dma2);
- clear_dma_ff(sm->hdrv.ptt_out.dma2);
- dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag);
- enable_dma(sm->hdrv.ptt_out.dma2);
- } else {
- captint = 1;
- disable_dma(sm->hdrv.ptt_out.dma2);
- clear_dma_ff(sm->hdrv.ptt_out.dma2);
- dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag);
- enable_dma(sm->hdrv.ptt_out.dma2);
- }
- }
- restore_flags(flags);
- sm_int_freq(sm);
- sti();
- if (pbint) {
- if (dma_end_transmit(sm, ocfrag))
- dma_clear_transmit(sm);
- dma_transmit(sm);
- }
- if (captint) {
- dma_receive(sm, icfrag);
- hdlcdrv_arbitrate(dev, &sm->hdrv);
- }
- sm_output_status(sm);
- hdlcdrv_transmitter(dev, &sm->hdrv);
- hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_open(struct net_device *dev, struct sm_state *sm)
-{
- int err;
-
- if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) {
- printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n",
- sizeof(struct sc_state_sbc), sizeof(sm->m));
- return -ENODEV;
- }
- if (!dev || !sm)
- return -ENXIO;
- if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT ||
- dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
- return -ENXIO;
- if (check_region(dev->base_addr, SBC_EXTENT))
- return -EACCES;
- /*
- * check if a card is available
- */
- if (!reset_dsp(dev)) {
- printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n",
- sm_drvname, dev->base_addr);
- return -ENODEV;
- }
- write_dsp(dev, SBC_GET_REVISION);
- if (!read_dsp(dev, &SCSTATE->revhi) ||
- !read_dsp(dev, &SCSTATE->revlo))
- return -ENODEV;
- printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname,
- SCSTATE->revhi, SCSTATE->revlo);
- if (SCSTATE->revhi < 4) {
- printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname);
- return -ENODEV;
- }
- if ((err = config_resources(dev, sm, 1))) {
- printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname);
- return err;
- }
- /*
- * initialize some variables
- */
- if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
- return -ENOMEM;
- if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
- kfree(sm->dma.ibuf);
- return -ENOMEM;
- }
- dma_init_transmit(sm);
- dma_init_receive(sm);
-
- memset(&sm->m, 0, sizeof(sm->m));
- memset(&sm->d, 0, sizeof(sm->d));
- if (sm->mode_tx->init)
- sm->mode_tx->init(sm);
- if (sm->mode_rx->init)
- sm->mode_rx->init(sm);
-
- if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- free_dma(dev->dma);
- return -EBUSY;
- }
- if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT,
- sm->hwdrv->hw_name, dev)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- free_dma(dev->dma);
- free_dma(sm->hdrv.ptt_out.dma2);
- return -EBUSY;
- }
- request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name);
- setup_dma_fdx_dsp(dev, sm);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_close(struct net_device *dev, struct sm_state *sm)
-{
- if (!dev || !sm)
- return -EINVAL;
- /*
- * disable interrupts
- */
- disable_dma(dev->dma);
- disable_dma(sm->hdrv.ptt_out.dma2);
- reset_dsp(dev);
- free_irq(dev->irq, dev);
- free_dma(dev->dma);
- free_dma(sm->hdrv.ptt_out.dma2);
- release_region(dev->base_addr, SBC_EXTENT);
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
-{
- char *cp = strchr(mode, '.');
- const struct modem_tx_info **mtp = sm_modem_tx_table;
- const struct modem_rx_info **mrp;
-
- if (!strcmp(mode, "off")) {
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return 0;
- }
- if (cp)
- *cp++ = '\0';
- else
- cp = mode;
- for (; *mtp; mtp++) {
- if ((*mtp)->loc_storage > sizeof(sm->m)) {
- printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
- sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
- continue;
- }
- if (!(*mtp)->name || strcmp((*mtp)->name, mode))
- continue;
- if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100)
- continue;
- for (mrp = sm_modem_rx_table; *mrp; mrp++) {
- if ((*mrp)->loc_storage > sizeof(sm->d)) {
- printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
- sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
- continue;
- }
- if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
- (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 &&
- (*mrp)->srate == (*mtp)->srate) {
- sm->mode_tx = *mtp;
- sm->mode_rx = *mrp;
- SCSTATE->sr[0] = sm->mode_rx->srate;
- SCSTATE->sr[1] = sm->mode_tx->srate;
- sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
- sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
- if (sm->dma.ifragsz < sm->mode_rx->overlap)
- sm->dma.ifragsz = sm->mode_rx->overlap;
- if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) {
- sm->dma.i16bit = 1;
- sm->dma.o16bit = 0;
- sm->dma.ifragsz <<= 1;
- } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) {
- sm->dma.i16bit = 0;
- sm->dma.o16bit = 1;
- sm->dma.ofragsz <<= 1;
- } else {
- printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
- sm->mode_rx->name, sm->mode_tx->name);
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return -EINVAL;
- }
- return 0;
- }
- }
- }
- return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int sbcfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd)
-{
- if (cmd != SIOCDEVPRIVATE)
- return -ENOIOCTLCMD;
-
- if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
- return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
- HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE |
- HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
- return sbc_ioctl(dev, sm, ifr, hi, cmd);
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_sbcfdx = {
- "sbcfdx", sizeof(struct sc_state_sbc),
- sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw
-};
-
-/* --------------------------------------------------------------------- */
diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c
deleted file mode 100644
index 19840c491..000000000
--- a/drivers/net/hamradio/soundmodem/sm_wss.c
+++ /dev/null
@@ -1,965 +0,0 @@
-/*****************************************************************************/
-
-/*
- * sm_wss.c -- soundcard radio modem driver, WSS (half duplex) driver
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#include <linux/ptrace.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <asm/io.h>
-#include <asm/dma.h>
-#include <linux/ioport.h>
-#include <linux/soundmodem.h>
-#include "sm.h"
-#include "smdma.h"
-
-/* --------------------------------------------------------------------- */
-
-/*
- * currently this module is supposed to support both module styles, i.e.
- * the old one present up to about 2.1.9, and the new one functioning
- * starting with 2.1.21. The reason is I have a kit allowing to compile
- * this module also under 2.0.x which was requested by several people.
- * This will go in 2.2
- */
-#include <linux/version.h>
-
-#if LINUX_VERSION_CODE >= 0x20100
-#include <asm/uaccess.h>
-#else
-#include <asm/segment.h>
-#include <linux/mm.h>
-
-#undef put_user
-#undef get_user
-
-#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; })
-#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; })
-
-extern inline int copy_from_user(void *to, const void *from, unsigned long n)
-{
- int i = verify_area(VERIFY_READ, from, n);
- if (i)
- return i;
- memcpy_fromfs(to, from, n);
- return 0;
-}
-
-extern inline int copy_to_user(void *to, const void *from, unsigned long n)
-{
- int i = verify_area(VERIFY_WRITE, to, n);
- if (i)
- return i;
- memcpy_tofs(to, from, n);
- return 0;
-}
-#endif
-
-/* --------------------------------------------------------------------- */
-
-struct sc_state_wss {
- unsigned char revwss, revid, revv, revcid;
- unsigned char fmt[2];
- unsigned char crystal;
-};
-
-#define SCSTATE ((struct sc_state_wss *)(&sm->hw))
-
-/* --------------------------------------------------------------------- */
-
-#define WSS_CONFIG(iobase) (iobase+0)
-#define WSS_STATUS(iobase) (iobase+3)
-#define WSS_CODEC_IA(iobase) (iobase+4)
-#define WSS_CODEC_ID(iobase) (iobase+5)
-#define WSS_CODEC_STATUS(iobase) (iobase+6)
-#define WSS_CODEC_DATA(iobase) (iobase+7)
-
-#define WSS_EXTENT 8
-
-#define CS423X_HOTFIX
-
-/* --------------------------------------------------------------------- */
-
-static void write_codec(struct net_device *dev, unsigned char idx,
- unsigned char data)
-{
- int timeout = 900000;
-
- /* wait until codec ready */
- while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
- timeout--;
- outb(idx, WSS_CODEC_IA(dev->base_addr));
- outb(data, WSS_CODEC_ID(dev->base_addr));
-}
-
-
-/* --------------------------------------------------------------------- */
-
-static unsigned char read_codec(struct net_device *dev, unsigned char idx)
-{
- int timeout = 900000;
-
- /* wait until codec ready */
- while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80)
- timeout--;
- outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr));
- return inb(WSS_CODEC_ID(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-extern void inline wss_ack_int(struct net_device *dev)
-{
- outb(0, WSS_CODEC_STATUS(dev->base_addr));
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_srate_tab[16] = {
- 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050,
- -1, 37800, -1, 44100, 48000, 33075, 9600, 6620
-};
-
-static int wss_srate_index(int srate)
-{
- int i;
-
- for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++)
- if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0)
- return i;
- return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_set_codec_fmt(struct net_device *dev, struct sm_state *sm, unsigned char fmt,
- unsigned char fmt2, char fdx, char fullcalib)
-{
- unsigned long time;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- /* Clock and data format register */
- write_codec(dev, 0x48, fmt);
- if (SCSTATE->crystal) {
- write_codec(dev, 0x5c, fmt2 & 0xf0);
- /* MCE and interface config reg */
- write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0));
- } else
- /* MCE and interface config reg */
- write_codec(dev, 0x49, fdx ? 0x8 : 0xc);
- outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */
- if (SCSTATE->crystal && !fullcalib)
- return 0;
- /*
- * wait for ACI start
- */
- time = 1000;
- while (!(read_codec(dev, 0x0b) & 0x20))
- if (!(--time)) {
- printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n",
- sm_drvname);
- restore_flags(flags);
- return -1;
- }
- /*
- * wait for ACI end
- */
- sti();
- time = jiffies + HZ/4;
- while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0));
- restore_flags(flags);
- if ((signed)(jiffies - time) >= 0) {
- printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n",
- sm_drvname);
- return -1;
- }
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_init_codec(struct net_device *dev, struct sm_state *sm, char fdx,
- unsigned char src_l, unsigned char src_r,
- int igain_l, int igain_r,
- int ogain_l, int ogain_r)
-{
- unsigned char tmp, reg0, reg1, reg6, reg7;
- static const signed char irqtab[16] =
- { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1,
- -1, -1 };
- static const signed char dmatab[4] = { 1, 2, -1, 3 };
-
- tmp = inb(WSS_STATUS(dev->base_addr));
- if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 &&
- (tmp & 0x3f) != 0x0f) {
- printk(KERN_WARNING "sm: WSS card id register not found, "
- "address 0x%lx, ID register 0x%02x\n",
- dev->base_addr, (int)tmp);
- /* return -1; */
- SCSTATE->revwss = 0;
- } else {
- if ((tmp & 0x80) && ((dev->dma == 0) ||
- ((dev->irq >= 8) && (dev->irq != 9)))) {
- printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 "
- "(except IRQ9) cannot be used on an 8bit "
- "card\n", sm_drvname);
- return -1;
- }
- if (dev->irq > 15 || irqtab[dev->irq] == -1) {
- printk(KERN_ERR "%s: WSS: invalid interrupt %d\n",
- sm_drvname, (int)dev->irq);
- return -1;
- }
- if (dev->dma > 3 || dmatab[dev->dma] == -1) {
- printk(KERN_ERR "%s: WSS: invalid dma channel %d\n",
- sm_drvname, (int)dev->dma);
- return -1;
- }
- tmp = irqtab[dev->irq] | dmatab[dev->dma];
- /* irq probe */
- outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr));
- if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) {
- outb(0, WSS_CONFIG(dev->base_addr));
- printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n",
- sm_drvname, dev->irq);
- }
- outb(tmp, WSS_CONFIG(dev->base_addr));
- SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f;
- }
- /*
- * initialize the codec
- */
- if (igain_l < 0)
- igain_l = 0;
- if (igain_r < 0)
- igain_r = 0;
- if (ogain_l > 0)
- ogain_l = 0;
- if (ogain_r > 0)
- ogain_r = 0;
- reg0 = (src_l << 6) & 0xc0;
- reg1 = (src_r << 6) & 0xc0;
- if (reg0 == 0x80 && igain_l >= 20) {
- reg0 |= 0x20;
- igain_l -= 20;
- }
- if (reg1 == 0x80 && igain_r >= 20) {
- reg1 |= 0x20;
- igain_r -= 20;
- }
- if (igain_l > 23)
- igain_l = 23;
- if (igain_r > 23)
- igain_r = 23;
- reg0 |= igain_l * 2 / 3;
- reg1 |= igain_r * 2 / 3;
- reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3);
- reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3);
- write_codec(dev, 9, 0);
- write_codec(dev, 0, 0x45);
- if (read_codec(dev, 0) != 0x45)
- goto codec_err;
- write_codec(dev, 0, 0xaa);
- if (read_codec(dev, 0) != 0xaa)
- goto codec_err;
- write_codec(dev, 12, 0x40); /* enable MODE2 */
- write_codec(dev, 16, 0);
- write_codec(dev, 0, 0x45);
- SCSTATE->crystal = (read_codec(dev, 16) != 0x45);
- write_codec(dev, 0, 0xaa);
- SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa);
- if (SCSTATE->crystal) {
- SCSTATE->revcid = read_codec(dev, 0x19);
- SCSTATE->revv = (SCSTATE->revcid >> 5) & 7;
- SCSTATE->revcid &= 7;
- write_codec(dev, 0x10, 0x80); /* maximum output level */
- write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */
- write_codec(dev, 0x12, 0x80); /* left line input control */
- write_codec(dev, 0x13, 0x80); /* right line input control */
- write_codec(dev, 0x16, 0); /* disable alternative freq sel */
- write_codec(dev, 0x1a, 0xe0); /* mono IO disable */
- write_codec(dev, 0x1b, 0x00); /* left out no att */
- write_codec(dev, 0x1d, 0x00); /* right out no att */
- }
-
- if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1))
- goto codec_err;
-
- write_codec(dev, 0, reg0); /* left input control */
- write_codec(dev, 1, reg1); /* right input control */
- write_codec(dev, 2, 0x80); /* left aux#1 input control */
- write_codec(dev, 3, 0x80); /* right aux#1 input control */
- write_codec(dev, 4, 0x80); /* left aux#2 input control */
- write_codec(dev, 5, 0x80); /* right aux#2 input control */
- write_codec(dev, 6, reg6); /* left dac control */
- write_codec(dev, 7, reg7); /* right dac control */
- write_codec(dev, 0xa, 0x2); /* pin control register */
- write_codec(dev, 0xd, 0x0); /* digital mix control */
- SCSTATE->revid = read_codec(dev, 0xc) & 0xf;
- /*
- * print revisions
- */
- if (SCSTATE->crystal)
- printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, "
- " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid,
- (int)SCSTATE->revv, (int)SCSTATE->revcid);
- else
- printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n",
- sm_drvname, (int)SCSTATE->revwss,
- (int)SCSTATE->revid);
- return 0;
- codec_err:
- outb(0, WSS_CONFIG(dev->base_addr));
- printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n",
- sm_drvname, dev->base_addr);
- return -1;
-}
-
-/* --------------------------------------------------------------------- */
-
-static void setup_dma_wss(struct net_device *dev, struct sm_state *sm, int send)
-{
- unsigned long flags;
- static const unsigned char codecmode[2] = { 0x0e, 0x0d };
- unsigned char oldcodecmode;
- long abrt;
- unsigned char fmt;
- unsigned int numsamps;
-
- send = !!send;
- fmt = SCSTATE->fmt[send];
- save_flags(flags);
- cli();
- /*
- * perform the final DMA sequence to disable the codec request
- */
- oldcodecmode = read_codec(dev, 9);
- write_codec(dev, 9, 0xc); /* disable codec */
- wss_ack_int(dev);
- if (read_codec(dev, 11) & 0x10) {
- dma_setup(sm, oldcodecmode & 1, dev->dma);
- abrt = 0;
- while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000));
- }
-#ifdef CS423X_HOTFIX
- if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal)
- wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
-#else /* CS423X_HOTFIX */
- if (read_codec(dev, 0x8) != fmt)
- wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0);
-#endif /* CS423X_HOTFIX */
- numsamps = dma_setup(sm, send, dev->dma) - 1;
- write_codec(dev, 15, numsamps & 0xff);
- write_codec(dev, 14, numsamps >> 8);
- write_codec(dev, 9, codecmode[send]);
- restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- struct sm_state *sm = (struct sm_state *)dev->priv;
- unsigned int curfrag;
- unsigned int nums;
-
- if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
- sm->hdrv.magic != HDLCDRV_MAGIC)
- return;
- cli();
- wss_ack_int(dev);
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1;
- write_codec(dev, 15, nums & 0xff);
- write_codec(dev, 14, nums >> 8);
- enable_dma(dev->dma);
- sm_int_freq(sm);
- sti();
- if (sm->dma.ptt_cnt <= 0) {
- dma_receive(sm, curfrag);
- hdlcdrv_arbitrate(dev, &sm->hdrv);
- if (hdlcdrv_ptt(&sm->hdrv)) {
- /* starting to transmit */
- disable_dma(dev->dma);
- hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */
- dma_start_transmit(sm);
- setup_dma_wss(dev, sm, 1);
- dma_transmit(sm);
- }
- } else if (dma_end_transmit(sm, curfrag)) {
- /* stopping transmission */
- disable_dma(dev->dma);
- dma_init_receive(sm);
- setup_dma_wss(dev, sm, 0);
- } else
- dma_transmit(sm);
- sm_output_status(sm);
- hdlcdrv_transmitter(dev, &sm->hdrv);
- hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_open(struct net_device *dev, struct sm_state *sm)
-{
- unsigned int dmasz, u;
-
- if (sizeof(sm->m) < sizeof(struct sc_state_wss)) {
- printk(KERN_ERR "sm wss: wss state too big: %d > %d\n",
- sizeof(struct sc_state_wss), sizeof(sm->m));
- return -ENODEV;
- }
- if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
- return -ENXIO;
- if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
- dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
- return -ENXIO;
- if (check_region(dev->base_addr, WSS_EXTENT))
- return -EACCES;
- /*
- * check if a card is available
- */
- if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45))
- return -ENODEV;
- /*
- * initialize some variables
- */
- dma_init_receive(sm);
- dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz;
- u = NUM_FRAGMENTS * sm->dma.ofragsz;
- if (u > dmasz)
- dmasz = u;
- if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA)))
- return -ENOMEM;
- dma_init_transmit(sm);
- dma_init_receive(sm);
-
- memset(&sm->m, 0, sizeof(sm->m));
- memset(&sm->d, 0, sizeof(sm->d));
- if (sm->mode_tx->init)
- sm->mode_tx->init(sm);
- if (sm->mode_rx->init)
- sm->mode_rx->init(sm);
-
- if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT,
- sm->hwdrv->hw_name, dev)) {
- free_dma(dev->dma);
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
- setup_dma_wss(dev, sm, 0);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_close(struct net_device *dev, struct sm_state *sm)
-{
- if (!dev || !sm)
- return -EINVAL;
- /*
- * disable interrupts
- */
- disable_dma(dev->dma);
- write_codec(dev, 9, 0xc); /* disable codec */
- free_irq(dev->irq, dev);
- free_dma(dev->dma);
- release_region(dev->base_addr, WSS_EXTENT);
- kfree(sm->dma.obuf);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
-{
- char *cp = strchr(mode, '.');
- const struct modem_tx_info **mtp = sm_modem_tx_table;
- const struct modem_rx_info **mrp;
- int i, j;
-
- if (!strcmp(mode, "off")) {
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return 0;
- }
- if (cp)
- *cp++ = '\0';
- else
- cp = mode;
- for (; *mtp; mtp++) {
- if ((*mtp)->loc_storage > sizeof(sm->m)) {
- printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
- sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
- continue;
- }
- if (!(*mtp)->name || strcmp((*mtp)->name, mode))
- continue;
- if ((i = wss_srate_index((*mtp)->srate)) < 0)
- continue;
- for (mrp = sm_modem_rx_table; *mrp; mrp++) {
- if ((*mrp)->loc_storage > sizeof(sm->d)) {
- printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
- sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
- continue;
- }
- if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
- ((j = wss_srate_index((*mrp)->srate)) >= 0)) {
- sm->mode_tx = *mtp;
- sm->mode_rx = *mrp;
- SCSTATE->fmt[0] = j;
- SCSTATE->fmt[1] = i;
- sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100;
- sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100;
- if (sm->dma.ifragsz < sm->mode_rx->overlap)
- sm->dma.ifragsz = sm->mode_rx->overlap;
- /* prefer same data format if possible to minimize switching times */
- sm->dma.i16bit = sm->dma.o16bit = 2;
- if (sm->mode_rx->srate == sm->mode_tx->srate) {
- if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16)
- sm->dma.i16bit = sm->dma.o16bit = 1;
- else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8)
- sm->dma.i16bit = sm->dma.o16bit = 0;
- }
- if (sm->dma.i16bit == 2) {
- if (sm->mode_rx->demodulator_s16)
- sm->dma.i16bit = 1;
- else if (sm->mode_rx->demodulator_u8)
- sm->dma.i16bit = 0;
- }
- if (sm->dma.o16bit == 2) {
- if (sm->mode_tx->modulator_s16)
- sm->dma.o16bit = 1;
- else if (sm->mode_tx->modulator_u8)
- sm->dma.o16bit = 0;
- }
- if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
- printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
- sm->mode_rx->name, sm->mode_tx->name);
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return -EINVAL;
- }
-#ifdef __BIG_ENDIAN
- /* big endian 16bit only works on crystal cards... */
- if (sm->dma.i16bit) {
- SCSTATE->fmt[0] |= 0xc0;
- sm->dma.ifragsz <<= 1;
- }
- if (sm->dma.o16bit) {
- SCSTATE->fmt[1] |= 0xc0;
- sm->dma.ofragsz <<= 1;
- }
-#else /* __BIG_ENDIAN */
- if (sm->dma.i16bit) {
- SCSTATE->fmt[0] |= 0x40;
- sm->dma.ifragsz <<= 1;
- }
- if (sm->dma.o16bit) {
- SCSTATE->fmt[1] |= 0x40;
- sm->dma.ofragsz <<= 1;
- }
-#endif /* __BIG_ENDIAN */
- return 0;
- }
- }
- }
- return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd)
-{
- struct sm_ioctl bi;
- int i;
-
- if (cmd != SIOCDEVPRIVATE)
- return -ENOIOCTLCMD;
-
- if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
- return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
- HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE |
- HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE;
-
- if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi)))
- return -EFAULT;
-
- switch (bi.cmd) {
- default:
- return -ENOIOCTLCMD;
-
- case SMCTL_GETMIXER:
- i = 0;
- bi.data.mix.sample_rate = sm->mode_rx->srate;
- bi.data.mix.bit_rate = sm->hdrv.par.bitrate;
- bi.data.mix.mixer_type = SCSTATE->crystal ?
- SM_MIXER_CRYSTAL : SM_MIXER_AD1848;
- if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu)
- >> bi.data.mix.reg) & 1) {
- bi.data.mix.data = read_codec(dev, bi.data.mix.reg);
- i = 1;
- }
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return i;
-
- case SMCTL_SETMIXER:
- if (!capable(CAP_SYS_RAWIO))
- return -EACCES;
- if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL ||
- !SCSTATE->crystal) &&
- (bi.data.mix.mixer_type != SM_MIXER_AD1848 ||
- bi.data.mix.reg >= 0x10))
- return -EINVAL;
- if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1))
- return -EACCES;
- write_codec(dev, bi.data.mix.reg, bi.data.mix.data);
- return 0;
-
- }
- if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi)))
- return -EFAULT;
- return 0;
-
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_wss = {
- "wss", sizeof(struct sc_state_wss),
- wss_open, wss_close, wss_ioctl, wss_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-static void setup_fdx_dma_wss(struct net_device *dev, struct sm_state *sm)
-{
- unsigned long flags;
- unsigned char oldcodecmode, codecdma;
- long abrt;
- unsigned int osamps, isamps;
-
- save_flags(flags);
- cli();
- /*
- * perform the final DMA sequence to disable the codec request
- */
- oldcodecmode = read_codec(dev, 9);
- write_codec(dev, 9, 0); /* disable codec DMA */
- wss_ack_int(dev);
- if ((codecdma = read_codec(dev, 11)) & 0x10) {
- dma_setup(sm, 1, dev->dma);
- dma_setup(sm, 0, sm->hdrv.ptt_out.dma2);
- abrt = 0;
- while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000));
- }
- wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1);
- osamps = dma_setup(sm, 1, dev->dma) - 1;
- isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1;
- write_codec(dev, 15, osamps & 0xff);
- write_codec(dev, 14, osamps >> 8);
- if (SCSTATE->crystal) {
- write_codec(dev, 31, isamps & 0xff);
- write_codec(dev, 30, isamps >> 8);
- }
- write_codec(dev, 9, 3);
- restore_flags(flags);
-}
-
-/* --------------------------------------------------------------------- */
-
-static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- struct net_device *dev = (struct net_device *)dev_id;
- struct sm_state *sm = (struct sm_state *)dev->priv;
- unsigned long flags;
- unsigned char cry_int_src;
- unsigned icfrag, ocfrag, isamps, osamps;
-
- if (!dev || !sm || !sm->mode_rx || !sm->mode_tx ||
- sm->hdrv.magic != HDLCDRV_MAGIC)
- return;
- save_flags(flags);
- cli();
- if (SCSTATE->crystal) {
- /* Crystal has an essentially different interrupt handler! */
- cry_int_src = read_codec(dev, 0x18);
- wss_ack_int(dev);
- if (cry_int_src & 0x10) { /* playback interrupt */
- disable_dma(dev->dma);
- clear_dma_ff(dev->dma);
- osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
- write_codec(dev, 15, osamps & 0xff);
- write_codec(dev, 14, osamps >> 8);
- enable_dma(dev->dma);
- }
- if (cry_int_src & 0x20) { /* capture interrupt */
- disable_dma(sm->hdrv.ptt_out.dma2);
- clear_dma_ff(sm->hdrv.ptt_out.dma2);
- isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
- write_codec(dev, 31, isamps & 0xff);
- write_codec(dev, 30, isamps >> 8);
- enable_dma(sm->hdrv.ptt_out.dma2);
- }
- restore_flags(flags);
- sm_int_freq(sm);
- sti();
- if (cry_int_src & 0x10) {
- if (dma_end_transmit(sm, ocfrag))
- dma_clear_transmit(sm);
- dma_transmit(sm);
- }
- if (cry_int_src & 0x20) {
- dma_receive(sm, icfrag);
- hdlcdrv_arbitrate(dev, &sm->hdrv);
- }
- sm_output_status(sm);
- hdlcdrv_transmitter(dev, &sm->hdrv);
- hdlcdrv_receiver(dev, &sm->hdrv);
- return;
- }
- wss_ack_int(dev);
- disable_dma(dev->dma);
- disable_dma(sm->hdrv.ptt_out.dma2);
- clear_dma_ff(dev->dma);
- clear_dma_ff(sm->hdrv.ptt_out.dma2);
- osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1;
- isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1;
- write_codec(dev, 15, osamps & 0xff);
- write_codec(dev, 14, osamps >> 8);
- if (SCSTATE->crystal) {
- write_codec(dev, 31, isamps & 0xff);
- write_codec(dev, 30, isamps >> 8);
- }
- enable_dma(dev->dma);
- enable_dma(sm->hdrv.ptt_out.dma2);
- restore_flags(flags);
- sm_int_freq(sm);
- sti();
- if (dma_end_transmit(sm, ocfrag))
- dma_clear_transmit(sm);
- dma_transmit(sm);
- dma_receive(sm, icfrag);
- hdlcdrv_arbitrate(dev, &sm->hdrv);
- sm_output_status(sm);
- hdlcdrv_transmitter(dev, &sm->hdrv);
- hdlcdrv_receiver(dev, &sm->hdrv);
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_open(struct net_device *dev, struct sm_state *sm)
-{
- if (!dev || !sm || !sm->mode_rx || !sm->mode_tx)
- return -ENXIO;
- if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT ||
- dev->irq < 2 || dev->irq > 15 || dev->dma > 3)
- return -ENXIO;
- if (check_region(dev->base_addr, WSS_EXTENT))
- return -EACCES;
- /*
- * check if a card is available
- */
- if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45))
- return -ENODEV;
- /*
- * initialize some variables
- */
- if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA)))
- return -ENOMEM;
- if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) {
- kfree(sm->dma.ibuf);
- return -ENOMEM;
- }
- dma_init_transmit(sm);
- dma_init_receive(sm);
-
- memset(&sm->m, 0, sizeof(sm->m));
- memset(&sm->d, 0, sizeof(sm->d));
- if (sm->mode_tx->init)
- sm->mode_tx->init(sm);
- if (sm->mode_rx->init)
- sm->mode_rx->init(sm);
-
- if (request_dma(dev->dma, sm->hwdrv->hw_name)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- return -EBUSY;
- }
- if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- free_dma(dev->dma);
- return -EBUSY;
- }
- if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT,
- sm->hwdrv->hw_name, dev)) {
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- free_dma(dev->dma);
- free_dma(sm->hdrv.ptt_out.dma2);
- return -EBUSY;
- }
- request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name);
- setup_fdx_dma_wss(dev, sm);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_close(struct net_device *dev, struct sm_state *sm)
-{
- if (!dev || !sm)
- return -EINVAL;
- /*
- * disable interrupts
- */
- disable_dma(dev->dma);
- disable_dma(sm->hdrv.ptt_out.dma2);
- write_codec(dev, 9, 0xc); /* disable codec */
- free_irq(dev->irq, dev);
- free_dma(dev->dma);
- free_dma(sm->hdrv.ptt_out.dma2);
- release_region(dev->base_addr, WSS_EXTENT);
- kfree(sm->dma.ibuf);
- kfree(sm->dma.obuf);
- return 0;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode)
-{
- char *cp = strchr(mode, '.');
- const struct modem_tx_info **mtp = sm_modem_tx_table;
- const struct modem_rx_info **mrp;
- int i;
-
- if (!strcmp(mode, "off")) {
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return 0;
- }
- if (cp)
- *cp++ = '\0';
- else
- cp = mode;
- for (; *mtp; mtp++) {
- if ((*mtp)->loc_storage > sizeof(sm->m)) {
- printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n",
- sm_drvname, (*mtp)->name, (*mtp)->loc_storage);
- continue;
- }
- if (!(*mtp)->name || strcmp((*mtp)->name, mode))
- continue;
- if ((i = wss_srate_index((*mtp)->srate)) < 0)
- continue;
- for (mrp = sm_modem_rx_table; *mrp; mrp++) {
- if ((*mrp)->loc_storage > sizeof(sm->d)) {
- printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n",
- sm_drvname, (*mrp)->name, (*mrp)->loc_storage);
- continue;
- }
- if ((*mrp)->name && !strcmp((*mrp)->name, cp) &&
- (*mtp)->srate == (*mrp)->srate) {
- sm->mode_tx = *mtp;
- sm->mode_rx = *mrp;
- SCSTATE->fmt[0] = SCSTATE->fmt[1] = i;
- sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100;
- if (sm->dma.ifragsz < sm->mode_rx->overlap)
- sm->dma.ifragsz = sm->mode_rx->overlap;
- sm->dma.i16bit = sm->dma.o16bit = 2;
- if (sm->mode_rx->demodulator_s16) {
- sm->dma.i16bit = 1;
- sm->dma.ifragsz <<= 1;
-#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
- SCSTATE->fmt[0] |= 0xc0;
-#else /* __BIG_ENDIAN */
- SCSTATE->fmt[0] |= 0x40;
-#endif /* __BIG_ENDIAN */
- } else if (sm->mode_rx->demodulator_u8)
- sm->dma.i16bit = 0;
- if (sm->mode_tx->modulator_s16) {
- sm->dma.o16bit = 1;
- sm->dma.ofragsz <<= 1;
-#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */
- SCSTATE->fmt[1] |= 0xc0;
-#else /* __BIG_ENDIAN */
- SCSTATE->fmt[1] |= 0x40;
-#endif /* __BIG_ENDIAN */
- } else if (sm->mode_tx->modulator_u8)
- sm->dma.o16bit = 0;
- if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) {
- printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname,
- sm->mode_rx->name, sm->mode_tx->name);
- sm->mode_tx = NULL;
- sm->mode_rx = NULL;
- return -EINVAL;
- }
- return 0;
- }
- }
- }
- return -EINVAL;
-}
-
-/* --------------------------------------------------------------------- */
-
-static int wssfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr,
- struct hdlcdrv_ioctl *hi, int cmd)
-{
- if (cmd != SIOCDEVPRIVATE)
- return -ENOIOCTLCMD;
-
- if (hi->cmd == HDLCDRVCTL_MODEMPARMASK)
- return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ |
- HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 |
- HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE |
- HDLCDRV_PARMASK_MIDIIOBASE;
-
- return wss_ioctl(dev, sm, ifr, hi, cmd);
-}
-
-/* --------------------------------------------------------------------- */
-
-const struct hardware_info sm_hw_wssfdx = {
- "wssfdx", sizeof(struct sc_state_wss),
- wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw
-};
-
-/* --------------------------------------------------------------------- */
-
-#undef SCSTATE
diff --git a/drivers/net/hamradio/soundmodem/smdma.h b/drivers/net/hamradio/soundmodem/smdma.h
deleted file mode 100644
index 44e457a7a..000000000
--- a/drivers/net/hamradio/soundmodem/smdma.h
+++ /dev/null
@@ -1,217 +0,0 @@
-/*****************************************************************************/
-
-/*
- * smdma.h -- soundcard radio modem driver dma buffer routines.
- *
- * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch)
- *
- * This program 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 program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Please note that the GPL allows you to use the driver, NOT the radio.
- * In order to use the radio, you need a license from the communications
- * authority of your country.
- *
- */
-
-#ifndef _SMDMA_H
-#define _SMDMA_H
-
-/* ---------------------------------------------------------------------- */
-
-#include "sm.h"
-
-/* ---------------------------------------------------------------------- */
-
-#define DMA_MODE_AUTOINIT 0x10
-#define NUM_FRAGMENTS 4
-
-/*
- * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space
- * for the modulator to fill the whole DMA buffer without underrun
- * at the highest possible baud rate, otherwise the TX state machine will
- * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS
- */
-
-/* --------------------------------------------------------------------- */
-/*
- * ===================== DMA buffer management ===========================
- */
-
-/*
- * returns the number of samples per fragment
- */
-extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr)
-{
- if (send) {
- disable_dma(dmanr);
- clear_dma_ff(dmanr);
- set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT);
- set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf));
- set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS);
- enable_dma(dmanr);
- if (sm->dma.o16bit)
- return sm->dma.ofragsz/2;
- return sm->dma.ofragsz;
- } else {
- disable_dma(dmanr);
- clear_dma_ff(dmanr);
- set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT);
- set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf));
- set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS);
- enable_dma(dmanr);
- if (sm->dma.i16bit)
- return sm->dma.ifragsz/2;
- return sm->dma.ifragsz;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr,
- unsigned int *curfrag)
-{
- unsigned int dmaptr, sz, frg, offs;
-
- dmaptr = get_dma_residue(dmanr);
- if (send) {
- sz = sm->dma.ofragsz * NUM_FRAGMENTS;
- if (dmaptr == 0 || dmaptr > sz)
- dmaptr = sz;
- dmaptr--;
- frg = dmaptr / sm->dma.ofragsz;
- offs = (dmaptr % sm->dma.ofragsz) + 1;
- *curfrag = NUM_FRAGMENTS - 1 - frg;
-#ifdef SM_DEBUG
- if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
- sm->debug_vals.dma_residue = offs;
-#endif /* SM_DEBUG */
- if (sm->dma.o16bit)
- return offs/2;
- return offs;
- } else {
- sz = sm->dma.ifragsz * NUM_FRAGMENTS;
- if (dmaptr == 0 || dmaptr > sz)
- dmaptr = sz;
- dmaptr--;
- frg = dmaptr / sm->dma.ifragsz;
- offs = (dmaptr % sm->dma.ifragsz) + 1;
- *curfrag = NUM_FRAGMENTS - 1 - frg;
-#ifdef SM_DEBUG
- if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue)
- sm->debug_vals.dma_residue = offs;
-#endif /* SM_DEBUG */
- if (sm->dma.i16bit)
- return offs/2;
- return offs;
- }
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag)
-{
- unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS;
-
- sm->dma.ofragptr = curfrag;
- if (sm->dma.ptt_cnt <= 0) {
- sm->dma.ptt_cnt = 0;
- return 0;
- }
- sm->dma.ptt_cnt -= diff;
- if (sm->dma.ptt_cnt <= 0) {
- sm->dma.ptt_cnt = 0;
- return -1;
- }
- return 0;
-}
-
-extern __inline__ void dma_transmit(struct sm_state *sm)
-{
- void *p;
-
- while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) {
- p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz *
- ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS);
- if (sm->dma.o16bit) {
- time_exec(sm->debug_vals.mod_cyc,
- sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2));
- } else {
- time_exec(sm->debug_vals.mod_cyc,
- sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz));
- }
- sm->dma.ptt_cnt++;
- }
-}
-
-extern __inline__ void dma_init_transmit(struct sm_state *sm)
-{
- sm->dma.ofragptr = 0;
- sm->dma.ptt_cnt = 0;
-}
-
-extern __inline__ void dma_start_transmit(struct sm_state *sm)
-{
- sm->dma.ofragptr = 0;
- if (sm->dma.o16bit) {
- time_exec(sm->debug_vals.mod_cyc,
- sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2));
- } else {
- time_exec(sm->debug_vals.mod_cyc,
- sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz));
- }
- sm->dma.ptt_cnt = 1;
-}
-
-extern __inline__ void dma_clear_transmit(struct sm_state *sm)
-{
- sm->dma.ptt_cnt = 0;
- memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS);
-}
-
-/* --------------------------------------------------------------------- */
-
-extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag)
-{
- void *p;
-
- while (sm->dma.ifragptr != curfrag) {
- if (sm->dma.ifragptr)
- p = (unsigned char *)sm->dma.ibuf +
- sm->dma.ifragsz * sm->dma.ifragptr;
- else {
- p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz;
- memcpy(p, sm->dma.ibuf, sm->dma.ifragsz);
- }
- if (sm->dma.o16bit) {
- time_exec(sm->debug_vals.demod_cyc,
- sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2));
- } else {
- time_exec(sm->debug_vals.demod_cyc,
- sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz));
- }
- sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS;
- }
-}
-
-extern __inline__ void dma_init_receive(struct sm_state *sm)
-{
- sm->dma.ifragptr = 0;
-}
-
-/* --------------------------------------------------------------------- */
-#endif /* _SMDMA_H */
-
-
-
diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c
index 836414a53..97f12491d 100644
--- a/drivers/net/hamradio/yam.c
+++ b/drivers/net/hamradio/yam.c
@@ -1,6 +1,5 @@
-/*****************************************************************************/
-
-/*
+/*****************************************************************************
+ *
* yam.c -- YAM radio modem driver.
*
* Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr)
@@ -26,22 +25,25 @@
*
*
* History:
- * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3
- * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration
- * 0.2 F6FBB 08.06.98 Added delay after FPGA programming
- * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2
- * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistance
- * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics
- * 0.6 F6FBB 25.08.98 Added 1200Bds format
- * 0.7 F6FBB 12.09.98 Added to the kernel configuration
- * 0.8 F6FBB 14.10.98 Fixed slottime/persistance timing bug
- */
-
-/*****************************************************************************/
+ * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3
+ * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration
+ * 0.2 F6FBB 08.06.98 Added delay after FPGA programming
+ * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2
+ * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistance
+ * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics
+ * 0.6 F6FBB 25.08.98 Added 1200Bds format
+ * 0.7 F6FBB 12.09.98 Added to the kernel configuration
+ * 0.8 F6FBB 14.10.98 Fixed slottime/persistance timing bug
+ * 0.9 DG1KJD 10.03.00 Fixed media access and converted to new DDI
+ *
+ *****************************************************************************/
#include <linux/config.h>
-#include <linux/module.h>
+#include <linux/version.h>
#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/if.h>
@@ -52,28 +54,17 @@
#include <asm/system.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
-
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
-/* prototypes for ax25_encapsulate and ax25_rebuild_header */
#include <net/ax25.h>
-#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
-
-/* make genksyms happy */
+#include <net/ax25dev.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/tcp.h>
-
#include <linux/kernel.h>
#include <linux/proc_fs.h>
-
-#include <linux/version.h>
-#include <asm/uaccess.h>
-#include <linux/init.h>
-
#include <linux/yam.h>
#include "yam9600.h"
#include "yam1200.h"
@@ -81,71 +72,53 @@
/* --------------------------------------------------------------------- */
static const char yam_drvname[] = "yam";
-static const char yam_drvinfo[] __initdata = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n";
+static const char yam_drvinfo[] = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n";
/* --------------------------------------------------------------------- */
#define YAM_9600 1
#define YAM_1200 2
-
-#define NR_PORTS 4
+#define NR_PORTS 4
#define YAM_MAGIC 0xF10A7654
/* Transmitter states */
-
-#define TX_OFF 0
+#define TX_OFF 0
#define TX_HEAD 1
-#define TX_DATA 2
-#define TX_CRC1 3
-#define TX_CRC2 4
-#define TX_TAIL 5
+#define TX_DATA 2
+#define TX_CRC1 3
+#define TX_CRC2 4
+#define TX_TAIL 5
#define YAM_MAX_FRAME 1024
-#define DEFAULT_BITRATE 9600 /* bps */
-#define DEFAULT_HOLDD 10 /* sec */
-#define DEFAULT_TXD 300 /* ms */
-#define DEFAULT_TXTAIL 10 /* ms */
-#define DEFAULT_SLOT 100 /* ms */
-#define DEFAULT_PERS 64 /* 0->255 */
+#define DEFAULT_BITRATE 9600 /* bps */
+#define DEFAULT_HOLDD 10 /* sec */
+#define DEFAULT_TXD 300 /* ms */
+#define DEFAULT_TXTAIL 10 /* ms */
struct yam_port {
int magic;
- int bitrate;
- int baudrate;
int iobase;
int irq;
- int dupmode;
+ /* device stuff */
struct net_device dev;
+ struct ax25_dev ax25dev;
/* Stats section */
-
struct net_device_stats stats;
-
int nb_rxint;
int nb_mdint;
- /* Parameters section */
-
- int txd; /* tx delay */
- int holdd; /* duplex ptt delay */
- int txtail; /* txtail delay */
- int slot; /* slottime */
- int pers; /* persistence */
-
/* Tx section */
-
int tx_state;
int tx_count;
- int slotcnt;
unsigned char tx_buf[YAM_MAX_FRAME];
int tx_len;
int tx_crcl, tx_crch;
struct sk_buff_head send_queue; /* Packets awaiting transmission */
/* Rx section */
-
int dcd;
unsigned char rx_buf[YAM_MAX_FRAME];
int rx_len;
@@ -158,49 +131,65 @@ struct yam_mcs {
struct yam_mcs *next;
};
+enum uart {
+ c_uart_unknown, c_uart_8250,
+ c_uart_16450, c_uart_16550, c_uart_16550A
+};
+
+static const char *uart_str[] =
+{"unknown", "8250", "16450", "16550", "16550A"};
+
static struct yam_port yam_ports[NR_PORTS];
+static struct yam_mcs *yam_data = NULL;
+static unsigned irqs[16];
+static struct timer_list yam_timer;
-static struct yam_mcs *yam_data;
+#ifdef CONFIG_PROC_FS
+static int yam_net_get_info(char *buffer, char **start, off_t offset, int length);
-static char ax25_bcast[7] =
-{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
-static char ax25_test[7] =
-{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+#define yam_net_procfs_init() proc_net_create("yam", 0, yam_net_get_info);
+#define yam_net_procfs_remove() proc_net_remove("yam");
+#else
+#define yam_net_procfs_init()
+#define yam_net_procfs_remove()
+#endif
-static struct timer_list yam_timer;
+static unsigned int yam_report_dcd(struct net_device *dev);
+static unsigned int yam_report_ptt(struct net_device *dev);
+static void yam_param_notify(struct net_device *dev, int valueno, int old, int new);
/* --------------------------------------------------------------------- */
-#define RBR(iobase) (iobase+0)
-#define THR(iobase) (iobase+0)
-#define IER(iobase) (iobase+1)
-#define IIR(iobase) (iobase+2)
-#define FCR(iobase) (iobase+2)
-#define LCR(iobase) (iobase+3)
-#define MCR(iobase) (iobase+4)
-#define LSR(iobase) (iobase+5)
-#define MSR(iobase) (iobase+6)
-#define SCR(iobase) (iobase+7)
-#define DLL(iobase) (iobase+0)
-#define DLM(iobase) (iobase+1)
-
-#define YAM_EXTENT 8
+#define RBR(iobase) (iobase+0)
+#define THR(iobase) (iobase+0)
+#define IER(iobase) (iobase+1)
+#define IIR(iobase) (iobase+2)
+#define FCR(iobase) (iobase+2)
+#define LCR(iobase) (iobase+3)
+#define MCR(iobase) (iobase+4)
+#define LSR(iobase) (iobase+5)
+#define MSR(iobase) (iobase+6)
+#define SCR(iobase) (iobase+7)
+#define DLL(iobase) (iobase+0)
+#define DLM(iobase) (iobase+1)
+
+#define YAM_EXTENT 8
/* Interrupt Identification Register Bit Masks */
-#define IIR_NOPEND 1
-#define IIR_MSR 0
-#define IIR_TX 2
-#define IIR_RX 4
-#define IIR_LSR 6
+#define IIR_NOPEND 1
+#define IIR_MSR 0
+#define IIR_TX 2
+#define IIR_RX 4
+#define IIR_LSR 6
#define IIR_TIMEOUT 12 /* Fifo mode only */
#define IIR_MASK 0x0F
/* Interrupt Enable Register Bit Masks */
-#define IER_RX 1 /* enable rx interrupt */
-#define IER_TX 2 /* enable tx interrupt */
-#define IER_LSR 4 /* enable line status interrupts */
-#define IER_MSR 8 /* enable modem status interrupts */
+#define IER_RX 1 /* enable rx interrupt */
+#define IER_TX 2 /* enable tx interrupt */
+#define IER_LSR 4 /* enable line status interrupts */
+#define IER_MSR 8 /* enable modem status interrupts */
/* Modem Control Register Bit Masks */
#define MCR_DTR 0x01 /* DTR output */
@@ -254,6 +243,8 @@ static struct timer_list yam_timer;
#define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */
#define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/*************************************************************************
* CRC Tables
@@ -298,6 +289,8 @@ static const unsigned char chktabh[256] =
0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e,
0x0f};
+/* --------------------------------------------------------------------- */
+
/*************************************************************************
* FPGA functions
************************************************************************/
@@ -308,10 +301,11 @@ static void delay(int ms)
while (jiffies < timeout);
}
+/* --------------------------------------------------------------------- */
+
/*
* reset FPGA
*/
-
static void fpga_reset(int iobase)
{
outb(0, IER(iobase));
@@ -330,10 +324,11 @@ static void fpga_reset(int iobase)
delay(100);
}
+/* --------------------------------------------------------------------- */
+
/*
* send one byte to FPGA
*/
-
static int fpga_write(int iobase, unsigned char wrd)
{
unsigned char bit;
@@ -349,10 +344,26 @@ static int fpga_write(int iobase, unsigned char wrd)
if (jiffies > timeout)
return -1;
}
-
return 0;
}
+/* --------------------------------------------------------------------- */
+
+#ifdef MODULE
+static void free_mcs(void)
+{
+ struct yam_mcs *p;
+
+ while (yam_data) {
+ p = yam_data;
+ yam_data = yam_data->next;
+ kfree(p);
+ }
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+
static unsigned char *add_mcs(unsigned char *bits, int bitrate)
{
struct yam_mcs *p;
@@ -368,7 +379,8 @@ static unsigned char *add_mcs(unsigned char *bits, int bitrate)
}
/* Allocate a new mcs */
- if ((p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL)) == NULL) {
+ p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL);
+ if (p == NULL) {
printk(KERN_WARNING "YAM: no memory to allocate mcs\n");
return NULL;
}
@@ -380,6 +392,8 @@ static unsigned char *add_mcs(unsigned char *bits, int bitrate)
return p->bits;
}
+/* --------------------------------------------------------------------- */
+
static unsigned char *get_mcs(int bitrate)
{
struct yam_mcs *p;
@@ -400,11 +414,12 @@ static unsigned char *get_mcs(int bitrate)
}
}
+/* --------------------------------------------------------------------- */
+
/*
* download bitstream to FPGA
* data is contained in bits[] array in yam1200.h resp. yam9600.h
*/
-
static int fpga_download(int iobase, int bitrate)
{
int i, rc;
@@ -417,7 +432,7 @@ static int fpga_download(int iobase, int bitrate)
fpga_reset(iobase);
for (i = 0; i < YAM_FPGA_SIZE; i++) {
if (fpga_write(iobase, pbits[i])) {
- printk(KERN_ERR "yam: error in write cycle\n");
+ printk("yam: error in write cycle\n");
return -1; /* write... */
}
}
@@ -431,6 +446,7 @@ static int fpga_download(int iobase, int bitrate)
return (rc & MSR_DSR) ? 0 : -1;
}
+/* --------------------------------------------------------------------- */
/************************************************************************
* Serial port init
@@ -438,8 +454,7 @@ static int fpga_download(int iobase, int bitrate)
static void yam_set_uart(struct net_device *dev)
{
- struct yam_port *yp = (struct yam_port *) dev->priv;
- int divisor = 115200 / yp->baudrate;
+ int divisor = 115200 / (2 * ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE));
outb(0, IER(dev->base_addr));
outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr));
@@ -459,17 +474,8 @@ static void yam_set_uart(struct net_device *dev)
outb(ENABLE_RTXINT, IER(dev->base_addr));
}
-
/* --------------------------------------------------------------------- */
-enum uart {
- c_uart_unknown, c_uart_8250,
- c_uart_16450, c_uart_16550, c_uart_16550A
-};
-
-static const char *uart_str[] =
-{"unknown", "8250", "16450", "16550", "16550A"};
-
static enum uart yam_check_uart(unsigned int iobase)
{
unsigned char b1, b2, b3;
@@ -501,27 +507,29 @@ static enum uart yam_check_uart(unsigned int iobase)
return u;
}
+/* --------------------------------------------------------------------- */
+
/******************************************************************************
* Rx Section
******************************************************************************/
-static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp)
+
+static void inline yam_rx_flag(struct net_device *dev, struct yam_port *yp)
{
if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) {
- int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */
+ int pkt_len = yp->rx_len - 2; /* -CRC */
struct sk_buff *skb;
if ((yp->rx_crch & yp->rx_crcl) != 0xFF) {
/* Bad crc */
} else {
if (!(skb = dev_alloc_skb(pkt_len))) {
- printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name);
+ printk("%s: memory squeeze, dropping packet\n", dev->name);
++yp->stats.rx_dropped;
} else {
unsigned char *cp;
skb->dev = dev;
cp = skb_put(skb, pkt_len);
- *cp++ = 0; /* KISS kludge */
- memcpy(cp, yp->rx_buf, pkt_len - 1);
+ memcpy(cp, yp->rx_buf, pkt_len);
skb->protocol = htons(ETH_P_AX25);
skb->mac.raw = skb->data;
netif_rx(skb);
@@ -534,7 +542,9 @@ static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp)
yp->rx_crch = 0xf3;
}
-static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb)
+/* --------------------------------------------------------------------- */
+
+static void inline yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb)
{
if (yp->rx_len < YAM_MAX_FRAME) {
unsigned char c = yp->rx_crcl;
@@ -544,6 +554,8 @@ static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsi
}
}
+/* --------------------------------------------------------------------- */
+
/********************************************************************************
* TX Section
********************************************************************************/
@@ -553,38 +565,48 @@ static void ptt_on(struct net_device *dev)
outb(PTT_ON, MCR(dev->base_addr));
}
+/* --------------------------------------------------------------------- */
+
static void ptt_off(struct net_device *dev)
{
outb(PTT_OFF, MCR(dev->base_addr));
}
+/* --------------------------------------------------------------------- */
+
static int yam_send_packet(struct sk_buff *skb, struct net_device *dev)
{
struct yam_port *yp = dev->priv;
+ if (skb == NULL) {
+ return 0;
+ }
skb_queue_tail(&yp->send_queue, skb);
dev->trans_start = jiffies;
return 0;
}
+/* --------------------------------------------------------------------- */
+
static void yam_start_tx(struct net_device *dev, struct yam_port *yp)
{
- if ((yp->tx_state == TX_TAIL) || (yp->txd == 0))
+ int bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ int txd = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY);
+
+ if ((yp->tx_state == TX_TAIL) || (txd == 0))
yp->tx_count = 1;
else
- yp->tx_count = (yp->bitrate * yp->txd) / 8000;
+ yp->tx_count = (bitrate * txd) / 8000;
yp->tx_state = TX_HEAD;
ptt_on(dev);
}
-static unsigned short random_seed;
-
-static inline unsigned short random_num(void)
-{
- random_seed = 28629 * random_seed + 157;
- return random_seed;
-}
+/* --------------------------------------------------------------------- */
+/*
+ * note: this used to be a real channel arbiter, but this is now done in
+ * the DDI layer of AX.25 protocol engine.
+ */
static void yam_arbitrate(struct net_device *dev)
{
struct yam_port *yp = dev->priv;
@@ -593,31 +615,13 @@ static void yam_arbitrate(struct net_device *dev)
|| yp->tx_state != TX_OFF || skb_queue_empty(&yp->send_queue)) {
return;
}
- /* tx_state is TX_OFF and there is data to send */
-
- if (yp->dupmode) {
- /* Full duplex mode, don't wait */
- yam_start_tx(dev, yp);
- return;
- }
- if (yp->dcd) {
- /* DCD on, wait slotime ... */
- yp->slotcnt = yp->slot / 10;
- return;
- }
- /* Is slottime passed ? */
- if ((--yp->slotcnt) > 0)
- return;
-
- yp->slotcnt = yp->slot / 10;
-
- /* is random > persist ? */
- if ((random_num() % 256) > yp->pers)
- return;
+ /* tx_state is TX_OFF and there is data to send */
yam_start_tx(dev, yp);
}
+/* --------------------------------------------------------------------- */
+
static void yam_dotimer(unsigned long dummy)
{
int i;
@@ -631,10 +635,14 @@ static void yam_dotimer(unsigned long dummy)
add_timer(&yam_timer);
}
+/* --------------------------------------------------------------------- */
+
static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
{
struct sk_buff *skb;
unsigned char b, temp;
+ int bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ int txtail = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL);
switch (yp->tx_state) {
case TX_OFF:
@@ -647,17 +655,12 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
break;
}
yp->tx_state = TX_DATA;
- if (skb->data[0] != 0) {
-/* do_kiss_params(s, skb->data, skb->len); */
- dev_kfree_skb(skb);
- break;
- }
- yp->tx_len = skb->len - 1; /* strip KISS byte */
+ yp->tx_len = skb->len;
if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) {
dev_kfree_skb(skb);
break;
}
- memcpy(yp->tx_buf, skb->data + 1, yp->tx_len);
+ memcpy(yp->tx_buf, skb->data, yp->tx_len);
dev_kfree_skb(skb);
yp->tx_count = 0;
yp->tx_crcl = 0x21;
@@ -684,9 +687,7 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
case TX_CRC2:
outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr));
if (skb_queue_empty(&yp->send_queue)) {
- yp->tx_count = (yp->bitrate * yp->txtail) / 8000;
- if (yp->dupmode == 2)
- yp->tx_count += (yp->bitrate * yp->holdd) / 8;
+ yp->tx_count = (bitrate * txtail) / 8000;
if (yp->tx_count == 0)
yp->tx_count = 1;
yp->tx_state = TX_TAIL;
@@ -705,6 +706,8 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp)
}
}
+/* --------------------------------------------------------------------- */
+
/***********************************************************************************
* ISR routine
************************************************************************************/
@@ -714,7 +717,7 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct net_device *dev;
struct yam_port *yp;
unsigned char iir;
- int counter = 100;
+ int irq_work = 100;
int i;
sti();
@@ -736,8 +739,8 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs)
yp->dcd = (msr & RX_DCD) ? 1 : 0;
- if (--counter <= 0) {
- printk(KERN_ERR "%s: too many irq iir=%d\n", dev->name, iir);
+ if (--irq_work <= 0) {
+ printk("%s: too many irq iir=%d\n", dev->name, iir);
return;
}
if (msr & TX_RDY) {
@@ -756,6 +759,8 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
}
+/* --------------------------------------------------------------------- */
+
static int yam_net_get_info(char *buffer, char **start, off_t offset, int length)
{
int len = 0;
@@ -766,21 +771,31 @@ static int yam_net_get_info(char *buffer, char **start, off_t offset, int length
cli();
for (i = 0; i < NR_PORTS; i++) {
+ struct net_device *dev;
+
if (yam_ports[i].iobase == 0 || yam_ports[i].irq == 0)
continue;
- len += sprintf(buffer + len, "Device yam%d\n", i);
+ dev = &yam_ports[i].dev;
+
+ len += sprintf(buffer + len, "Device %d\n", i);
len += sprintf(buffer + len, " Up %d\n", netif_running(&yam_ports[i].dev));
- len += sprintf(buffer + len, " Speed %u\n", yam_ports[i].bitrate);
+ len += sprintf(buffer + len, " Speed %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE));
len += sprintf(buffer + len, " IoBase 0x%x\n", yam_ports[i].iobase);
- len += sprintf(buffer + len, " BaudRate %u\n", yam_ports[i].baudrate);
+ len += sprintf(buffer + len, " BaudRate %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE)*2);
len += sprintf(buffer + len, " IRQ %u\n", yam_ports[i].irq);
len += sprintf(buffer + len, " TxState %u\n", yam_ports[i].tx_state);
- len += sprintf(buffer + len, " Duplex %u\n", yam_ports[i].dupmode);
- len += sprintf(buffer + len, " HoldDly %u\n", yam_ports[i].holdd);
- len += sprintf(buffer + len, " TxDelay %u\n", yam_ports[i].txd);
- len += sprintf(buffer + len, " TxTail %u\n", yam_ports[i].txtail);
- len += sprintf(buffer + len, " SlotTime %u\n", yam_ports[i].slot);
- len += sprintf(buffer + len, " Persist %u\n", yam_ports[i].pers);
+ len += sprintf(buffer + len, " Duplex %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX));
+ len += sprintf(buffer + len, " TxDelay %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY));
+ len += sprintf(buffer + len, " TxTail %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL));
+ len += sprintf(buffer + len, " SlotTime %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME));
+ len += sprintf(buffer + len, " Persist %u\n",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE));
len += sprintf(buffer + len, " TxFrames %lu\n", yam_ports[i].stats.tx_packets);
len += sprintf(buffer + len, " RxFrames %lu\n", yam_ports[i].stats.rx_packets);
len += sprintf(buffer + len, " TxInt %u\n", yam_ports[i].nb_mdint);
@@ -805,13 +820,14 @@ static int yam_net_get_info(char *buffer, char **start, off_t offset, int length
if (len > length)
len = length;
-
return len;
}
+
+
/* --------------------------------------------------------------------- */
-static struct net_device_stats *yam_get_stats(struct net_device *dev)
+static struct net_device_stats * yam_get_stats(struct net_device *dev)
{
struct yam_port *yp;
@@ -836,39 +852,38 @@ static int yam_open(struct net_device *dev)
struct yam_port *yp = (struct yam_port *) dev->priv;
enum uart u;
int i;
+ int bitrate;
printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq);
- if (!dev || !yp || !yp->bitrate)
- return -ENXIO;
+ if (!dev) return -ENXIO;
+ bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ if (!yp || !bitrate) return -ENXIO;
if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT ||
dev->irq < 2 || dev->irq > 15) {
return -ENXIO;
}
if (check_region(dev->base_addr, YAM_EXTENT)) {
- printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr);
+ printk("%s: cannot 0x%lx busy\n", dev->name, dev->base_addr);
return -EACCES;
}
if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) {
- printk(KERN_ERR "%s: cannot find uart type\n", dev->name);
+ printk("%s: cannot find uart type\n", dev->name);
return -EIO;
}
- if (fpga_download(dev->base_addr, yp->bitrate)) {
- printk(KERN_ERR "%s: cannot init FPGA\n", dev->name);
+ if (fpga_download(dev->base_addr, bitrate)) {
+ printk("%s: cannot init FPGA\n", dev->name);
return -EIO;
}
outb(0, IER(dev->base_addr));
if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, NULL)) {
- printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq);
+ printk("%s: irq %d busy\n", dev->name, dev->irq);
return -EBUSY;
}
request_region(dev->base_addr, YAM_EXTENT, dev->name);
yam_set_uart(dev);
-
netif_start_queue(dev);
-
- yp->slotcnt = yp->slot / 10;
/* Reset overruns for all ports - FPGA programming makes overruns */
for (i = 0; i < NR_PORTS; i++) {
@@ -898,7 +913,9 @@ static int yam_close(struct net_device *dev)
/* Remove IRQ handler if last */
free_irq(dev->irq, NULL);
release_region(dev->base_addr, YAM_EXTENT);
+
netif_stop_queue(dev);
+
while ((skb = skb_dequeue(&yp->send_queue)))
dev_kfree_skb(skb);
@@ -963,9 +980,6 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
return -EINVAL; /* Cannot change this parameter when up */
if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev))
return -EINVAL; /* Cannot change this parameter when up */
- if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev))
- return -EINVAL; /* Cannot change this parameter when up */
-
if (yi.cfg.mask & YAM_IOBASE) {
yp->iobase = yi.cfg.iobase;
dev->base_addr = yi.cfg.iobase;
@@ -979,43 +993,23 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
if (yi.cfg.mask & YAM_BITRATE) {
if (yi.cfg.bitrate > YAM_MAXBITRATE)
return -EINVAL;
- yp->bitrate = yi.cfg.bitrate;
- }
- if (yi.cfg.mask & YAM_BAUDRATE) {
- if (yi.cfg.baudrate > YAM_MAXBAUDRATE)
- return -EINVAL;
- yp->baudrate = yi.cfg.baudrate;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, yi.cfg.bitrate);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, yi.cfg.bitrate);
}
if (yi.cfg.mask & YAM_MODE) {
if (yi.cfg.mode > YAM_MAXMODE)
return -EINVAL;
- yp->dupmode = yi.cfg.mode;
- }
- if (yi.cfg.mask & YAM_HOLDDLY) {
- if (yi.cfg.holddly > YAM_MAXHOLDDLY)
- return -EINVAL;
- yp->holdd = yi.cfg.holddly;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, yi.cfg.mode);
}
if (yi.cfg.mask & YAM_TXDELAY) {
if (yi.cfg.txdelay > YAM_MAXTXDELAY)
return -EINVAL;
- yp->txd = yi.cfg.txdelay;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, yi.cfg.txdelay);
}
if (yi.cfg.mask & YAM_TXTAIL) {
if (yi.cfg.txtail > YAM_MAXTXTAIL)
return -EINVAL;
- yp->txtail = yi.cfg.txtail;
- }
- if (yi.cfg.mask & YAM_PERSIST) {
- if (yi.cfg.persist > YAM_MAXPERSIST)
- return -EINVAL;
- yp->pers = yi.cfg.persist;
- }
- if (yi.cfg.mask & YAM_SLOTTIME) {
- if (yi.cfg.slottime > YAM_MAXSLOTTIME)
- return -EINVAL;
- yp->slot = yi.cfg.slottime;
- yp->slotcnt = yp->slot / 10;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, yi.cfg.txtail);
}
break;
@@ -1023,14 +1017,10 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
yi.cfg.mask = 0xffffffff;
yi.cfg.iobase = yp->iobase;
yi.cfg.irq = yp->irq;
- yi.cfg.bitrate = yp->bitrate;
- yi.cfg.baudrate = yp->baudrate;
- yi.cfg.mode = yp->dupmode;
- yi.cfg.txdelay = yp->txd;
- yi.cfg.holddly = yp->holdd;
- yi.cfg.txtail = yp->txtail;
- yi.cfg.persist = yp->pers;
- yi.cfg.slottime = yp->slot;
+ yi.cfg.bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ yi.cfg.mode = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX);
+ yi.cfg.txdelay = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY);
+ yi.cfg.txtail = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL);
if (copy_to_user(ifr->ifr_data, &yi, sizeof(struct yamdrv_ioctl_cfg)))
return -EFAULT;
break;
@@ -1045,6 +1035,50 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
/* --------------------------------------------------------------------- */
+static unsigned int yam_report_dcd(struct net_device *dev)
+{
+ struct yam_port *yp = (struct yam_port *) dev->priv;
+
+ if (!yp) return 0; /* paranoia */
+ return yp->dcd;
+}
+
+/* --------------------------------------------------------------------- */
+
+static unsigned int yam_report_ptt(struct net_device *dev)
+{
+ struct yam_port *yp = (struct yam_port *) dev->priv;
+
+ return (yp->tx_state != TX_OFF);
+}
+
+/* --------------------------------------------------------------------- */
+
+static void yam_param_notify(struct net_device *dev, int valueno, int old, int new)
+{
+ switch (valueno) {
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ if (netif_running(dev)) break;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, new);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, new);
+ return;
+ case AX25_VALUES_MEDIA_DUPLEX:
+ if (netif_running(dev)) break;
+ return;
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ default:
+ /* just let them do it. */
+ return;
+ }
+ /* reject */
+ ax25_dev_set_value(dev, valueno, old);
+ return;
+}
+
+/* --------------------------------------------------------------------- */
+
static int yam_set_mac_address(struct net_device *dev, void *addr)
{
struct sockaddr *sa = (struct sockaddr *) addr;
@@ -1065,62 +1099,58 @@ static int yam_probe(struct net_device *dev)
yp = (struct yam_port *) dev->priv;
- dev->open = yam_open;
- dev->stop = yam_close;
- dev->do_ioctl = yam_ioctl;
- dev->hard_start_xmit = yam_send_packet;
- dev->get_stats = yam_get_stats;
-
+ dev->open = yam_open;
+ dev->stop = yam_close;
+ dev->do_ioctl = yam_ioctl;
+ dev->hard_start_xmit = yam_send_packet;
+ dev->get_stats = yam_get_stats;
+ dev->hard_header = NULL;
+ dev->rebuild_header = NULL;
+ dev->set_mac_address = yam_set_mac_address;
+ dev->type = ARPHRD_AX25; /* AF_AX25 device */
+ dev->hard_header_len = AX25_MAX_HEADER_LEN; /* We do digipeaters now */
+ dev->mtu = 256; /* AX25 is the default */
+ dev->addr_len = 7; /* sizeof an ax.25 address */
dev_init_buffers(dev);
skb_queue_head_init(&yp->send_queue);
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
- dev->hard_header = ax25_encapsulate;
- dev->rebuild_header = ax25_rebuild_header;
-#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
-#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */
-
- dev->set_mac_address = yam_set_mac_address;
-
- dev->type = ARPHRD_AX25; /* AF_AX25 device */
- dev->hard_header_len = 73; /* We do digipeaters now */
- dev->mtu = 256; /* AX25 is the default */
- dev->addr_len = 7; /* sizeof an ax.25 address */
- memcpy(dev->broadcast, ax25_bcast, 7);
- memcpy(dev->dev_addr, ax25_test, 7);
-
/* New style flags */
- dev->flags = 0;
+ dev->flags = IFF_BROADCAST;
+ /* AX.25 downcalls */
+ AX25_PTR(dev)->hw.dcd = yam_report_dcd;
+ AX25_PTR(dev)->hw.ptt = yam_report_ptt;
+ AX25_PTR(dev)->hw.parameter_change_notify = yam_param_notify;
return 0;
}
/* --------------------------------------------------------------------- */
-static int __init yam_init_driver(void)
+int __init yam_init(void)
{
struct net_device *dev;
int i;
printk(yam_drvinfo);
+ /* Clears the IRQ table */
+
+ memset(irqs, 0, sizeof(irqs));
+ memset(yam_ports, 0, sizeof(yam_ports));
+
for (i = 0; i < NR_PORTS; i++) {
sprintf(yam_ports[i].dev.name, "yam%d", i);
yam_ports[i].magic = YAM_MAGIC;
- yam_ports[i].bitrate = DEFAULT_BITRATE;
- yam_ports[i].baudrate = DEFAULT_BITRATE * 2;
+ dev = &yam_ports[i].dev;
+ AX25_PTR(dev)=&yam_ports[i].ax25dev;
+ memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, DEFAULT_BITRATE);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, DEFAULT_BITRATE);
yam_ports[i].iobase = 0;
yam_ports[i].irq = 0;
- yam_ports[i].dupmode = 0;
- yam_ports[i].holdd = DEFAULT_HOLDD;
- yam_ports[i].txd = DEFAULT_TXD;
- yam_ports[i].txtail = DEFAULT_TXTAIL;
- yam_ports[i].slot = DEFAULT_SLOT;
- yam_ports[i].pers = DEFAULT_PERS;
-
- dev = &yam_ports[i].dev;
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, DEFAULT_TXD);
+ ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, DEFAULT_TXTAIL);
dev->priv = &yam_ports[i];
dev->base_addr = yam_ports[i].iobase;
@@ -1141,15 +1171,14 @@ static int __init yam_init_driver(void)
yam_timer.expires = jiffies + HZ / 100;
add_timer(&yam_timer);
- proc_net_create("yam", 0, yam_net_get_info);
+ yam_net_procfs_init();
return 0;
}
/* --------------------------------------------------------------------- */
-static void __exit yam_cleanup_driver(void)
+void __exit yam_exit(void)
{
- struct yam_mcs *p;
int i;
del_timer(&yam_timer);
@@ -1161,23 +1190,13 @@ static void __exit yam_cleanup_driver(void)
yam_close(dev);
unregister_netdev(dev);
}
-
- while (yam_data) {
- p = yam_data;
- yam_data = yam_data->next;
- kfree(p);
- }
-
- proc_net_remove("yam");
+ free_mcs();
+ yam_net_procfs_remove();
}
/* --------------------------------------------------------------------- */
MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr");
-MODULE_DESCRIPTION("Yam amateur radio modem driver");
-
-module_init(yam_init_driver);
-module_exit(yam_cleanup_driver);
-
-/* --------------------------------------------------------------------- */
-
+MODULE_DESCRIPTION("YAM Amateur Radio modem driver");
+module_init(yam_init);
+module_exit(yam_exit);
diff --git a/include/asm-alpha/termios.h b/include/asm-alpha/termios.h
index 374793646..b1bf59210 100644
--- a/include/asm-alpha/termios.h
+++ b/include/asm-alpha/termios.h
@@ -72,7 +72,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-arm/termios.h b/include/asm-arm/termios.h
index 3da727e71..95a242b9a 100644
--- a/include/asm-arm/termios.h
+++ b/include/asm-arm/termios.h
@@ -55,7 +55,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-i386/termios.h b/include/asm-i386/termios.h
index 70c1ecefd..d8c6f6a93 100644
--- a/include/asm-i386/termios.h
+++ b/include/asm-i386/termios.h
@@ -45,7 +45,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-m68k/termios.h b/include/asm-m68k/termios.h
index 411018fdb..75b0d9432 100644
--- a/include/asm-m68k/termios.h
+++ b/include/asm-m68k/termios.h
@@ -55,7 +55,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-mips/termios.h b/include/asm-mips/termios.h
index abc5deb3c..a37f05970 100644
--- a/include/asm-mips/termios.h
+++ b/include/asm-mips/termios.h
@@ -90,7 +90,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved fo Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-ppc/termios.h b/include/asm-ppc/termios.h
index 942c6e4b6..c481f19e4 100644
--- a/include/asm-ppc/termios.h
+++ b/include/asm-ppc/termios.h
@@ -180,7 +180,7 @@ struct termio {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6 /* X.25 async */
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-sparc/termios.h b/include/asm-sparc/termios.h
index a05f65856..2ff48aaa6 100644
--- a/include/asm-sparc/termios.h
+++ b/include/asm-sparc/termios.h
@@ -60,7 +60,7 @@ struct winsize {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/asm-sparc64/termios.h b/include/asm-sparc64/termios.h
index 082b57ac7..55d542645 100644
--- a/include/asm-sparc64/termios.h
+++ b/include/asm-sparc64/termios.h
@@ -60,7 +60,7 @@ struct winsize {
#define N_MOUSE 2
#define N_PPP 3
#define N_STRIP 4
-#define N_AX25 5
+#define N_KISS 5
#define N_X25 6
#define N_6PACK 7
#define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */
diff --git a/include/linux/ax25.h b/include/linux/ax25.h
index 9191445bb..f316c9960 100644
--- a/include/linux/ax25.h
+++ b/include/linux/ax25.h
@@ -6,8 +6,10 @@
#ifndef AX25_KERNEL_H
#define AX25_KERNEL_H
+#define AX25_NEW_DEVIF 1
+
#define AX25_MTU 256
-#define AX25_MAX_DIGIS 8
+#define AX25_MAX_DIGIS 8
#define AX25_WINDOW 1
#define AX25_T1 2
@@ -35,6 +37,9 @@
#define SIOCAX25DEVCTL (SIOCPROTOPRIVATE+12)
#define SIOCAX25GETINFO (SIOCPROTOPRIVATE+13)
+#define AX25_SET_DEV_BITRATE 0
+#define AX25_SET_DEV_MODE 1
+
#define AX25_SET_RT_IPMODE 2
#define AX25_NOUID_DEFAULT 0
@@ -58,6 +63,59 @@ struct full_sockaddr_ax25 {
ax25_address fsa_digipeater[AX25_MAX_DIGIS];
};
+#define fsax25_family fsa_ax25.sax25_family
+#define fsax25_call fsa_ax25.sax25_call
+#define fsax25_ndigis fsa_ax25.sax25_ndigis
+
+typedef struct {
+ ax25_address addr;
+ ax25_address digipeater[AX25_MAX_DIGIS];;
+ unsigned char dcount;
+} ax25_path_t;
+
+typedef struct {
+ ax25_address dest;
+ ax25_address src;
+ ax25_address digipeater[AX25_MAX_DIGIS];
+ unsigned char dcount;
+ char lastrepeat;
+} ax25_addr_t;
+
+struct ax25_rtmsg {
+ char port_name[32];
+ unsigned int mode;
+ ax25_addr_t addr;
+};
+
+struct ax25_armsg {
+ char port_name[32];
+ unsigned int ip_addr;
+ ax25_address ax_addr;
+};
+
+struct ax25_pathmsg {
+ char port_name[32];
+ unsigned int mode;
+ ax25_path_t path;
+};
+
+struct ax25_nlmsg {
+
+#define AX25_MSG_RTINFO 0
+#define AX25_MSG_ARINFO 1
+#define AX25_MSG_SETRT 2
+#define AX25_MSG_DELRT 3
+#define AX25_MSG_OPTRT 4
+ unsigned int msg_type;
+
+ union {
+ unsigned char raw[128-sizeof(unsigned int)];
+ struct ax25_rtmsg rtmsg;
+ struct ax25_armsg armsg;
+ struct ax25_pathmsg pathmsg;
+ } msg;
+};
+
struct ax25_routes_struct {
ax25_address port_addr;
ax25_address dest_addr;
@@ -73,13 +131,11 @@ struct ax25_route_opt_struct {
};
struct ax25_ctl_struct {
- ax25_address port_addr;
- ax25_address source_addr;
- ax25_address dest_addr;
- unsigned int cmd;
- unsigned long arg;
- unsigned char digi_count;
- ax25_address digi_addr[AX25_MAX_DIGIS];
+ ax25_address port_addr;
+ ax25_address source_addr;
+ ax25_address dest_addr;
+ unsigned int cmd;
+ unsigned long arg;
};
/* this will go away. Please do not export to user land */
diff --git a/include/linux/ax25_userdev.h b/include/linux/ax25_userdev.h
new file mode 100644
index 000000000..cc58a3e5e
--- /dev/null
+++ b/include/linux/ax25_userdev.h
@@ -0,0 +1,120 @@
+/*
+ * AX.25 usermode device driver.
+ * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com>
+ * Copyright 2001, by Joerg Reuter <jreuter@yaina.de>
+ *
+ * This program 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __IF_AX25_UDEV_H
+#define __IF_AX25_UDEV_H
+
+#define AX25_UDEV_VER "0.1"
+#define AX25_UDEV_DEBUG
+
+/* Uncomment to enable debugging */
+/* #define AX25_UDEV_DEBUG 1 */
+
+#ifdef __KERNEL__
+
+#ifdef AX25_UDEV_DEBUG
+#define DBG if(ax25_udev->debug)printk
+#define DBG1 if(debug==2)printk
+#else
+#define DBG( a... )
+#define DBG1( a... )
+#endif
+
+struct ax25_udev_struct {
+ char name[IFNAMSIZ];
+ unsigned long flags;
+
+ struct fasync_struct *fasync;
+ wait_queue_head_t read_wait;
+
+ struct net_device dev;
+ struct sk_buff_head txq;
+ struct net_device_stats stats;
+ struct ax25_dev ax25_dev;
+
+ struct {
+ int duplex;
+ int arbitration;
+ } capabilities;
+
+ struct {
+ int dcd:1;
+ int cts:1;
+ int rts:1;
+ int ptt:1;
+ } status;
+
+#ifdef AX25_UDEV_DEBUG
+ int debug;
+#endif
+};
+
+#endif /* __KERNEL__ */
+
+/* Number of devices */
+#define AX25_UDEV_MAX_DEV 255
+
+/* TX queue size */
+#define AX25_UDEV_TXQ_SIZE 10
+
+/* Max frame size */
+#define AX25_UDEV_MAX_FRAME 4096
+
+/* AX25_UDEV device flags */
+#define AX25_UDEV_TYPE_MASK 0x000f
+#define AX25_UDEV_FASYNC 0x0010
+#define AX25_UDEV_IFF_SET 0x1000
+
+/* AX25_UDEV IFF ioctl */
+#define AX25_UDEV_SETIFF _IOW(0x25, 0, void *)
+#define AX25_UDEV_SETDEBUG _IOW(0x25, 1, int)
+
+/* Packet layout:
+ *
+ * (int) packet_type
+ * (int) data
+ * ...
+ *
+ * Exception for AX25_UDEV_DATA:
+ * (int) 0
+ * (char) octet
+ * ...
+ *
+ */
+
+typedef enum ax25_user_dev_commands_e {
+ AX25_UDEV_DATA,
+ AX25_UDEV_CAPABILITIES,
+ AX25_UDEV_DCD_STATUS,
+ AX25_UDEV_CTS_STATUS,
+ AX25_UDEV_PTT_STATUS,
+ AX25_UDEV_SET_MAC_VALUE,
+ AX25_UDEV_REQUEST_RTS
+} ax25_user_dev_commands_t;
+
+typedef enum ax25_user_dev_capabilities_e {
+ AX25_UDEV_CAP_HALF_DUPLEX,
+ AX25_UDEV_CAP_FULL_DUPLEX,
+ AX25_UDEV_CAP_ADVANCED_ARBITRATION,
+ AX25_UDEV_CAP_OWN_ARBITRATION,
+ AX25_UDEV_CAP_SIMPLE_ARBITRATION
+} ax25_user_dev_capabilities_t;
+
+typedef int ax25_udev_pt_t;
+typedef int ax25_udev_val_t;
+
+#endif /* __IF_AX25_UDEV_H */
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index 34c8dcb04..1fd9db550 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -33,6 +33,7 @@
#define SGI_USEMACLONE 151
#define TUN_MINOR 200
+#define AX25_UDEV_MINOR 210
extern int misc_init(void);
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 011caf23d..686ebda28 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -317,6 +317,7 @@ struct net_device
void *dn_ptr; /* DECnet specific data */
void *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */
+ void *ax25_ptr; /* AX.25 specific data */
struct Qdisc *qdisc;
struct Qdisc *qdisc_sleeping;
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index 2afb52e98..6eaaa86bf 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -5,6 +5,7 @@
#define NETLINK_SKIP 1 /* Reserved for ENskip */
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
#define NETLINK_FIREWALL 3 /* Firewalling hook */
+#define NETLINK_AX25 4 /* AX25 routing daemon hook */
#define NETLINK_ARPD 8
#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
#define NETLINK_IP6_FW 13
diff --git a/include/linux/scc.h b/include/linux/scc.h
index a896704e6..1ec441746 100644
--- a/include/linux/scc.h
+++ b/include/linux/scc.h
@@ -8,7 +8,7 @@
/* selection of hardware types */
#define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */
-#define EAGLE 0x01 /* hardware type for EAGLE card */
+#define EAGLE 0x01 /* hardware type for EAGLE card */
#define PC100 0x02 /* hardware type for PC100 card */
#define PRIMUS 0x04 /* hardware type for PRIMUS-PC (DG9BL) card */
#define DRSI 0x08 /* hardware type for DRSI PC*Packet card */
@@ -22,87 +22,45 @@ enum SCC_ioctl_cmds {
SIOCSCCINI,
SIOCSCCCHANINI,
SIOCSCCSMEM,
- SIOCSCCGKISS,
- SIOCSCCSKISS,
+ SIOCSCCGMODEM,
+ SIOCSCCSMODEM,
SIOCSCCGSTAT,
SIOCSCCCAL
};
-/* Device parameter control (from WAMPES) */
-
-enum L1_params {
- PARAM_DATA,
- PARAM_TXDELAY,
- PARAM_PERSIST,
- PARAM_SLOTTIME,
- PARAM_TXTAIL,
- PARAM_FULLDUP,
- PARAM_SOFTDCD, /* was: PARAM_HW */
- PARAM_MUTE, /* ??? */
- PARAM_DTR,
- PARAM_RTS,
- PARAM_SPEED,
- PARAM_ENDDELAY, /* ??? */
- PARAM_GROUP,
- PARAM_IDLE,
- PARAM_MIN,
- PARAM_MAXKEY,
- PARAM_WAIT,
- PARAM_MAXDEFER,
- PARAM_TX,
- PARAM_HWEVENT = 31,
- PARAM_RETURN = 255 /* reset kiss mode */
-};
-
-/* fulldup parameter */
+/* magic number */
-enum FULLDUP_modes {
- KISS_DUPLEX_HALF, /* normal CSMA operation */
- KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */
- KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */
- KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */
-};
+#define SCC_MAGIC 0x8530 /* ;-) */
/* misc. parameters */
#define TIMER_OFF 65535U /* to switch off timers */
-#define NO_SUCH_PARAM 65534U /* param not implemented */
-
-/* HWEVENT parameter */
-enum HWEVENT_opts {
- HWEV_DCD_ON,
- HWEV_DCD_OFF,
- HWEV_ALL_SENT
-};
-
-/* channel grouping */
+/* Tx/Rx clock sources */
-#define RXGROUP 0100 /* if set, only tx when all channels clear */
-#define TXGROUP 0200 /* if set, don't transmit simultaneously */
+typedef enum {
+ CLOCK_SOURCE_RTxC, /* use RTxC pin */
+ CLOCK_SOURCE_TRxC, /* use TRxC pin */
+ CLOCK_SOURCE_BRG, /* use baud rate generator */
+ CLOCK_SOURCE_DPLL /* use DPLL */
+} Scc_clock_sources;
-/* Tx/Rx clock sources */
+typedef enum {
+ TRXCP_MODE_IN, /* TRxC pin is input */
+ TRXCP_MODE_TXC_OUT, /* TRxC bears Tx clock */
+ TRXCP_MODE_BRG_OUT, /* TRxC bears BRG output */
+ TRXCP_MODE_DPLL_OUT /* TRxC bears DPLL */
-enum CLOCK_sources {
- CLK_DPLL, /* normal halfduplex operation */
- CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */
- CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */
- /* modems without clock regeneration */
- CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */
- /* MODEMs without clock recovery */
-};
+} Scc_trxc_pin_modes;
/* Tx state */
-enum TX_state {
+typedef enum {
TXS_IDLE, /* Transmitter off, no data pending */
- TXS_BUSY, /* waiting for permission to send / tailtime */
+ TXS_TXDELAY, /* waiting for transmitter keyed up */
TXS_ACTIVE, /* Transmitter on, sending data */
- TXS_NEWFRAME, /* reset CRC and send (next) frame */
- TXS_IDLE2, /* Transmitter on, no data pending */
- TXS_WAIT, /* Waiting for Mintime to expire */
- TXS_TIMEOUT /* We had a transmission timeout */
-};
+ TXS_TAIL, /* Transmition tail time */
+} Scc_tx_states;
typedef unsigned long io_port; /* type definition for an 'io port address' */
@@ -124,50 +82,22 @@ struct scc_stat {
unsigned int tx_under; /* Transmitter Underruns */
unsigned int tx_state; /* Transmitter state */
- int tx_queued; /* tx frames enqueued */
-
- unsigned int maxqueue; /* allocated tx_buffers */
unsigned int bufsize; /* used buffersize */
};
struct scc_modem {
- long speed; /* Line speed, bps */
- char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */
- char nrz; /* NRZ instead of NRZI */
-};
-
-struct scc_kiss_cmd {
- int command; /* one of the KISS-Commands defined above */
- unsigned param; /* KISS-Param */
-};
-
-struct scc_hw_config {
- io_port data_a; /* data port channel A */
- io_port ctrl_a; /* control port channel A */
- io_port data_b; /* data port channel B */
- io_port ctrl_b; /* control port channel B */
- io_port vector_latch; /* INTACK-Latch (#) */
- io_port special; /* special function port */
-
- int irq; /* irq */
- long clock; /* clock */
- char option; /* command for function port */
-
- char brand; /* hardware type */
- char escc; /* use ext. features of a 8580/85180/85280 */
-};
-
-/* (#) only one INTACK latch allowed. */
-
-
-struct scc_mem_config {
- unsigned int dummy;
- unsigned int bufsize;
-};
-
-struct scc_calibrate {
- unsigned int time;
- unsigned char pattern;
+ int rx_speed; /* Line speed, bps */
+ int tx_speed;
+ int rx_clock_source; /* see CLOCK_sources above */
+ int tx_clock_source; /* dito */
+ int trxc_pin_mode; /* see TRXCP_modes above */
+ int nrz_mode; /* 0 = NRZ, 1 = NRZI */
+ int tx_delay; /* tx delay in msec */
+ int tx_tail; /* tx tail time in msec */
+ int fullduplex; /* Duplex mode: 0 = "halfduplex", 1 = "fullduplex" */
+ int trx_feedback; /* TRX feeds transmit signal back to RxD */
+ int tx_inhibit; /* Transmit is not allowed when set */
+ int softdcd; /* Use DPLL instead of DCD pin for carrier detect */
};
#ifdef __KERNEL__
@@ -182,71 +112,53 @@ enum {TX_OFF, TX_ON}; /* command for scc_key_trx() */
#define RXINT 0x04
#define SPINT 0x06
-#ifdef CONFIG_SCC_DELAY
-#define Inb(port) inb_p(port)
-#define Outb(port, val) outb_p(val, port)
-#else
-#define Inb(port) inb(port)
-#define Outb(port, val) outb(val, port)
-#endif
-
-/* SCC channel control structure for KISS */
-
-struct scc_kiss {
- unsigned char txdelay; /* Transmit Delay 10 ms/cnt */
- unsigned char persist; /* Persistence (0-255) as a % */
- unsigned char slottime; /* Delay to wait on persistence hit */
- unsigned char tailtime; /* Delay after last byte written */
- unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */
- unsigned char waittime; /* Waittime before any transmit attempt */
- unsigned int maxkeyup; /* Maximum time to transmit (seconds) */
- unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */
- unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */
- unsigned int maxdefer; /* Timer for CSMA channel busy limit */
- unsigned char tx_inhibit; /* Transmit is not allowed when set */
- unsigned char group; /* Group ID for AX.25 TX interlocking */
- unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */
- unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */
-};
-
-
/* SCC channel structure */
struct scc_channel {
- int magic; /* magic word */
-
int init; /* channel exists? */
struct net_device *dev; /* link to device control structure */
struct net_device_stats dev_stat;/* device statistics */
+ struct ax25_dev ax25dev;
+ char * proc_dev_name; /* points to dev->name or NULL */
char brand; /* manufacturer of the board */
long clock; /* used clock */
io_port ctrl; /* I/O address of CONTROL register */
io_port data; /* I/O address of DATA register */
- io_port special; /* I/O address of special function port */
+ io_port special_port; /* I/O address of special function port */
int irq; /* Number of Interrupt */
- char option;
+ char special_option;
char enhanced; /* Enhanced SCC support */
unsigned char wreg[16]; /* Copy of last written value in WRx */
unsigned char status; /* Copy of R0 at last external interrupt */
unsigned char dcd; /* DCD status */
- struct scc_kiss kiss; /* control structure for KISS params */
- struct scc_stat stat; /* statistical information */
struct scc_modem modem; /* modem information */
+ struct scc_stat stat; /* statistical information */
+
+ struct {
+ spinlock_t hwaccess; /* hardware access */
+ spinlock_t timer; /* timer functions */
+ spinlock_t kick_tx; /* start of transmission */
+ } spinlocks;
+
+ struct {
+ ctl_table channel[6]; /* /.../chip_<n>/channel_<k>/ */
+ ctl_table modem[13]; /* /.../channel_<k>/modem/ */
+ ctl_table stats[1]; /* /.../channel_<k>/stats/ */
+ } proc_tables;
+
- struct sk_buff_head tx_queue; /* next tx buffer */
struct sk_buff *rx_buff; /* pointer to frame currently received */
struct sk_buff *tx_buff; /* pointer to frame currently transmitted */
-
- /* Timer */
- struct timer_list tx_t; /* tx timer for this channel */
- struct timer_list tx_wdog; /* tx watchdogs */
+ struct sk_buff *tx_new; /* next transmit buffer */
+ struct timer_list tx_timer; /* Tx delay & tail timer */
};
+int scc_init(void);
#endif /* defined(__KERNEL__) */
#endif /* defined(_SCC_H) */
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index cbfaafbfe..24ca11425 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -155,7 +155,8 @@ enum
NET_TR=14,
NET_DECNET=15,
NET_ECONET=16,
- NET_KHTTPD=17
+ NET_KHTTPD=17,
+ NET_DEV=18
};
/* /proc/sys/kernel/random */
@@ -423,7 +424,16 @@ enum {
NET_AX25_N2=11,
NET_AX25_PACLEN=12,
NET_AX25_PROTOCOL=13,
- NET_AX25_DAMA_SLAVE_TIMEOUT=14
+ NET_AX25_DAMA_SLAVE_TIMEOUT=14,
+ NET_AX25_MEDIA_DUPLEX=15,
+ NET_AX25_MEDIA_TXDELAY=16,
+ NET_AX25_MEDIA_TXTAIL=17,
+ NET_AX25_MEDIA_TXBITRATE=18,
+ NET_AX25_MEDIA_RXBITRATE=19,
+ NET_AX25_MEDIA_SLOTTIME=20,
+ NET_AX25_MEDIA_PPERSISTENCE=21,
+ NET_AX25_MEDIA_AUTO_ADJUST=22,
+ NET_AX25_DEVICE_NAME=23
};
/* /proc/sys/net/rose */
@@ -510,6 +520,80 @@ enum {
NET_DECNET_CONF_DEV_STATE = 7
};
+/* /proc/sys/net/dev/ (hardware configuration for net devices) */
+
+enum {
+ NET_DEV_Z8530DRV=1
+};
+
+/* /proc/sys/net/dev/z8530drv/ */
+
+enum {
+ NET_DEV_Z8530DRV_NCHIPS=1,
+ NET_DEV_Z8530DRV_DELAY=2,
+ NET_DEV_Z8530DRV_CHIP_BASE=32
+};
+
+/* /proc/sys/net/dev/z8530drv/chip<n>/ */
+
+enum {
+ NET_DEV_Z8530DRV_VENDOR=1,
+ NET_DEV_Z8530DRV_CLOCK=2,
+ NET_DEV_Z8530DRV_IRQ=3,
+ NET_DEV_Z8530DRV_ESCC=4,
+ NET_DEV_Z8530DRV_SPECIAL_PORT=5,
+ NET_DEV_Z8530DRV_SPECIAL_OPT=6,
+ NET_DEV_Z8530DRV_CHANNEL_A=32,
+ NET_DEV_Z8530DRV_CHANNEL_B=33
+};
+
+/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/ */
+
+enum {
+ NET_DEV_Z8530DRV_DATA_PORT=1,
+ NET_DEV_Z8530DRV_CTRL_PORT=2,
+ NET_DEV_Z8530DRV_IFNAME=3,
+ NET_DEV_Z8530DRV_BUFSIZE=4,
+/*
+ NET_DEV_Z8530DRV_SPECIAL_PORT=5,
+ NET_DEV_Z8530DRV_SPECIAL_OPT=6,
+*/
+ NET_DEV_Z8530DRV_MODEM=32,
+ NET_DEV_Z8530DRV_STAT=33
+};
+
+/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/modem/ */
+
+enum {
+ NET_DEV_Z8530DRV_RX_SPEED=1,
+ NET_DEV_Z8530DRV_TX_SPEED=2,
+ NET_DEV_Z8530DRV_RX_CLOCK_SOURCE=3,
+ NET_DEV_Z8530DRV_TX_CLOCK_SOURCE=4,
+ NET_DEV_Z8530DRV_TRXC_PIN_MODE=5,
+ NET_DEV_Z8530DRV_FULLDUPLEX=6,
+ NET_DEV_Z8530DRV_TRX_FEEDBACK=7,
+ NET_DEV_Z8530DRV_NRZ_MODE=8,
+ NET_DEV_Z8530DRV_TX_INHIBIT=9,
+ NET_DEV_Z8530DRV_SOFTDCD=10,
+ NET_DEV_Z8530DRV_TX_DELAY=11,
+ NET_DEV_Z8530DRV_TX_TAIL=12
+};
+
+/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/stats/ */
+
+enum {
+ NET_DEV_Z8530DRV_STAT_RX_INTS=1,
+ NET_DEV_Z8530DRV_STAT_TX_INTS=2,
+ NET_DEV_Z8530DRV_STAT_EX_INTS=3,
+ NET_DEV_Z8530DRV_STAT_TX_FRAMES=4,
+ NET_DEV_Z8530DRV_STAT_RX_FRAMES=5,
+ NET_DEV_Z8530DRV_STAT_TX_ERRORS=6,
+ NET_DEV_Z8530DRV_STAT_RX_ERRORS=7,
+ NET_DEV_Z8530DRV_STAT_TX_UNDERRUNS=8,
+ NET_DEV_Z8530DRV_STAT_RX_OVERRUNS=9,
+ NET_DEV_Z8530DRV_STAT_NO_SPACE=10,
+ NET_DEV_Z8530DRV_STAT_TX_STATE=11
+};
/* CTL_PROC names: */
/* CTL_FS names: */
diff --git a/include/net/ax25.h b/include/net/ax25.h
index 0957d1aae..fc0509a3f 100644
--- a/include/net/ax25.h
+++ b/include/net/ax25.h
@@ -6,19 +6,27 @@
#ifndef _AX25_H
#define _AX25_H
-#include <linux/config.h>
#include <linux/ax25.h>
-#define AX25_T1CLAMPLO 1
-#define AX25_T1CLAMPHI (30 * HZ)
+#define AX25_TICS (HZ/10) /* AX25 timertic is 1/10 sec (100 ms) */
+#define AX25_SLOWHZ 10 /* 10 timertics, 1 second */
+
+#define AX25_T1CLAMPLO AX25_SLOWHZ /* 1 sec */
+#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* 30 secs */
#define AX25_BPQ_HEADER_LEN 16
-#define AX25_KISS_HEADER_LEN 1
-#define AX25_HEADER_LEN 17
#define AX25_ADDR_LEN 7
-#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN)
-#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
+#define AX25_MIN_HEADER_LEN (AX25_ADDR_LEN*2+1) /* Source, Destination, Control */
+#define AX25_MAX_HEADER_LEN (AX25_MIN_HEADER_LEN+1+AX25_ADDR_LEN*AX25_MAX_DIGIS) /* ... including Digipeaters */
+
+/*
+ * these are obsolete
+ *
+ * #define AX25_KISS_HEADER_LEN 1
+ * #define AX25_HEADER_LEN 17
+ * #define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN)
+ */
/* AX.25 Protocol IDs */
#define AX25_P_ROSE 0x01
@@ -27,6 +35,8 @@
#define AX25_P_TEXT 0xF0
#define AX25_P_NETROM 0xCF
#define AX25_P_SEGMENT 0x08
+#define AX25_P_VJCOMP 0x06 /* Matthias Welwarsky (DG2FEF) */
+#define AX25_P_VJUNCOMP 0x07 /* Matthias Welwarsky (DG2FEF) */
/* AX.25 Segment control values */
#define AX25_SEG_REM 0x7F
@@ -38,18 +48,45 @@
#define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */
#define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */
-#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */
+#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121) */
#define AX25_COND_ACK_PENDING 0x01
#define AX25_COND_REJECT 0x02
#define AX25_COND_PEER_RX_BUSY 0x04
#define AX25_COND_OWN_RX_BUSY 0x08
-#define AX25_COND_DAMA_MODE 0x10
+#define AX25_COND_STATE_CHANGE 0x10
+#define AX25_COND_RELEASE 0x20
+#define AX25_COND_SETUP 0x40
+#define AX25_COND_START_T1 0x80
+
+#define AX25_SCHED_IDLE 0
+#define AX25_SCHED_READY 1
#ifndef _LINUX_NETDEVICE_H
#include <linux/netdevice.h>
#endif
+/*
+ * These headers are taken from the KA9Q package by Phil Karn. These specific
+ * files have been placed under the GPL (not the whole package) by Phil.
+ *
+ *
+ * Copyright 1991 Phil Karn, KA9Q
+ *
+ * This program 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; version 2 dated June, 1991.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
+ */
+
/* Upper sub-layer (LAPB) definitions */
/* Control field templates */
@@ -81,270 +118,122 @@
/* Define Link State constants. */
-enum {
- AX25_STATE_0,
+enum {
+ AX25_STATE_0 = 0,
AX25_STATE_1,
AX25_STATE_2,
AX25_STATE_3,
- AX25_STATE_4
-};
-
-#define AX25_MODULUS 8 /* Standard AX.25 modulus */
-#define AX25_EMODULUS 128 /* Extended AX.25 modulus */
-
-enum {
- AX25_PROTO_STD_SIMPLEX,
- AX25_PROTO_STD_DUPLEX,
- AX25_PROTO_DAMA_SLAVE,
- AX25_PROTO_DAMA_MASTER
+ AX25_STATE_4,
+ AX25_LISTEN
};
-enum {
- AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */
- AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */
- AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */
- AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */
- AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */
- AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */
- AX25_VALUES_T1, /* Default T1 timeout value */
- AX25_VALUES_T2, /* Default T2 timeout value */
- AX25_VALUES_T3, /* Default T3 timeout value */
- AX25_VALUES_IDLE, /* Connected mode idle timer */
- AX25_VALUES_N2, /* Default N2 value */
- AX25_VALUES_PACLEN, /* AX.25 MTU */
- AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave, DAMA Master */
- AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */
- AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
-};
+typedef enum {
+ AX25_SEQMASK = 7,
+ AX25_ESEQMASK = 127
+} ax25_seqmask_t;
-#define AX25_DEF_IPDEFMODE 0 /* Datagram */
-#define AX25_DEF_AXDEFMODE 0 /* Normal */
-#define AX25_DEF_BACKOFF 1 /* Linear backoff */
-#define AX25_DEF_CONMODE 2 /* Connected mode allowed */
-#define AX25_DEF_WINDOW 2 /* Window=2 */
-#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */
-#define AX25_DEF_T1 (10 * HZ) /* T1=10s */
-#define AX25_DEF_T2 (3 * HZ) /* T2=3s */
-#define AX25_DEF_T3 (300 * HZ) /* T3=300s */
-#define AX25_DEF_N2 10 /* N2=10 */
-#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */
-#define AX25_DEF_PACLEN 256 /* Paclen=256 */
-#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */
-#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */
-
-typedef struct ax25_uid_assoc {
- struct ax25_uid_assoc *next;
- uid_t uid;
- ax25_address call;
-} ax25_uid_assoc;
+#ifndef _AX25_VJ_H
+struct axvj_slcomp;
+#endif
typedef struct {
- ax25_address calls[AX25_MAX_DIGIS];
- unsigned char repeated[AX25_MAX_DIGIS];
- unsigned char ndigi;
- char lastrepeat;
-} ax25_digi;
-
-typedef struct ax25_route {
- struct ax25_route *next;
- ax25_address callsign;
- struct net_device *dev;
- ax25_digi *digipeat;
- char ip_mode;
-} ax25_route;
-
-typedef struct {
- char slave; /* slave_mode? */
- struct timer_list slave_timer; /* timeout timer */
- unsigned short slave_timeout; /* when? */
-} ax25_dama_info;
-
-struct ctl_table;
-
-typedef struct ax25_dev {
- struct ax25_dev *next;
- struct net_device *dev;
- struct net_device *forward;
- struct ctl_table *systable;
- int values[AX25_MAX_VALUES];
-#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER)
- ax25_dama_info dama;
-#endif
-} ax25_dev;
+ unsigned int csum;
+ struct sk_buff *skb;
+} ax25_reseq_t;
typedef struct ax25_cb {
+ struct ax25_cb *prev; /* doubly linked list now */
struct ax25_cb *next;
- ax25_address source_addr, dest_addr;
- ax25_digi *digipeat;
- ax25_dev *ax25_dev;
- unsigned char iamdigi;
- unsigned char state, modulus, pidincl;
- unsigned short vs, vr, va;
- unsigned char condition, backoff;
+ struct ax25_cb *peer; /* for FlexNet style digipeating */
+ struct net_device *device; /* backlink to device structure */
+ int inserted; /* Socket is on a list */
+ struct sock *sk; /* Backlink to socket */
+
+ /* used by the DDI layer */
+ struct {
+ int state;
+ struct ax25_cb *prev;
+ struct ax25_cb *next;
+ } ready;
+
+ unsigned short tx_cmd, tx_rsp;
+
+ /* layer II values
+ *
+ * vs = sequence number assigned to next to be transmitted I frame
+ * va = sequence number of first I frame our peer has not acked yet
+ * vr = sequence number of next frame we expect to receive from peer
+ * vl = last frame we ack'ed yet
+ */
+ ax25_addr_t addr;
+ unsigned char state, condition;
+ unsigned short vs, vr, va, vs_max, vl, window;
+ ax25_seqmask_t seqmask;
+ unsigned long rtt_timestamp;
+ short rtt, vs_rtt;
unsigned char n2, n2count;
- struct timer_list t1timer, t2timer, t3timer, idletimer;
- unsigned long t1, t2, t3, idle, rtt;
+ unsigned short t1, t2, t3, idle;
+ unsigned short wrt_timer, ack_timer, idletimer, killtimer;
unsigned short paclen, fragno, fraglen;
+ unsigned char iamdigi, pidincl, backoff;
struct sk_buff_head write_queue;
- struct sk_buff_head reseq_queue;
+ struct sk_buff_head rcv_queue; /* MW: for flow control handling */
struct sk_buff_head ack_queue;
struct sk_buff_head frag_queue;
- unsigned char window;
- struct timer_list timer;
- struct sock *sk; /* Backlink to socket */
+ struct axvj_slcomp *slcomp; /* MW: for VJ Compression */
+ unsigned char slcomp_enable; /* MW: dito. */
+ ax25_reseq_t reseq[AX25_SEQMASK+1]; /* MW: resequencer, not for EMODULUS */
+ rwlock_t timer_lock;
} ax25_cb;
-/* af_ax25.c */
-extern ax25_cb *volatile ax25_list;
-extern void ax25_free_cb(ax25_cb *);
-extern void ax25_insert_socket(ax25_cb *);
-struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int);
-struct sock *ax25_find_socket(ax25_address *, ax25_address *, int);
-extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *);
-extern struct sock *ax25_addr_match(ax25_address *);
-extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int);
-extern void ax25_destroy_socket(ax25_cb *);
-extern ax25_cb *ax25_create_cb(void);
-extern void ax25_fillin_cb(ax25_cb *, ax25_dev *);
-extern int ax25_create(struct socket *, int);
-extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *);
-
-/* ax25_addr.c */
+typedef struct {
+ ax25_addr_t addr;
+ unsigned short frametype;
+ unsigned short nr;
+ unsigned short ns;
+ unsigned char pf;
+ unsigned char cmdrsp;
+ unsigned char dama;
+} ax25_pktinfo;
+
+#define DAMA_STATE(ax25_cb) (AX25_PTR(ax25_cb->device)->dama_mode)
+
+/* needed by netrom/rose */
+extern __inline__ unsigned long ax25_display_timer(struct timer_list *timer)
+{
+ if (!timer_pending(timer))
+ return 0;
+ return timer->expires - jiffies;
+}
+
+#include <net/ax25call.h>
+
+extern ax25_address* asc2ax(char *);
extern ax25_address null_ax25_address;
-extern char *ax2asc(ax25_address *);
-extern ax25_address *asc2ax(char *);
-extern int ax25cmp(ax25_address *, ax25_address *);
-extern int ax25digicmp(ax25_digi *, ax25_digi *);
-extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *);
-extern int ax25_addr_build(unsigned char *, ax25_address *, ax25_address *, ax25_digi *, int, int);
-extern int ax25_addr_size(ax25_digi *);
-extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
-
-/* ax25_dev.c */
-extern ax25_dev *ax25_dev_list;
-extern ax25_dev *ax25_dev_ax25dev(struct net_device *);
-extern ax25_dev *ax25_addr_ax25dev(ax25_address *);
-extern void ax25_dev_device_up(struct net_device *);
-extern void ax25_dev_device_down(struct net_device *);
-extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
-extern struct net_device *ax25_fwd_dev(struct net_device *);
-extern void ax25_dev_free(void);
-
-/* ax25_ds_in.c */
-extern int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int);
-
-/* ax25_ds_subr.c */
-extern void ax25_ds_nr_error_recovery(ax25_cb *);
-extern void ax25_ds_enquiry_response(ax25_cb *);
-extern void ax25_ds_establish_data_link(ax25_cb *);
-extern void ax25_dev_dama_on(ax25_dev *);
-extern void ax25_dev_dama_off(ax25_dev *);
-extern void ax25_dama_on(ax25_cb *);
-extern void ax25_dama_off(ax25_cb *);
-
-/* ax25_ds_timer.c */
-extern void ax25_ds_set_timer(ax25_dev *);
-extern void ax25_ds_del_timer(ax25_dev *);
-extern void ax25_ds_timer(ax25_cb *);
-extern void ax25_ds_t1_timeout(ax25_cb *);
-extern void ax25_ds_heartbeat_expiry(ax25_cb *);
-extern void ax25_ds_t3timer_expiry(ax25_cb *);
-extern void ax25_ds_idletimer_expiry(ax25_cb *);
-
-/* ax25_iface.c */
-extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
-extern void ax25_protocol_release(unsigned int);
-extern int ax25_linkfail_register(void (*)(ax25_cb *, int));
-extern void ax25_linkfail_release(void (*)(ax25_cb *, int));
-extern int ax25_listen_register(ax25_address *, struct net_device *);
-extern void ax25_listen_release(ax25_address *, struct net_device *);
-extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
-extern int ax25_listen_mine(ax25_address *, struct net_device *);
-extern void ax25_link_failed(ax25_cb *, int);
-extern int ax25_protocol_is_registered(unsigned int);
-
-/* ax25_in.c */
-extern int ax25_rx_iframe(ax25_cb *, struct sk_buff *);
-extern int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *);
-
-/* ax25_ip.c */
-extern int ax25_encapsulate(struct sk_buff *, struct net_device *, unsigned short, void *, void *, unsigned int);
-extern int ax25_rebuild_header(struct sk_buff *);
+extern char* ax2asc(ax25_address *);
+extern int ax25cmp(ax25_address *, ax25_address *);
+extern ax25_cb *ax25_send_frame(struct sk_buff*, int, ax25_addr_t*, struct net_device*);
+extern ax25_cb *ax25_find_cb(ax25_addr_t*, struct net_device*);
/* ax25_out.c */
-extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct net_device *);
extern void ax25_output(ax25_cb *, int, struct sk_buff *);
-extern void ax25_kick(ax25_cb *);
-extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
-extern void ax25_queue_xmit(struct sk_buff *);
-extern int ax25_check_iframes_acked(ax25_cb *, unsigned short);
-
-/* ax25_route.c */
-extern void ax25_rt_device_down(struct net_device *);
-extern int ax25_rt_ioctl(unsigned int, void *);
-extern int ax25_rt_get_info(char *, char **, off_t, int);
-extern int ax25_rt_autobind(ax25_cb *, ax25_address *);
-extern ax25_route *ax25_rt_find_route(ax25_address *, struct net_device *);
-extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
-extern void ax25_rt_free(void);
-
-/* ax25_std_in.c */
-extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
-
-/* ax25_std_subr.c */
-extern void ax25_std_nr_error_recovery(ax25_cb *);
-extern void ax25_std_establish_data_link(ax25_cb *);
-extern void ax25_std_transmit_enquiry(ax25_cb *);
-extern void ax25_std_enquiry_response(ax25_cb *);
-extern void ax25_std_timeout_response(ax25_cb *);
-
-/* ax25_std_timer.c */
-extern void ax25_std_heartbeat_expiry(ax25_cb *);
-extern void ax25_std_t1timer_expiry(ax25_cb *);
-extern void ax25_std_t2timer_expiry(ax25_cb *);
-extern void ax25_std_t3timer_expiry(ax25_cb *);
-extern void ax25_std_idletimer_expiry(ax25_cb *);
-
-/* ax25_subr.c */
-extern void ax25_clear_queues(ax25_cb *);
-extern void ax25_frames_acked(ax25_cb *, unsigned short);
-extern void ax25_requeue_frames(ax25_cb *);
-extern int ax25_validate_nr(ax25_cb *, unsigned short);
-extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *);
-extern void ax25_send_control(ax25_cb *, int, int, int);
-extern void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, ax25_digi *);
-extern void ax25_calculate_t1(ax25_cb *);
-extern void ax25_calculate_rtt(ax25_cb *);
-extern void ax25_disconnect(ax25_cb *, int);
/* ax25_timer.c */
-extern void ax25_start_heartbeat(ax25_cb *);
-extern void ax25_start_t1timer(ax25_cb *);
-extern void ax25_start_t2timer(ax25_cb *);
-extern void ax25_start_t3timer(ax25_cb *);
-extern void ax25_start_idletimer(ax25_cb *);
-extern void ax25_stop_heartbeat(ax25_cb *);
-extern void ax25_stop_t1timer(ax25_cb *);
-extern void ax25_stop_t2timer(ax25_cb *);
-extern void ax25_stop_t3timer(ax25_cb *);
-extern void ax25_stop_idletimer(ax25_cb *);
-extern int ax25_t1timer_running(ax25_cb *);
-extern unsigned long ax25_display_timer(struct timer_list *);
-
-/* ax25_uid.c */
-extern int ax25_uid_policy;
-extern ax25_address *ax25_findbyuid(uid_t);
-extern int ax25_uid_ioctl(int, struct sockaddr_ax25 *);
-extern int ax25_uid_get_info(char *, char **, off_t, int);
-extern void ax25_uid_free(void);
+extern void ax25_timer(ax25_cb *);
+extern void ax25_link_failed(ax25_cb *, int);
+extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *);
/* sysctl_net_ax25.c */
-#ifdef CONFIG_SYSCTL
extern void ax25_register_sysctl(void);
extern void ax25_unregister_sysctl(void);
-#else
-extern inline void ax25_register_sysctl(void) {};
-extern inline void ax25_unregister_sysctl(void) {};
-#endif /* CONFIG_SYSCTL */
+
+/* support routines for modules that use AX.25, in ax25_timer.c */
+extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *));
+extern void ax25_protocol_release(unsigned int);
+extern int ax25_linkfail_register(void (*)(ax25_cb *, int));
+extern void ax25_linkfail_release(void (*)(ax25_cb *, int));
+extern int ax25_listen_register(ax25_address *, struct net_device *);
+extern void ax25_listen_release(ax25_address *, struct net_device *);
+extern int ax25_protocol_is_registered(unsigned int);
#endif
diff --git a/include/net/ax25_uid.h b/include/net/ax25_uid.h
new file mode 100644
index 000000000..ae40ff0fd
--- /dev/null
+++ b/include/net/ax25_uid.h
@@ -0,0 +1,33 @@
+/*
+ * This code REQUIRES 1.2.1 or higher/ NET3.029
+ *
+ * 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.
+ */
+
+#ifndef _AX25_UID_H
+#define _AX25_UID_H
+
+typedef struct ax25_uid_assoc {
+ struct ax25_uid_assoc *next;
+ uid_t uid;
+ ax25_address call;
+ char device[IFNAMSIZ];
+} ax25_uid_assoc;
+
+typedef enum {
+ AX25_UID_POLICY_ARBITRARY_CALLSIGN,
+ AX25_UID_POLICY_GIVEN_CALLSIGN,
+ AX25_UID_POLICY_ANY_SSID
+} t_ax25_uid_policy;
+
+extern t_ax25_uid_policy ax25_uid_policy;
+extern int ax25_cs_get_info(char *, char **, off_t, int);
+extern int ax25_uid_ioctl(int, struct sockaddr_ax25*);
+extern ax25_address* ax25_find_by_uid(uid_t uid);
+extern ax25_address* ax25_find_match_for_uid(uid_t uid, ax25_address *providec, char *device);
+
+#endif
diff --git a/include/net/ax25call.h b/include/net/ax25call.h
new file mode 100644
index 000000000..68b8a70c0
--- /dev/null
+++ b/include/net/ax25call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void ax25_proto_init(struct net_proto *pro);
diff --git a/include/net/ax25dev.h b/include/net/ax25dev.h
new file mode 100644
index 000000000..f2e125e6b
--- /dev/null
+++ b/include/net/ax25dev.h
@@ -0,0 +1,164 @@
+/*
+ * include/net/ax25dev.h
+ *
+ * intra-kernel interface declaration, anything needed by device drivers for
+ * AX.25 interfaces is defined here.
+ *
+ */
+
+#ifndef _NET_AX25DEV_H
+#define _NET_AX25DEV_H
+
+#include <linux/netdevice.h>
+#include <linux/sysctl.h>
+#include <linux/tqueue.h>
+#include <linux/timer.h>
+
+#ifndef AX25_TICS
+#define AX25_TICS (HZ/10)
+#endif
+
+#define AX25_MAX_DEVICES 20 /* Max No of AX.25 devices */
+#define AX25_DEV_MAGIC 0x88babe73
+
+enum {
+ AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */
+ AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */
+ AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */
+ AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */
+ AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */
+ AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */
+ AX25_VALUES_T1, /* Default T1 timeout value */
+ AX25_VALUES_T2, /* Default T2 timeout value */
+ AX25_VALUES_T3, /* Default T3 timeout value */
+ AX25_VALUES_IDLE, /* Connected mode idle timer */
+ AX25_VALUES_N2, /* Default N2 value */
+ AX25_VALUES_PACLEN, /* AX.25 MTU */
+ AX25_VALUES_PROTOCOL, /* What is this? */
+ AX25_VALUES_DAMA_SLAVE_TIMEOUT, /* DAMA */
+ AX25_VALUES_MEDIA_DUPLEX, /* Duplex type */
+ AX25_VALUES_MEDIA_TXDELAY, /* TX-Delay */
+ AX25_VALUES_MEDIA_TXTAIL, /* TX-Tail */
+ AX25_VALUES_MEDIA_TXBITRATE, /* TX-Bitrate */
+ AX25_VALUES_MEDIA_RXBITRATE, /* RX-Bitrate */
+ AX25_VALUES_MEDIA_SLOTTIME, /* Slottime */
+ AX25_VALUES_MEDIA_PPERSISTENCE, /* P-Persistence */
+ AX25_VALUES_MEDIA_AUTO_ADJUST, /* Enable/disable auto adjustment */
+ AX25_VALUES_DUMMY_0, /* For future use */
+ AX25_VALUES_DUMMY_1, /* For future use */
+ AX25_VALUES_DUMMY_2, /* For future use */
+ AX25_VALUES_DUMMY_3, /* For future use */
+ AX25_VALUES_DUMMY_4, /* For future use */
+ AX25_VALUES_DUMMY_5, /* For future use */
+ AX25_VALUES_DUMMY_6, /* For future use */
+ AX25_VALUES_DUMMY_7, /* For future use */
+ AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */
+};
+
+#define AX25_DEF_IPDEFMODE 1 /* VC */
+#define AX25_DEF_AXDEFMODE 0 /* Normal */
+#define AX25_DEF_BACKOFF 1 /* Linear backoff */
+#define AX25_DEF_CONMODE 2 /* Connected mode allowed */
+#define AX25_DEF_WINDOW 7 /* Window=7 */
+#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */
+#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */
+#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */
+#define AX25_DEF_T3 (90 * AX25_SLOWHZ) /* T3=90s */
+#define AX25_DEF_N2 10 /* N2=10 */
+#define AX25_DEF_IDLE (120 * AX25_SLOWHZ) /* Idle=120s */
+#define AX25_DEF_PACLEN 256 /* Paclen=256 */
+#define AX25_DEF_PROTOCOL 1 /* 0=CSMA; 1=CSMA/DAMA slave auto; 2=DAMA master */
+#define AX25_DEF_DAMA_SLAVE_TIMEOUT 0 /* ? */
+#define AX25_DEF_MEDIA_DUPLEX 0 /* Half duplex */
+#define AX25_DEF_MEDIA_TXDELAY 60 /* 60 ms */
+#define AX25_DEF_MEDIA_TXTAIL 10 /* 10 ms */
+#define AX25_DEF_MEDIA_TXBITRATE 9600 /* 9600 bps */
+#define AX25_DEF_MEDIA_RXBITRATE 9600 /* 9600 bps */
+#define AX25_DEF_MEDIA_SLOTTIME 10 /* 10 ms */
+#define AX25_DEF_MEDIA_PPERSISTENCE 32 /* 32/256 */
+#define AX25_DEF_MEDIA_AUTO_ADJUST 1 /* Auto adjust on */
+
+struct ax25_dev {
+ unsigned long magic;
+ struct net_device *netdev; /* backlink to the network device */
+ struct net_device *forward;
+ struct timer_list timer;
+ struct timer_list tics;
+ struct tq_struct task_queue;
+ struct sk_buff_head unproto_queue;
+ int needs_transmit;
+ unsigned int dama_mode;
+ struct {
+ struct ax25_cb *ready;
+ struct ax25_cb *all;
+ } list;
+
+ struct {
+ unsigned char fast; /*
+ * set for "infinitely"
+ * fast channels to skip
+ * arbitration
+ */
+
+ /* downcalls */
+ unsigned int (*dcd)(struct net_device *dev); /* required: report dcd state */
+ unsigned int (*ptt)(struct net_device *dev); /* required: report ptt state */
+ unsigned int (*cts)(struct net_device *dev); /* optional: report "clear to send" */
+ void (*rts)(struct net_device *dev); /* optional: forced "key transmitter" */
+ void (*parameter_change_notify)( /* configure media parameter */
+ struct net_device *dev,
+ int id, /* parameter type */
+ int old, /* old value */
+ int new); /* new value */
+ } hw;
+
+ struct ctl_table systable[AX25_MAX_VALUES+1];
+ int values[AX25_MAX_VALUES];
+
+ /* handled by DDI layer */
+ unsigned int bit_per_jiffie; /* internal use */
+ unsigned int jiffies_per_slot; /* internal use */
+ unsigned char dcd_memory; /* internal use */
+ unsigned char dcd_dropped; /* internal use */
+ unsigned char dama_polled; /* internal use */
+ unsigned int bytes_sent;
+ unsigned int max_bytes;
+
+ /* statistics */
+ unsigned long tx_iframes;
+ unsigned long rx_iframes;
+ unsigned long tx_bytes;
+ unsigned long rx_bytes;
+ unsigned long rx_rejects;
+
+ rwlock_t ready_lock;
+};
+
+#define DAMA_SLAVE (1<<0)
+#define DAMA_MASTER (1<<1)
+
+#define AX25_PTR(dev) ((struct ax25_dev *)dev->ax25_ptr)
+
+extern void register_ax25device(struct net_device *dev);
+extern void unregister_ax25device(struct net_device *dev);
+
+
+/*
+ * Call this function from device driver code for reading a parameter
+ */
+extern inline int ax25_dev_get_value(struct net_device *dev, int valueno)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+ return ax25_device->values[valueno];
+}
+
+/*
+ * Call this function from device driver code for writing a parameter
+ */
+extern inline void ax25_dev_set_value(struct net_device *dev, int valueno, int newvalue)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+ ax25_device->values[valueno] = newvalue;
+}
+
+#endif /* _NET_AX25DEV_H */
diff --git a/include/net/netrom.h b/include/net/netrom.h
index 21c9f8b86..b6ccf33a9 100644
--- a/include/net/netrom.h
+++ b/include/net/netrom.h
@@ -76,8 +76,7 @@ typedef struct {
struct nr_neigh {
struct nr_neigh *next;
- ax25_address callsign;
- ax25_digi *digipeat;
+ ax25_addr_t addr;
ax25_cb *ax25;
struct net_device *dev;
unsigned char quality;
diff --git a/include/net/rose.h b/include/net/rose.h
index f1f893e27..167e629b9 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -81,8 +81,7 @@ enum {
struct rose_neigh {
struct rose_neigh *next;
- ax25_address callsign;
- ax25_digi *digipeat;
+ ax25_addr_t addr;
ax25_cb *ax25;
struct net_device *dev;
unsigned short count;
diff --git a/net/ax25/Config.in b/net/ax25/Config.in
index b8e5d7333..bba299843 100644
--- a/net/ax25/Config.in
+++ b/net/ax25/Config.in
@@ -3,7 +3,7 @@
#
# 19971130 Now in an own category to make correct compilation of the
# AX.25 stuff easier...
-# Joerg Reuter DL1BKE <jreuter@yaina.de>
+# Joerg Reuter DL1BKE <jreuter@poboxes.com>
# 19980129 Moved to net/ax25/Config.in, sourcing device drivers.
mainmenu_option next_comment
@@ -11,26 +11,23 @@ comment 'Amateur Radio support'
bool 'Amateur Radio support' CONFIG_HAMRADIO
if [ "$CONFIG_HAMRADIO" != "n" ]; then
- if [ "$CONFIG_NET" != "n" ]; then
- comment 'Packet Radio protocols'
- tristate ' Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25
- if [ "$CONFIG_AX25" != "n" ]; then
- bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
-# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
- dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
- dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
- fi
-
- if [ "$CONFIG_AX25" != "n" ]; then
- mainmenu_option next_comment
- comment 'AX.25 network device drivers'
-
- source drivers/net/hamradio/Config.in
-
- endmenu
- fi
- fi
+ if [ "$CONFIG_NET" != "n" ]; then
+ comment 'Packet Radio protocols'
+ tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 $CONFIG_NETLINK
+ if [ "$CONFIG_AX25" != "n" ]; then
+# bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE
+# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER
+ tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25
+ tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25
+ fi
+ if [ "$CONFIG_AX25" != "n" ]; then
+ mainmenu_option next_comment
+ comment 'AX.25 network device drivers'
+ source drivers/net/hamradio/Config.in
+ endmenu
+ fi
+ fi
fi
endmenu
diff --git a/net/ax25/Makefile b/net/ax25/Makefile
index 5974031f4..c35f59105 100644
--- a/net/ax25/Makefile
+++ b/net/ax25/Makefile
@@ -6,20 +6,19 @@
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definition is now in the main makefile...
-
+#
O_TARGET := ax25.o
export-objs := af_ax25.o
-obj-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \
- ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \
- ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o
-
-obj-m := $(O_TARGET)
+obj-y := af_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o \
+ ax25_vj.o ax25_ddi.o ax25_uid.o ax25_ctl.o ax25_core.o \
+ ax25_ipax.o ax25_lapb.o ax25_netlink.o sysctl_net_ax25.o
-obj-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o
-obj-$(CONFIG_SYSCTL) += sysctl_net_ax25.o
+obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make
+tar:
+ tar -cvf /dev/f1 .
diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c
index 4a2684ebe..6fa817d65 100644
--- a/net/ax25/af_ax25.c
+++ b/net/ax25/af_ax25.c
@@ -1,107 +1,22 @@
/*
- * AX.25 release 038
+ * af_ax25.c: Network subsystem interface and NEW-AX.25 main functions
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS)
*
- * 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.
+ * Comment: SOCK_DGRAM support is missing and should be implemented ASAP.
+ * There is currently no clean way for unproto operation. Most
+ * application use AF_PACKET, SOCK_RAW which is seriously broken
+ * because it skips the DDI arbiter etc. etc.
*
- * History
- * AX.25 006 Alan(GW4PTS) Nearly died of shock - it's working 8-)
- * AX.25 007 Alan(GW4PTS) Removed the silliest bugs
- * AX.25 008 Alan(GW4PTS) Cleaned up, fixed a few state machine problems, added callbacks
- * AX.25 009 Alan(GW4PTS) Emergency patch kit to fix memory corruption
- * AX.25 010 Alan(GW4PTS) Added RAW sockets/Digipeat.
- * AX.25 011 Alan(GW4PTS) RAW socket and datagram fixes (thanks) - Raw sendto now gets PID right
- * datagram sendto uses correct target address.
- * AX.25 012 Alan(GW4PTS) Correct incoming connection handling, send DM to failed connects.
- * Use skb->data not skb+1. Support sk->priority correctly.
- * Correct receive on SOCK_DGRAM.
- * AX.25 013 Alan(GW4PTS) Send DM to all unknown frames, missing initialiser fixed
- * Leave spare SSID bits set (DAMA etc) - thanks for bug report,
- * removed device registration (it's not used or needed). Clean up for
- * gcc 2.5.8. PID to AX25_P_
- * AX.25 014 Alan(GW4PTS) Cleanup and NET3 merge
- * AX.25 015 Alan(GW4PTS) Internal test version.
- * AX.25 016 Alan(GW4PTS) Semi Internal version for PI card
- * work.
- * AX.25 017 Alan(GW4PTS) Fixed some small bugs reported by
- * G4KLX
- * AX.25 018 Alan(GW4PTS) Fixed a small error in SOCK_DGRAM
- * AX.25 019 Alan(GW4PTS) Clean ups for the non INET kernel and device ioctls in AX.25
- * AX.25 020 Jonathan(G4KLX) /proc support and other changes.
- * AX.25 021 Alan(GW4PTS) Added AX25_T1, AX25_N2, AX25_T3 as requested.
- * AX.25 022 Jonathan(G4KLX) More work on the ax25 auto router and /proc improved (again)!
- * Alan(GW4PTS) Added TIOCINQ/OUTQ
- * AX.25 023 Alan(GW4PTS) Fixed shutdown bug
- * AX.25 023 Alan(GW4PTS) Linus changed timers
- * AX.25 024 Alan(GW4PTS) Small bug fixes
- * AX.25 025 Alan(GW4PTS) More fixes, Linux 1.1.51 compatibility stuff, timers again!
- * AX.25 026 Alan(GW4PTS) Small state fix.
- * AX.25 027 Alan(GW4PTS) Socket close crash fixes.
- * AX.25 028 Alan(GW4PTS) Callsign control including settings per uid.
- * Small bug fixes.
- * Protocol set by sockets only.
- * Small changes to allow for start of NET/ROM layer.
- * AX.25 028a Jonathan(G4KLX) Changes to state machine.
- * AX.25 028b Jonathan(G4KLX) Extracted ax25 control block
- * from sock structure.
- * AX.25 029 Alan(GW4PTS) Combined 028b and some KA9Q code
- * Jonathan(G4KLX) and removed all the old Berkeley, added IP mode registration.
- * Darryl(G7LED) stuff. Cross-port digipeating. Minor fixes and enhancements.
- * Alan(GW4PTS) Missed suser() on axassociate checks
- * AX.25 030 Alan(GW4PTS) Added variable length headers.
- * Jonathan(G4KLX) Added BPQ Ethernet interface.
- * Steven(GW7RRM) Added digi-peating control ioctl.
- * Added extended AX.25 support.
- * Added AX.25 frame segmentation.
- * Darryl(G7LED) Changed connect(), recvfrom(), sendto() sockaddr/addrlen to
- * fall inline with bind() and new policy.
- * Moved digipeating ctl to new ax25_dev structs.
- * Fixed ax25_release(), set TCP_CLOSE, wakeup app
- * context, THEN make the sock dead.
- * Alan(GW4PTS) Cleaned up for single recvmsg methods.
- * Alan(GW4PTS) Fixed not clearing error on connect failure.
- * AX.25 031 Jonathan(G4KLX) Added binding to any device.
- * Joerg(DL1BKE) Added DAMA support, fixed (?) digipeating, fixed buffer locking
- * for "virtual connect" mode... Result: Probably the
- * "Most Buggiest Code You've Ever Seen" (TM)
- * HaJo(DD8NE) Implementation of a T5 (idle) timer
- * Joerg(DL1BKE) Renamed T5 to IDLE and changed behaviour:
- * the timer gets reloaded on every received or transmitted
- * I frame for IP or NETROM. The idle timer is not active
- * on "vanilla AX.25" connections. Furthermore added PACLEN
- * to provide AX.25-layer based fragmentation (like WAMPES)
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout error.
- * ax25_send_frame() limits the number of enqueued
- * datagrams per socket.
- * AX.25 033 Jonathan(G4KLX) Removed auto-router.
- * Hans(PE1AYX) Converted to Module.
- * Joerg(DL1BKE) Moved BPQ Ethernet to separate driver.
- * AX.25 034 Jonathan(G4KLX) 2.1 changes
- * Alan(GW4PTS) Small POSIXisations
- * AX.25 035 Alan(GW4PTS) Started fixing to the new
- * format.
- * Hans(PE1AYX) Fixed interface to IP layer.
- * Alan(GW4PTS) Added asynchronous support.
- * Frederic(F1OAT) Support for pseudo-digipeating.
- * Jonathan(G4KLX) Support for packet forwarding.
- * AX.25 036 Jonathan(G4KLX) Major restructuring.
- * Joerg(DL1BKE) Fixed DAMA Slave.
- * Jonathan(G4KLX) Fix wildcard listen parameter setting.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel
- * independent of AX25_MAX_DIGIS used by applications.
- * Tomi(OH2BNS) Fixed ax25_getname().
- * Joerg(DL1BKE) Starting to phase out the support for full_sockaddr_ax25
- * with only 6 digipeaters and sockaddr_ax25 in ax25_bind(),
- * ax25_connect() and ax25_sendmsg()
- * Joerg(DL1BKE) Added support for SO_BINDTODEVICE
- * Arnaldo C. Melo s/suser/capable(CAP_NET_ADMIN)/, some more cleanups
- * Michal Ostrowski Module initialization cleanup.
+ * Changelog:
+ * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de>
+ * port to kernel 2.4.1
+ *
+ * License: 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.
*/
#include <linux/config.h>
@@ -111,518 +26,327 @@
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/kernel.h>
-#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/string.h>
#include <linux/sockios.h>
#include <linux/net.h>
-#include <net/ax25.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
+#include <linux/if.h>
#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
+#include <linux/spinlock.h>
#include <linux/fcntl.h>
#include <linux/termios.h> /* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
#include <linux/init.h>
-#include <net/ip.h>
-#include <net/arp.h>
-
-
-
-ax25_cb *volatile ax25_list;
+#include <linux/smp.h>
+#include <linux/smp_lock.h>
-static struct proto_ops ax25_proto_ops;
+#include <net/sock.h>
+#include <net/ax25.h>
+#include <net/ax25_uid.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_route.h"
+#include "ax25_core.h"
+#include "ax25_ctl.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_netlink.h"
+#include "ax25_timer.h"
/*
- * Free an allocated ax25 control block. This is done to centralise
- * the MOD count code.
+ * ------------------------------------------------------------------------
+ * declaration of static functions
+ * ------------------------------------------------------------------------
*/
-void ax25_free_cb(ax25_cb *ax25)
-{
- if (ax25->digipeat != NULL) {
- kfree(ax25->digipeat);
- ax25->digipeat = NULL;
- }
-
- kfree(ax25);
-
- MOD_DEC_USE_COUNT;
-}
-
-static void ax25_free_sock(struct sock *sk)
-{
- ax25_free_cb(sk->protinfo.ax25);
-}
+static int ax25_device_event(struct notifier_block* ,unsigned long, void*);
+static int ax25_setsockopt(struct socket*, int, int, char*, int);
+static int ax25_getsockopt(struct socket*, int, int, char*, int*);
+static int ax25_listen(struct socket*, int);
+static int ax25_shutdown(struct socket*, int);
+static int ax25_create(struct socket*, int);
+static int ax25_release(struct socket *);
+static int ax25_bind(struct socket*, struct sockaddr*, int);
+static int ax25_connect(struct socket*, struct sockaddr*, int, int);
+static int ax25_accept(struct socket*, struct socket*, int);
+static int ax25_getname(struct socket*, struct sockaddr*, int*, int);
+static int ax25_sendmsg(struct socket*, struct msghdr*, int, struct scm_cookie*);
+static int ax25_recvmsg(struct socket*, struct msghdr*, int, int, struct scm_cookie*);
+static int ax25_ioctl(struct socket*, unsigned int, unsigned long);
+static int ax25_print_list(char*, off_t*, off_t, int, off_t*, ax25_cb*, char*);
+static int ax25_get_info(char*, char **, off_t, int);
+static int ax25_gifconf(struct net_device *dev, char *buf, int len);
+
+/* in ax25_ipax.c */
+int ipax_init(void);
+int ipax_cleanup(void);
/*
- * Socket removal during an interrupt is now safe.
+ * ------------------------------------------------------------------------
+ * static variables and structures
+ * ------------------------------------------------------------------------
*/
-static void ax25_remove_socket(ax25_cb *ax25)
-{
- ax25_cb *s;
- unsigned long flags;
-
- save_flags(flags); cli();
-
- if ((s = ax25_list) == ax25) {
- ax25_list = s->next;
- restore_flags(flags);
- return;
- }
-
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25) {
- s->next = ax25->next;
- restore_flags(flags);
- return;
- }
-
- s = s->next;
- }
-
- restore_flags(flags);
-}
/*
- * Kill all bound sockets on a dropped device.
+ * table of exportes symbols
*/
-static void ax25_kill_by_device(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
- ax25_cb *s;
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return;
+EXPORT_SYMBOL(ax25_find_match_for_uid);
+EXPORT_SYMBOL(ax25_find_cb);
+EXPORT_SYMBOL(ax25_linkfail_register);
+EXPORT_SYMBOL(ax25_linkfail_release);
+EXPORT_SYMBOL(ax25_listen_register);
+EXPORT_SYMBOL(ax25_listen_release);
+EXPORT_SYMBOL(ax25_protocol_register);
+EXPORT_SYMBOL(ax25_protocol_release);
+EXPORT_SYMBOL(ax25_send_frame);
+EXPORT_SYMBOL(ax25_uid_policy);
+EXPORT_SYMBOL(ax25cmp);
+EXPORT_SYMBOL(ax2asc);
+EXPORT_SYMBOL(asc2ax);
+EXPORT_SYMBOL(null_ax25_address);
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->ax25_dev == ax25_dev) {
- s->ax25_dev = NULL;
- ax25_disconnect(s, ENETUNREACH);
- }
- }
-}
+/* for debugging */
+EXPORT_SYMBOL(ax25_kill_by_device);
/*
- * Handle device status changes.
+ * This list contains all sockets that are not bound to
+ * a specific device.
*/
-static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr)
-{
- struct net_device *dev = (struct net_device *)ptr;
-
- /* Reject non AX.25 devices */
- if (dev->type != ARPHRD_AX25)
- return NOTIFY_DONE;
-
- switch (event) {
- case NETDEV_UP:
- ax25_dev_device_up(dev);
- break;
- case NETDEV_DOWN:
- ax25_kill_by_device(dev);
- ax25_rt_device_down(dev);
- ax25_dev_device_down(dev);
- break;
- default:
- break;
- }
-
- return NOTIFY_DONE;
-}
+ax25_cb *ax25_list = NULL;
/*
- * Add a socket to the bound sockets list.
+ * Protocol family registration data
*/
-void ax25_insert_socket(ax25_cb *ax25)
+static struct net_proto_family ax25_family_ops =
{
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- ax25->next = ax25_list;
- ax25_list = ax25;
-
- restore_flags(flags);
-}
+ family: PF_AX25,
+ create: ax25_create,
+};
/*
- * Find a socket that wants to accept the SABM we have just
- * received.
+ * Protocol operations for AF_AX25
*/
-struct sock *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev, int type)
-{
- unsigned long flags;
- ax25_cb *s;
-
- save_flags(flags);
- cli();
+static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = {
+ family: PF_AX25,
- for (s = ax25_list; s != NULL; s = s->next) {
- if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
- continue;
- if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) {
- /* If device is null we match any device */
- if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) {
- restore_flags(flags);
- return s->sk;
- }
- }
- }
+ release: ax25_release,
+ bind: ax25_bind,
+ connect: ax25_connect,
+ socketpair: sock_no_socketpair,
+ accept: ax25_accept,
+ getname: ax25_getname,
+ poll: datagram_poll,
+ ioctl: ax25_ioctl,
+ listen: ax25_listen,
+ shutdown: ax25_shutdown,
+ setsockopt: ax25_setsockopt,
+ getsockopt: ax25_getsockopt,
+ sendmsg: ax25_sendmsg,
+ recvmsg: ax25_recvmsg,
+ mmap: sock_no_mmap
+};
- restore_flags(flags);
- return NULL;
-}
+SOCKOPS_WRAP(ax25_proto, PF_AX25);
/*
- * Find an AX.25 socket given both ends.
+ * Device up/down notifier block
*/
-struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type)
-{
- ax25_cb *s;
- unsigned long flags;
-
- save_flags(flags);
- cli();
-
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->sk->type == type) {
- restore_flags(flags);
- return s->sk;
- }
- }
-
- restore_flags(flags);
+static struct notifier_block ax25_dev_notifier = {
+ ax25_device_event,
+ 0
+};
- return NULL;
-}
+/*
+ * ------------------------------------------------------------------------
+ * Interface implementation
+ * All public functions of this module are defined here
+ * ------------------------------------------------------------------------
+ */
/*
- * Find an AX.25 control block given both ends. It will only pick up
- * floating AX.25 control blocks or non Raw socket bound control blocks.
+ * ------------------------------------------------------------------------
+ * Init functions. called by the kernel on startup
+ * ------------------------------------------------------------------------
*/
-ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct net_device *dev)
+void __init ax25_proto_init(struct net_proto *pro)
{
- ax25_cb *s;
- unsigned long flags;
+ struct net_device *dev;
- save_flags(flags);
- cli();
+ sock_register(&ax25_family_ops);
+ ax25_packet_type.type = htons(ETH_P_AX25);
+ dev_add_pack(&ax25_packet_type);
+ register_gifconf(PF_AX25, ax25_gifconf);
+ register_netdevice_notifier(&ax25_dev_notifier);
+ ax25_register_sysctl();
+ ax25_ddi_init();
+ ax25_netlink_init();
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
- continue;
- if (s->ax25_dev == NULL)
- continue;
- if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) {
- if (digi != NULL && digi->ndigi != 0) {
- if (s->digipeat == NULL)
- continue;
- if (ax25digicmp(s->digipeat, digi) != 0)
- continue;
- } else {
- if (s->digipeat != NULL && s->digipeat->ndigi != 0)
- continue;
- }
- restore_flags(flags);
- return s;
+ proc_net_create("ax25_route", 0, ax25_rt_get_info);
+ proc_net_create("ax25", 0, ax25_get_info);
+ proc_net_create("ax25_calls", 0, ax25_cs_get_info);
+ proc_net_create("ax25_ports", 0, ax25_dev_get_info);
+ printk(KERN_INFO "NET4: AX.25 for Linux 2.4-NET4 by DG2FEF\n");
+
+#ifdef CONFIG_INET
+ ipax_init();
+#endif
+ for (dev=dev_base; dev!=NULL; dev=dev->next) {
+ if (dev->type == ARPHRD_AX25 && AX25_PTR(dev)) {
+ register_ax25device(dev);
+ if (netif_running(dev)) ax25_dev_device_up(dev);
}
}
-
- restore_flags(flags);
-
- return NULL;
}
-/*
- * Look for any matching address - RAW sockets can bind to arbitrary names
- */
-struct sock *ax25_addr_match(ax25_address *addr)
+void __exit ax25_proto_remove(void)
{
- unsigned long flags;
- ax25_cb *s;
+ int i;
+ struct net_device *dev;
- save_flags(flags);
- cli();
+ proc_net_remove("ax25_route");
+ proc_net_remove("ax25");
+ proc_net_remove("ax25_calls");
+ proc_net_remove("ax25_ports");
- for (s = ax25_list; s != NULL; s = s->next) {
- if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) {
- restore_flags(flags);
- return s->sk;
+ for (i=0; i<AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i])) {
+ if (netif_running(dev)) ax25_dev_device_down(dev);
+ unregister_ax25device(dev);
}
}
- restore_flags(flags);
-
- return NULL;
-}
-
-void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
-{
- struct sk_buff *copy;
-
- while (sk != NULL) {
- if (sk->type == SOCK_RAW &&
- sk->protocol == proto &&
- atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
- if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
- return;
+ ax25_rt_free();
+#ifdef CONFIG_INET
+ ipax_cleanup();
+#endif
+ ax25_netlink_cleanup();
+ ax25_unregister_sysctl();
- if (sock_queue_rcv_skb(sk, copy) != 0)
- kfree_skb(copy);
- }
+ unregister_netdevice_notifier(&ax25_dev_notifier);
+ register_gifconf(PF_AX25, NULL);
+ dev_remove_pack(&ax25_packet_type);
+ sock_unregister(AF_AX25);
- sk = sk->next;
- }
}
/*
- * Deferred destroy.
+ * ------------------------------------------------------------------------
+ * module registration/unregistration
+ * ------------------------------------------------------------------------
*/
-void ax25_destroy_socket(ax25_cb *);
-/*
- * Handler for deferred kills.
- */
-static void ax25_destroy_timer(unsigned long data)
-{
- ax25_destroy_socket((ax25_cb *)data);
-}
-/*
- * This is called from user mode and the timers. Thus it protects itself against
- * interrupt users but doesn't worry about being called during work.
- * Once it is removed from the queue no interrupt or bottom half will
- * touch it and we are (fairly 8-) ) safe.
- */
-void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */
+int __init ax25_init_module(void)
{
- struct sk_buff *skb;
- unsigned long flags;
-
- save_flags(flags); cli();
+ ax25_proto_init(NULL);
+ return 0;
+}
- ax25_stop_heartbeat(ax25);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_idletimer(ax25);
+MODULE_AUTHOR("Matthias Welwarsky, dg2fef@afthd.tu-darmstadt.de, dg2fef@db0ais.ampr.org");
+MODULE_DESCRIPTION("Packet Radio AX.25 Protocol stack");
+module_init(ax25_init_module);
+module_exit(ax25_proto_remove);
- ax25_remove_socket(ax25);
- ax25_clear_queues(ax25); /* Flush the queues */
- if (ax25->sk != NULL) {
- while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) {
- if (skb->sk != ax25->sk) { /* A pending connection */
- skb->sk->dead = 1; /* Queue the unaccepted socket for death */
- ax25_start_heartbeat(skb->sk->protinfo.ax25);
- skb->sk->protinfo.ax25->state = AX25_STATE_0;
- }
+/* ---------------------------------------------------------------------*/
+/*
+ * Find the AX.25 device that matches the hardware address supplied.
+ */
- kfree_skb(skb);
- }
- }
+struct net_device *ax25rtr_get_dev(ax25_address *addr)
+{
+ struct net_device *dev = NULL;
+ int i;
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->wmem_alloc) != 0 ||
- atomic_read(&ax25->sk->rmem_alloc) != 0) {
- /* Defer: outstanding buffers */
- init_timer(&ax25->timer);
- ax25->timer.expires = jiffies + 10 * HZ;
- ax25->timer.function = ax25_destroy_timer;
- ax25->timer.data = (unsigned long)ax25;
- add_timer(&ax25->timer);
- } else {
- sk_free(ax25->sk);
- }
- } else {
- ax25_free_cb(ax25);
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ dev = ax25_devices[i];
+ if (dev != NULL && !ax25cmp(addr, (ax25_address *)dev->dev_addr))
+ break;
}
+ read_unlock(&ax25_dev_lock);
- restore_flags(flags);
+ return dev;
}
+/* ---------------------------------------------------------------------*/
/*
- * dl1bke 960311: set parameters for existing AX.25 connections,
- * includes a KILL command to abort any connection.
- * VERY useful for debugging ;-)
+ * Kill all bound sockets on a dropped device.
*/
-static int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
+void ax25_kill_by_device(struct net_device *dev)
{
- struct ax25_ctl_struct ax25_ctl;
- ax25_digi digi;
- ax25_dev *ax25_dev;
- ax25_cb *ax25;
- unsigned int k;
-
- if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
- return -EFAULT;
-
- if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL)
- return -ENODEV;
-
- if (ax25_ctl.digi_count > AX25_MAX_DIGIS)
- return -EINVAL;
-
- digi.ndigi = ax25_ctl.digi_count;
- for (k = 0; k < digi.ndigi; k++)
- digi.calls[k] = ax25_ctl.digi_addr[k];
-
- if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL)
- return -ENOTCONN;
-
- switch (ax25_ctl.cmd) {
- case AX25_KILL:
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
-#ifdef CONFIG_AX25_DAMA_SLAVE
- if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
- ax25_dama_off(ax25);
-#endif
- ax25_disconnect(ax25, ENETRESET);
- break;
+ ax25_cb *s;
+ unsigned long flags;
- case AX25_WINDOW:
- if (ax25->modulus == AX25_MODULUS) {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
- return -EINVAL;
- } else {
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
- return -EINVAL;
- }
- ax25->window = ax25_ctl.arg;
- break;
-
- case AX25_T1:
- if (ax25_ctl.arg < 1)
- return -EINVAL;
- ax25->rtt = (ax25_ctl.arg * HZ) / 2;
- ax25->t1 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_T2:
- if (ax25_ctl.arg < 1)
- return -EINVAL;
- ax25->t2 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_N2:
- if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
- return -EINVAL;
- ax25->n2count = 0;
- ax25->n2 = ax25_ctl.arg;
- break;
-
- case AX25_T3:
- if (ax25_ctl.arg < 0)
- return -EINVAL;
- ax25->t3 = ax25_ctl.arg * HZ;
- break;
-
- case AX25_IDLE:
- if (ax25_ctl.arg < 0)
- return -EINVAL;
- ax25->idle = ax25_ctl.arg * 60 * HZ;
- break;
-
- case AX25_PACLEN:
- if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
- return -EINVAL;
- ax25->paclen = ax25_ctl.arg;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
+ printk(KERN_WARNING "ax25_kill_by_device(%s)\n", dev->name);
+ save_flags(flags);
+ cli();
+ for (s = ax25_dev_list(dev); s != NULL; s = ax25_dev_list(dev)) {
+ /*
+ * list structure is being modified by ax25_remove_cb,
+ * so we can not walk along ax25->next path
+ */
+ if (s->peer && s->peer->device != s->device)
+ ax25_destroy_cb(s->peer);
+ if (s->sk) {
+ ax25_remove_cb(s);
+ ax25_disconnect(s, ENETUNREACH);
+ ax25_close_socket(s->sk, ENETUNREACH);
+ } else
+ ax25_destroy_cb(s);
+ }
+ restore_flags(flags);
}
/*
- * Fill in a created AX.25 created control block with the default
- * values for a particular device.
+ * ------------------------------------------------------------------------
+ * End of public area, all private functions of this module are defined
+ * here.
+ * ------------------------------------------------------------------------
*/
-void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev)
-{
- ax25->ax25_dev = ax25_dev;
-
- if (ax25->ax25_dev != NULL) {
- ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2;
- ax25->t1 = ax25_dev->values[AX25_VALUES_T1];
- ax25->t2 = ax25_dev->values[AX25_VALUES_T2];
- ax25->t3 = ax25_dev->values[AX25_VALUES_T3];
- ax25->n2 = ax25_dev->values[AX25_VALUES_N2];
- ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN];
- ax25->idle = ax25_dev->values[AX25_VALUES_IDLE];
- ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF];
-
- if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
- }
- } else {
- ax25->rtt = AX25_DEF_T1 / 2;
- ax25->t1 = AX25_DEF_T1;
- ax25->t2 = AX25_DEF_T2;
- ax25->t3 = AX25_DEF_T3;
- ax25->n2 = AX25_DEF_N2;
- ax25->paclen = AX25_DEF_PACLEN;
- ax25->idle = AX25_DEF_IDLE;
- ax25->backoff = AX25_DEF_BACKOFF;
-
- if (AX25_DEF_AXDEFMODE) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = AX25_DEF_EWINDOW;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = AX25_DEF_WINDOW;
- }
- }
-}
+/* ---------------------------------------------------------------------*/
/*
- * Create an empty AX.25 control block.
+ * Handle device status changes.
*/
-ax25_cb *ax25_create_cb(void)
+static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr)
{
- ax25_cb *ax25;
-
- if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL)
- return NULL;
-
- MOD_INC_USE_COUNT;
-
- memset(ax25, 0x00, sizeof(*ax25));
-
- skb_queue_head_init(&ax25->write_queue);
- skb_queue_head_init(&ax25->frag_queue);
- skb_queue_head_init(&ax25->ack_queue);
- skb_queue_head_init(&ax25->reseq_queue);
-
- init_timer(&ax25->timer);
- init_timer(&ax25->t1timer);
- init_timer(&ax25->t2timer);
- init_timer(&ax25->t3timer);
- init_timer(&ax25->idletimer);
-
- ax25_fillin_cb(ax25, NULL);
+ struct net_device *dev = (struct net_device *)ptr;
- ax25->state = AX25_STATE_0;
+ /* Reject non AX.25 devices */
+ if (dev->type != ARPHRD_AX25 || !AX25_PTR(dev))
+ return NOTIFY_DONE;
- return ax25;
+ switch (event) {
+ case NETDEV_UP:
+ ax25_dev_device_up(dev);
+ break;
+ case NETDEV_DOWN:
+ ax25_dev_device_down(dev);
+ break;
+ case NETDEV_REGISTER:
+ register_ax25device(dev);
+ break;
+ case NETDEV_UNREGISTER:
+ unregister_ax25device(dev);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
}
+
+/* ---------------------------------------------------------------------*/
/*
* Handling for system calls applied via the various interfaces to an
* AX25 socket object
@@ -646,7 +370,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
switch (optname) {
case AX25_WINDOW:
- if (sk->protinfo.ax25->modulus == AX25_MODULUS) {
+ if (sk->protinfo.ax25->seqmask == AX25_SEQMASK) {
if (opt < 1 || opt > 7)
return -EINVAL;
} else {
@@ -659,14 +383,14 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
case AX25_T1:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->rtt = (opt * HZ) / 2;
- sk->protinfo.ax25->t1 = opt * HZ;
+ sk->protinfo.ax25->t1 = opt;
+ sk->protinfo.ax25->rtt = (opt * AX25_TICS) / 4;
return 0;
case AX25_T2:
- if (opt < 1)
+ if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->t2 = opt * HZ;
+ sk->protinfo.ax25->t2 = opt;
return 0;
case AX25_N2:
@@ -678,13 +402,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
case AX25_T3:
if (opt < 1)
return -EINVAL;
- sk->protinfo.ax25->t3 = opt * HZ;
+ sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ;
return 0;
case AX25_IDLE:
if (opt < 0)
return -EINVAL;
- sk->protinfo.ax25->idle = opt * 60 * HZ;
+ sk->protinfo.ax25->idle = opt * AX25_SLOWHZ;
return 0;
case AX25_BACKOFF:
@@ -694,7 +418,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
return 0;
case AX25_EXTSEQ:
- sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS;
+ sk->protinfo.ax25->seqmask = opt ? AX25_ESEQMASK : AX25_SEQMASK;
return 0;
case AX25_PIDINCL:
@@ -712,19 +436,18 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
return 0;
case SO_BINDTODEVICE:
- if (optlen > IFNAMSIZ) optlen=IFNAMSIZ;
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
if (copy_from_user(devname, optval, optlen))
return -EFAULT;
dev = dev_get_by_name(devname);
if (dev == NULL) return -ENODEV;
- if (sk->type == SOCK_SEQPACKET &&
+ if (sk->type == SOCK_SEQPACKET &&
(sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
return -EADDRNOTAVAIL;
-
- sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev);
- ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
+
+ ax25_fillin_cb(sk->protinfo.ax25, dev);
return 0;
default:
@@ -732,10 +455,12 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op
}
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
{
struct sock *sk = sock->sk;
- struct ax25_dev *ax25_dev;
+ struct net_device *dev;
char devname[IFNAMSIZ];
void *valptr;
int val = 0;
@@ -759,11 +484,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_T1:
- val = sk->protinfo.ax25->t1 / HZ;
+ val = sk->protinfo.ax25->t1;
break;
case AX25_T2:
- val = sk->protinfo.ax25->t2 / HZ;
+ val = sk->protinfo.ax25->t2;
break;
case AX25_N2:
@@ -771,11 +496,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_T3:
- val = sk->protinfo.ax25->t3 / HZ;
+ val = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
break;
case AX25_IDLE:
- val = sk->protinfo.ax25->idle / (60 * HZ);
+ val = sk->protinfo.ax25->idle / AX25_SLOWHZ;
break;
case AX25_BACKOFF:
@@ -783,7 +508,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case AX25_EXTSEQ:
- val = (sk->protinfo.ax25->modulus == AX25_EMODULUS);
+ val = (sk->protinfo.ax25->seqmask == AX25_ESEQMASK);
break;
case AX25_PIDINCL:
@@ -799,11 +524,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
break;
case SO_BINDTODEVICE:
- ax25_dev = sk->protinfo.ax25->ax25_dev;
+ dev = sk->protinfo.ax25->device;
- if (ax25_dev != NULL && ax25_dev->dev != NULL) {
- strncpy(devname, ax25_dev->dev->name, IFNAMSIZ);
- length = min(strlen(ax25_dev->dev->name)+1, maxlen);
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
devname[length-1] = '\0';
} else {
*devname = '\0';
@@ -820,68 +545,84 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op
if (put_user(length, optlen))
return -EFAULT;
- return copy_to_user(optval, valptr, length) ? -EFAULT : 0;
+ if (copy_to_user(optval, valptr, length))
+ return -EFAULT;
+
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
- if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) {
+ if (sk->type != SOCK_SEQPACKET)
+ return -EOPNOTSUPP;
+
+ if (sk->state != TCP_LISTEN) {
+ /*
+ * POSIX VIOLATION: according to listen(2) the call can
+ * never return EADDRINUSE. bind(2) should have done this.
+ * However, things are different with AX.25. You are
+ * _required_ to bind() before connect() to set the
+ * source callsign of the outgoing connection. But as you
+ * may open multiple connections at one time with the
+ * same source callsign, you cannot perform this check
+ * within bind(). And as I like to have descriptive errors,
+ * EADDRINUSE is perfect to be returned here.
+ */
+ if (ax25_find_listener(&sk->protinfo.ax25->addr.src, sk->protinfo.ax25->iamdigi, sk->protinfo.ax25->device))
+ return -EADDRINUSE;
+
+ ax25_insert_cb(sk->protinfo.ax25);
sk->max_ack_backlog = backlog;
sk->state = TCP_LISTEN;
return 0;
}
- return -EOPNOTSUPP;
+ return -EINVAL;
}
-int ax25_create(struct socket *sock, int protocol)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_create(struct socket *sock, int protocol)
{
struct sock *sk;
ax25_cb *ax25;
switch (sock->type) {
- case SOCK_DGRAM:
- if (protocol == 0 || protocol == PF_AX25)
- protocol = AX25_P_TEXT;
+ case SOCK_DGRAM:
+ if (protocol == 0 || protocol == PF_AX25)
+ protocol = AX25_P_TEXT;
+ break;
+ case SOCK_SEQPACKET:
+ switch (protocol) {
+ case 0:
+ case PF_AX25: /* For CLX */
+ protocol = AX25_P_TEXT;
break;
- case SOCK_SEQPACKET:
- switch (protocol) {
- case 0:
- case PF_AX25: /* For CLX */
- protocol = AX25_P_TEXT;
- break;
- case AX25_P_SEGMENT:
+ case AX25_P_SEGMENT:
#ifdef CONFIG_INET
- case AX25_P_ARP:
- case AX25_P_IP:
+ case AX25_P_ARP:
#endif
#ifdef CONFIG_NETROM
- case AX25_P_NETROM:
+ case AX25_P_NETROM:
#endif
#ifdef CONFIG_ROSE
- case AX25_P_ROSE:
-#endif
- return -ESOCKTNOSUPPORT;
-#ifdef CONFIG_NETROM_MODULE
- case AX25_P_NETROM:
- if (ax25_protocol_is_registered(AX25_P_NETROM))
- return -ESOCKTNOSUPPORT;
+ case AX25_P_ROSE:
#endif
-#ifdef CONFIG_ROSE_MODULE
- case AX25_P_ROSE:
- if (ax25_protocol_is_registered(AX25_P_ROSE))
- return -ESOCKTNOSUPPORT;
-#endif
- default:
- break;
- }
- break;
- case SOCK_RAW:
- break;
- default:
return -ESOCKTNOSUPPORT;
+
+ default:
+ if (ax25_protocol_is_registered(protocol))
+ return -ESOCKTNOSUPPORT;
+ }
+ break;
+ case SOCK_RAW:
+ break;
+ default:
+ return -ESOCKTNOSUPPORT;
}
if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
@@ -904,226 +645,162 @@ int ax25_create(struct socket *sock, int protocol)
return 0;
}
-struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev)
+static int ax25_release(struct socket *sock)
{
- struct sock *sk;
+ struct sock *sk = sock->sk;
ax25_cb *ax25;
- if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
- return NULL;
+ if (sk == NULL)
+ return 0;
- if ((ax25 = ax25_create_cb()) == NULL) {
- sk_free(sk);
- return NULL;
- }
+ ax25 = sk->protinfo.ax25;
- switch (osk->type) {
- case SOCK_DGRAM:
- break;
- case SOCK_SEQPACKET:
- break;
- default:
- sk_free(sk);
- ax25_free_cb(ax25);
- return NULL;
- }
+ sk->state = TCP_CLOSE;
+ sk->shutdown = SHUTDOWN_MASK;
+ /*
+ * don't wake me, I'm dying: this one has cost me nerves a bit.
+ * Seems that we should not attempt to wake an application
+ * that is currently exiting, which is exactly what state_change()
+ * does. It results in calling __wake_up() with invalid arguments
+ */
+ if (!(current->flags & PF_EXITING))
+ sk->state_change(sk);
+ sk->dead = 1;
- sock_init_data(NULL, sk);
+ if (sk->type == SOCK_STREAM || sk->type == SOCK_SEQPACKET) {
+ switch (ax25->state) {
+ default:
+ ax25_remove_cb(ax25);
+ break;
- sk->destruct = ax25_free_sock;
- sk->type = osk->type;
- sk->socket = osk->socket;
- sk->priority = osk->priority;
- sk->protocol = osk->protocol;
- sk->rcvbuf = osk->rcvbuf;
- sk->sndbuf = osk->sndbuf;
- sk->debug = osk->debug;
- sk->state = TCP_ESTABLISHED;
- sk->sleep = osk->sleep;
- sk->zapped = osk->zapped;
-
- ax25->modulus = osk->protinfo.ax25->modulus;
- ax25->backoff = osk->protinfo.ax25->backoff;
- ax25->pidincl = osk->protinfo.ax25->pidincl;
- ax25->iamdigi = osk->protinfo.ax25->iamdigi;
- ax25->rtt = osk->protinfo.ax25->rtt;
- ax25->t1 = osk->protinfo.ax25->t1;
- ax25->t2 = osk->protinfo.ax25->t2;
- ax25->t3 = osk->protinfo.ax25->t3;
- ax25->n2 = osk->protinfo.ax25->n2;
- ax25->idle = osk->protinfo.ax25->idle;
- ax25->paclen = osk->protinfo.ax25->paclen;
- ax25->window = osk->protinfo.ax25->window;
-
- ax25->ax25_dev = ax25_dev;
- ax25->source_addr = osk->protinfo.ax25->source_addr;
-
- if (osk->protinfo.ax25->digipeat != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- sk_free(sk);
- return NULL;
+ case AX25_STATE_3: /* connected */
+ case AX25_STATE_4: /* timer recovery */
+ ax25_set_cond(ax25, AX25_COND_RELEASE);
+ break;
}
-
- memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi));
}
- sk->protinfo.ax25 = ax25;
- ax25->sk = sk;
-
- return sk;
-}
-
-static int ax25_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
-
- if (sk == NULL) return 0;
-
- if (sk->type == SOCK_SEQPACKET) {
- switch (sk->protinfo.ax25->state) {
- case AX25_STATE_0:
- ax25_disconnect(sk->protinfo.ax25, 0);
- ax25_destroy_socket(sk->protinfo.ax25);
- break;
-
- case AX25_STATE_1:
- case AX25_STATE_2:
- ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(sk->protinfo.ax25, 0);
- ax25_destroy_socket(sk->protinfo.ax25);
- break;
-
- case AX25_STATE_3:
- case AX25_STATE_4:
- ax25_clear_queues(sk->protinfo.ax25);
- sk->protinfo.ax25->n2count = 0;
- switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_stop_t2timer(sk->protinfo.ax25);
- ax25_stop_t3timer(sk->protinfo.ax25);
- ax25_stop_idletimer(sk->protinfo.ax25);
- break;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- ax25_stop_t3timer(sk->protinfo.ax25);
- ax25_stop_idletimer(sk->protinfo.ax25);
- break;
-#endif
- }
- ax25_calculate_t1(sk->protinfo.ax25);
- ax25_start_t1timer(sk->protinfo.ax25);
- sk->protinfo.ax25->state = AX25_STATE_2;
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- sk->destroy = 1;
- break;
-
- default:
- break;
- }
+ sk->protinfo.ax25 = NULL;
+ if (ax25->inserted && ax25->device != NULL) {
+ ax25->killtimer = 0;
+ ax25->sk = NULL;
} else {
- sk->state = TCP_CLOSE;
- sk->shutdown |= SEND_SHUTDOWN;
- sk->state_change(sk);
- sk->dead = 1;
- ax25_destroy_socket(sk->protinfo.ax25);
+ ax25_destroy_cb(ax25);
}
-
- sock->sk = NULL;
- sk->socket = NULL; /* Not used, but we should do this */
-
+ ax25_destroy_socket(sk);
return 0;
}
+/* ---------------------------------------------------------------------*/
/*
- * We support a funny extension here so you can (as root) give any callsign
- * digipeated via a local address as source. This hack is obsolete now
- * that we've implemented support for SO_BINDTODEVICE. It is however small
- * and trivially backward compatible.
+ * Former semantics:
+ *
+ * - struct sockaddr_ax25 contains the interface callsign, outgoing
+ * user connects either get the interface callsign or the one
+ * provided by the uid/callsign translation table for the source
+ * address
+ *
+ * - struct full_sockaddr_ax25 provides the interface callsign as
+ * the first digipeater, fsa.fsa_ax25call provides the source
+ * address for the connection.
+ *
+ * New semantics:
+ *
+ * We now have SO_BINDTODEVICE, ax25_bind (should) only set the
+ * source address. Thus we'll allow the "bind to device callsign
+ * provided with the digipeater field" hack only for backward
+ * compatibility.
+ *
+ * NB: I don't follow Matthias' and Jens' patch here as I do
+ * plan to allow multiple callsigns for one uid (including
+ * multiple SSIDs) and assigning user callsigns per interface.
*/
+
static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
- ax25_address *call;
- ax25_dev *ax25_dev = NULL;
+ struct net_device *dev;
+ ax25_address *call = NULL;
+ /* already bound */
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len != sizeof(struct sockaddr_ax25) &&
- addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
-
- if (addr->fsa_ax25.sax25_family != AF_AX25)
+ if (addr_len != sizeof(struct sockaddr_ax25) &&
+ addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
- call = ax25_findbyuid(current->euid);
- if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN))
- return -EACCES;
-
- if (call == NULL)
- sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call;
- else
- sk->protinfo.ax25->source_addr = *call;
+ /* wrong family */
+ if (addr->fsax25_family != AF_AX25)
+ return -EINVAL;
/*
- * User already set interface with SO_BINDTODEVICE
+ * User did not set interface with SO_BINDTODEVICE
+ * thus we'll use the compatibility code
*/
- if (sk->protinfo.ax25->ax25_dev != NULL)
- goto done;
+ dev = sk->protinfo.ax25->device;
+ if (dev == NULL) {
+ /* Try to find the interface... */
+ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) {
+ /* device callsign provided... */
+ if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
+ (dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ } else {
+ /* addr->fsax25_call is device callsign */
+ if ((dev = ax25rtr_get_dev(&addr->fsax25_call)) == NULL)
+ return -EADDRNOTAVAIL;
+ }
+
+ ax25_fillin_cb(sk->protinfo.ax25, dev);
+ }
- if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) {
- if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 &&
- (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL)
- return -EADDRNOTAVAIL;
- } else {
- if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL)
- return -EADDRNOTAVAIL;
+ /* root can do whatever (s)he likes, but anyone else... */
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ call = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name);
+ if (call == NULL && ax25_uid_policy)
+ return -EACCES;
}
- if (ax25_dev != NULL)
- ax25_fillin_cb(sk->protinfo.ax25, ax25_dev);
+ if (call == NULL) call = &addr->fsax25_call;
-done:
- ax25_insert_socket(sk->protinfo.ax25);
+ sk->protinfo.ax25->addr.src = *call;
+ sk->protinfo.ax25->addr.dcount = 0;
+ sk->protinfo.ax25->addr.lastrepeat = -1;
+
+// ax25_insert_socket(sk->protinfo.ax25); /* FIXME: gone with Matthias' patch, intentionally? */
sk->zapped = 0;
return 0;
}
-/*
- * FIXME: nonblock behaviour looks like it may have a bug.
- */
-static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_connect(struct socket *sock, struct sockaddr *uaddr,
+ int addr_len, int flags)
{
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
- ax25_digi *digi = NULL;
+ ax25_addr_t *addr;
+ ax25_address *dest;
int ct = 0, err;
/* deal with restarts */
if (sock->state == SS_CONNECTING) {
switch (sk->state) {
case TCP_SYN_SENT: /* still trying */
- return -EINPROGRESS;
+ if (!(flags & O_NONBLOCK))
+ goto wait_for_con;
+ return -EALREADY;
case TCP_ESTABLISHED: /* connection established */
sock->state = SS_CONNECTED;
return 0;
case TCP_CLOSE: /* connection refused */
+ case TCP_CLOSE_WAIT:
sock->state = SS_UNCONNECTED;
return -ECONNREFUSED;
}
@@ -1139,118 +816,77 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
* some sanity checks. code further down depends on this
*/
- if (addr_len == sizeof(struct sockaddr_ax25)) {
- /* support for this will go away in early 2.5.x */
+ if (addr_len == sizeof(struct sockaddr_ax25))
printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n",
current->comm);
- }
- else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
-
- if (fsa->fsa_ax25.sax25_family != AF_AX25)
+ else if (addr_len != sizeof(struct full_sockaddr_ax25))
return -EINVAL;
- if (sk->protinfo.ax25->digipeat != NULL) {
- kfree(sk->protinfo.ax25->digipeat);
- sk->protinfo.ax25->digipeat = NULL;
- }
-
/*
* Handle digi-peaters to be used.
*/
- if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) {
+ SOCK_DEBUG(sk, "ax25_connect: ndigi=%d\n", fsa->fsax25_ndigis);
+
+ addr = &sk->protinfo.ax25->addr;
+ addr->dcount = 0;
+ addr->lastrepeat = -1;
+ if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsax25_ndigis != 0) {
/* Valid number of digipeaters ? */
- if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS)
+ if (fsa->fsax25_ndigis < 0 || fsa->fsax25_ndigis > AX25_MAX_DIGIS)
return -EINVAL;
- if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL)
- return -ENOBUFS;
+ addr->dcount = fsa->fsax25_ndigis;
- digi->ndigi = fsa->fsa_ax25.sax25_ndigis;
- digi->lastrepeat = -1;
-
- while (ct < fsa->fsa_ax25.sax25_ndigis) {
- if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) {
- digi->repeated[ct] = 1;
- digi->lastrepeat = ct;
- } else {
- digi->repeated[ct] = 0;
- }
- digi->calls[ct] = fsa->fsa_digipeater[ct];
+ while (ct < fsa->fsax25_ndigis) {
+ if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi)
+ addr->lastrepeat = ct;
+ addr->digipeater[ct] = fsa->fsa_digipeater[ct];
ct++;
}
+
+ /* where to go next? either to next digipeater in path ...*/
+ dest = &addr->digipeater[addr->lastrepeat+1];
+ } else {
+ /* ... or directly to the destination */
+ dest = &fsa->fsax25_call;
}
- /*
- * Must bind first - autobinding in this may or may not work. If
- * the socket is already bound, check to see if the device has
- * been filled in, error if it hasn't.
- */
- if (sk->zapped) {
- /* check if we can remove this feature. It is broken. */
- printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n",
- current->comm);
- if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0)
+ if (sk->protinfo.ax25->device == NULL) {
+ if ((err = ax25_rt_fillin_dev(sk->protinfo.ax25, dest)) < 0)
return err;
- ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev);
- ax25_insert_socket(sk->protinfo.ax25);
- } else {
- if (sk->protinfo.ax25->ax25_dev == NULL)
- return -EHOSTUNREACH;
+ SOCK_DEBUG(sk, "ax25_connect: device filled in\n");
}
+ addr->dest = fsa->fsax25_call;
- if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) {
- if (digi != NULL) kfree(digi);
- return -EADDRINUSE; /* Already such a connection */
+ if (sk->type == SOCK_SEQPACKET) {
+ ax25_cb* ax25 = ax25_find_cb(addr, sk->protinfo.ax25->device);
+ if (ax25) {
+ if (ax25->state != AX25_STATE_0)
+ return -EADDRINUSE; /* Already such a connection */
+ ax25_destroy_cb(ax25);
+ }
}
- sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call;
- sk->protinfo.ax25->digipeat = digi;
+ ax25_insert_cb(sk->protinfo.ax25);
/* First the easy one */
- if (sk->type != SOCK_SEQPACKET) {
+ if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM) {
sock->state = SS_CONNECTED;
sk->state = TCP_ESTABLISHED;
return 0;
}
/* Move to connecting socket, ax.25 lapb WAIT_UA.. */
- sock->state = SS_CONNECTING;
- sk->state = TCP_SYN_SENT;
-
- switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_establish_data_link(sk->protinfo.ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- sk->protinfo.ax25->modulus = AX25_MODULUS;
- sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- if (sk->protinfo.ax25->ax25_dev->dama.slave)
- ax25_ds_establish_data_link(sk->protinfo.ax25);
- else
- ax25_std_establish_data_link(sk->protinfo.ax25);
- break;
-#endif
- }
-
- sk->protinfo.ax25->state = AX25_STATE_1;
-
- ax25_start_heartbeat(sk->protinfo.ax25);
+ sock->state = SS_CONNECTING;
+ sk->state = TCP_SYN_SENT;
+ /* Start going SABM SABM until a UA or a give up and DM */
+ ax25_establish_data_link(sk->protinfo.ax25);
/* Now the loop */
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
return -EINPROGRESS;
+ wait_for_con:
cli(); /* To avoid races on the sleep */
/* A DM or timeout will go to closed, a UA will go to ABM */
@@ -1270,12 +906,11 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
}
sock->state = SS_CONNECTED;
-
sti();
-
return 0;
}
+/* ---------------------------------------------------------------------*/
static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
{
@@ -1310,12 +945,14 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
}
} while (skb == NULL);
- newsk = skb->sk;
- newsk->pair = NULL;
+ newsk = skb->sk;
+ newsk->pair = NULL;
newsk->socket = newsock;
newsk->sleep = &newsock->wait;
/* Now attach up the new socket */
+ skb->sk = NULL;
+ skb->destructor = NULL;
kfree_skb(skb);
sk->ack_backlog--;
newsock->sk = newsk;
@@ -1324,132 +961,108 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags)
return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
{
+ struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr;
struct sock *sk = sock->sk;
- struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr;
- unsigned char ndigi, i;
+ unsigned char dcount;
- if (peer != 0) {
+ if (peer) {
if (sk->state != TCP_ESTABLISHED)
return -ENOTCONN;
- fsa->fsa_ax25.sax25_family = AF_AX25;
- fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr;
- fsa->fsa_ax25.sax25_ndigis = 0;
-
- if (sk->protinfo.ax25->digipeat != NULL) {
- ndigi = sk->protinfo.ax25->digipeat->ndigi;
- fsa->fsa_ax25.sax25_ndigis = ndigi;
- for (i = 0; i < ndigi; i++)
- fsa->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i];
- }
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_call = sk->protinfo.ax25->addr.dest;
+ dcount = sax->fsax25_ndigis = sk->protinfo.ax25->addr.dcount;
+ memcpy(sax->fsa_digipeater, sk->protinfo.ax25->addr.digipeater, dcount * AX25_ADDR_LEN);
} else {
- fsa->fsa_ax25.sax25_family = AF_AX25;
- fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr;
- fsa->fsa_ax25.sax25_ndigis = 1;
- if (sk->protinfo.ax25->ax25_dev != NULL) {
- memcpy(&fsa->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN);
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_call = sk->protinfo.ax25->addr.src;
+
+ if (sk->protinfo.ax25->device != NULL) {
+ sax->fsax25_ndigis = 1;
+ memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN);
} else {
- fsa->fsa_digipeater[0] = null_ax25_address;
+ sax->fsax25_ndigis = 0;
}
}
*uaddr_len = sizeof (struct full_sockaddr_ax25);
return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name;
int err;
- struct sockaddr_ax25 sax;
struct sk_buff *skb;
unsigned char *asmptr;
int size;
- ax25_digi *dp;
- ax25_digi dtmp;
int lv;
int addr_len = msg->msg_namelen;
+ ax25_addr_t addr;
if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR))
return -EINVAL;
+ /* socket must be bound to a name */
if (sk->zapped)
return -EADDRNOTAVAIL;
+ /* socket ist shut down */
if (sk->shutdown & SEND_SHUTDOWN) {
send_sig(SIGPIPE, current, 0);
return -EPIPE;
}
- if (sk->protinfo.ax25->ax25_dev == NULL)
+ if (sk->protinfo.ax25->device == NULL)
return -ENETUNREACH;
- if (usax != NULL) {
+ if (addr_len != 0) {
if (usax->sax25_family != AF_AX25)
return -EINVAL;
+ if (sk->type == SOCK_SEQPACKET)
+ return -EISCONN;
- if (addr_len == sizeof(struct sockaddr_ax25)) {
+ if (addr_len != sizeof(struct sockaddr_ax25)) {
printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n",
current->comm);
- }
- else if (addr_len != sizeof(struct full_sockaddr_ax25)) {
- /* support for old structure may go away some time */
- if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) ||
- (addr_len > sizeof(struct full_sockaddr_ax25)))
- return -EINVAL;
-
- printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n",
- current->comm);
- }
+ } else if (addr_len != sizeof(struct full_sockaddr_ax25))
+ return -EINVAL;
if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) {
- int ct = 0;
struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax;
+ int ct;
/* Valid number of digipeaters ? */
- if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS)
+ if (usax->sax25_ndigis < 0 || usax->sax25_ndigis > AX25_MAX_DIGIS)
return -EINVAL;
- dtmp.ndigi = usax->sax25_ndigis;
-
- while (ct < usax->sax25_ndigis) {
- dtmp.repeated[ct] = 0;
- dtmp.calls[ct] = fsa->fsa_digipeater[ct];
- ct++;
- }
-
- dtmp.lastrepeat = 0;
+ for (ct = 0; ct < usax->sax25_ndigis; ct++)
+ addr.digipeater[ct] = fsa->fsa_digipeater[ct];
+ addr.lastrepeat = -1;
}
-
- sax = *usax;
- if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0)
- return -EISCONN;
- if (usax->sax25_ndigis == 0)
- dp = NULL;
- else
- dp = &dtmp;
+ addr.dcount = usax->sax25_ndigis;
+ addr.dest = usax->sax25_call;
} else {
- /*
- * FIXME: 1003.1g - if the socket is like this because
- * it has become closed (not started closed) and is VC
- * we ought to SIGPIPE, EPIPE
- */
- if (sk->state != TCP_ESTABLISHED)
+ if (sk->state != TCP_ESTABLISHED) {
+ if (sk->dead) {
+ send_sig(SIGPIPE, current, 0);
+ return -EPIPE;
+ }
return -ENOTCONN;
- sax.sax25_family = AF_AX25;
- sax.sax25_call = sk->protinfo.ax25->dest_addr;
- dp = sk->protinfo.ax25->digipeat;
+ }
+ addr = sk->protinfo.ax25->addr;
}
- SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n");
-
- /* Build a packet */
- SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n");
+ SOCK_DEBUG(sk, "AX.25: sendto: Addresses built, building packet.\n");
/* Assume the worst case */
- size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN;
+ size = len + 3 + ax25_sizeof_addr(&addr) + AX25_BPQ_HEADER_LEN;
if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL)
return err;
@@ -1463,10 +1076,8 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
skb->nh.raw = skb->data;
/* Add the PID if one is not supplied by the user in the skb */
- if (!sk->protinfo.ax25->pidincl) {
- asmptr = skb_push(skb, 1);
- *asmptr = sk->protocol;
- }
+ if (!sk->protinfo.ax25->pidincl)
+ *skb_push(skb, 1) = sk->protocol;
SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n");
@@ -1476,44 +1087,38 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct
kfree_skb(skb);
return -ENOTCONN;
}
-
ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */
-
return len;
- } else {
- asmptr = skb_push(skb, 1 + ax25_addr_size(dp));
-
- SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp);
-
- if (dp != NULL)
- SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi);
-
- /* Build an AX.25 header */
- asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS));
+ }
- SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
+ addr.src = sk->protinfo.ax25->addr.src;
+ asmptr = skb_push(skb, ax25_sizeof_addr(&addr)+1);
- skb->h.raw = asmptr;
+ SOCK_DEBUG(sk, "Num digipeaters=%d\n", addr.dcount);
- SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
+ /* Build an AX.25 header */
+ lv = ax25_build_addr(asmptr, &addr, AX25_COMMAND, AX25_SEQMASK);
+ asmptr += lv;
- *asmptr = AX25_UI;
+ SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv);
+ SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr);
- /* Datagram frames go straight out of the door as UI */
- skb->dev = sk->protinfo.ax25->ax25_dev->dev;
+ *asmptr = AX25_UI;
- ax25_queue_xmit(skb);
+ /* Datagram frames go straight out of the door as UI */
+ ax25_send_unproto(skb, sk->protinfo.ax25->device);
- return len;
- }
+ return len;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm)
{
struct sock *sk = sock->sk;
int copied;
struct sk_buff *skb;
- int er;
+ int err;
/*
* This works for seqpacket too. The receiver has ordered the
@@ -1523,8 +1128,8 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
return -ENOTCONN;
/* Now we can treat all alike */
- if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL)
- return er;
+ if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL)
+ return err;
if (!sk->protinfo.ax25->pidincl)
skb_pull(skb, 1); /* Remove PID */
@@ -1540,25 +1145,19 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (msg->msg_namelen != 0) {
- struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name;
- ax25_digi digi;
- ax25_address dest;
+ struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)msg->msg_name;
+ ax25_pktinfo pkt;
- ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, NULL, &dest, &digi, NULL, NULL);
+ ax25_parse_addr(skb->mac.raw, skb->data-skb->mac.raw, &pkt);
- sax->sax25_family = AF_AX25;
- /* We set this correctly, even though we may not let the
- application know the digi calls further down (because it
- did NOT ask to know them). This could get political... **/
- sax->sax25_ndigis = digi.ndigi;
- sax->sax25_call = dest;
+ sax->fsax25_family = AF_AX25;
+ sax->fsax25_ndigis = pkt.addr.dcount;
+ sax->fsax25_call = pkt.addr.dest;
- if (sax->sax25_ndigis != 0) {
+ if (sax->fsax25_ndigis != 0) {
int ct;
- struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax;
-
- for (ct = 0; ct < digi.ndigi; ct++)
- fsa->fsa_digipeater[ct] = digi.calls[ct];
+ for (ct = 0; ct < pkt.addr.dcount; ct++)
+ sax->fsa_digipeater[ct] = pkt.addr.digipeater[ct];
}
msg->msg_namelen = sizeof(struct full_sockaddr_ax25);
}
@@ -1568,12 +1167,31 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f
return copied;
}
-static int ax25_shutdown(struct socket *sk, int how)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_shutdown(struct socket *sock, int how)
{
- /* FIXME - generate DM and RNR states */
- return -EOPNOTSUPP;
+ switch (how) {
+ case 0:
+ sock->sk->shutdown = RCV_SHUTDOWN;
+ break;
+
+ case 1:
+ sock->sk->shutdown = SEND_SHUTDOWN;
+ break;
+
+ case 2:
+ sock->sk->shutdown = SHUTDOWN_MASK;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
@@ -1584,7 +1202,9 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
amount = sk->sndbuf - atomic_read(&sk->wmem_alloc);
if (amount < 0)
amount = 0;
- return put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
+ return 0;
}
case TIOCINQ: {
@@ -1593,15 +1213,19 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
/* These two are safe on a single CPU system as only user tasks fiddle here */
if ((skb = skb_peek(&sk->receive_queue)) != NULL)
amount = skb->len;
- return put_user(amount, (int *)arg);
+ if (put_user(amount, (int *)arg))
+ return -EFAULT;
+ return 0;
}
case SIOCGSTAMP:
if (sk != NULL) {
if (sk->stamp.tv_sec == 0)
return -ENOENT;
- return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0;
- }
+ if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)))
+ return -EFAULT;
+ return 0;
+ }
return -EINVAL;
case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */
@@ -1637,29 +1261,31 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return -EPERM;
return ax25_ctl_ioctl(cmd, (void *)arg);
- case SIOCAX25GETINFO:
+ case SIOCAX25GETINFO:
case SIOCAX25GETINFOOLD: {
struct ax25_info_struct ax25_info;
- ax25_info.t1 = sk->protinfo.ax25->t1 / HZ;
- ax25_info.t2 = sk->protinfo.ax25->t2 / HZ;
- ax25_info.t3 = sk->protinfo.ax25->t3 / HZ;
- ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ);
+ read_lock(&sk->protinfo.ax25->timer_lock);
+ ax25_info.t1 = sk->protinfo.ax25->t1;
+ ax25_info.t2 = sk->protinfo.ax25->t2;
+ ax25_info.t3 = sk->protinfo.ax25->t3 / AX25_SLOWHZ;
+ ax25_info.idle = sk->protinfo.ax25->idle / AX25_SLOWHZ;
ax25_info.n2 = sk->protinfo.ax25->n2;
- ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ;
- ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ;
- ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ;
- ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ);
+ ax25_info.t1timer = sk->protinfo.ax25->wrt_timer;
+ ax25_info.t3timer = sk->protinfo.ax25->wrt_timer / AX25_SLOWHZ;
+ ax25_info.t2timer = sk->protinfo.ax25->ack_timer;
+ ax25_info.idletimer = sk->protinfo.ax25->idletimer / AX25_SLOWHZ;
ax25_info.n2count = sk->protinfo.ax25->n2count;
ax25_info.state = sk->protinfo.ax25->state;
ax25_info.rcv_q = atomic_read(&sk->rmem_alloc);
ax25_info.snd_q = atomic_read(&sk->wmem_alloc);
ax25_info.vs = sk->protinfo.ax25->vs;
- ax25_info.vr = sk->protinfo.ax25->vr;
- ax25_info.va = sk->protinfo.ax25->va;
- ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */
+ ax25_info.vr = sk->protinfo.ax25->vr;
+ ax25_info.va = sk->protinfo.ax25->va;
+ ax25_info.vs_max = sk->protinfo.ax25->vs_max;
ax25_info.paclen = sk->protinfo.ax25->paclen;
ax25_info.window = sk->protinfo.ax25->window;
+ read_unlock(&sk->protinfo.ax25->timer_lock);
/* old structure? */
if (cmd == SIOCAX25GETINFOOLD) {
@@ -1670,12 +1296,14 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
warned=1;
}
- if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated)))
+ if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated)))
return -EFAULT;
} else {
- if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct)))
+ if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct)))
return -EINVAL;
- }
+ }
+
+
return 0;
}
@@ -1709,48 +1337,47 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
return 0;
}
-static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
-{
- ax25_cb *ax25;
- int k;
- int len = 0;
- off_t pos = 0;
- off_t begin = 0;
+/* ---------------------------------------------------------------------*/
- cli();
+static int ax25_print_list(char *buffer, off_t *begin, off_t offset, int length, off_t *pos, ax25_cb *ax25, char *devname) {
+ int len = 0;
/*
* New format:
* magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode
*/
-
- for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) {
+
+ for ( ; ax25 != NULL; ax25 = ax25->next) {
+ int k;
+
len += sprintf(buffer+len, "%8.8lx %s %s%s ",
- (long) ax25,
- ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name,
- ax2asc(&ax25->source_addr),
- ax25->iamdigi? "*":"");
-
- len += sprintf(buffer+len, "%s", ax2asc(&ax25->dest_addr));
-
- for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) {
+ (long) ax25,
+ devname,
+ ax2asc(&ax25->addr.src),
+ ax25->iamdigi? "*":"");
+
+ len += sprintf(buffer+len, "%s", ax2asc(&ax25->addr.dest));
+
+ for (k=0; k < ax25->addr.dcount; k++) {
len += sprintf(buffer+len, ",%s%s",
- ax2asc(&ax25->digipeat->calls[k]),
- ax25->digipeat->repeated[k]? "*":"");
+ ax2asc(&ax25->addr.digipeater[k]),
+ ax25->addr.lastrepeat == k ? "*":"");
}
-
- len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d",
+
+ read_lock(&ax25->timer_lock);
+ len += sprintf(buffer+len, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
ax25->state,
ax25->vs, ax25->vr, ax25->va,
- ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ,
- ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ,
- ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ,
- ax25_display_timer(&ax25->idletimer) / (60 * HZ),
- ax25->idle / (60 * HZ),
+ ax25->wrt_timer , ax25->t1,
+ ax25->ack_timer , ax25->t2,
+ ax25->t3 , ax25->t3,
+ ax25->idletimer / AX25_SLOWHZ,
+ ax25->idle / AX25_SLOWHZ,
ax25->n2count, ax25->n2,
- ax25->rtt / HZ,
+ ax25->rtt / AX25_TICS,
ax25->window,
ax25->paclen);
+ read_unlock(&ax25->timer_lock);
if (ax25->sk != NULL) {
len += sprintf(buffer + len, " %d %d %ld\n",
@@ -1761,121 +1388,70 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
len += sprintf(buffer + len, " * * *\n");
}
- pos = begin + len;
+ *pos = *begin + len;
- if (pos < offset) {
- len = 0;
- begin = pos;
+ if (*pos < offset) {
+ len = 0;
+ *begin = *pos;
}
- if (pos > offset + length)
+ if (*pos > offset + length)
break;
}
- sti();
-
- *start = buffer + (offset - begin);
- len -= (offset - begin);
-
- if (len > length) len = length;
-
- return(len);
+ return len;
}
-static struct net_proto_family ax25_family_ops = {
- family: PF_AX25,
- create: ax25_create,
-};
-static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = {
- family: PF_AX25,
+/* ---------------------------------------------------------------------*/
- release: ax25_release,
- bind: ax25_bind,
- connect: ax25_connect,
- socketpair: sock_no_socketpair,
- accept: ax25_accept,
- getname: ax25_getname,
- poll: datagram_poll,
- ioctl: ax25_ioctl,
- listen: ax25_listen,
- shutdown: ax25_shutdown,
- setsockopt: ax25_setsockopt,
- getsockopt: ax25_getsockopt,
- sendmsg: ax25_sendmsg,
- recvmsg: ax25_recvmsg,
- mmap: sock_no_mmap,
-};
+static int ax25_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ struct net_device *dev;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = begin;
+ int i;
-#include <linux/smp_lock.h>
-SOCKOPS_WRAP(ax25_proto, PF_AX25);
+ read_lock(&ax25_dev_lock);
-/*
- * Called by socket.c on kernel start up
- */
-static struct packet_type ax25_packet_type = {
- type: __constant_htons(ETH_P_AX25),
- func: ax25_kiss_rcv,
-};
+ len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_list, "*");
-static struct notifier_block ax25_dev_notifier = {
- notifier_call: ax25_device_event,
-};
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ dev = ax25_devices[i];
+ if (!dev || !(dev->flags & IFF_UP))
+ continue;
-EXPORT_SYMBOL(ax25_encapsulate);
-EXPORT_SYMBOL(ax25_rebuild_header);
-EXPORT_SYMBOL(ax25_findbyuid);
-EXPORT_SYMBOL(ax25_find_cb);
-EXPORT_SYMBOL(ax25_linkfail_register);
-EXPORT_SYMBOL(ax25_linkfail_release);
-EXPORT_SYMBOL(ax25_listen_register);
-EXPORT_SYMBOL(ax25_listen_release);
-EXPORT_SYMBOL(ax25_protocol_register);
-EXPORT_SYMBOL(ax25_protocol_release);
-EXPORT_SYMBOL(ax25_send_frame);
-EXPORT_SYMBOL(ax25_uid_policy);
-EXPORT_SYMBOL(ax25cmp);
-EXPORT_SYMBOL(ax2asc);
-EXPORT_SYMBOL(asc2ax);
-EXPORT_SYMBOL(null_ax25_address);
-EXPORT_SYMBOL(ax25_display_timer);
+ len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_dev_list(dev), dev->name);
+ }
-static const char banner[] __initdata = KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n";
+ read_unlock(&ax25_dev_lock);
-static int __init ax25_init(void)
-{
- sock_register(&ax25_family_ops);
- dev_add_pack(&ax25_packet_type);
- register_netdevice_notifier(&ax25_dev_notifier);
- ax25_register_sysctl();
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
- proc_net_create("ax25_route", 0, ax25_rt_get_info);
- proc_net_create("ax25", 0, ax25_get_info);
- proc_net_create("ax25_calls", 0, ax25_uid_get_info);
+ if (len > length) len = length;
- printk(banner);
- return 0;
+ return len;
}
-module_init(ax25_init);
-
-MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>");
-MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol");
+/* ---------------------------------------------------------------------*/
-static void __exit ax25_exit(void)
+static int ax25_gifconf(struct net_device *dev, char *buf, int len)
{
- proc_net_remove("ax25_route");
- proc_net_remove("ax25");
- proc_net_remove("ax25_calls");
- ax25_rt_free();
- ax25_uid_free();
- ax25_dev_free();
+ struct ifreq ifr;
+ int done=0;
- ax25_unregister_sysctl();
- unregister_netdevice_notifier(&ax25_dev_notifier);
-
- dev_remove_pack(&ax25_packet_type);
-
- sock_unregister(PF_AX25);
+ if (!buf) {
+ done += sizeof(ifr);
+ return done;
+ }
+ if (len < (int) sizeof(ifr)) return done;
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_AX25;
+ if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) return -EFAULT;
+ done += sizeof(struct ifreq);
+ return done;
}
-module_exit(ax25_exit);
+
diff --git a/net/ax25/af_ax25.h b/net/ax25/af_ax25.h
new file mode 100644
index 000000000..9980f54f6
--- /dev/null
+++ b/net/ax25/af_ax25.h
@@ -0,0 +1,21 @@
+/*
+ * Interface declaration for AF_AX25 base routines layer.
+ *
+ * Matthias Welwarsky (DG2FEF) 05/28/98
+ *
+ */
+
+
+#ifndef _AF_AX25_H
+#define _AF_AX25_H
+
+#include <linux/skbuff.h>
+#include <net/ax25.h>
+
+extern struct net_device* ax25rtr_get_dev(ax25_address *);
+extern struct sock* ax25_make_new(struct sock*, struct net_device*);
+extern ax25_cb * ax25_list;
+extern void ax25_kill_by_device(struct net_device*);
+extern rwlock_t ax25_dev_lock;
+
+#endif
diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c
deleted file mode 100644
index f96c65586..000000000
--- a/net/ax25/ax25_addr.c
+++ /dev/null
@@ -1,303 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * The null address is defined as a callsign of all spaces with an
- * SSID of zero.
- */
-ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
-
-/*
- * ax25 -> ascii conversion
- */
-char *ax2asc(ax25_address *a)
-{
- static char buf[11];
- char c, *s;
- int n;
-
- for (n = 0, s = buf; n < 6; n++) {
- c = (a->ax25_call[n] >> 1) & 0x7F;
-
- if (c != ' ') *s++ = c;
- }
-
- *s++ = '-';
-
- if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
- *s++ = '1';
- n -= 10;
- }
-
- *s++ = n + '0';
- *s++ = '\0';
-
- if (*buf == '\0' || *buf == '-')
- return "*";
-
- return buf;
-
-}
-
-/*
- * ascii -> ax25 conversion
- */
-ax25_address *asc2ax(char *callsign)
-{
- static ax25_address addr;
- char *s;
- int n;
-
- for (s = callsign, n = 0; n < 6; n++) {
- if (*s != '\0' && *s != '-')
- addr.ax25_call[n] = *s++;
- else
- addr.ax25_call[n] = ' ';
- addr.ax25_call[n] <<= 1;
- addr.ax25_call[n] &= 0xFE;
- }
-
- if (*s++ == '\0') {
- addr.ax25_call[6] = 0x00;
- return &addr;
- }
-
- addr.ax25_call[6] = *s++ - '0';
-
- if (*s != '\0') {
- addr.ax25_call[6] *= 10;
- addr.ax25_call[6] += *s++ - '0';
- }
-
- addr.ax25_call[6] <<= 1;
- addr.ax25_call[6] &= 0x1E;
-
- return &addr;
-}
-
-/*
- * Compare two ax.25 addresses
- */
-int ax25cmp(ax25_address *a, ax25_address *b)
-{
- int ct = 0;
-
- while (ct < 6) {
- if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
- return 1;
- ct++;
- }
-
- if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
- return 0;
-
- return 2; /* Partial match */
-}
-
-/*
- * Compare two AX.25 digipeater paths.
- */
-int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2)
-{
- int i;
-
- if (digi1->ndigi != digi2->ndigi)
- return 1;
-
- if (digi1->lastrepeat != digi2->lastrepeat)
- return 1;
-
- for (i = 0; i < digi1->ndigi; i++)
- if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0)
- return 1;
-
- return 0;
-}
-
-/*
- * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
- *
- */
-unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama)
-{
- int d = 0;
-
- if (len < 14) return NULL;
-
- if (flags != NULL) {
- *flags = 0;
-
- if (buf[6] & AX25_CBIT)
- *flags = AX25_COMMAND;
- if (buf[13] & AX25_CBIT)
- *flags = AX25_RESPONSE;
- }
-
- if (dama != NULL)
- *dama = ~buf[13] & AX25_DAMA_FLAG;
-
- /* Copy to, from */
- if (dest != NULL)
- memcpy(dest, buf + 0, AX25_ADDR_LEN);
- if (src != NULL)
- memcpy(src, buf + 7, AX25_ADDR_LEN);
-
- buf += 2 * AX25_ADDR_LEN;
- len -= 2 * AX25_ADDR_LEN;
-
- digi->lastrepeat = -1;
- digi->ndigi = 0;
-
- while (!(buf[-1] & AX25_EBIT)) {
- if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */
- if (len < 7) return NULL; /* Short packet */
-
- memcpy(&digi->calls[d], buf, AX25_ADDR_LEN);
- digi->ndigi = d + 1;
-
- if (buf[6] & AX25_HBIT) {
- digi->repeated[d] = 1;
- digi->lastrepeat = d;
- } else {
- digi->repeated[d] = 0;
- }
-
- buf += AX25_ADDR_LEN;
- len -= AX25_ADDR_LEN;
- d++;
- }
-
- return buf;
-}
-
-/*
- * Assemble an AX.25 header from the bits
- */
-int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus)
-{
- int len = 0;
- int ct = 0;
-
- memcpy(buf, dest, AX25_ADDR_LEN);
- buf[6] &= ~(AX25_EBIT | AX25_CBIT);
- buf[6] |= AX25_SSSID_SPARE;
-
- if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- memcpy(buf, src, AX25_ADDR_LEN);
- buf[6] &= ~(AX25_EBIT | AX25_CBIT);
- buf[6] &= ~AX25_SSSID_SPARE;
-
- if (modulus == AX25_MODULUS)
- buf[6] |= AX25_SSSID_SPARE;
- else
- buf[6] |= AX25_ESSID_SPARE;
-
- if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT;
-
- /*
- * Fast path the normal digiless path
- */
- if (d == NULL || d->ndigi == 0) {
- buf[6] |= AX25_EBIT;
- return 2 * AX25_ADDR_LEN;
- }
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
-
- while (ct < d->ndigi) {
- memcpy(buf, &d->calls[ct], AX25_ADDR_LEN);
-
- if (d->repeated[ct])
- buf[6] |= AX25_HBIT;
- else
- buf[6] &= ~AX25_HBIT;
-
- buf[6] &= ~AX25_EBIT;
- buf[6] |= AX25_SSSID_SPARE;
-
- buf += AX25_ADDR_LEN;
- len += AX25_ADDR_LEN;
- ct++;
- }
-
- buf[-1] |= AX25_EBIT;
-
- return len;
-}
-
-int ax25_addr_size(ax25_digi *dp)
-{
- if (dp == NULL)
- return 2 * AX25_ADDR_LEN;
-
- return AX25_ADDR_LEN * (2 + dp->ndigi);
-}
-
-/*
- * Reverse Digipeat List. May not pass both parameters as same struct
- */
-void ax25_digi_invert(ax25_digi *in, ax25_digi *out)
-{
- int ct;
-
- out->ndigi = in->ndigi;
- out->lastrepeat = in->ndigi - in->lastrepeat - 2;
-
- /* Invert the digipeaters */
- for (ct = 0; ct < in->ndigi; ct++) {
- out->calls[ct] = in->calls[in->ndigi - ct - 1];
-
- if (ct <= out->lastrepeat) {
- out->calls[ct].ax25_call[6] |= AX25_HBIT;
- out->repeated[ct] = 1;
- } else {
- out->calls[ct].ax25_call[6] &= ~AX25_HBIT;
- out->repeated[ct] = 0;
- }
- }
-}
-
diff --git a/net/ax25/ax25_core.c b/net/ax25/ax25_core.c
new file mode 100644
index 000000000..37d171d83
--- /dev/null
+++ b/net/ax25/ax25_core.c
@@ -0,0 +1,513 @@
+/*
+ * ax25_core.c: AX.25 core and support functions for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS)
+ *
+ * Comment:
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#include <linux/module.h>
+#include <linux/socket.h>
+#include <linux/ax25.h>
+#include <linux/spinlock.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <net/sock.h>
+
+#include "af_ax25.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_core.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_ddi.h"
+
+
+rwlock_t ax25_list_lock = RW_LOCK_UNLOCKED;
+
+/* ---------------------------------------------------------------------*/
+/*
+ * The null address is defined as a callsign of all spaces with an
+ * SSID of zero.
+ */
+ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}};
+
+/* --------------------------------------------------------------------- */
+/*
+ * ax25 -> ascii conversion
+ */
+char *ax2asc(ax25_address *a)
+{
+ static char buf[11];
+ char c, *s;
+ int n;
+
+ for (n = 0, s = buf; n < 6; n++) {
+ c = (a->ax25_call[n] >> 1) & 0x7F;
+
+ if (c != ' ') *s++ = c;
+ }
+
+ *s++ = '-';
+
+ if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) {
+ *s++ = '1';
+ n -= 10;
+ }
+
+ *s++ = n + '0';
+ *s++ = '\0';
+
+ if (*buf == '\0' || *buf == '-')
+ return "*";
+
+ return buf;
+
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * ascii -> ax25 conversion
+ */
+ax25_address *asc2ax(char *callsign)
+{
+ static ax25_address addr;
+ char *s;
+ int n;
+
+ for (s = callsign, n = 0; n < 6; n++) {
+ if (*s != '\0' && *s != '-')
+ addr.ax25_call[n] = *s++;
+ else
+ addr.ax25_call[n] = ' ';
+ addr.ax25_call[n] <<= 1;
+ addr.ax25_call[n] &= 0xFE;
+ }
+
+ if (*s++ == '\0') {
+ addr.ax25_call[6] = 0x00;
+ return &addr;
+ }
+
+ addr.ax25_call[6] = *s++ - '0';
+
+ if (*s != '\0') {
+ addr.ax25_call[6] *= 10;
+ addr.ax25_call[6] += *s++ - '0';
+ }
+
+ addr.ax25_call[6] <<= 1;
+ addr.ax25_call[6] &= 0x1E;
+
+ return &addr;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Compare two ax.25 addresses
+ */
+int ax25cmp(ax25_address *a, ax25_address *b)
+{
+ int ct = 0;
+
+ while (ct < 6) {
+ if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */
+ return 1;
+ ct++;
+ }
+
+ if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */
+ return 0;
+
+ return 2; /* Partial match */
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Add a socket to the bound sockets list.
+ */
+void ax25_insert_cb(ax25_cb *ax25)
+{
+ if (ax25->device != NULL) {
+ ax25_dev_insert_cb(ax25);
+ return;
+ }
+
+ ax25->prev = NULL;
+ ax25->next = ax25_list;
+
+ write_lock(&ax25_list_lock);
+
+ if (ax25_list != NULL)
+ ax25_list->prev = ax25;
+ ax25_list = ax25;
+
+ write_unlock(&ax25_list_lock);
+
+ ax25->inserted = 1;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Socket removal is now protected agains bottom half ints
+ * with a start/end_bh_atomic bracket. There should be no
+ * need to mask interrupts on hardware level.
+ */
+void ax25_remove_cb(ax25_cb *ax25)
+{
+ /*
+ * unbound sockets are not in any list
+ */
+
+ if (!ax25->inserted)
+ return;
+
+ if (ax25->device != NULL) {
+ ax25_dev_remove_cb(ax25);
+ return;
+ }
+
+
+ if (ax25_list != NULL) {
+ write_lock(&ax25_list_lock);
+ if (ax25->prev == NULL)
+ ax25_list = ax25->next;
+ else
+ ax25->prev->next = ax25->next;
+
+ if (ax25->next != NULL)
+ ax25->next->prev = ax25->prev;
+ write_unlock(&ax25_list_lock);
+ }
+
+ ax25->inserted = 0;
+
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Find an AX.25 control block given both ends.
+ */
+ax25_cb *ax25_find_cb(ax25_addr_t *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_list_lock);
+ for (s = ax25_dev_list(dev) ; s != NULL; s = s->next) {
+
+ if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET)
+ continue;
+
+ if (s->addr.dcount == addr->dcount && !ax25cmp(&s->addr.src, &addr->src)
+ && !ax25cmp(&s->addr.dest, &addr->dest))
+ {
+ int i;
+
+ if (addr->dcount == 0)
+ break;
+ if (addr->lastrepeat != s->addr.lastrepeat)
+ continue;
+ i = addr->dcount;
+ while (i--) {
+ if (ax25cmp(&s->addr.digipeater[i], &addr->digipeater[i]))
+ break;
+ }
+ if (i < 0)
+ break;
+ }
+ }
+ read_unlock(&ax25_list_lock);
+ return s;
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_destroy_cb(ax25_cb *ax25)
+{
+ ax25_remove_cb(ax25);
+ ax25_clear_queues(ax25);
+ ax25_free_cb(ax25);
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_destroy_socket(struct sock *sk)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ /*
+ * this may be a pending SABM, waiting in the receive queue
+ * to become accepted. But the listener just died :-)
+ */
+ if (skb->sk != sk) {
+ /*
+ * signal the peer that we have closed the socket.
+ * if he has already disconnected himself, just mark
+ * the socket dead and move to TCP_CLOSE. This is only
+ * for security, as ax25_disconnect should have already
+ * done this.
+ */
+ if (skb->sk->state == TCP_ESTABLISHED)
+ skb->sk->protinfo.ax25->condition |= AX25_COND_RELEASE;
+ else {
+ printk(KERN_DEBUG "ax25_destroy_socket: TCP_CLOSE\n");
+ skb->sk->state = TCP_CLOSE;
+ }
+ skb->sk->dead = 1;
+ }
+ kfree_skb(skb);
+ }
+ sk_free(sk);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Fill in a created AX.25 control block with the default
+ * values for a particular device.
+ */
+void ax25_fillin_cb(ax25_cb *ax25, struct net_device *dev)
+{
+ ax25->device = dev;
+ ax25->vs_rtt = -1;
+
+ if (dev != NULL) {
+ ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1) / 4;
+ ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1);
+ ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2);
+ ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3);
+ ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2);
+ ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
+ ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE);
+ ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF);
+
+ if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
+ } else {
+ ax25->rtt = AX25_DEF_T1 / 4;
+ ax25->t1 = AX25_DEF_T1;
+ ax25->t2 = AX25_DEF_T2;
+ ax25->t3 = AX25_DEF_T3;
+ ax25->n2 = AX25_DEF_N2;
+ ax25->paclen = AX25_DEF_PACLEN;
+ ax25->idle = AX25_DEF_IDLE;
+ ax25->backoff = AX25_DEF_BACKOFF;
+
+ if (AX25_DEF_AXDEFMODE) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = AX25_DEF_EWINDOW;
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = AX25_DEF_WINDOW;
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Create an empty AX.25 control block.
+ */
+ax25_cb *ax25_create_cb(void)
+{
+ ax25_cb *ax25;
+
+ if ((ax25 = (ax25_cb *)kmalloc(sizeof(ax25_cb), GFP_ATOMIC)) == NULL)
+ return NULL;
+
+ MOD_INC_USE_COUNT;
+
+ memset(ax25, 0x00, sizeof(ax25_cb));
+
+ skb_queue_head_init(&ax25->write_queue);
+ skb_queue_head_init(&ax25->frag_queue);
+ skb_queue_head_init(&ax25->ack_queue);
+ skb_queue_head_init(&ax25->rcv_queue);
+
+ ax25->state = AX25_LISTEN;
+ ax25->condition = AX25_COND_SETUP;
+
+ return ax25;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Free an allocated ax25 control block. This is done to centralise
+ * the MOD count code.
+ */
+void ax25_free_cb(ax25_cb *ax25)
+{
+ if (ax25->slcomp != NULL) {
+ axhc_free(ax25->slcomp);
+ }
+
+ if (ax25->peer != NULL) {
+ ax25->peer->peer = NULL;
+ }
+
+ kfree(ax25);
+
+ MOD_DEC_USE_COUNT;
+}
+
+/* ---------------------------------------------------------------------*/
+
+void ax25_free_sock(struct sock *sk)
+{
+ if (sk->protinfo.ax25 != NULL)
+ ax25_free_cb(sk->protinfo.ax25);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Given an AX.25 address pull of to, from, digi list, command/response and the start of data
+ *
+ */
+unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_pktinfo *pkt_info)
+{
+ int d = 0;
+
+ if (len < 15)
+ return NULL;
+
+ pkt_info->cmdrsp = 0;
+
+ memcpy(&pkt_info->addr.dest, buf, AX25_ADDR_LEN);
+
+ if (buf[6] & AX25_CBIT)
+ pkt_info->cmdrsp = AX25_COMMAND;
+ buf += AX25_ADDR_LEN;
+ len -= AX25_ADDR_LEN;
+
+ memcpy(&pkt_info->addr.src, buf, AX25_ADDR_LEN);
+
+ if (buf[6] & AX25_CBIT)
+ pkt_info->cmdrsp = AX25_RESPONSE;
+
+ pkt_info->dama = !(buf[6] & AX25_DAMA_FLAG);
+
+ pkt_info->addr.lastrepeat = -1;
+ pkt_info->addr.dcount = 0;
+
+ while (!(buf[6] & AX25_EBIT)) {
+ buf += AX25_ADDR_LEN;
+ len -= AX25_ADDR_LEN;
+
+ if (d < AX25_MAX_DIGIS && len >= 7) {
+ memcpy(&pkt_info->addr.digipeater[d], buf, AX25_ADDR_LEN);
+ if (buf[6] & AX25_HBIT)
+ pkt_info->addr.lastrepeat = d;
+ ++d;
+ pkt_info->addr.dcount = d;
+ } else
+ return NULL;
+ }
+
+ return buf + AX25_ADDR_LEN;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Assemble an AX.25 header from the bits
+ */
+int ax25_build_addr(unsigned char *buf, ax25_addr_t *addr, int flag, int seqmask)
+{
+ int len = 0;
+ int ct = 0;
+
+ memcpy(buf, &addr->dest, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+
+ if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ memcpy(buf, &addr->src, AX25_ADDR_LEN);
+ buf[6] &= ~(AX25_EBIT | AX25_CBIT);
+ buf[6] &= ~AX25_SSSID_SPARE;
+
+ if (seqmask == AX25_SEQMASK)
+ buf[6] |= AX25_SSSID_SPARE;
+ else
+ buf[6] |= AX25_ESSID_SPARE;
+
+ if (flag == AX25_RESPONSE)
+ buf[6] |= AX25_CBIT;
+
+ /*
+ * Fast path the normal digiless path
+ */
+ if (addr->dcount == 0) {
+ buf[6] |= AX25_EBIT;
+ return 2 * AX25_ADDR_LEN;
+ }
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+
+ while (ct < addr->dcount) {
+ memcpy(buf, &addr->digipeater[ct], AX25_ADDR_LEN);
+
+ if (ct <= addr->lastrepeat)
+ buf[6] |= AX25_HBIT;
+ else
+ buf[6] &= ~AX25_HBIT;
+
+ buf[6] &= ~AX25_EBIT;
+ buf[6] |= AX25_SSSID_SPARE;
+
+ buf += AX25_ADDR_LEN;
+ len += AX25_ADDR_LEN;
+ ct++;
+ }
+
+ buf[-1] |= AX25_EBIT;
+
+ return len;
+}
+
+/* ---------------------------------------------------------------------*/
+
+int ax25_sizeof_addr(ax25_addr_t *addr)
+{
+ return AX25_ADDR_LEN * (addr->dcount+2);
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Invert AX.25 address. May not pass both parameters as same struct
+ */
+void ax25_invert_addr(ax25_addr_t *in, ax25_addr_t *out)
+{
+ ax25_address *ip, *op;
+ int dcount;
+
+ dcount = out->dcount = in->dcount;
+ out->lastrepeat = dcount - in->lastrepeat - 2;
+
+ /* source/destination */
+ out->dest = in->src;
+ out->src = in->dest;
+
+ /* Invert the digipeaters */
+ if (dcount) {
+ ip = in->digipeater;
+ op = out->digipeater + (dcount-1); /* pointer scaled! */
+ while (dcount--)
+ *op-- = *ip++;
+ }
+}
diff --git a/net/ax25/ax25_core.h b/net/ax25/ax25_core.h
new file mode 100644
index 000000000..bbb31183f
--- /dev/null
+++ b/net/ax25/ax25_core.h
@@ -0,0 +1,21 @@
+#ifndef _AX25_CORE_H
+#define _AX25_CORE_H
+
+extern char* ax2asc(ax25_address*);
+extern int ax25cmp(ax25_address*, ax25_address*);
+extern ax25_address* asc2ax(char*);
+extern void ax25_insert_cb(ax25_cb*);
+extern void ax25_remove_cb(ax25_cb*);
+extern ax25_cb* ax25_find_cb(ax25_addr_t*, struct net_device*);
+extern void ax25_destroy_cb(ax25_cb*);
+extern void ax25_destroy_socket(struct sock*);
+extern void ax25_fillin_cb(ax25_cb*, struct net_device*);
+extern ax25_cb* ax25_create_cb(void);
+extern void ax25_free_cb(ax25_cb*);
+extern void ax25_free_sock(struct sock*);
+extern int ax25_build_addr(unsigned char*, ax25_addr_t*, int, int);
+extern int ax25_sizeof_addr(ax25_addr_t*);
+extern void ax25_invert_addr(ax25_addr_t*, ax25_addr_t*);
+
+extern ax25_address null_ax25_address;
+#endif
diff --git a/net/ax25/ax25_ctl.c b/net/ax25/ax25_ctl.c
new file mode 100644
index 000000000..da4cd49c8
--- /dev/null
+++ b/net/ax25/ax25_ctl.c
@@ -0,0 +1,133 @@
+/*
+ * ax25_ctl.c: Implements ioctl()s on persisting VC sockets (NEW-AX.25)
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Joerg (DL1BKE)
+ *
+ * Comment:
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#include <asm/uaccess.h>
+#include <linux/ax25.h>
+#include <net/ax25.h>
+
+#include "af_ax25.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
+
+/*
+ * dl1bke 960311: set parameters for existing AX.25 connections,
+ * includes a KILL command to abort any connection.
+ * VERY useful for debugging ;-)
+ */
+int ax25_ctl_ioctl(const unsigned int cmd, void *arg)
+{
+ struct ax25_ctl_struct ax25_ctl;
+ struct net_device *dev;
+ ax25_cb *ax25;
+ ax25_addr_t addr;
+ int err;
+
+ if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0)
+ return err;
+
+ if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl)))
+ return -EFAULT;;
+
+ if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL)
+ return -ENODEV;
+
+ addr.src = ax25_ctl.source_addr;
+ addr.dest = ax25_ctl.dest_addr;
+ addr.dcount = 0;
+
+ if ((ax25 = ax25_find_cb(&addr, dev)) == NULL)
+ return -ENOTCONN;
+
+ switch (ax25_ctl.cmd) {
+ case AX25_KILL:
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, ENETRESET);
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, ENETRESET);
+ break;
+
+ case AX25_WINDOW:
+ if (ax25->seqmask == AX25_SEQMASK) {
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7)
+ return -EINVAL;
+ } else {
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63)
+ return -EINVAL;
+ }
+ ax25->window = ax25_ctl.arg;
+ break;
+
+ case AX25_T1:
+ if (ax25_ctl.arg < 1)
+ return -EINVAL;
+ ax25->rtt = (ax25_ctl.arg * AX25_TICS) / 4;
+ ax25->t1 = ax25_ctl.arg;
+ write_lock(&ax25->timer_lock);
+ if (ax25->wrt_timer > ax25->t1)
+ ax25->wrt_timer = ax25->t1;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_T2:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->t2 = ax25_ctl.arg;
+ write_lock(&ax25->timer_lock);
+ if (ax25->ack_timer > ax25->t2)
+ ax25->ack_timer = ax25->t2;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_N2:
+ if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31)
+ return -EINVAL;
+ ax25->n2count = 0;
+ ax25->n2 = ax25_ctl.arg;
+ break;
+
+ case AX25_T3:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ;
+ write_lock(&ax25->timer_lock);
+ if (ax25->wrt_timer != 0)
+ ax25->wrt_timer = ax25->t3;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_IDLE:
+ if (ax25_ctl.arg < 0)
+ return -EINVAL;
+ ax25->idle = ax25_ctl.arg * AX25_SLOWHZ;
+ write_lock(&ax25->timer_lock);
+ if (ax25->idletimer != 0)
+ ax25->idletimer = ax25->idle;
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_PACLEN:
+ if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535)
+ return -EINVAL;
+ ax25->paclen = ax25_ctl.arg;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
diff --git a/net/ax25/ax25_ctl.h b/net/ax25/ax25_ctl.h
new file mode 100644
index 000000000..10a41d991
--- /dev/null
+++ b/net/ax25/ax25_ctl.h
@@ -0,0 +1,6 @@
+#ifndef _AX25_CTL_H
+#define _AX25_CTL_H
+
+extern int ax25_ctl_ioctl(const unsigned int, void*);
+
+#endif
diff --git a/net/ax25/ax25_ddi.c b/net/ax25/ax25_ddi.c
new file mode 100644
index 000000000..594484c77
--- /dev/null
+++ b/net/ax25/ax25_ddi.c
@@ -0,0 +1,1186 @@
+/*
+ * ax25_ddi.c: Device Driver Independent Module for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ *
+ * Comment: Contains device driver interface, scheduler, channel arbitration
+ * LAPB state machine is synchronized here to avoid race conditions
+ * Written from scratch by Matthias Welwarsky in 1998.
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/tqueue.h>
+#include <linux/time.h>
+#include <linux/spinlock.h>
+
+#include <net/tcp.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_ddi.h"
+#include "ax25_core.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_route.h"
+#include "ax25_timer.h"
+
+struct net_device *ax25_devices[AX25_MAX_DEVICES];
+rwlock_t ax25_dev_lock = RW_LOCK_UNLOCKED;
+
+
+
+/*
+ * ------------------------------------------------------------------------
+ * declaration of private functions
+ * ------------------------------------------------------------------------
+ */
+
+static void clear_ax25devices(void);
+static void ax25_dev_timer(unsigned long);
+static void ax25_dev_tic(unsigned long);
+static void ax25_transmit_buffer(ax25_cb*, struct sk_buff*, int);
+static void ax25_send_iframe(ax25_cb*, struct sk_buff*, int);
+static void ax25_send_control(ax25_cb*, int, int, int);
+static void ax25_kick_device(struct ax25_dev*);
+static __inline__ void ax25_dev_add_ready(struct ax25_dev *, ax25_cb *);
+static __inline__ void ax25_dev_remove_active(struct ax25_dev *);
+static __inline__ void ax25_dev_remove_ready(struct ax25_dev *, ax25_cb *);
+static void ax25_dev_set_tic(struct ax25_dev *);
+static void ax25_dev_set_timer(struct ax25_dev *, unsigned int);
+static void ax25_queue_xmit(struct sk_buff *);
+static struct ax25_dev *ax25_dev_get_dev(struct net_device *);
+
+/*
+ * ------------------------------------------------------------------------
+ * Interface implementation
+ * All public functions of this module are defined here
+ * ------------------------------------------------------------------------
+ */
+
+void ax25_ddi_init(void)
+{
+ clear_ax25devices();
+}
+
+/*
+ * queue a fully assembled frame in the unproto queue of the
+ * device and mark the channel ready for transmission
+ */
+void ax25_send_unproto(struct sk_buff* skb, struct net_device* dev)
+{
+ struct ax25_dev* ax25_device = AX25_PTR(dev);
+
+ skb->dev = dev;
+ skb_queue_tail(&ax25_device->unproto_queue, skb);
+ ax25_kick_device(ax25_device);
+}
+
+void ax25_send_broadcast(struct sk_buff *skb)
+{
+ int i;
+
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ struct net_device *dev = ax25_devices[i];
+
+ if (dev != NULL && (dev->flags & (IFF_UP|IFF_BROADCAST)) != 0) {
+ struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+ if (newskb != NULL)
+ ax25_send_unproto(newskb, dev);
+ else
+ printk(KERN_ERR "ax25_send_broadcast: unable to clone packet.\n");
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+
+ /* caller frees original packet */
+}
+
+/*
+ * put a connection on the ready list of it's device and mark the device
+ * ready for transmission.
+ */
+void ax25_kick(ax25_cb *ax25)
+{
+ if (ax25->device != NULL) {
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+
+ /*
+ * put the connection on the readylist of this channel,
+ * if it's not already there.
+ */
+ ax25_dev_add_ready(ax25_device, ax25);
+
+ /*
+ * mark the channel ready
+ */
+ ax25_kick_device(ax25_device);
+ }
+}
+
+/*
+ * return the connection list to a given device
+ */
+ax25_cb *ax25_dev_list(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device;
+
+ if (dev == NULL)
+ return ax25_list;
+
+ if ((ax25_device = AX25_PTR(dev)) != NULL && ax25_device->magic == AX25_DEV_MAGIC)
+ return ax25_device->list.all;
+
+ return NULL;
+}
+
+/*
+ * insert a connection into a device queue
+ */
+void ax25_dev_insert_cb(ax25_cb *ax25)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+
+ if (ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_insert_cb: wrong magic number.\n");
+ return;
+ }
+
+ ax25->prev = NULL;
+ ax25->next = ax25_device->list.all;
+
+ write_lock(&ax25_dev_lock);
+ if (ax25_device->list.all != NULL)
+ ax25_device->list.all->prev = ax25;
+ ax25_device->list.all = ax25;
+ write_unlock(&ax25_dev_lock);
+
+ ax25->inserted = 1;
+}
+
+/*
+ * remove a connection from a device queue
+ */
+void ax25_dev_remove_cb(ax25_cb *ax25)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(ax25->device);
+ struct net_device *dev = ax25->device;
+ struct ax25_cb *axp;
+
+ if (ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_remove_cb: wrong magic number.\n");
+ return;
+ }
+
+ if (ax25_device->list.all == NULL) {
+ printk(KERN_ERR "ax25_dev_remove_cb: empty list.\n");
+ return;
+ }
+
+ write_lock(&ax25_dev_lock);
+ if (ax25->prev == NULL) {
+ ax25_device->list.all = ax25->next;
+ } else {
+ ax25->prev->next = ax25->next;
+ }
+
+ if (ax25->next != NULL)
+ ax25->next->prev = ax25->prev;
+
+ ax25->inserted = 0;
+
+ if (xchg(&ax25->ready.state, AX25_SCHED_IDLE) == AX25_SCHED_READY)
+ ax25_dev_remove_ready(ax25_device, ax25);
+
+ /* search for active circuits and set DAMA flag accordingly */
+ for (axp=ax25_device->list.all; axp!=NULL; axp=axp->next)
+ if ((axp->state == AX25_STATE_3) || (axp->state == AX25_STATE_4)) break;
+ if (axp == NULL) ax25_dev_set_dama(dev, 0);
+
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * Look for any matching address.
+ */
+int ax25_dev_match_addr(ax25_address *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Find a control block that wants to accept the SABM we have just
+ * received.
+ */
+ax25_cb *ax25_dev_find_listener(ax25_address *addr, int digi, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_dev_lock);
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->state != AX25_LISTEN)
+ continue;
+ if ((s->iamdigi && !digi) || (!s->iamdigi && digi))
+ continue;
+ if (ax25cmp(&s->addr.src, addr) == 0)
+ break;
+ }
+ read_unlock(&ax25_dev_lock);
+ return s;
+}
+
+/*
+ * Find an AX.25 socket given both ends.
+ */
+struct sock *ax25_dev_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type)
+{
+ ax25_cb *s;
+
+ read_lock(&ax25_dev_lock);
+ for (s = ax25_dev_list(dev); s != NULL; s = s->next) {
+ if (s->sk != NULL && ax25cmp(&s->addr.src, my_addr) == 0
+ && ax25cmp(&s->addr.dest, dest_addr) == 0 && s->sk->type == type) {
+ read_unlock(&ax25_dev_lock);
+ return s->sk;
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+ return NULL;
+}
+
+/*
+ * This function is called whenever a parameter is modified using
+ * ax25_dev_set_value_notify or via the proc/sysctl interface. It
+ * decides whether to notify the device driver of the event. If the
+ * decision is positive, it uses the parameter_change downcall.
+ * The driver can then react and re-set the value or pick the
+ * closest value the hardware allows (e.g. by baud rate divider etc.).
+ * The most important values for the device driver are duplex, txdelay,
+ * txtail, {tx,rx}bitrate. Slottime and p-persistence are currently
+ * only "for info" since channel arbitration is done by DDI layer now.
+ */
+void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval)
+{
+ struct ax25_dev *ax_dev;
+
+ if (!dev) return; /* paranoia */
+ ax_dev = AX25_PTR(dev);
+ if (!ax_dev) return; /* paranoia */
+
+ switch (id) {
+ case AX25_VALUES_MEDIA_DUPLEX:
+ case AX25_VALUES_MEDIA_TXDELAY:
+ case AX25_VALUES_MEDIA_TXTAIL:
+ case AX25_VALUES_MEDIA_TXBITRATE:
+ case AX25_VALUES_MEDIA_RXBITRATE:
+ case AX25_VALUES_MEDIA_SLOTTIME:
+ case AX25_VALUES_MEDIA_PPERSISTENCE:
+ case AX25_VALUES_MEDIA_AUTO_ADJUST:
+ if (ax_dev->hw.parameter_change_notify) {
+ (ax_dev->hw.parameter_change_notify)(dev, id, oldval, newval);
+ }
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+/*
+ * Call this function from AX.25 driver to check if driver has
+ * to be notified of the event.
+ */
+void ax25_dev_set_value_notify(struct net_device *dev, int valueno, int newvalue)
+{
+ int oldvalue;
+
+ oldvalue = ax25_dev_get_value(dev, valueno);
+ ax25_dev_set_value(dev, valueno, newvalue);
+ if (oldvalue != newvalue)
+ ax25_notify_dispatcher(dev, valueno, oldvalue, newvalue);
+}
+
+/*
+ * This is called when an interface is brought up. These are
+ * reasonable defaults. We try not to mess with the media parameters
+ * if they appear as having been set already.
+ */
+void ax25_dev_device_up(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+ int txbitrate;
+
+ if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC)
+ return;
+
+ ax25_device->ready_lock = RW_LOCK_UNLOCKED;
+ ax25_device->forward = NULL;
+ ax25_device->list.all = NULL;
+ ax25_device->list.ready = NULL;
+ skb_queue_head_init(&ax25_device->unproto_queue);
+ ax25_device->bytes_sent = 0;
+ ax25_device->dama_mode = 0;
+
+ ax25_dev_set_value_notify(dev, AX25_VALUES_IPDEFMODE, AX25_DEF_IPDEFMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_AXDEFMODE, AX25_DEF_AXDEFMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_BACKOFF, AX25_DEF_BACKOFF);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_CONMODE, AX25_DEF_CONMODE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_WINDOW, AX25_DEF_WINDOW);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_EWINDOW, AX25_DEF_EWINDOW);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T1, AX25_DEF_T1);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T3, AX25_DEF_T3);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_IDLE, AX25_DEF_IDLE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_N2, AX25_DEF_N2);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_PACLEN, AX25_DEF_PACLEN);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_PROTOCOL, AX25_DEF_PROTOCOL);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_DAMA_SLAVE_TIMEOUT, AX25_DEF_DAMA_SLAVE_TIMEOUT);
+
+ txbitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_T2,
+ txbitrate > 0 ? (3600 / AX25_TICS) * HZ / txbitrate : 0);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, AX25_DEF_MEDIA_PPERSISTENCE);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_SLOTTIME, AX25_DEF_MEDIA_SLOTTIME);
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, AX25_DEF_MEDIA_AUTO_ADJUST);
+
+ init_timer(&ax25_device->timer);
+ ax25_dev_set_timer(ax25_device, AX25_TICS);
+ init_timer(&ax25_device->tics);
+ ax25_dev_set_tic(ax25_device);
+}
+
+/*
+ * this is called when a device is brought down. Delete the device
+ * timers and update the sysctl interface.
+ */
+void ax25_dev_device_down(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+
+ ax25_kill_by_device(dev);
+ ax25_rt_device_down(dev);
+
+ if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) {
+ printk(KERN_ERR "ax25_dev_device_down: not an AX.25 device.\n");
+ return;
+ }
+
+ del_timer(&ax25_device->timer);
+ del_timer(&ax25_device->tics);
+
+ /* FIXME: do I have to lock this or not? */
+ /* start_bh_atomic(); */
+ skb_queue_purge(&ax25_device->unproto_queue);
+ /* end_bh_atomic(); */
+}
+
+/*
+ * Packet forwarding control IOCTL
+ * FIXME: does anybody really need this feature?
+ */
+int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
+{
+ struct net_device *dev;
+ struct ax25_dev *ax25_dev;
+
+ if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL)
+ return -EINVAL;
+
+ if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+ return -EINVAL;
+
+ switch (cmd) {
+ case SIOCAX25ADDFWD:
+ if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL)
+ return -EINVAL;
+ if (ax25_dev->forward != NULL)
+ return -EINVAL;
+ ax25_dev->forward = dev;
+ break;
+
+ case SIOCAX25DELFWD:
+ if (ax25_dev->forward == NULL)
+ return -EINVAL;
+ ax25_dev->forward = NULL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct net_device *ax25_fwd_dev(struct net_device *dev)
+{
+ struct ax25_dev *ax25_dev;
+
+ if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL)
+ return dev;
+
+ if (ax25_dev->forward == NULL)
+ return dev;
+
+ return ax25_dev->forward;
+}
+
+int ax25_dev_get_info(char *buffer, char **start, off_t offset, int length)
+{
+ int i;
+ struct net_device *dev;
+ char devname[7];
+
+ int len = 0;
+ off_t pos = 0;
+ off_t begin = 0;
+
+ len += sprintf(buffer, "device hwaddr rifr tifr rrej rkby tkby duplex tx-bps rx-bps ppers slot auto txd txt \n");
+
+ read_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i]) != NULL) {
+ strncpy(devname, dev->name, 6);
+ devname[6] = 0;
+ len += sprintf(buffer+len, "%-6s %-9s %-6ld %-6ld %-6ld %-9ld %-9ld %-6s %-8d %-8d %-5d %-4d %-4s %-4d %-4d\n",
+ devname, ax2asc((ax25_address *)dev->dev_addr),
+ AX25_PTR(dev)->rx_iframes, AX25_PTR(dev)->tx_iframes,
+ AX25_PTR(dev)->rx_rejects,
+ AX25_PTR(dev)->rx_bytes/1024,
+ AX25_PTR(dev)->tx_bytes/1024,
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX) ? "full" : "half",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST) ? "on" : "off",
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY),
+ ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL));
+
+ pos = begin + len;
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ break;
+ }
+ }
+ read_unlock(&ax25_dev_lock);
+
+ *start = buffer + (offset - begin);
+ len -= offset - begin;
+
+ if (len > length) len = length;
+
+ return len;
+}
+
+/*
+ * This function is called by core/dev.c whenever a new netdevice is
+ * being registerd. We initialize its ax25_dev structure and include
+ * it in our list. We also register the sysctl tree for it and initialize
+ * its parameters.
+ */
+void register_ax25device(struct net_device *dev)
+{
+ int i;
+ struct ax25_dev *axdev = AX25_PTR(dev);
+
+ axdev->magic = AX25_DEV_MAGIC;
+ axdev->netdev = dev;
+
+ memcpy((char *) dev->broadcast, (char *) asc2ax("QST-0"), AX25_ADDR_LEN);
+
+ ax25_unregister_sysctl();
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if (ax25_devices[i] == NULL) {
+ ax25_devices[i] = dev;
+ break;
+ }
+ }
+
+ ax25_register_sysctl();
+ if (i == AX25_MAX_DEVICES) {
+ printk(KERN_ERR "AX.25: Too many devices, could not register.\n");
+ goto done;
+ }
+
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXDELAY, AX25_DEF_MEDIA_TXDELAY);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXTAIL, AX25_DEF_MEDIA_TXTAIL);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXBITRATE, AX25_DEF_MEDIA_TXBITRATE);
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE) == 0)
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_RXBITRATE, AX25_DEF_MEDIA_RXBITRATE);
+ /*
+ * slottime, p-persistence and auto-adjust defaults are
+ * loaded upon interface start
+ */
+
+done:
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * This function is executed when an interface is about to be removed.
+ * It must already have been downed before. We remove it from our
+ * list and remove sysctl directory entry.
+ */
+void unregister_ax25device(struct net_device *dev)
+{
+ int i;
+
+ ax25_unregister_sysctl();
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if (ax25_devices[i] == dev) {
+ ax25_devices[i] = NULL;
+ break;
+ }
+ }
+ write_unlock(&ax25_dev_lock);
+ ax25_register_sysctl();
+}
+
+/*
+ * Activate/Deactivate DAMA on a given interface.
+ * We automagically configure the media for full duplex if neccessary.
+ */
+void ax25_dev_set_dama(struct net_device *dev, int dama)
+{
+ if (dama && (ax25_dev_get_value(dev, AX25_VALUES_PROTOCOL) == 1)) {
+ if (!(AX25_PTR(dev)->dama_mode & DAMA_SLAVE)) {
+ AX25_PTR(dev)->dama_mode |= DAMA_SLAVE;
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 1);
+ }
+ } else {
+ if (AX25_PTR(dev)->dama_mode & DAMA_SLAVE) {
+ AX25_PTR(dev)->dama_mode &= ~DAMA_SLAVE;
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0);
+ }
+ }
+ return;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ * End of public area, all private functions of this module are defined
+ * here.
+ * ------------------------------------------------------------------------
+ */
+
+static void clear_ax25devices(void)
+{
+ int i;
+
+ write_lock(&ax25_dev_lock);
+ for (i = 0; i < AX25_MAX_DEVICES; i++)
+ ax25_devices[i] = NULL;
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * simple pseudo-random number generator, stolen from hdlcdrv.c :)
+ */
+static inline unsigned short random_num(void)
+{
+ static unsigned short random_seed;
+
+ random_seed = 28629 * random_seed + 157;
+ return random_seed & 0xFF;
+}
+
+/*
+ * add a connection to the channels readylist
+ */
+static inline void ax25_dev_add_ready(struct ax25_dev *ax25_device, ax25_cb *ax25)
+{
+ write_lock(&ax25_device->ready_lock);
+ if (ax25->ready.state != AX25_SCHED_READY) {
+ ax25->ready.state = AX25_SCHED_READY;
+ if (ax25_device->list.ready == NULL) {
+ ax25->ready.prev = ax25;
+ ax25->ready.next = ax25;
+ ax25_device->list.ready = ax25;
+ } else {
+ ax25->ready.next = ax25_device->list.ready;
+ ax25->ready.prev = ax25_device->list.ready->ready.prev;
+ ax25_device->list.ready->ready.prev->ready.next = ax25;
+ ax25_device->list.ready->ready.prev = ax25;
+ }
+ }
+ write_unlock(&ax25_device->ready_lock);
+}
+
+/*
+ * remove the active connection from the channels readylist
+ * NB: caller must do write_lock() on ax25_device->ready_lock!
+ */
+static inline void ax25_dev_remove_active(struct ax25_dev *ax25_device)
+{
+ ax25_cb *active = ax25_device->list.ready;
+ if (active->ready.next == active) {
+ ax25_device->list.ready = NULL;
+ } else {
+ ax25_device->list.ready = active->ready.next;
+ active->ready.next->ready.prev = active->ready.prev;
+ active->ready.prev->ready.next = active->ready.next;
+ }
+ active->ready.state = AX25_SCHED_IDLE;
+}
+
+/*
+ * remove a connection from the channels readylist
+ */
+static inline void ax25_dev_remove_ready(struct ax25_dev *ax25_device, ax25_cb *ax25)
+{
+ write_lock(&ax25_device->ready_lock);
+
+ if (ax25 == ax25_device->list.ready) {
+ ax25_dev_remove_active(ax25_device);
+ } else {
+ ax25->ready.next->ready.prev = ax25->ready.prev;
+ ax25->ready.prev->ready.next = ax25->ready.next;
+ ax25->ready.state = AX25_SCHED_IDLE;
+ }
+
+ write_unlock(&ax25_device->ready_lock);
+}
+
+/*
+ * Timer for a per device 100ms timing tic. AX.25 Timers of all
+ * connections on this device are driven by this timer.
+ */
+static void ax25_dev_set_tic(struct ax25_dev *this)
+{
+ this->tics.data = (unsigned long)this;
+ this->tics.function = &ax25_dev_tic;
+ this->tics.expires = jiffies + AX25_TICS;
+
+ add_timer(&this->tics);
+}
+
+static void ax25_dev_tic(unsigned long param)
+{
+ ax25_cb *active;
+ struct ax25_dev *this = (struct ax25_dev *) param;
+
+ if (!this->needs_transmit && ((!this->hw.ptt) || (!this->hw.ptt(this->netdev)))) {
+ for (active = this->list.all; active; active = active->next) {
+ /*
+ * only run the timer on idle connections.
+ */
+ if (!active->ready.state)
+ ax25_timer(active);
+ }
+ }
+ ax25_dev_set_tic(this);
+}
+
+/*
+ * Timer for channel access arbitration. Fires every 100ms if the channel
+ * is idle (i.e. no connections need to transmit), and in intervals of
+ * half of a frame length if trying to transmit
+ */
+static void ax25_dev_set_timer(struct ax25_dev *this, unsigned int tics)
+{
+ this->timer.data = (unsigned long)this;
+ this->timer.function = &ax25_dev_timer;
+ this->timer.expires = jiffies + tics;
+
+ add_timer(&this->timer);
+}
+
+static void ax25_dev_timer(unsigned long param)
+{
+ struct ax25_dev *this = (struct ax25_dev *) param;
+ struct net_device *dev = this->netdev;
+ ax25_cb *active;
+ struct sk_buff *skb;
+ unsigned int bytes_sent = 0;
+ unsigned int max_bytes;
+ int ppers = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE);
+ int br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
+ int duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX);
+ int bit_per_jiffie;
+ int jiffies_per_slot;
+
+ if (br == 0) {
+ printk(KERN_ERR "ax25_dev_timer(%s): TX-Bitrate unset!!!\n", dev->name);
+ }
+ bit_per_jiffie = br / HZ;
+ jiffies_per_slot = 1200 * HZ / br + 1;
+
+ if (this->dama_mode & DAMA_SLAVE) {
+ /* >>>>> DAMA slave <<<<<
+ *
+ * we only transmit when we are asked to do so or when
+ * T3 ran out, which should only occur if the master forgot
+ * our circuits (i.e. had a reset or is broken otherwise).
+ */
+ if (this->dama_polled) {
+ /* we have been polled, it's ok to transmit */
+ this->dama_polled = 0;
+ goto arbitration_ok;
+ } else {
+ /*
+ * we are not allowed to transmit. Maybe next time.
+ */
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ } else if (this->dama_mode & DAMA_MASTER) {
+ /* >>>>> DAMA master <<<<<
+ *
+ * insert code here
+ * this could have been your ad! :-)
+ */
+ } else {
+ /* >>>>> CSMA <<<<<
+ *
+ * this implements a rather innovative channel access method.
+ * the basic idea is to run the usual slottime/persistence
+ * scheme, but with two significant changes:
+ * 1. slottime is derived from the bitrate of the channel
+ * 2. persistence is variable, depending on the dcd pattern
+ * of the channel.
+ *
+ * "Sample the dcd in intervals of half of a frames length and
+ * - increment persistence value if dcd is inactive,
+ * - decrement persistence value if dcd is active."
+ *
+ * simulations show that this scheme gives good collision
+ * avoidance and throughput without knowledge about the
+ * dcd propagation delay and station count. It will probably
+ * perform *much* too aggressive in a hidden station environment.
+ *
+ * Note: The check for hw.fast skips the channel arbitration
+ * stuff. Set this for KISS and ethernet devices.
+ */
+ if (!this->hw.fast && !duplex && !this->hw.ptt(this->netdev)) {
+ /* decide whether this is a "good" slot or not */
+ if (random_num() < ppers) {
+ /* ok, a good one, check the dcd now */
+ if (this->hw.dcd(this->netdev)) {
+ this->dcd_memory = 1;
+ /*
+ * too bad, dcd is up. we're too aggressive,
+ * but we must wait for a falling edge of the dcd
+ * before we can decrement persistence
+ */
+ if (this->dcd_dropped && ppers > 1)
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST))
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers);
+ if (this->needs_transmit)
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ /* update dcd memory */
+ this->dcd_memory = 0;
+ this->dcd_dropped = 0;
+ goto arbitration_ok;
+ } else {
+ /* a bad slot, check the dcd */
+ if (!this->hw.dcd(this->netdev)) {
+ /* um. dcd is down, we should have tx'd here. */
+ if (ppers < 128)
+ if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST))
+ ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers+1);
+ /* was it up the slot before? */
+ if (this->dcd_memory) {
+ this->dcd_dropped = 1;
+ }
+ this->dcd_memory = 0;
+ } else {
+ this->dcd_memory = 1;
+ }
+ if (this->needs_transmit)
+ ax25_dev_set_timer(this, jiffies_per_slot);
+ return;
+ }
+ }
+ }
+
+arbitration_ok:
+ /*
+ * OK, we may transmit, arbitration successful.
+ */
+ if (this->hw.rts) this->hw.rts(this->netdev);
+
+ /*
+ * compute the amount of bytes to send during 100ms (AX25_TICS)
+ */
+ max_bytes = (bit_per_jiffie * AX25_TICS);
+
+ /*
+ * UI Frames
+ */
+ while ((bytes_sent < max_bytes || this->hw.fast)
+ && ((skb = skb_dequeue(&this->unproto_queue)) != NULL)) {
+ ax25_queue_xmit(skb);
+ bytes_sent += skb->len;
+ }
+
+ /*
+ * traverse our list of connections. we're messing with a
+ * private list here and we will not sleep and schedule, so no
+ * further protection should be necessary.
+ *
+ * we implement a simple round robin style packet scheduler here.
+ * each device has a list of cnnections ready to transmit packets,
+ * and we loop through the connections until
+ * a. the list becomes empty
+ * b. the transmit time limit is reached.
+ * if a connection has no more packets left or exceeds its window
+ * of outbound packets, it is removed from the list.
+ */
+ while ((active = this->list.ready) != NULL && ((bytes_sent < max_bytes) || this->hw.fast)) {
+ unsigned short start;
+ unsigned short end;
+ struct sk_buff *skbn;
+ ax25_cb *peer;
+ int in_retransmit = 0;
+
+ skbn = skb_peek(&active->ack_queue);
+
+ /* transmit supervisory stuff first */
+ if (active->tx_rsp) {
+ int poll_bit = active->tx_rsp & 0x100;
+ int frametype = active->tx_rsp & 0x0ff;
+ active->tx_rsp = 0;
+ ax25_send_control(active, frametype, poll_bit, AX25_RESPONSE);
+
+
+ /*
+ * supervisory stuff is all done, clear state-change flag
+ */
+ ax25_clr_cond(active, AX25_COND_STATE_CHANGE);
+
+ if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */
+ active->ack_timer = 0;
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+ }
+ }
+
+ if (active->tx_cmd) {
+ int poll_bit = active->tx_cmd & 0x100;
+ int frametype = active->tx_cmd & 0x0ff;
+
+ active->tx_cmd = 0;
+
+ /*
+ * a problem exists due to a race condition between linux'
+ * packet-scheduler and the timer routine: a write timeout might
+ * happen before the packet actually reaches the device and is copied
+ * for transmission. our transmit routine will then grab the first
+ * packet off the ack queue, put a header in front of the data and
+ * queue it for transmission. now we have the obscure situation that
+ * we have two packets in our transmit queue that share a single data
+ * segment. this isn't bad by itself, but since the first
+ * retransmitted frame will have the poll bit set and eventually will
+ * carry an updated N(r), we modify the header of a yet unsent packet,
+ * resulting in a protocol violation.
+ *
+ * we do the obvious thing to prevent this here: if the packet we
+ * got from the ack queue is cloned, we make a private copy of the
+ * data.
+ */
+ if (poll_bit && skbn
+ && frametype == AX25_RR
+ && !(active->condition & (AX25_COND_PEER_RX_BUSY|AX25_COND_STATE_CHANGE))
+ && active->n2count < 4)
+ {
+ if (skb_cloned(skbn)) {
+ skb = skb_copy(skbn, GFP_ATOMIC);
+ } else
+ skb = skb_clone(skbn, GFP_ATOMIC);
+ if (skb) {
+ active->vs = active->va;
+ ax25_send_iframe(active, skb, AX25_POLLON);
+ active->vs = active->vs_max;
+ }
+ } else {
+ ax25_send_control(active, frametype, poll_bit, AX25_COMMAND);
+ }
+ /*
+ * supervisory stuff is all done, clear state-change flag
+ */
+ ax25_clr_cond(active, AX25_COND_STATE_CHANGE);
+ if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */
+ active->ack_timer = 0;
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+ }
+ }
+
+ /*
+ * if the write queue and ack queue are both empty,
+ * or connection is not in info transfer state
+ * or the peer station is busy
+ * or the window is closed
+ * or the write queue is empty and we may not retransmit yet
+ * then remove connection from the devices' readylist;
+ *
+ * NOTE: ax25_dev_remove_active implicitly advances the
+ * round robin pointer to schedule the next connection
+ * on the readylist.
+ */
+ skb = skb_peek(&active->write_queue);
+ if ((skb == NULL && skbn == NULL)
+ || active->state != AX25_STATE_3
+ || (active->condition & AX25_COND_PEER_RX_BUSY) != 0
+ || (start = active->vs) == (end = (active->va + active->window) & active->seqmask)
+ || (skb == NULL && start != active->va))
+ {
+ if (active->condition & AX25_COND_START_T1) {
+ ax25_clr_cond(active, AX25_COND_START_T1);
+ write_lock(&active->timer_lock);
+ active->wrt_timer = active->t1 = ax25_calculate_t1(active);
+ write_unlock(&active->timer_lock);
+ }
+ write_lock(&this->ready_lock); /* paranoia */
+ ax25_dev_remove_active(this);
+ write_unlock(&this->ready_lock);
+ continue;
+ }
+
+ /*
+ * handle RTS/CTS handshaking. drivers can request TX-Delay
+ * by returning 0 in the cts method. Note, that the driver still
+ * has to handle handshaking itself, but it can prevent to be
+ * flooded with frames while it's not ready to send.
+ */
+ if (this->needs_transmit < AX25_TX_STATE_CTS) {
+ if (this->hw.cts == NULL || this->hw.cts(this->netdev))
+ this->needs_transmit = AX25_TX_STATE_CTS;
+ else if (this->needs_transmit == AX25_TX_STATE_RTS)
+ this->needs_transmit = AX25_TX_STATE_WAIT_CTS;
+ else
+ break;
+ }
+
+ if (skbn != NULL && start == active->va) {
+ skb = skbn;
+ in_retransmit = 1;
+ }
+
+ /*
+ * clone the buffer, put the original into the
+ * ack_queue and transmit the copy. That way the
+ * socket will be uncharged from the memory when
+ * the packet is acked, not when it's transmitted.
+ */
+ if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+
+ /* advance pointer to current connection */
+ this->list.ready = active->ready.next;
+
+ ax25_send_iframe(active, skbn, AX25_POLLOFF);
+ if (!(DAMA_STATE(active) & DAMA_SLAVE)) {
+ ax25_start_t1(active);
+ }
+
+ /* implicit ACK */
+ ax25_clr_cond(active, AX25_COND_ACK_PENDING);
+
+ if (!in_retransmit) {
+ active->vs_max = active->vs = (active->vs + 1) & active->seqmask;
+ skb_dequeue(&active->write_queue);
+ skb_queue_tail(&active->ack_queue, skb);
+
+ if (active->vs_rtt == -1) {
+ active->rtt_timestamp = jiffies;
+ active->vs_rtt = active->vs;
+ }
+
+ this->tx_iframes++;
+ this->tx_bytes += skbn->len;
+ } else {
+ active->vs = active->vs_max;
+ if (active->condition & AX25_COND_START_T1) {
+ ax25_clr_cond(active, AX25_COND_START_T1);
+ write_lock(&active->timer_lock);
+ active->wrt_timer = active->t1 = ax25_calculate_t1(active);
+ write_unlock(&active->timer_lock);
+ }
+ ax25_dev_remove_ready(this, active);
+ }
+
+ bytes_sent += skbn->len;
+
+ peer = active->peer;
+ if (peer && (peer->condition & AX25_COND_OWN_RX_BUSY)
+ && skb_queue_len(&active->write_queue) < 5)
+ {
+ ax25_clr_cond(peer, AX25_COND_OWN_RX_BUSY);
+ ax25_set_cond(peer, AX25_COND_STATE_CHANGE);
+ peer->state = AX25_STATE_4;
+ ax25_transmit_enquiry(peer);
+ }
+ }
+
+ this->bytes_sent += bytes_sent;
+
+ if (this->list.ready == NULL) {
+ this->bytes_sent = 0;
+ this->needs_transmit = AX25_TX_STATE_IDLE;
+ } else {
+ if (this->bytes_sent > this->max_bytes) {
+ this->bytes_sent = 0;
+ ax25_dev_set_timer(this, HZ/2);
+ } else
+ ax25_dev_set_timer(this, AX25_TICS);
+ }
+}
+
+/*
+ * send a control frame
+ */
+static void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
+{
+ struct sk_buff *skb;
+ unsigned char *dptr;
+ struct net_device *dev;
+
+ if ((dev = ax25->device) == NULL)
+ return; /* Route died */
+
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr) + 2, GFP_ATOMIC)) == NULL)
+ return;
+
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr));
+
+ /* Assume a response - address structure for DTE */
+ if (ax25->seqmask == AX25_SEQMASK) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? AX25_PF : 0;
+ if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
+ *dptr |= (ax25->vr << 5);
+ } else {
+ if ((frametype & AX25_U) == AX25_U) {
+ dptr = skb_put(skb, 1);
+ *dptr = frametype;
+ *dptr |= (poll_bit) ? AX25_PF : 0;
+ } else {
+ dptr = skb_put(skb, 2);
+ dptr[0] = frametype;
+ dptr[1] = (ax25->vr << 1);
+ dptr[1] |= (poll_bit) ? AX25_EPF : 0;
+ }
+ }
+
+ skb->nh.raw = skb->data;
+ ax25_transmit_buffer(ax25, skb, type);
+ ax25->vl = ax25->vr; /* vl: last acked frame */
+}
+
+static void ax25_kick_device(struct ax25_dev* ax25_device)
+{
+ write_lock(&ax25_dev_lock);
+ if (!ax25_device->needs_transmit) {
+ ax25_device->needs_transmit = AX25_TX_STATE_RTS;
+ ax25_device->task_queue.routine = (void *) ax25_dev_timer;
+ ax25_device->task_queue.data = (void *)ax25_device;
+ ax25_device->task_queue.sync = 0;
+ queue_task(&ax25_device->task_queue, &tq_immediate);
+ mark_bh(IMMEDIATE_BH);
+ }
+ write_unlock(&ax25_dev_lock);
+}
+
+/*
+ * This procedure is passed a buffer descriptor for an iframe. It builds
+ * the rest of the control part of the frame and then writes it out.
+ *
+ */
+static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
+{
+ unsigned char *frame;
+
+ skb->nh.raw = skb->data;
+
+ if (ax25->seqmask == AX25_SEQMASK) {
+ frame = skb_push(skb, 1);
+
+ *frame = AX25_I;
+ *frame |= (poll_bit) ? AX25_PF : 0;
+ *frame |= (ax25->vr << 5);
+ *frame |= (ax25->vs << 1);
+ } else {
+ frame = skb_push(skb, 2);
+
+ frame[0] = AX25_I;
+ frame[0] |= (ax25->vs << 1);
+ frame[1] = (poll_bit) ? AX25_EPF : 0;
+ frame[1] |= (ax25->vr << 1);
+ }
+
+ ax25->idletimer = ax25->idle;
+ ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
+ ax25->vl = ax25->vr; /* vl: last acked frame */
+}
+
+static void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
+{
+ unsigned char *ptr;
+
+ if (ax25->device == NULL)
+ return;
+
+ if (skb_headroom(skb) < ax25_sizeof_addr(&ax25->addr)) {
+ printk(KERN_WARNING "ax25_transmit_buffer: not enough room for digi-peaters\n");
+ kfree_skb(skb);
+ return;
+ }
+
+ ptr = skb_push(skb, ax25_sizeof_addr(&ax25->addr));
+ ax25_build_addr(ptr, &ax25->addr, type, ax25->seqmask);
+ skb->dev = ax25->device;
+ ax25_queue_xmit(skb);
+}
+
+/* ---------------------------------------------------------------------*/
+
+/* A small shim to dev_queue_xmit to do any packet forwarding in operation. */
+static void ax25_queue_xmit(struct sk_buff *skb)
+{
+ skb->protocol = htons(ETH_P_AX25);
+ skb->dev = ax25_fwd_dev(skb->dev);
+
+ dev_queue_xmit(skb);
+}
+
+/* ---------------------------------------------------------------------*/
+
+static struct ax25_dev *ax25_dev_get_dev(struct net_device *dev)
+{
+ struct ax25_dev *ax25_device = AX25_PTR(dev);
+
+ if (ax25_device == NULL)
+ return NULL;
+
+ if (ax25_device->magic == AX25_DEV_MAGIC)
+ return ax25_device;
+
+ return NULL;
+}
diff --git a/net/ax25/ax25_ddi.h b/net/ax25/ax25_ddi.h
new file mode 100644
index 000000000..acaf4aa37
--- /dev/null
+++ b/net/ax25/ax25_ddi.h
@@ -0,0 +1,39 @@
+/*
+ * Interface declaration for the DDI layer.
+ *
+ * Matthias Welwarsky (DG2FEF) 05/25/98
+ *
+ */
+
+
+#ifndef _AX25_DDI_H
+#define _AX25_DDI_H
+
+enum {
+ AX25_TX_STATE_IDLE = 0,
+ AX25_TX_STATE_RTS,
+ AX25_TX_STATE_WAIT_CTS,
+ AX25_TX_STATE_CTS
+};
+
+extern void ax25_ddi_init(void);
+extern ax25_cb* ax25_dev_list(struct net_device *);
+extern void ax25_dev_insert_cb(ax25_cb *);
+extern void ax25_dev_remove_cb(ax25_cb *);
+extern ax25_cb* ax25_dev_find_listener(ax25_address *, int, struct net_device *);
+extern struct sock* ax25_dev_find_socket(ax25_address *, ax25_address *, struct net_device *, int);
+extern int ax25_dev_match_addr(ax25_address *, struct net_device *);
+extern void ax25_kick(ax25_cb *);
+extern void ax25_send_unproto(struct sk_buff*, struct net_device*);
+extern void ax25_send_broadcast(struct sk_buff*);
+extern void ax25_dev_set_value_notify(struct net_device *dev, int, int);
+extern void ax25_dev_device_up(struct net_device *);
+extern void ax25_dev_device_down(struct net_device *);
+extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *);
+extern struct net_device* ax25_fwd_dev(struct net_device *);
+extern int ax25_dev_get_info(char*, char**, off_t, int);
+extern void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval);
+extern void ax25_dev_set_dama(struct net_device *dev, int dama);
+
+extern struct net_device* ax25_devices[];
+#endif
diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c
deleted file mode 100644
index efeec64e0..000000000
--- a/net/ax25/ax25_dev.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Other kernels modules in this kit are generally BSD derived. See the copyright headers.
- *
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_route.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-
-ax25_dev *ax25_dev_list;
-
-ax25_dev *ax25_dev_ax25dev(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
-
- for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- if (ax25_dev->dev == dev)
- return ax25_dev;
-
- return NULL;
-}
-
-ax25_dev *ax25_addr_ax25dev(ax25_address *addr)
-{
- ax25_dev *ax25_dev;
-
- for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0)
- return ax25_dev;
-
- return NULL;
-}
-
-/*
- * This is called when an interface is brought up. These are
- * reasonable defaults.
- */
-void ax25_dev_device_up(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
- unsigned long flags;
-
- if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) {
- printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n");
- return;
- }
-
- ax25_unregister_sysctl();
-
- memset(ax25_dev, 0x00, sizeof(*ax25_dev));
-
- ax25_dev->dev = dev;
- ax25_dev->forward = NULL;
-
- ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE;
- ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE;
- ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF;
- ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE;
- ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW;
- ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW;
- ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1;
- ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2;
- ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3;
- ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE;
- ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2;
- ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN;
- ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL;
- ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT;
-
- save_flags(flags); cli();
- ax25_dev->next = ax25_dev_list;
- ax25_dev_list = ax25_dev;
- restore_flags(flags);
-
- ax25_register_sysctl();
-}
-
-void ax25_dev_device_down(struct net_device *dev)
-{
- ax25_dev *s, *ax25_dev;
- unsigned long flags;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return;
-
- ax25_unregister_sysctl();
-
- save_flags(flags); cli();
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- ax25_ds_del_timer(ax25_dev);
-#endif
-
- /*
- * Remove any packet forwarding that points to this device.
- */
- for (s = ax25_dev_list; s != NULL; s = s->next)
- if (s->forward == dev)
- s->forward = NULL;
-
- if ((s = ax25_dev_list) == ax25_dev) {
- ax25_dev_list = s->next;
- restore_flags(flags);
- kfree(ax25_dev);
- ax25_register_sysctl();
- return;
- }
-
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25_dev) {
- s->next = ax25_dev->next;
- restore_flags(flags);
- kfree(ax25_dev);
- ax25_register_sysctl();
- return;
- }
-
- s = s->next;
- }
-
- restore_flags(flags);
- ax25_register_sysctl();
-}
-
-int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd)
-{
- ax25_dev *ax25_dev, *fwd_dev;
-
- if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL)
- return -EINVAL;
-
- switch (cmd) {
- case SIOCAX25ADDFWD:
- if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL)
- return -EINVAL;
- if (ax25_dev->forward != NULL)
- return -EINVAL;
- ax25_dev->forward = fwd_dev->dev;
- break;
-
- case SIOCAX25DELFWD:
- if (ax25_dev->forward == NULL)
- return -EINVAL;
- ax25_dev->forward = NULL;
- break;
-
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-struct net_device *ax25_fwd_dev(struct net_device *dev)
-{
- ax25_dev *ax25_dev;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return dev;
-
- if (ax25_dev->forward == NULL)
- return dev;
-
- return ax25_dev->forward;
-}
-
-/*
- * Free all memory associated with device structures.
- */
-void __exit ax25_dev_free(void)
-{
- ax25_dev *s, *ax25_dev = ax25_dev_list;
-
- while (ax25_dev != NULL) {
- s = ax25_dev;
- ax25_dev = ax25_dev->next;
-
- kfree(s);
- }
-}
diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c
deleted file mode 100644
index 6c65baea5..000000000
--- a/net/ax25/ax25_ds_in.c
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c
- * Joerg(DL1BKE) Fixed it.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Joerg(DL1BKE) ax25->n2count never got reset
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_ds_timer.c.
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_SABME:
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_UA:
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- }
- ax25_dama_on(ax25);
-
- /* according to DK4EG´s spec we are required to
- * send a RR RESPONSE FINAL NR=0.
- */
-
- ax25_std_enquiry_response(ax25);
- break;
-
- case AX25_DM:
- if (pf) ax25_disconnect(ax25, ECONNREFUSED);
- break;
-
- default:
- if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_ds_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- case AX25_UA:
- if (pf) {
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- }
- break;
-
- case AX25_I:
- case AX25_REJ:
- case AX25_RNR:
- case AX25_RR:
- if (pf) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 3, Connected State.
- * The handling of the timer(s) is in file ax25_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25_requeue_frames(ax25);
- ax25_dama_on(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
-
- if (ax25_validate_nr(ax25, nr)) {
- if (ax25_check_iframes_acked(ax25, nr))
- ax25->n2count=0;
- if (type == AX25_COMMAND && pf)
- ax25_ds_enquiry_response(ax25);
- } else {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
-
- if (ax25_validate_nr(ax25, nr)) {
- if (ax25->va != nr)
- ax25->n2count=0;
-
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_requeue_frames(ax25);
-
- if (type == AX25_COMMAND && pf)
- ax25_ds_enquiry_response(ax25);
- } else {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_ds_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
- ax25_frames_acked(ax25, nr);
- ax25->n2count = 0;
- } else {
- if (ax25_check_iframes_acked(ax25, nr))
- ax25->n2count = 0;
- }
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_ds_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_ds_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_ds_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_ds_enquiry_response(ax25);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_ds_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * Higher level upcall for a LAPB frame
- */
-int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- int queued = 0, frametype, ns, nr, pf;
-
- frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
-
- switch (ax25->state) {
- case AX25_STATE_1:
- queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_2:
- queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_3:
- queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- }
-
- return queued;
-}
-
diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c
deleted file mode 100644
index e3e88d771..000000000
--- a/net/ax25/ax25_ds_subr.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c.
- * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(),
- * fixed ax25_dama_on() and ax25_dama_off().
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-void ax25_ds_nr_error_recovery(ax25_cb *ax25)
-{
- ax25_ds_establish_data_link(ax25);
-}
-
-/*
- * dl1bke 960114: transmit I frames on DAMA poll
- */
-void ax25_ds_enquiry_response(ax25_cb *ax25)
-{
- ax25_cb *ax25o;
-
- /* Please note that neither DK4EG´s nor DG2FEF´s
- * DAMA spec mention the following behaviour as seen
- * with TheFirmware:
- *
- * DB0ACH->DL1BKE <RR C P R0> [DAMA]
- * DL1BKE->DB0ACH <I NR=0 NS=0>
- * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
- * DL1BKE->DB0ACH <RR R F R0>
- *
- * The Flexnet DAMA Master implementation apparently
- * insists on the "proper" AX.25 behaviour:
- *
- * DB0ACH->DL1BKE <RR C P R0> [DAMA]
- * DL1BKE->DB0ACH <RR R F R0>
- * DL1BKE->DB0ACH <I NR=0 NS=0>
- * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5>
- *
- * Flexnet refuses to send us *any* I frame if we send
- * a REJ in case AX25_COND_REJECT is set. It is superfluous in
- * this mode anyway (a RR or RNR invokes the retransmission).
- * Is this a Flexnet bug?
- */
-
- ax25_std_enquiry_response(ax25);
-
- if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) {
- ax25_requeue_frames(ax25);
- ax25_kick(ax25);
- }
-
- if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL)
- ax25_ds_t1_timeout(ax25);
- else
- ax25->n2count = 0;
-
- ax25_start_t3timer(ax25);
- ax25_ds_set_timer(ax25->ax25_dev);
-
- for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
- if (ax25o == ax25)
- continue;
-
- if (ax25o->ax25_dev != ax25->ax25_dev)
- continue;
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) {
- ax25_ds_t1_timeout(ax25o);
- continue;
- }
-
- if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) {
- ax25_requeue_frames(ax25o);
- ax25_kick(ax25o);
- }
-
- if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL)
- ax25_ds_t1_timeout(ax25o);
-
- /* do not start T3 for listening sockets (tnx DD8NE) */
-
- if (ax25o->state != AX25_STATE_0)
- ax25_start_t3timer(ax25o);
- }
-}
-
-void ax25_ds_establish_data_link(ax25_cb *ax25)
-{
- ax25->condition &= AX25_COND_DAMA_MODE;
- ax25->n2count = 0;
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
-}
-
-/*
- * :::FIXME:::
- * This is a kludge. Not all drivers recognize kiss commands.
- * We need a driver level request to switch duplex mode, that does
- * either SCC changing, PI config or KISS as required. Currently
- * this request isn't reliable.
- */
-static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param)
-{
- struct sk_buff *skb;
- unsigned char *p;
-
- if (ax25_dev->dev == NULL)
- return;
-
- if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL)
- return;
-
- skb->nh.raw = skb->data;
- p = skb_put(skb, 2);
-
- *p++ = cmd;
- *p++ = param;
-
- skb->dev = ax25_dev->dev;
- skb->protocol = htons(ETH_P_AX25);
-
- dev_queue_xmit(skb);
-}
-
-/*
- * A nasty problem arises if we count the number of DAMA connections
- * wrong, especially when connections on the device already existed
- * and our network node (or the sysop) decides to turn on DAMA Master
- * mode. We thus flag the 'real' slave connections with
- * ax25->dama_slave=1 and look on every disconnect if still slave
- * connections exist.
- */
-static int ax25_check_dama_slave(ax25_dev *ax25_dev)
-{
- ax25_cb *ax25;
-
- for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
- if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1)
- return 1;
-
- return 0;
-}
-
-void ax25_dev_dama_on(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL)
- return;
-
- if (ax25_dev->dama.slave == 0)
- ax25_kiss_cmd(ax25_dev, 5, 1);
-
- ax25_dev->dama.slave = 1;
- ax25_ds_set_timer(ax25_dev);
-}
-
-void ax25_dev_dama_off(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL)
- return;
-
- if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) {
- ax25_kiss_cmd(ax25_dev, 5, 0);
- ax25_dev->dama.slave = 0;
- ax25_ds_del_timer(ax25_dev);
- }
-}
-
-void ax25_dama_on(ax25_cb *ax25)
-{
- ax25_dev_dama_on(ax25->ax25_dev);
- ax25->condition |= AX25_COND_DAMA_MODE;
-}
-
-void ax25_dama_off(ax25_cb *ax25)
-{
- ax25->condition &= ~AX25_COND_DAMA_MODE;
- ax25_dev_dama_off(ax25->ax25_dev);
-}
-
diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c
deleted file mode 100644
index 3c5b2ea9c..000000000
--- a/net/ax25/ax25_ds_timer.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c.
- * Joerg(DL1BKE) Added DAMA Slave Timeout timer
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static void ax25_ds_timeout(unsigned long);
-
-/*
- * Add DAMA slave timeout timer to timer list.
- * Unlike the connection based timers the timeout function gets
- * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT
- * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in
- * 1/10th of a second.
- */
-
-static void ax25_ds_add_timer(ax25_dev *ax25_dev)
-{
- struct timer_list *t = &ax25_dev->dama.slave_timer;
- t->data = (unsigned long) ax25_dev;
- t->function = &ax25_ds_timeout;
- t->expires = jiffies + HZ;
- add_timer(t);
-}
-
-void ax25_ds_del_timer(ax25_dev *ax25_dev)
-{
- if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer);
-}
-
-void ax25_ds_set_timer(ax25_dev *ax25_dev)
-{
- if (ax25_dev == NULL) /* paranoia */
- return;
-
- del_timer(&ax25_dev->dama.slave_timer);
- ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10;
- ax25_ds_add_timer(ax25_dev);
-}
-
-/*
- * DAMA Slave Timeout
- * Silently discard all (slave) connections in case our master forgot us...
- */
-
-static void ax25_ds_timeout(unsigned long arg)
-{
- ax25_dev *ax25_dev = (struct ax25_dev *) arg;
- ax25_cb *ax25;
-
- if (ax25_dev == NULL || !ax25_dev->dama.slave)
- return; /* Yikes! */
-
- if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) {
- ax25_ds_set_timer(ax25_dev);
- return;
- }
-
- for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) {
- if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
- continue;
-
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- }
-
- ax25_dev_dama_off(ax25_dev);
-}
-
-void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_0:
- /* Magic here: If we listen() and a new link dies before it
- is accepted() it isn't 'dead' so doesn't get removed. */
- if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- ax25_destroy_socket(ax25);
- return;
- }
- break;
-
- case AX25_STATE_3:
- /*
- * Check the state of the receive buffer.
- */
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
- (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
- ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- break;
- }
- }
- break;
- }
-
- ax25_start_heartbeat(ax25);
-}
-
-/* dl1bke 960114: T3 works much like the IDLE timeout, but
- * gets reloaded with every frame for this
- * connection.
- */
-void ax25_ds_t3timer_expiry(ax25_cb *ax25)
-{
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_dama_off(ax25);
- ax25_disconnect(ax25, ETIMEDOUT);
-}
-
-/* dl1bke 960228: close the connection when IDLE expires.
- * unlike T3 this timer gets reloaded only on
- * I frames.
- */
-void ax25_ds_idletimer_expiry(ax25_cb *ax25)
-{
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25->state = AX25_STATE_2;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t3timer(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-}
-
-/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC
- * within the poll of any connected channel. Remember
- * that we are not allowed to send anything unless we
- * get polled by the Master.
- *
- * Thus we'll have to do parts of our T1 handling in
- * ax25_enquiry_response().
- */
-void ax25_ds_t1_timeout(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_1:
- if (ax25->n2count == ax25->n2) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
- }
- } else {
- ax25->n2count++;
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_2:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- }
- break;
-
- case AX25_STATE_3:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- }
- break;
- }
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c
deleted file mode 100644
index 15ca9c453..000000000
--- a/net/ax25/ax25_iface.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static struct protocol_struct {
- struct protocol_struct *next;
- unsigned int pid;
- int (*func)(struct sk_buff *, ax25_cb *);
-} *protocol_list;
-
-static struct linkfail_struct {
- struct linkfail_struct *next;
- void (*func)(ax25_cb *, int);
-} *linkfail_list;
-
-static struct listen_struct {
- struct listen_struct *next;
- ax25_address callsign;
- struct net_device *dev;
-} *listen_list;
-
-int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
-{
- struct protocol_struct *protocol;
- unsigned long flags;
-
- if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
- return 0;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP || pid == AX25_P_ARP)
- return 0;
-#endif
- if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
- return 0;
-
- protocol->pid = pid;
- protocol->func = func;
-
- save_flags(flags);
- cli();
-
- protocol->next = protocol_list;
- protocol_list = protocol;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_protocol_release(unsigned int pid)
-{
- struct protocol_struct *s, *protocol = protocol_list;
- unsigned long flags;
-
- if (protocol == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (protocol->pid == pid) {
- protocol_list = protocol->next;
- restore_flags(flags);
- kfree(protocol);
- return;
- }
-
- while (protocol != NULL && protocol->next != NULL) {
- if (protocol->next->pid == pid) {
- s = protocol->next;
- protocol->next = protocol->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- protocol = protocol->next;
- }
-
- restore_flags(flags);
-}
-
-int ax25_linkfail_register(void (*func)(ax25_cb *, int))
-{
- struct linkfail_struct *linkfail;
- unsigned long flags;
-
- if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
- return 0;
-
- linkfail->func = func;
-
- save_flags(flags);
- cli();
-
- linkfail->next = linkfail_list;
- linkfail_list = linkfail;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_linkfail_release(void (*func)(ax25_cb *, int))
-{
- struct linkfail_struct *s, *linkfail = linkfail_list;
- unsigned long flags;
-
- if (linkfail == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (linkfail->func == func) {
- linkfail_list = linkfail->next;
- restore_flags(flags);
- kfree(linkfail);
- return;
- }
-
- while (linkfail != NULL && linkfail->next != NULL) {
- if (linkfail->next->func == func) {
- s = linkfail->next;
- linkfail->next = linkfail->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- linkfail = linkfail->next;
- }
-
- restore_flags(flags);
-}
-
-int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *listen;
- unsigned long flags;
-
- if (ax25_listen_mine(callsign, dev))
- return 0;
-
- if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL)
- return 0;
-
- listen->callsign = *callsign;
- listen->dev = dev;
-
- save_flags(flags);
- cli();
-
- listen->next = listen_list;
- listen_list = listen;
-
- restore_flags(flags);
-
- return 1;
-}
-
-void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *s, *listen = listen_list;
- unsigned long flags;
-
- if (listen == NULL)
- return;
-
- save_flags(flags);
- cli();
-
- if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) {
- listen_list = listen->next;
- restore_flags(flags);
- kfree(listen);
- return;
- }
-
- while (listen != NULL && listen->next != NULL) {
- if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) {
- s = listen->next;
- listen->next = listen->next->next;
- restore_flags(flags);
- kfree(s);
- return;
- }
-
- listen = listen->next;
- }
-
- restore_flags(flags);
-}
-
-int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
-{
- struct protocol_struct *protocol;
-
- for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
- if (protocol->pid == pid)
- return protocol->func;
-
- return NULL;
-}
-
-int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
-{
- struct listen_struct *listen;
-
- for (listen = listen_list; listen != NULL; listen = listen->next)
- if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
- return 1;
-
- return 0;
-}
-
-void ax25_link_failed(ax25_cb *ax25, int reason)
-{
- struct linkfail_struct *linkfail;
-
- for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
- (linkfail->func)(ax25, reason);
-}
-
-int ax25_protocol_is_registered(unsigned int pid)
-{
- struct protocol_struct *protocol;
-
- for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
- if (protocol->pid == pid)
- return 1;
-
- return 0;
-}
-
diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c
index 7c1484d6e..1e51005c4 100644
--- a/net/ax25/ax25_in.c
+++ b/net/ax25/ax25_in.c
@@ -1,68 +1,333 @@
/*
- * AX.25 release 037
+ * ax25_in.c: Routines for processing of incoming frames (NEW-AX.25)
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF),
+ * Jonathan (G4KLX), Alan Cox (GW4PTS), HaJo (DD8NE)
*
- * 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
- * the sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Added IP mode registration.
- * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
- * Upgraded state machine for SABME.
- * Added arbitrary protocol id support.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * HaJo(DD8NE) Added Idle Disc Timer T5
- * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
- * different behaviour. Fixed defrag
- * routine (I hope)
- * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Modularisation changes.
- * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
- * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file.
- * Joerg(DL1BKE) Fixed DAMA Slave.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Thomas(DL9SAU) Fixed missing initialization of skb->protocol.
+ * License: 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.
*/
#include <linux/config.h>
-#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <linux/netfilter.h>
+#include <linux/netdevice.h>
#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <net/arp.h> /* For arp_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
+#include <net/ip.h>
+#include <net/arp.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "af_ax25.h"
+#include "ax25_route.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_netlink.h"
+
+static int ax25_rx_fragment(ax25_cb*, struct sk_buff*);
+static void ax25_decode(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+static struct sock* ax25_find_socket(ax25_address*, ax25_address*, struct net_device*, int);
+static int ax25_match_addr(ax25_address*, struct net_device*);
+/* static void ax25_send_to_raw(struct sock*, struct sk_buff*, int); */
+static int ax25_rcv(struct sk_buff*, struct net_device*, struct packet_type*);
+
+/*
+ * Packet type registration data
+ */
+struct packet_type ax25_packet_type =
+{
+ 0, /* MUTTER ntohs(ETH_P_AX25),*/
+ 0, /* copy */
+ ax25_rcv,
+ NULL,
+ NULL,
+};
+
+/*
+ * Higher level upcall for a LAPB frame
+ */
+int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo* pkt_info)
+{
+ struct net_device *dev = skb->dev;
+
+ ax25_decode(ax25, skb, pkt_info);
+ ax25_dev_set_dama(dev, pkt_info->dama);
+ if (pkt_info->pf && (pkt_info->cmdrsp == AX25_COMMAND))
+ AX25_PTR(dev)->dama_polled = 1;
+ return ax25_lapb_table[ax25->state](ax25, skb, pkt_info);
+}
+
+/*
+ * Resequencer
+ */
+static inline unsigned int ax25_csum_skb(struct sk_buff *skb)
+{
+ unsigned char *cp = skb->data;
+ unsigned int csum = 0;
+
+ while (cp < (skb->data + skb->len))
+ csum += *cp++;
+
+ return csum;
+}
+
+void ax25_reseq_update(ax25_cb* ax25, struct sk_buff *skb, int ns)
+{
+ if (ax25->reseq[ns].skb != NULL)
+ return;
+
+ ax25->reseq[ns].csum = ax25_csum_skb(skb);
+}
+
+int ax25_reseq_in(ax25_cb *ax25, struct sk_buff *skb, int ns, int pf)
+{
+ unsigned int csum;
+
+ /*
+ * if either
+ * - the poll flag is set
+ * - the slot is not empty
+ * - we've already seen this frame
+ * drop it
+ */
+
+ if (pf || ax25->reseq[ns].skb != NULL ||
+ ax25->reseq[ns].csum == (csum = ax25_csum_skb(skb)))
+ return 0;
+
+ /*
+ * otherwise queue it
+ */
+ ax25->reseq[ns].skb = skb;
+ ax25->reseq[ns].csum = csum;
+
+ return 1;
+}
+
+void ax25_reseq_out(ax25_cb *ax25)
+{
+ struct sk_buff *skb;
+
+ while ((skb = ax25->reseq[ax25->vr].skb) != NULL && ax25_rx_iframe(ax25, skb)) {
+ ax25->reseq[ax25->vr].skb = NULL;
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ }
+}
+
+/*
+ * This is where all valid I frames are sent to, to be dispatched to
+ * whichever protocol requires them.
+ */
+int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+{
+ ax25_cb *peer;
+ unsigned char pid = *skb->data;
+ struct sock *sk = NULL;
+ int queued = 0;
+
+ int (*func)(struct sk_buff *, ax25_cb *);
+
+ if (ax25->device) {
+ AX25_PTR(ax25->device)->rx_iframes++;
+ AX25_PTR(ax25->device)->rx_bytes += skb->len;
+ }
+
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+
+ /*
+ * don't take any more data if we're about to close
+ * the connection.
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ ax25_set_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ kfree_skb(skb);
+ return 1;
+ }
+
+ /*
+ * we're only digipeating, don't care about pid
+ * and idle timeouts.
+ */
+ if ((peer = ax25->peer) != NULL) {
+ int max_qlen;
+
+ /*
+ * don't queue any more data if the peer is just about to
+ * be disconnected.
+ */
+ if (peer->state < AX25_STATE_3 || peer->condition & AX25_COND_RELEASE) {
+ kfree_skb(skb);
+ return 1;
+ }
+
+ skb_queue_tail(&peer->write_queue, skb);
+
+ /*
+ * check the length of write_queue, set OWN_RX_BUSY if
+ * enough queued.
+ */
+ max_qlen = peer->seqmask << 1;
+ if (max_qlen > 16)
+ max_qlen = 16;
+ if (skb_queue_len(&peer->write_queue) > max_qlen) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ }
+ ax25_kick(peer);
+
+ return 1;
+ }
+
+ ax25->idletimer = ax25->idle;
+
+ if (pid == AX25_P_SEGMENT) {
+ skb_pull(skb, 1);
+ queued = ax25_rx_fragment(ax25, skb);
+
+ } else if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 1); /* Remove PID */
+ queued = func(skb, ax25);
+
+ } else if ((sk = ax25->sk) != NULL) {
+ struct sk_buff *oskb;
+ /*
+ * check if we have frames left in ax25->rcv_queue, these
+ * must be delivered first to maintain the sequence.
+ */
+ while ((oskb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&sk->rmem_alloc) + oskb->truesize < sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(sk, oskb);
+ } else
+ break;
+ }
+ if (oskb == NULL)
+ ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * now handle the frame that has just come in.
+ */
+ if (ax25->pidincl || sk->protocol == pid) {
+ if (sk->shutdown & RCV_SHUTDOWN) {
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ kfree_skb(skb);
+ } else if (sock_queue_rcv_skb(sk, skb) != 0) {
+ /*
+ * no space left on the socket, become busy
+ * but keep queueing up the data.
+ */
+ ax25->condition |= AX25_COND_OWN_RX_BUSY;
+
+ /*
+ * don't queue an infinite amount of
+ * data, 10 frames should be enough
+ * and we'll send an RNR anyway. If
+ * the peer keeps bombing us with
+ * valid iframes, "return 0" forces
+ * the l2 to drop the frame without
+ * ACK'ng it. This will produce heavy
+ * load on the channel due to constantly
+ * retransmitted frames, but we cannot
+ * just drop the packets, the application
+ * cannot handle the data loss induced.
+ */
+ if (skb_queue_len(&ax25->rcv_queue) < ax25->seqmask)
+ skb_queue_tail(&ax25->rcv_queue, skb);
+ else
+ return 0;
+ }
+ queued = 1;
+ }
+ }
+
+ /*
+ * this is a kludge to satisfy the IMHO broken interface
+ * between the L2 and upper protocol handlers. There was no
+ * handler for the PID and the socket didn't accept the PID
+ * either, so we free the buffer and pretend to have queued
+ * it.
+ */
+ if (!queued)
+ kfree_skb(skb);
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+struct sock *ax25_make_new(struct sock *osk, struct net_device *dev)
+{
+ struct sock *sk;
+ ax25_cb *ax25;
+
+ if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL)
+ return NULL;
+
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ sk_free(sk);
+ return NULL;
+ }
+
+ switch (osk->type) {
+ case SOCK_DGRAM:
+ break;
+ case SOCK_SEQPACKET:
+ break;
+ default:
+ sk_free(sk);
+ ax25_free_cb(ax25);
+ return NULL;
+ }
+
+ sock_init_data(NULL, sk);
+
+ sk->destruct = ax25_free_sock;
+ sk->type = osk->type;
+ sk->socket = osk->socket;
+ sk->priority = osk->priority;
+ sk->protocol = osk->protocol;
+ sk->rcvbuf = osk->rcvbuf;
+ sk->sndbuf = osk->sndbuf;
+ sk->debug = osk->debug;
+ sk->state = TCP_ESTABLISHED;
+ sk->sleep = osk->sleep;
+ sk->zapped = osk->zapped;
+
+ ax25->seqmask = osk->protinfo.ax25->seqmask;
+ ax25->backoff = osk->protinfo.ax25->backoff;
+ ax25->pidincl = osk->protinfo.ax25->pidincl;
+ ax25->iamdigi = osk->protinfo.ax25->iamdigi;
+ ax25->rtt = osk->protinfo.ax25->rtt;
+ ax25->t1 = osk->protinfo.ax25->t1;
+ ax25->t2 = osk->protinfo.ax25->t2;
+ ax25->t3 = osk->protinfo.ax25->t3;
+ ax25->n2 = osk->protinfo.ax25->n2;
+ ax25->idle = osk->protinfo.ax25->idle;
+ ax25->paclen = osk->protinfo.ax25->paclen;
+ ax25->window = osk->protinfo.ax25->window;
+
+ ax25->device = dev;
+ ax25->addr = osk->protinfo.ax25->addr;
+
+ sk->protinfo.ax25 = ax25;
+ ax25->sk = sk;
+
+ return sk;
+}
/*
* Given a fragment, queue it on the fragment queue and if the fragment
@@ -83,16 +348,15 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
/* Last fragment received ? */
if (ax25->fragno == 0) {
- if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) {
+ if ((skbn = alloc_skb(MAX_HEADER + ax25->fraglen, GFP_ATOMIC)) == NULL) {
while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL)
kfree_skb(skbo);
return 1;
}
- skb_reserve(skbn, AX25_MAX_HEADER_LEN);
+ skb_reserve(skbn, MAX_HEADER);
- skbn->dev = ax25->ax25_dev->dev;
- skbn->h.raw = skbn->data;
+ skbn->dev = ax25->device;
skbn->nh.raw = skbn->data;
/* Copy data from the fragments */
@@ -126,360 +390,422 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb)
return 0;
}
+
/*
- * This is where all valid I frames are sent to, to be dispatched to
- * whichever protocol requires them.
+ * This routine is the centralised routine for parsing the control
+ * information for the different frame formats.
*/
-int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
+static void ax25_decode(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt_info)
{
- int (*func)(struct sk_buff *, ax25_cb *);
- volatile int queued = 0;
- unsigned char pid;
-
- if (skb == NULL) return 0;
-
- ax25_start_idletimer(ax25);
+ unsigned char *frame;
+
+ frame = skb->data;
+ pkt_info->frametype = AX25_ILLEGAL;
+ pkt_info->ns = pkt_info->nr = pkt_info->pf = 0;
+
+ if (ax25->seqmask == AX25_SEQMASK) {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x07;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[0] >> 5) & 0x07;
+ pkt_info->pf = frame[0] & AX25_PF;
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ }
+ skb_pull(skb, 1);
+ } else {
+ if ((frame[0] & AX25_S) == 0) {
+ pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */
+ pkt_info->ns = (frame[0] >> 1) & 0x7F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
+ pkt_info->frametype = frame[0] & 0x0F;
+ pkt_info->nr = (frame[1] >> 1) & 0x7F;
+ pkt_info->pf = frame[1] & AX25_EPF;
+ skb_pull(skb, 2);
+ } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
+ pkt_info->frametype = frame[0] & ~AX25_PF;
+ pkt_info->pf = frame[0] & AX25_PF;
+ skb_pull(skb, 1);
+ }
+ }
+}
- pid = *skb->data;
+/* ---------------------------------------------------------------------*/
+/*
+ * Find a control block that wants to accept the SABM we have just
+ * received.
+ */
+ax25_cb *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev)
+{
+ ax25_cb *s;
-#ifdef CONFIG_INET
- if (pid == AX25_P_IP) {
- /* working around a TCP bug to keep additional listeners
- * happy. TCP re-uses the buffer and destroys the original
- * content.
- */
- struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC);
- if (skbn != NULL) {
- kfree_skb(skb);
- skb = skbn;
- }
+ if (dev != NULL)
+ if ((s = ax25_dev_find_listener(addr, digi, dev)) != NULL)
+ return s;
- skb_pull(skb, 1); /* Remove PID */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = ax25->ax25_dev->dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */
- return 1;
- }
-#endif
- if (pid == AX25_P_SEGMENT) {
- skb_pull(skb, 1); /* Remove PID */
- return ax25_rx_fragment(ax25, skb);
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN
+ && s->iamdigi == digi
+ && !ax25cmp(&s->addr.src, addr))
+ return s;
}
+ return NULL;
+}
- if ((func = ax25_protocol_function(pid)) != NULL) {
- skb_pull(skb, 1); /* Remove PID */
- return (*func)(skb, ax25);
- }
+/* ---------------------------------------------------------------------*/
+/*
+ * Find an AX.25 socket given both ends.
+ */
+static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type)
+{
+ ax25_cb *s;
+ struct sock *sk;
- if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
- if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) {
- if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
- queued = 1;
- else
- ax25->condition |= AX25_COND_OWN_RX_BUSY;
+ if (dev != NULL)
+ if ((sk = ax25_dev_find_socket(my_addr, dest_addr, dev, type)) != NULL)
+ return sk;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->sk != NULL && s->sk->type == type
+ && !ax25cmp(&s->addr.src, my_addr)
+ && !ax25cmp(&s->addr.dest, dest_addr))
+ {
+ return s->sk;
}
}
+ return NULL;
+}
+
+/* ---------------------------------------------------------------------*/
- return queued;
+static int ax25_match_addr(ax25_address *addr, struct net_device *dev)
+{
+ ax25_cb *s;
+
+ if (dev != NULL && ax25_dev_match_addr(addr, dev))
+ return 1;
+
+ for (s = ax25_list; s != NULL; s = s->next) {
+ if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0)
+ return 1;
+ }
+ return 0;
}
+/* ---------------------------------------------------------------------*/
+
+#ifdef notdef
/*
- * Higher level upcall for a LAPB frame
+ * this is completely broken.
+ *
+ * it depends on that sk is the first of a linked list of sockets
+ * and traverses the list to find them all, cloning the packet if the
+ * protocol and socket type both match.
*/
-static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama)
+static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto)
{
- int queued = 0;
+ struct sk_buff *copy;
- if (ax25->state == AX25_STATE_0)
- return 0;
+ while (sk != NULL) {
+ if (sk->type == SOCK_RAW &&
+ sk->protocol == proto &&
+ atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) {
+ if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ return;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
+ if (sock_queue_rcv_skb(sk, copy) != 0)
+ kfree_skb(copy);
+ }
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (dama || ax25->ax25_dev->dama.slave)
- queued = ax25_ds_frame_in(ax25, skb, type);
- else
- queued = ax25_std_frame_in(ax25, skb, type);
- break;
-#endif
+ sk = sk->next;
}
-
- return queued;
}
+#endif
-static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *dev_addr, struct packet_type *ptype)
+/* ---------------------------------------------------------------------*/
+
+static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
{
struct sock *make;
struct sock *sk;
- int type = 0;
- ax25_digi dp, reverse_dp;
- ax25_cb *ax25;
- ax25_address src, dest;
- ax25_address *next_digi = NULL;
- ax25_dev *ax25_dev;
- struct sock *raw;
- int mine = 0;
- int dama;
+ ax25_cb *ax25, *peer = NULL;
+ ax25_address *next_digi;
+ ax25_pktinfo pinf;
+ ax25_addr_t reverse_addr;
+ int queued;
/*
* Process the AX.25/LAPB frame.
*/
- skb->h.raw = skb->data;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+ skb->sk = NULL;
+ queued = 0;
+ AX25_PTR(dev)->dama_polled = 0;
/*
- * Parse the address header.
+ * TODO: packet filter
*/
-
- if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) {
- kfree_skb(skb);
- return 0;
- }
+#ifdef undef
+ if (call_in_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT)
+ goto out_normal;
+#endif
/*
- * Ours perhaps ?
+ * Parse the frame and Pull of the AX.25 headers
+ * leaving the CTRL/PID byte
*/
- if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */
- next_digi = &dp.calls[dp.lastrepeat + 1];
+ if (ax25_parse_addr(skb->data, skb->len, &pinf) == NULL)
+ goto out_normal;
+ skb_pull(skb, ax25_sizeof_addr(&pinf.addr));
+
/*
- * Pull of the AX.25 headers leaving the CTRL/PID bytes
+ * Steps to perform while processing the frame, fast path'd for
+ * digipeating.
*/
- skb_pull(skb, ax25_addr_size(&dp));
-
- /* For our port addresses ? */
- if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* Also match on any registered callsign from L3/4 */
- if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi)
- mine = 1;
-
- /* UI frame - bypass LAPB processing */
- if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) {
- skb->h.raw = skb->data + 2; /* skip control and pid */
-
- if ((raw = ax25_addr_match(&dest)) != NULL)
- ax25_send_to_raw(raw, skb, skb->data[1]);
-
- if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) {
- kfree_skb(skb);
+ next_digi = NULL;
+ if (pinf.addr.lastrepeat + 1 < pinf.addr.dcount) {
+ /* Not yet digipeated completely */
+ struct net_device *out_dev;
+ next_digi = &pinf.addr.digipeater[pinf.addr.lastrepeat + 1];
+
+ /* check the frame type. do the easy thing first */
+ if ((*skb->data & ~AX25_PF) == AX25_UI) {
+ /* digipeat UI frame */
+
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* Now we are pointing at the pid byte */
- switch (skb->data[1]) {
-#ifdef CONFIG_INET
- case AX25_P_IP:
- skb_pull(skb,2); /* drop PID/CTRL */
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_IP);
- ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */
- break;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
- case AX25_P_ARP:
- skb_pull(skb,2);
- skb->h.raw = skb->data;
- skb->nh.raw = skb->data;
- skb->dev = dev;
- skb->pkt_type = PACKET_HOST;
- skb->protocol = htons(ETH_P_ARP);
- arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */
- break;
-#endif
- case AX25_P_TEXT:
- /* Now find a suitable dgram socket */
- if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) {
- if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
- kfree_skb(skb);
- } else {
- /*
- * Remove the control and PID.
- */
- skb_pull(skb, 2);
- if (sock_queue_rcv_skb(sk, skb) != 0)
- kfree_skb(skb);
- }
- } else {
- kfree_skb(skb);
- }
- break;
-
- default:
- kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
- break;
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /* no UI, but matches a given context */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- return 0;
- }
+ if ((*skb->data & ~AX25_PF) == AX25_SABM || (*skb->data & ~AX25_PF) == AX25_SABME) {
+ if ((ax25 = ax25_find_listener(next_digi, 1, dev)) != NULL)
+ goto listener_found;
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+ if ((peer = ax25_create_cb()) == NULL)
+ goto out_normal;
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_free_cb(peer);
+ goto out_normal;
+ }
- /*
- * Is connected mode supported on this device ?
- * If not, should we DM the incoming frame (except DMs) or
- * silently ignore them. For now we stay quiet.
- */
- if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) {
- kfree_skb(skb);
- return 0;
- }
+ out_dev = ax25_rt_set_addr(&peer->addr, &pinf.addr, dev, out_dev);
+ ax25_fillin_cb(peer, out_dev);
+ ax25_nlpost_route(&pinf, dev);
- /* LAPB */
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- /* AX.25 state 1-4 */
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_digi_invert(&dp, &reverse_dp);
+ /* crosslink the peers */
+ ax25->peer = peer;
+ peer->peer = ax25;
- if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) {
- /*
- * Process the frame. If it is queued up internally it returns one otherwise we
- * free it immediately. This routine itself wakes the user context layers so we
- * do no further work
- */
- if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
- kfree_skb(skb);
+ /* set window and modulus */
+ peer->window = ax25->window;
+ peer->seqmask = ax25->seqmask;
- return 0;
- }
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->state = AX25_STATE_0;
+ ax25_insert_cb(ax25);
- /* AX.25 state 0 (disconnected) */
+ peer->state = AX25_STATE_1;
+ ax25_insert_cb(peer);
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
- /* a) received not a SABM(E) */
+ goto out_normal;
+ }
- if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
/*
- * Never reply to a DM. Also ignore any connects for
- * addresses that are not our interfaces and not a socket.
+ * the packet was no UI or SABM(E) and didn't match an existing
+ * context. If it's via us, digipeat blindly.
*/
- if ((*skb->data & ~AX25_PF) != AX25_DM && mine)
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
+ /* check if next_digi matches one of our interfaces */
+ if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL)
+ goto out_normal;
+
+ /* rebuild the header and transmit the frame */
+ out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev);
+ skb->nh.raw = skb->data;
+ skb_push(skb, ax25_sizeof_addr(&reverse_addr));
+ ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK);
+ ax25_send_unproto(skb, out_dev);
return 0;
}
- /* b) received SABM(E) */
-
- if (dp.lastrepeat + 1 == dp.ndigi)
- sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET);
- else
- sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
+ /*
+ * UI frame - bypass LAPB processing
+ */
+ if ((*skb->data & ~0x10) == AX25_UI) {
+ int queued = 0;
+ int pid;
+ /* skip control and pid */
+ skb->nh.raw = skb->data + 2;
- if (sk != NULL) {
- if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) {
- if (mine) ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
- }
+ /* copy the datagram to all raw sockets */
+/* ax25_send_to_raw(&dest, skb, dev); */
- ax25 = make->protinfo.ax25;
- skb_set_owner_r(skb, make);
- skb_queue_head(&sk->receive_queue, skb);
+ /* if it's not a broadcast or one of our floating listeners, drop it */
+ if (!ax25_match_addr(&pinf.addr.dest, dev)
+ && ax25cmp(&pinf.addr.dest, (ax25_address *)dev->broadcast))
+ goto out_normal;
- make->state = TCP_ESTABLISHED;
- make->pair = sk;
+ /* Now we are pointing at the pid byte */
+ switch (pid = skb->data[1]) {
+ int (*func)(struct sk_buff *, ax25_cb *);
- sk->ack_backlog++;
- } else {
- if (!mine) {
- kfree_skb(skb);
- return 0;
- }
+ case AX25_P_TEXT:
+ /* Now find a suitable dgram socket */
+ if ((sk = ax25_find_socket(&pinf.addr.dest, &pinf.addr.src, dev, SOCK_DGRAM)) != NULL) {
+ if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) {
+ kfree_skb(skb);
+ } else {
+ /*
+ * Remove the control and PID.
+ */
+ skb_pull(skb, 2);
+ if (sock_queue_rcv_skb(sk, skb) != 0)
+ kfree_skb(skb);
+ }
+ } else {
+ kfree_skb(skb);
+ }
+ break;
- if ((ax25 = ax25_create_cb()) == NULL) {
- ax25_return_dm(dev, &src, &dest, &dp);
- kfree_skb(skb);
- return 0;
+ default:
+ if ((func = ax25_protocol_function(pid)) != NULL) {
+ skb_pull(skb, 2); /* Remove CTL/PID */
+ queued = func(skb, NULL);
+ }
+ if (!queued)
+ kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */
+ break;
}
- ax25_fillin_cb(ax25, ax25_dev);
+ return 0;
}
- ax25->source_addr = dest;
- ax25->dest_addr = src;
-
+ /* LAPB */
/*
- * Sort out any digipeated paths.
+ * invert the digipeater path and try to find a context for the
+ * received packet.
*/
- if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree_skb(skb);
- ax25_destroy_socket(ax25);
- return 0;
+ ax25_invert_addr(&pinf.addr, &reverse_addr);
+ if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) {
+ /*
+ * Process the frame. If it is queued up internally it returns one otherwise we
+ * free it immediately. This routine itself wakes the user context layers so we
+ * do no further work
+ */
+ queued = ax25_process_rx_frame(ax25, skb, &pinf);
+ goto out_queued;
}
- if (dp.ndigi == 0) {
- if (ax25->digipeat != NULL) {
- kfree(ax25->digipeat);
- ax25->digipeat = NULL;
+ if ((ax25 = ax25_find_listener(&pinf.addr.dest, 0, dev)) != NULL) {
+ listener_found:
+ /*
+ * if this frame is not a SABM(E) return DM to the sender.
+ * it belongs to a stale connection. maybe we have rebootet or the
+ * peer didn't see our UA/DM
+ */
+ if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
}
- } else {
- /* Reverse the source SABM's path */
- memcpy(&ax25->digipeat, &reverse_dp, sizeof(ax25_digi));
- }
- if ((*skb->data & ~AX25_PF) == AX25_SABME) {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW];
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25_dev->values[AX25_VALUES_WINDOW];
- }
-
- ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE);
+ /* ok, we have a listener */
+ ax25_nlpost_route(&pinf, dev);
+ if ((sk = ax25->sk) != NULL) {
+ /* the listener is a socket */
+ if (sk->type != SOCK_SEQPACKET || sk->state != TCP_LISTEN)
+ goto out_normal;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE)
- ax25_dama_on(ax25);
-#endif
+ if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ skb->sk = make;
+ make->state = TCP_ESTABLISHED;
+ make->pair = sk;
+ ax25 = make->protinfo.ax25;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ if (!sk->dead)
+ sk->data_ready(sk, skb->len);
+ queued = 1;
+ } else {
+ /*
+ * the listener is no socket, must be a callsign registered
+ * by a higher protocol, we just take the connection
+ */
+ if ((ax25 = ax25_create_cb()) == NULL) {
+ ax25_return_dm(dev, &pinf);
+ goto out_normal;
+ }
+ ax25->slcomp_enable = ax25_rt_mode_get(&pinf.addr.src) == 'C';
+ }
- ax25->state = AX25_STATE_3;
+ /* set up the new control block */
+ ax25_fillin_cb(ax25, dev);
+ ax25->addr = reverse_addr;
- ax25_insert_socket(ax25);
+ if ((*skb->data & ~AX25_PF) == AX25_SABME) {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW);
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW);
+ }
- ax25_start_heartbeat(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
+ ax25->device = dev;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->state = AX25_STATE_3;
+ ax25_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ }
- if (sk != NULL) {
- if (!sk->dead)
- sk->data_ready(sk, skb->len);
- } else {
+ out_queued:
+ if (!queued) {
+ out_normal:
kfree_skb(skb);
}
-
return 0;
}
-
-/*
- * Receive an AX.25 frame via a SLIP interface.
- */
-int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype)
-{
- skb->sk = NULL; /* Initially we don't know who it's for */
- skb->destructor = NULL; /* Who initializes this, dammit?! */
-
- if ((*skb->data & 0x0F) != 0) {
- kfree_skb(skb); /* Not a KISS data frame */
- return 0;
- }
-
- skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */
-
- return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype);
-}
-
diff --git a/net/ax25/ax25_in.h b/net/ax25/ax25_in.h
new file mode 100644
index 000000000..90aa12b1e
--- /dev/null
+++ b/net/ax25/ax25_in.h
@@ -0,0 +1,20 @@
+#ifndef _AX25_IN_H
+#define _AX25_IN_H
+
+/*
+ * prototype for state machine functions.
+ */
+
+typedef int (*ax25_statefunc_t)(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+
+extern int ax25_process_rx_frame(ax25_cb*, struct sk_buff*, ax25_pktinfo*);
+extern int ax25_rx_iframe(ax25_cb*, struct sk_buff*);
+extern void ax25_reseq_update(ax25_cb*, struct sk_buff*, int);
+extern int ax25_reseq_in(ax25_cb*, struct sk_buff*, int, int);
+extern void ax25_reseq_out(ax25_cb*);
+extern ax25_cb* ax25_find_listener(ax25_address*, int, struct net_device*);
+extern struct sock* ax25_make_new(struct sock*, struct net_device*);
+
+extern struct packet_type ax25_packet_type;
+
+#endif
diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c
deleted file mode 100644
index e6dcda83d..000000000
--- a/net/ax25/ax25_ip.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
- */
-
-#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/termios.h> /* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
-#include <net/ip.h>
-#include <net/arp.h>
-
-/*
- * IP over AX.25 encapsulation.
- */
-
-/*
- * Shove an AX.25 UI header on an IP packet and handle ARP
- */
-
-#ifdef CONFIG_INET
-
-int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- /* header is an AX.25 UI frame from us to them */
- unsigned char *buff = skb_push(skb, AX25_HEADER_LEN);
-
- *buff++ = 0x00; /* KISS DATA */
-
- if (daddr != NULL)
- memcpy(buff, daddr, dev->addr_len); /* Address specified */
-
- buff[6] &= ~AX25_CBIT;
- buff[6] &= ~AX25_EBIT;
- buff[6] |= AX25_SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- if (saddr != NULL)
- memcpy(buff, saddr, dev->addr_len);
- else
- memcpy(buff, dev->dev_addr, dev->addr_len);
-
- buff[6] &= ~AX25_CBIT;
- buff[6] |= AX25_EBIT;
- buff[6] |= AX25_SSSID_SPARE;
- buff += AX25_ADDR_LEN;
-
- *buff++ = AX25_UI; /* UI */
-
- /* Append a suitable AX.25 PID */
- switch (type) {
- case ETH_P_IP:
- *buff++ = AX25_P_IP;
- break;
- case ETH_P_ARP:
- *buff++ = AX25_P_ARP;
- break;
- default:
- printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%x2.2\n", type);
- *buff++ = 0;
- break;
- }
-
- if (daddr != NULL)
- return AX25_HEADER_LEN;
-
- return -AX25_HEADER_LEN; /* Unfinished header */
-}
-
-int ax25_rebuild_header(struct sk_buff *skb)
-{
- struct sk_buff *ourskb;
- unsigned char *bp = skb->data;
- struct net_device *dev;
- ax25_address *src, *dst;
- ax25_route *route;
- ax25_dev *ax25_dev;
-
- dst = (ax25_address *)(bp + 1);
- src = (ax25_address *)(bp + 8);
-
- if (arp_find(bp + 1, skb))
- return 1;
-
- route = ax25_rt_find_route(dst, NULL);
- dev = route->dev;
-
- if (dev == NULL)
- dev = skb->dev;
-
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return 1;
-
- if (bp[16] == AX25_P_IP) {
- if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
- /*
- * We copy the buffer and release the original thereby
- * keeping it straight
- *
- * Note: we report 1 back so the caller will
- * not feed the frame direct to the physical device
- * We don't want that to happen. (It won't be upset
- * as we have pulled the frame from the queue by
- * freeing it).
- *
- * NB: TCP modifies buffers that are still
- * on a device queue, thus we use skb_copy()
- * instead of using skb_clone() unless this
- * gets fixed.
- */
-
- ax25_address src_c;
- ax25_address dst_c;
-
- if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) {
- kfree_skb(skb);
- return 1;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(ourskb, skb->sk);
-
- kfree_skb(skb);
-
- src_c = *src;
- dst_c = *dst;
-
- skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
- skb->nh.raw = skb->data;
-
- ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c,
-&dst_c, route->digipeat, dev);
-
- return 1;
- }
- }
-
- bp[7] &= ~AX25_CBIT;
- bp[7] &= ~AX25_EBIT;
- bp[7] |= AX25_SSSID_SPARE;
-
- bp[14] &= ~AX25_CBIT;
- bp[14] |= AX25_EBIT;
- bp[14] |= AX25_SSSID_SPARE;
-
- skb_pull(skb, AX25_KISS_HEADER_LEN);
-
- if (route->digipeat != NULL) {
- if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
- kfree_skb(skb);
- return 1;
- }
-
- skb = ourskb;
- }
-
- skb->dev = dev;
-
- ax25_queue_xmit(skb);
-
- return 1;
-}
-
-#else /* INET */
-
-int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- return -AX25_HEADER_LEN;
-}
-
-int ax25_rebuild_header(struct sk_buff *skb)
-{
- return 1;
-}
-
-#endif
-
diff --git a/net/ax25/ax25_ipax.c b/net/ax25/ax25_ipax.c
new file mode 100644
index 000000000..65e8090a3
--- /dev/null
+++ b/net/ax25/ax25_ipax.c
@@ -0,0 +1,708 @@
+/*
+ * ax25_ipax.c: implements "virtual" interface ipax0 and en/decapsulation of IP
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Includes "Protocol Booster" and VJ compression switch.
+ * Written from scratch by Matthias Welwarsky in 1998.
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#define AX25_ENCAP_MODE_IGNORE_PROTOCOL
+
+#define __NO_VERSION__
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "ax25_route.h"
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_netlink.h"
+#include "ax25_subr.h"
+
+
+
+static int ipax_hard_header(struct sk_buff*, struct net_device*, unsigned short, void*, void*, unsigned);
+static int ipax_set_mac_address(struct net_device*, void*);
+static int ipax_probe(struct net_device*);
+static int ipax_device_event(struct notifier_block*, unsigned long, void*);
+static int ipax_open(struct net_device*);
+static int ipax_close(struct net_device*);
+static int ipax_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_arp_rcv(struct sk_buff*, ax25_cb*);
+static int ipax_vjc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_vjunc_rcv(struct sk_buff*, struct ax25_cb*);
+static int ipax_send_packet(struct sk_buff*, struct net_device*);
+static struct net_device_stats *ipax_get_stats(struct net_device*);
+
+/* --------------------------------------------------------------------- */
+
+static struct net_device ipax_device;
+
+static struct ipax_local_t {
+ struct net_device_stats stats;
+} ipax_local;
+
+/*
+ * Device up/down notifier block
+ */
+static struct notifier_block ipax_dev_notifier = {
+ ipax_device_event,
+ 0
+};
+
+static char ax25_bcast[7] =
+{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1};
+static char ax25_test[7] =
+{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1};
+
+/* ---------------------------------------------------------------------*/
+
+int ipax_init(void)
+{
+ struct net_device *dev = &ipax_device;
+
+ memset(dev, 0, sizeof (struct net_device));
+ strcpy(dev->name, "ipax0");
+ dev->if_port = 0;
+ dev->init = ipax_probe;
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ register_netdevice_notifier(&ipax_dev_notifier);
+
+ if (register_netdev(dev)) {
+ printk(KERN_WARNING "ipax: cannot register net device\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+int ipax_cleanup(void)
+{
+ unregister_netdevice_notifier(&ipax_dev_notifier);
+ unregister_netdev(&ipax_device);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_device_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+ return NOTIFY_DONE;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * set the MAC layer address of the interface. (i.e. the callsign)
+ *
+ */
+static int ipax_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = (struct sockaddr *)addr;
+
+ if (netif_running(dev))
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ memcpy(dev->dev_addr, sa->sa_data, dev->addr_len);
+ if (netif_running(dev))
+ ax25_listen_register((ax25_address *)dev->dev_addr, NULL);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Interface startup.
+ *
+ */
+static int ipax_probe(struct net_device *dev)
+{
+ if (dev == NULL)
+ return -ENXIO;
+
+ dev->base_addr = 0;
+ dev->irq = 0;
+ dev->dma = 0;
+
+ /* Initialize the device structure. */
+ dev->priv = &ipax_local;
+
+ memset(dev->priv, 0, sizeof(struct ipax_local_t));
+
+ dev->open = ipax_open;
+ dev->stop = ipax_close;
+ dev->hard_start_xmit = ipax_send_packet;
+ dev->get_stats = ipax_get_stats;
+
+ dev->hard_header = ipax_hard_header;
+ dev->set_mac_address = ipax_set_mac_address;
+
+ dev->type = ARPHRD_AX25;
+ dev->hard_header_len = AX25_MAX_HEADER_LEN;
+ dev->mtu = 1500;
+ dev->addr_len = AX25_ADDR_LEN;
+ dev->tx_queue_len = 8;
+
+ memcpy(dev->broadcast, ax25_bcast, dev->addr_len);
+ memcpy(dev->dev_addr, ax25_test, dev->addr_len);
+
+ dev->flags = IFF_BROADCAST;
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ */
+static int ipax_open(struct net_device *dev)
+{
+ if (netif_running(dev))
+ return 0;
+
+ if (!ax25_listen_register((ax25_address *)dev->dev_addr, NULL))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_IP, ipax_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_ARP, ipax_arp_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJCOMP, ipax_vjc_rcv))
+ return 0;
+
+ if (!ax25_protocol_register(AX25_P_VJUNCOMP, ipax_vjunc_rcv))
+ return 0;
+
+ /* dev_restart(dev); */ /* FIXME: anything to do here at all? */
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * the inverse routine to ipax_open. close the interface end
+ * decrement the module use count.
+ */
+static int ipax_close(struct net_device *dev)
+{
+ if (!netif_running(dev))
+ return 0;
+
+ netif_stop_queue(dev);
+
+ ax25_listen_release((ax25_address *)dev->dev_addr, NULL);
+ ax25_protocol_release(AX25_P_IP);
+ ax25_protocol_release(AX25_P_ARP);
+ ax25_protocol_release(AX25_P_VJCOMP);
+ ax25_protocol_release(AX25_P_VJUNCOMP);
+
+ return 0;
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ if (ax25) /* FIXME: works only for VC */
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL); /* Wrong ptype */
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_arp_rcv(struct sk_buff *skb, ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_ARP);
+ arp_rcv(skb, dev, NULL); /* Wrong ptype */
+
+ lp->stats.rx_packets++;
+
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * check if we already have initialized slots,
+ * if not, it's too late. flush the frame.
+ */
+ if (ax25->slcomp == NULL) {
+ ax25->slcomp = axhc_init(32, 32);
+ printk(KERN_DEBUG "ax25_vjc_recv: no vjc-slots allocated, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+
+ /*
+ * the data will grow upwards while decompressing,
+ * crippling the ax.25 header. copy the packet only if
+ * it's smaller than 256 bytes, else the defragmenter has
+ * already copied it, we don't do that twice.
+ */
+ if (skb->len <= 256) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) {
+ kfree_skb(skb);
+ skb = skbn;
+ }
+ }
+
+ /* set device the packet came in */
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+
+ /*
+ * make headroom for MAX_HEADER bytes of TCP/IP header
+ */
+ if (skb_headroom(skb) < MAX_HEADER) {
+ struct sk_buff *skbn;
+ if ((skbn = skb_realloc_headroom(skb, MAX_HEADER)) == NULL) {
+ printk(KERN_DEBUG "ax25_vjc_recv: cannot reallocate headroom, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ kfree_skb(skb);
+ skb = skbn;
+ }
+
+ if (axhc_uncompress(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjc_rcv: error decompressing packet, dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ /* adjust pointer to network header */
+ skb->nh.raw = skb->data;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_vjunc_rcv(struct sk_buff *skb, struct ax25_cb *ax25)
+{
+ struct net_device *dev = &ipax_device;
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+
+ if (!ax25)
+ return 0;
+
+ ax25->slcomp_enable = 1;
+
+ /*
+ * MW:
+ * check if we already have initialized slots,
+ * do so if not.
+ */
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+
+ if (axhc_remember(ax25->slcomp, skb) <= 0) {
+ printk(KERN_DEBUG "ipax_vjunc_rcv: unable to remember slot, packet dropped.\n");
+ lp->stats.rx_dropped++;
+ return 0;
+ }
+ skb->nh.raw = skb->data;
+ skb->dev = dev;
+ skb->pkt_type = PACKET_HOST;
+ skb->protocol = __constant_htons(ETH_P_IP);
+ ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev);
+ ip_rcv(skb, dev, NULL);
+ lp->stats.rx_packets++;
+ return 1;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static void ipax_fragment(struct sk_buff *skb, struct net_device *dev, ax25_addr_t *addr)
+{
+ struct iphdr *iph;
+ unsigned char *raw;
+ unsigned char *ptr;
+ struct sk_buff *skb2;
+ unsigned int mtu, hlen, left, len;
+ int offset;
+ int not_last_frag;
+
+ /*
+ * Point into the IP datagram header.
+ */
+
+ raw = skb->nh.raw;
+ iph = (struct iphdr*)raw;
+
+ /*
+ * Setup starting values.
+ */
+
+ hlen = iph->ihl * 4;
+ left = ntohs(iph->tot_len) - hlen; /* Space per frame */
+ mtu = dev->mtu; /* Size of data space */
+ ptr = raw + hlen; /* Where to start from */
+
+ /*
+ * The protocol doesn't seem to say what to do in the case that the
+ * frame + options doesn't fit the mtu. As it used to fall down dead
+ * in this case we were fortunate it didn't happen
+ *
+ * It is impossible, because mtu>=68. --ANK (980801)
+ */
+
+#ifdef CONFIG_NET_PARANOIA
+ if (mtu<8)
+ goto fail;
+#endif
+
+ /*
+ * Fragment the datagram.
+ */
+
+ offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
+ not_last_frag = iph->frag_off & htons(IP_MF);
+
+ /*
+ * Keep copying data until we run out.
+ */
+
+ while(left > 0) {
+ len = left;
+ /* IF: it doesn't fit, use 'mtu' - the data space left */
+ if (len > mtu)
+ len = mtu;
+ /* IF: we are not sending upto and including the packet end
+ then align the next start on an eight byte boundary */
+ if (len < left) {
+ len &= ~7;
+ }
+ /*
+ * Allocate buffer.
+ */
+
+ if ((skb2 = alloc_skb(len+hlen+AX25_MAX_HEADER_LEN+15,GFP_ATOMIC)) == NULL) {
+ NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
+ goto fail;
+ }
+
+ /*
+ * Set up data on packet
+ */
+
+ skb2->pkt_type = skb->pkt_type;
+ skb2->priority = skb->priority;
+ skb_reserve(skb2, (AX25_MAX_HEADER_LEN+15)&~15);
+ skb_put(skb2, len + hlen);
+ skb2->nh.raw = skb2->data;
+ skb2->h.raw = skb2->data + hlen;
+
+ /*
+ * Charge the memory for the fragment to any owner
+ * it might possess
+ */
+
+ if (skb->sk)
+ skb_set_owner_w(skb2, skb->sk);
+ skb2->dst = dst_clone(skb->dst);
+
+ /*
+ * Copy the packet header into the new buffer.
+ */
+
+ memcpy(skb2->nh.raw, raw, hlen);
+
+ /*
+ * Copy a block of the IP datagram.
+ */
+ memcpy(skb2->h.raw, ptr, len);
+ left -= len;
+
+ /*
+ * Fill in the new header fields.
+ */
+ iph = skb2->nh.iph;
+ iph->frag_off = htons((offset >> 3));
+
+ /* ANK: dirty, but effective trick. Upgrade options only if
+ * the segment to be fragmented was THE FIRST (otherwise,
+ * options are already fixed) and make it ONCE
+ * on the initial skb, so that all the following fragments
+ * will inherit fixed options.
+ */
+ if (offset == 0)
+ ip_options_fragment(skb);
+
+ /*
+ * Added AC : If we are fragmenting a fragment that's not the
+ * last fragment then keep MF on each bit
+ */
+ if (left > 0 || not_last_frag)
+ iph->frag_off |= htons(IP_MF);
+ ptr += len;
+ offset += len;
+
+ /*
+ * Put this fragment into the sending queue.
+ */
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragCreates++;
+ */
+
+ iph->tot_len = htons(len + hlen);
+
+ ip_send_check(iph);
+
+ /* build UI packet header */
+ *skb_push(skb2, 1) = AX25_P_IP;
+ *skb_push(skb2, 1) = AX25_UI;
+ skb_push(skb2, ax25_sizeof_addr(addr));
+ ax25_build_addr(skb2->data, addr, AX25_COMMAND, AX25_SEQMASK);
+ ax25_send_unproto(skb2, dev);
+ }
+ kfree_skb(skb);
+
+ /* FIXME: where did this go?
+ ip_statistics.IpFragOKs++;
+ */
+ return;
+
+fail:
+ kfree_skb(skb);
+ /* FIXME: where did this go?
+ ip_statistics.IpFragFails++;
+ */
+
+}
+
+/* ---------------------------------------------------------------------*/
+
+static unsigned char ipax_try_compress(ax25_cb *ax25, struct sk_buff *skb)
+{
+ unsigned char pid;
+
+ if (ax25->slcomp == NULL)
+ ax25->slcomp = axhc_init(32, 32);
+ /* attempt to compress the frame */
+ pid = axhc_compress(ax25->slcomp, skb, ax25->slcomp_enable);
+ if (pid)
+ *skb_push(skb, 1) = pid;
+ return pid;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ ax25_address *dest, *source;
+ struct ax25_route *axrt;
+ struct net_device *axdev;
+ ax25_addr_t addr;
+
+ /*
+ * send the packet down to the tunneling protocol
+ */
+ dest = (ax25_address *)skb->data;
+ source = (ax25_address *)(skb->data+AX25_ADDR_LEN);
+
+ if (!ax25cmp(dest, (ax25_address *)ax25_bcast)) {
+ ax25_send_broadcast(skb);
+ kfree_skb(skb);
+ lp->stats.tx_packets++;
+ return 0;
+ }
+
+ axrt = ax25_find_route(dest);
+ if (axrt == NULL) {
+ printk(KERN_DEBUG "ax25: ipax_send_packet(): no route found, discarding packet.\n");
+ kfree_skb(skb);
+ lp->stats.tx_dropped++;
+ return 0;
+ }
+
+ axdev = axrt->dev;
+
+ addr.dest = *dest;
+ addr.src = *source;
+ addr.dcount = axrt->path.dcount;
+ addr.lastrepeat = -1;
+ memcpy(addr.digipeater, axrt->path.digipeater, sizeof(ax25_address)*addr.dcount);
+
+ if (skb->data[15] == AX25_P_IP) {
+#ifndef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ struct iphdr *iph = (struct iphdr *)(skb->data+16);
+#endif
+ int mode = axrt->ip_mode;
+
+#ifdef AX25_ENCAP_MODE_IGNORE_PROTOCOL
+ if (1) {
+#else
+ if (iph->protocol != IPPROTO_UDP) {
+#endif
+ if (mode == 'V' || mode == 'C'
+ || (mode == ' ' && ax25_dev_get_value(axdev, AX25_VALUES_IPDEFMODE)))
+ {
+ int paclen = ax25_dev_get_value(axdev, AX25_VALUES_PACLEN);
+ ax25_cb* ax25 = ax25_find_cb(&addr, axdev);
+
+ if (ax25 != NULL) {
+ /* reuse a just disconnected control block */
+ if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) {
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ goto out_reused;
+ }
+ goto out_ok;
+ }
+ if ((ax25 = ax25_create_cb()) == NULL)
+ goto out_dropped;
+ ax25_fillin_cb(ax25, axdev);
+ ax25->addr = addr;
+ ax25_insert_cb(ax25);
+ out_reused:
+ ax25->slcomp_enable = (mode == 'C');
+ ax25_establish_data_link(ax25);
+ out_ok:
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ if (!ipax_try_compress(ax25, skb))
+ goto out_dropped;
+ ax25_output(ax25, paclen, skb);
+ dev->trans_start = jiffies;
+ /* netif_stop_queue(dev) */
+ lp->stats.tx_packets++;
+ return 0;
+ out_dropped:
+ lp->stats.tx_dropped++;
+ kfree_skb(skb);
+ /* netif_stop_queue(dev) */
+ return 0;
+ }
+ } else if (skb->len > axdev->mtu) {
+ /* completely strip the header */
+ skb_pull(skb, AX25_MIN_HEADER_LEN+1);
+ ipax_fragment(skb, axdev, &addr);
+ return 0;
+ }
+ }
+
+ /* rebuild the header if digipeaters are to be used */
+ if (addr.dcount != 0) {
+ /* strip address field, keep the control byte */
+ skb_pull(skb, AX25_MIN_HEADER_LEN-1);
+ skb_push(skb, ax25_sizeof_addr(&addr));
+ ax25_build_addr(skb->data, &addr, AX25_COMMAND, AX25_SEQMASK);
+ }
+
+ /*
+ * queue a completely assembled frame to the unproto
+ * queue of an interface
+ */
+ ax25_send_unproto(skb, axdev);
+ dev->trans_start = jiffies;
+ lp->stats.tx_packets++;
+ /* netif_stop_queue(dev) */
+ return 0;
+}
+
+/* ---------------------------------------------------------------------*/
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *ipax_get_stats(struct net_device *dev)
+{
+ struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv;
+ return &lp->stats;
+}
+
+/* ---------------------------------------------------------------------*/
+
+static int ipax_hard_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len)
+{
+ unsigned char pid;
+ unsigned char *buf;
+ int hlen;
+
+ if (type == ETH_P_IP)
+ pid = AX25_P_IP;
+ else if (type == ETH_P_ARP)
+ pid = AX25_P_ARP;
+ else {
+ printk(KERN_ERR "ax25: ipax_hard_header: unknown packet type %d.\n", type);
+ pid = 0;
+ }
+ *skb_push(skb, 1) = pid;
+ *skb_push(skb, 1) = AX25_UI;
+
+ if (saddr != NULL)
+ memcpy(skb_push(skb, AX25_ADDR_LEN), saddr, AX25_ADDR_LEN);
+ else
+ memcpy(skb_push(skb, dev->addr_len), dev->dev_addr, dev->addr_len);
+
+ if (daddr != NULL) {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), daddr, AX25_ADDR_LEN);
+ hlen = (AX25_MIN_HEADER_LEN+1);
+ } else {
+ memcpy(skb_push(skb, AX25_ADDR_LEN), &null_ax25_address, AX25_ADDR_LEN);
+ hlen = -(AX25_MIN_HEADER_LEN+1);
+ }
+
+ buf = skb->data;
+ buf[6] &= ~(AX25_CBIT|AX25_EBIT);
+ buf[6] |= AX25_SSSID_SPARE;
+ buf[13] &= ~AX25_CBIT;
+ buf[13] |= (AX25_EBIT|AX25_SSSID_SPARE);
+
+ return hlen;
+}
diff --git a/net/ax25/ax25_lapb.c b/net/ax25/ax25_lapb.c
new file mode 100644
index 000000000..849eeb44d
--- /dev/null
+++ b/net/ax25/ax25_lapb.c
@@ -0,0 +1,789 @@
+/*
+ * ax25_lapb.c: NEW-AX.25 state machine
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
+ * Rewritten from scratch by Matthias in 1998.
+ *
+ * Changelog:
+ *
+ * License: 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.
+ */
+
+#include <linux/socket.h>
+#include <linux/time.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include <net/tcp.h>
+
+#include "ax25_ddi.h"
+#include "ax25_vj.h"
+#include "ax25_route.h"
+#include "ax25_in.h"
+#include "ax25_lapb.h"
+#include "ax25_core.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
+
+/*
+ * Calculate the Round Trip Time
+ */
+static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw)
+{
+ switch (ax25->backoff) {
+ case 0:
+ ax25->rtt = rtt_raw;
+ break;
+
+ case 1:
+ case 2:
+ ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10;
+ break;
+ }
+}
+
+/*
+ * This routine purges the input queue of those frames that have been
+ * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
+ * SDL diagram.
+ */
+static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
+{
+ struct sk_buff *skb;
+ /*
+ * Remove all the ack-ed frames from ack_queue.
+ */
+ while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
+ ax25->va = (ax25->va + 1) & ax25->seqmask;
+ kfree_skb(skb);
+
+ if (ax25->vs_rtt == ax25->va) {
+ ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1);
+ ax25->vs_rtt = -1;
+ }
+ }
+ if (ax25->condition & AX25_COND_SETUP)
+ ax25_clr_cond(ax25, AX25_COND_SETUP);
+}
+
+/*
+ * This routine decides whether to restart T1 or not on an incoming
+ * frame. If the frame acks all outstanding frames, T1 is
+ * stopped, T3 is started. Else if it acks at least one
+ * outstanding frame T1 is restarted.
+ */
+static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
+{
+ write_lock(&ax25->timer_lock);
+ if (ax25->va != nr) {
+ /* at least one frame acked */
+ ax25_frames_acked(ax25, nr);
+ if ((ax25->vs != nr) && !(DAMA_STATE(ax25) & DAMA_SLAVE))
+ ax25->wrt_timer = ax25->t1;
+ }
+ if (ax25->vs == ax25->va) ax25->wrt_timer = ax25->t3;
+ write_unlock(&ax25->timer_lock);
+}
+
+/*
+ * Due to the changed retransmission handling frames are not
+ * requeued from the ack_queue into the write queue. All
+ * retransmission stuff is done in ax25_dev_timer() now. This
+ * function is for clarity only, it will either be removed
+ * completely or turn into an inline function.
+ */
+static void ax25_requeue_frames(ax25_cb *ax25)
+{
+ /*
+ * reset the send sequence counter.
+ */
+ ax25->vs = ax25->va;
+}
+
+/*
+ * Validate that the value of nr is between va and vs. Return true or
+ * false for testing.
+ */
+static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
+{
+ unsigned short ds;
+ unsigned short dr;
+
+ ds = ax25->vs_max - ax25->va;
+ ds &= ax25->seqmask;
+
+ dr = nr - ax25->va;
+ dr &= ax25->seqmask;
+
+ return (dr <= ds);
+}
+
+/*
+ * link reset handling. This is a rather complex routine, as it
+ * has to cope with a tricky situation in the protocol flow.
+ */
+static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb)
+{
+ ax25_cb* peer = ax25->peer;
+
+ write_lock(&ax25->timer_lock);
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->idletimer = ax25->idle;
+ ax25->n2count = 0;
+ write_unlock(&ax25->timer_lock);
+
+ /*
+ * peer just didn't get our UA, handle gracefully.
+ */
+ if (ax25->condition & AX25_COND_SETUP) {
+ ax25_requeue_frames(ax25);
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ return 0;
+ }
+
+ /*
+ * Ok, this is a *real* reset. If we have an attached socket,
+ * disconnect it and produce a new, accept-ready one.
+ */
+ if (ax25->sk != NULL) {
+ struct sock* make;
+ struct sock* sk;
+ ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device);
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n");
+
+ if (!tmp || !(sk = tmp->sk)
+ || sk->ack_backlog == sk->max_ack_backlog
+ || (make = ax25_make_new(sk, ax25->device)) == NULL)
+ {
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_0;
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ return 0;
+ }
+
+ /* set the destination address */
+ make->protinfo.ax25->addr = ax25->addr;
+
+ /*
+ * ax25_make_new() has produced a new ax25_cb, attached to
+ * a struct sock, we just throw away the old one the regular
+ * way and insert the new one into the appropriate device queue
+ *
+ * It should be enough to disconnect() the old control block,
+ * as this will wake up the application which then in turn
+ * does a close() on the respective socket descriptor.
+ */
+ ax25_remove_cb(ax25);
+ ax25_disconnect(ax25, ECONNRESET);
+ ax25_close_socket(ax25->sk, ECONNRESET);
+
+ /*
+ * done with the old one, now for the new one
+ */
+ write_lock(&ax25->timer_lock);
+ ax25 = make->protinfo.ax25;
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->vs_rtt = -1;
+ write_unlock(&ax25->timer_lock);
+
+ make->pair = sk;
+ skb->sk = make;
+ skb_queue_head(&sk->receive_queue, skb);
+ sk->ack_backlog++;
+ ax25_dev_insert_cb(ax25);
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+ sk->data_ready(sk, skb->len);
+ return 1;
+ }
+
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+
+ /*
+ * check if we have a peer connection to notify
+ */
+ if (peer) {
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n",
+ ax25->state, peer->state);
+
+ ax25_clear_queues(ax25);
+
+ write_lock(&ax25->timer_lock);
+
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->state = AX25_STATE_0;
+ ax25->condition = AX25_COND_SETUP;
+
+ write_unlock(&ax25->timer_lock);
+
+ ax25_clear_queues(peer);
+
+ write_lock(&peer->timer_lock);
+
+ peer->wrt_timer = 0;
+ peer->ack_timer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->condition = AX25_COND_SETUP;
+ peer->state = AX25_STATE_1;
+
+ write_unlock(&peer->timer_lock);
+
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ return 0;
+ }
+
+ printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n");
+
+ if (ax25->slcomp != NULL) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25->wrt_timer = ax25->t3;
+ ax25->condition = AX25_COND_SETUP;
+ ax25_tx_response(ax25, AX25_UA, AX25_POLLON);
+
+ return 0;
+}
+
+/*
+ * State machine for state 0, Disconnected State.
+ * we need this now for the hop-2-hop acknowledgement
+ */
+static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (peer) {
+ if (peer->condition & AX25_COND_SETUP) {
+ if (peer->seqmask == AX25_SEQMASK)
+ ax25_tx_command(peer, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(peer, AX25_SABME, AX25_POLLON);
+ ax25->killtimer = 30 * AX25_SLOWHZ;
+ } else {
+ struct net_device *dev;
+ if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL)
+ ax25_tx_command(ax25, AX25_DM, AX25_POLLON);
+ else {
+ ax25_fillin_cb(peer, dev);
+ ax25_reset_link(ax25, skb);
+ }
+ }
+ } else if (!ax25->sk)
+ ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ default:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+ }
+ return 0; /* we never queue */
+}
+
+
+/*
+ * State machine for state 1, Awaiting Connection State.
+ * The handling of the timer(s) is in file ax25_timer.c.
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ if (peer || ax25->sk == NULL) {
+ if (ax25->seqmask != AX25_SEQMASK) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ }
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_SABME:
+ if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) {
+ ax25->state = AX25_STATE_3;
+ ax25->vs_rtt = -1;
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ if (peer) {
+ peer->state = AX25_STATE_3;
+ peer->vs_rtt = -1;
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ }
+ }
+ break;
+
+ case AX25_DISC:
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_UA:
+ if (pkt->pf) {
+ write_lock(&ax25->timer_lock);
+ ax25->state = AX25_STATE_3;
+ ax25->wrt_timer = ax25->t3;
+ ax25->idletimer = ax25->idle;
+ ax25->killtimer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25->vs_rtt = -1;
+ ax25->n2count = 0;
+ ax25->tx_cmd = 0;
+ ax25->tx_rsp = 0;
+ write_unlock(&ax25->timer_lock);
+
+ if (peer != NULL) {
+ write_lock(&peer->timer_lock);
+ peer->wrt_timer = peer->t3;
+ peer->idletimer = 0;
+ peer->killtimer = 0;
+ peer->vs = 0;
+ peer->va = 0;
+ peer->vr = 0;
+ peer->vl = 0;
+ peer->vs_rtt = -1;
+ peer->n2count = 0;
+ peer->state = AX25_STATE_3;
+ ax25_clr_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_tx_response(peer, AX25_UA, pkt->pf);
+ write_unlock(&peer->timer_lock);
+ } else if (ax25->sk != NULL) {
+ ax25->sk->state = TCP_ESTABLISHED;
+ /* For WAIT_SABM connections we will produce an accept ready socket here */
+ ax25->sk->state_change(ax25->sk);
+ }
+
+ ax25_kick(ax25);
+ }
+ break;
+
+ case AX25_DM:
+ if (peer != NULL) {
+ ax25_set_cond(peer, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(peer, 0);
+ ax25_tx_response(peer, AX25_DM, pkt->pf);
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNREFUSED);
+ ax25_disconnect(ax25, ECONNREFUSED);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * State machine for state 2, Awaiting Release State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_DM, pkt->pf);
+ break;
+
+ case AX25_DM:
+ case AX25_UA:
+ if (pkt->pf) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ ax25->killtimer=0;
+ ax25->tx_cmd=0;
+ }
+ break;
+
+ case AX25_I:
+ case AX25_REJ:
+ case AX25_RNR:
+ case AX25_RR:
+ if (pkt->pf && pkt->cmdrsp == AX25_COMMAND)
+ ax25_tx_response(ax25, AX25_DM, AX25_POLLON);
+ break;
+
+ default:
+ break;
+ }
+ return queued;
+}
+
+/*
+ * State machine for state 3, Connected State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_COMMAND) {
+ ax25_enquiry_response(ax25);
+ }
+ if (ax25->vs == ax25->va) {
+ ax25->n2count=0;
+ } else {
+ ax25_requeue_frames(ax25);
+ if (DAMA_STATE(ax25) & DAMA_SLAVE) {
+ /*
+ * in DAMA mode we use n2count in state 3
+ * to track the number of DAMA polls
+ * ack'ing only part of our I frames. This
+ * is neccessary to prevent hangs with buggy
+ * masters.
+ */
+ if (++ax25->n2count == ax25->n2) {
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ }
+ }
+
+ ax25_kick(ax25);
+ break;
+
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_check_iframes_acked(ax25, pkt->nr);
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ ax25_requeue_frames(ax25);
+ ax25_kick(ax25);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_check_iframes_acked(ax25, pkt->nr);
+
+ if (pkt->ns == ax25->vr) {
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+#ifdef notdef
+ } else if (((pkt->ns+2) & ax25->seqmask) == ax25->vl) {
+ /*
+ * optimization (fast ack):
+ * If our peer's TX window is sent out
+ * completely we ack immediately without
+ * waiting for T2.
+ */
+ ax25_timeout_response(ax25);
+ } else {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+#else
+ } else ax25_timeout_response(ax25);
+#endif
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+/*
+ * State machine for state 4, Timer Recovery State.
+ * The handling of the timer(s) is in file ax25_timer.c
+ * Handling of state 0 and connection release is in ax25.c.
+ */
+static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ int queued = 0;
+ ax25_cb *peer = ax25->peer;
+
+ switch (pkt->frametype) {
+ case AX25_SABM:
+ case AX25_SABME:
+ if (pkt->frametype == AX25_SABM) {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ } else {
+ ax25->seqmask = AX25_ESEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW);
+ }
+ queued = ax25_reset_link(ax25, skb);
+ break;
+
+ case AX25_DISC:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25_disconnect(ax25, 0);
+ ax25_tx_response(ax25, AX25_UA, pkt->pf);
+ break;
+
+ case AX25_DM:
+ if (peer)
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ECONNRESET);
+ ax25_disconnect(ax25, ECONNRESET);
+ break;
+
+ case AX25_RR:
+ case AX25_RNR:
+ case AX25_REJ:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+
+ ax25_frames_acked(ax25, pkt->nr);
+
+ if (pkt->frametype == AX25_RR)
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ else if (pkt->frametype == AX25_REJ) {
+ ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY);
+ AX25_PTR(ax25->device)->rx_rejects++;
+ } else
+ ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY);
+
+ if (pkt->pf) {
+ if (pkt->cmdrsp == AX25_RESPONSE) {
+ write_lock(&ax25->timer_lock);
+ if (ax25->vs == ax25->va) {
+ ax25->n2count = 0;
+ ax25->wrt_timer = 0;
+ ax25->state = AX25_STATE_3;
+ ax25->tx_cmd = 0;
+ ax25->wrt_timer = ax25->t3;
+ }
+ write_unlock(&ax25->timer_lock);
+ ax25_requeue_frames(ax25);
+ } else {
+ if (DAMA_STATE(ax25) & DAMA_SLAVE)
+ ax25_requeue_frames(ax25);
+ ax25_enquiry_response(ax25);
+ }
+ }
+ ax25_kick(ax25);
+ break;
+
+ case AX25_I:
+ if (!ax25_validate_nr(ax25, pkt->nr)) {
+ ax25_nr_error_recovery(ax25);
+ break;
+ }
+ ax25_frames_acked(ax25, pkt->nr);
+ write_lock(&ax25->timer_lock);
+
+ if (pkt->ns == ax25->vr) {
+ /* frame is in sequence */
+ if ((queued = ax25_rx_iframe(ax25, skb)) != 0) {
+ ax25_reseq_update(ax25, skb, pkt->ns);
+ ax25->vr = (ax25->vr+1) & ax25->seqmask;
+ ax25_reseq_out(ax25);
+ ax25_clr_cond(ax25, AX25_COND_REJECT);
+ }
+ if (pkt->pf) {
+ ax25_enquiry_response(ax25);
+ } else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ /* frame is not in sequence */
+ queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf);
+ if (ax25->condition & AX25_COND_REJECT) {
+ if (pkt->pf)
+ ax25_enquiry_response(ax25);
+ else if (ax25->ack_timer == 0) {
+ ax25->ack_timer = ax25->t2;
+ ax25_set_cond(ax25, AX25_COND_ACK_PENDING);
+ }
+ } else {
+ ax25->ack_timer = 0;
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ ax25_set_cond(ax25, AX25_COND_REJECT);
+ ax25_tx_response(ax25, AX25_REJ, pkt->pf);
+ }
+ }
+
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ case AX25_FRMR:
+ case AX25_ILLEGAL:
+ ax25_nr_error_recovery(ax25);
+ break;
+
+ default:
+ break;
+ }
+
+ return queued;
+}
+
+static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt)
+{
+ printk(KERN_DEBUG "ax25_state_nop()\n");
+ return 0;
+}
+
+ax25_statefunc_t ax25_lapb_table[] =
+{
+ ax25_state0_machine,
+ ax25_state1_machine,
+ ax25_state2_machine,
+ ax25_state3_machine,
+ ax25_state4_machine,
+ ax25_state_nop
+};
diff --git a/net/ax25/ax25_lapb.h b/net/ax25/ax25_lapb.h
new file mode 100644
index 000000000..989eab629
--- /dev/null
+++ b/net/ax25/ax25_lapb.h
@@ -0,0 +1,11 @@
+#ifndef _AX25_LAPB_H
+#define _AX25_LAPB_H
+
+
+/*
+ * state machine tables for lapb and dama slave
+ */
+extern ax25_statefunc_t ax25_lapb_table[];
+extern ax25_statefunc_t ax25_dama_table[];
+
+#endif
diff --git a/net/ax25/ax25_netlink.c b/net/ax25/ax25_netlink.c
new file mode 100644
index 000000000..73e25e234
--- /dev/null
+++ b/net/ax25/ax25_netlink.c
@@ -0,0 +1,110 @@
+/*
+ * ax25_netlink.c: NETLINK interface for NEW-AX.25
+ *
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
+ *
+ * Comment: It is intended to develop a new AX.25 routing daemon using t
+ * method to communicate with the kernel part. Recent developme
+ * Linux' realtime abilities, however, suggest removing AX.25 c
+ * from kernel space.
+ *
+ * Changelog:
+ *
+ * License: 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.
+*/
+
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/ax25.h>
+#include <net/sock.h>
+#include <net/ax25.h>
+
+#include "ax25_netlink.h"
+#include "ax25_route.h"
+
+static struct sock *axrtnl;
+
+static void ax25_netlink_rcv(struct sock *sk, int len)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+ struct net_device *dev;
+
+ while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+ if (skb->len < sizeof(struct ax25_nlmsg)) {
+ kfree_skb(skb);
+ break;
+ }
+ nlmsg = (struct ax25_nlmsg *)skb->data;
+
+ switch (nlmsg->msg_type) {
+ case AX25_MSG_SETRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL
+ && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS)
+ ax25_add_route(&nlmsg->msg.pathmsg.path, dev);
+ break;
+
+ case AX25_MSG_DELRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL)
+ ax25_del_route(&nlmsg->msg.pathmsg.path.addr);
+ break;
+
+ case AX25_MSG_OPTRT:
+ if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL
+ && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS)
+ {
+ ax25_add_route(&nlmsg->msg.pathmsg.path, dev);
+ ax25_ipopt_route(&nlmsg->msg.pathmsg.path.addr, nlmsg->msg.pathmsg.mode);
+ }
+ }
+ kfree_skb(skb);
+ }
+}
+
+void ax25_nlpost_route(ax25_pktinfo *pkt, struct net_device *dev)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC);
+ if (!skb)
+ return;
+ nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg));
+
+ nlmsg->msg_type = AX25_MSG_RTINFO;
+ strncpy(nlmsg->msg.rtmsg.port_name, dev->name, sizeof(nlmsg->msg.rtmsg.port_name));
+ nlmsg->msg.rtmsg.addr = pkt->addr;
+ netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL);
+}
+
+void ax25_nlpost_armsg(unsigned int ip_addr, ax25_address *ax_addr, struct net_device *dev)
+{
+ struct ax25_nlmsg *nlmsg;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC);
+ if (!skb)
+ return;
+ nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg));
+
+ nlmsg->msg_type = AX25_MSG_ARINFO;
+ strncpy(nlmsg->msg.armsg.port_name, dev->name, sizeof(nlmsg->msg.armsg.port_name));
+ nlmsg->msg.armsg.ip_addr = ip_addr;
+ nlmsg->msg.armsg.ax_addr = *ax_addr;
+
+ netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL);
+}
+
+void ax25_netlink_init(void)
+{
+ axrtnl = netlink_kernel_create(NETLINK_AX25, ax25_netlink_rcv);
+}
+
+void ax25_netlink_cleanup(void)
+{
+ sock_release(axrtnl->socket);
+}
diff --git a/net/ax25/ax25_netlink.h b/net/ax25/ax25_netlink.h
new file mode 100644
index 000000000..c100ed212
--- /dev/null
+++ b/net/ax25/ax25_netlink.h
@@ -0,0 +1,10 @@
+#ifndef _AX25_NETLINK_H
+#define _AX25_NETLINK_H
+
+extern void ax25_netlink_init(void);
+extern void ax25_netlink_cleanup(void);
+
+extern void ax25_nlpost_route(ax25_pktinfo*, struct net_device*);
+extern void ax25_nlpost_armsg(unsigned int, ax25_address*, struct net_device*);
+
+#endif
diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c
index 231c170e7..a222ad1e8 100644
--- a/net/ax25/ax25_out.c
+++ b/net/ax25/ax25_out.c
@@ -1,129 +1,74 @@
/*
- * AX.25 release 037
+ * ax25_out.c: Subroutines for outgoing packets
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Only poll when window is full.
- * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output.
- * Added support for extended AX.25.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * Joerg(DL1BKE) Modified fragmenter to fragment vanilla
- * AX.25 I-Frames. Added PACLEN parameter.
- * Joerg(DL1BKE) Fixed a problem with buffer allocation
- * for fragments.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Joerg(DL1BKE) Fixed DAMA Slave mode: will work
- * on non-DAMA interfaces like AX25L2V2
- * again (this behaviour is _required_).
- * Joerg(DL1BKE) ax25_check_iframes_acked() returns a
- * value now (for DAMA n2count handling)
+ * License: 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.
*/
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <linux/netfilter.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev)
+#include "ax25_core.h"
+#include "ax25_route.h"
+#include "ax25_vj.h"
+#include "ax25_ddi.h"
+#include "ax25_subr.h"
+
+/* ---------------------------------------------------------------------*/
+/*
+ * send an iframe on a connection, maybe establish the connection first.
+ */
+ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_addr_t *addr, struct net_device *dev)
{
- ax25_dev *ax25_dev;
ax25_cb *ax25;
- /*
- * Take the default packet length for the device if zero is
- * specified.
- */
- if (paclen == 0) {
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return NULL;
-
- paclen = ax25_dev->values[AX25_VALUES_PACLEN];
- }
+ if (paclen == 0)
+ paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN);
/*
* Look for an existing connection.
*/
- if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) {
+ if ((ax25 = ax25_find_cb(addr, dev)) != NULL) {
+ /* reuse a just disconnected control block */
+ if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) {
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+ ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C';
+ ax25_establish_data_link(ax25);
+ }
ax25_output(ax25, paclen, skb);
return ax25; /* It already existed */
}
- if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL)
- return NULL;
-
if ((ax25 = ax25_create_cb()) == NULL)
return NULL;
- ax25_fillin_cb(ax25, ax25_dev);
+ ax25_fillin_cb(ax25, dev);
- ax25->source_addr = *src;
- ax25->dest_addr = *dest;
-
- if (digi != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- ax25_free_cb(ax25);
- return NULL;
- }
- memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
- }
-
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_establish_data_link(ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25_dev->dama.slave)
- ax25_ds_establish_data_link(ax25);
- else
- ax25_std_establish_data_link(ax25);
- break;
-#endif
- }
-
- ax25_insert_socket(ax25);
-
- ax25->state = AX25_STATE_1;
-
- ax25_start_heartbeat(ax25);
+ ax25->addr = *addr;
+ ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C';
+ ax25_establish_data_link(ax25);
+ ax25_insert_cb(ax25);
ax25_output(ax25, paclen, skb);
-
return ax25; /* We had to create it */
}
@@ -138,7 +83,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
struct sk_buff *skbn;
unsigned char *p;
int frontlen, len, fragno, ka9qfrag, first = 1;
- long flags;
if ((skb->len - 1) > paclen) {
if (*skb->data == AX25_P_TEXT) {
@@ -155,11 +99,7 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
frontlen = skb_headroom(skb); /* Address space + CTRL */
while (skb->len > 0) {
- save_flags(flags);
- cli();
-
if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) {
- restore_flags(flags);
printk(KERN_CRIT "AX.25: ax25_output - out of memory\n");
return;
}
@@ -167,13 +107,11 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
if (skb->sk != NULL)
skb_set_owner_w(skbn, skb->sk);
- restore_flags(flags);
-
len = (paclen > skb->len) ? skb->len : paclen;
if (ka9qfrag == 1) {
skb_reserve(skbn, frontlen + 2);
- skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
+
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 2);
@@ -186,7 +124,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
}
} else {
skb_reserve(skbn, frontlen + 1);
- skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data);
memcpy(skb_put(skbn, len), skb->data, len);
p = skb_push(skbn, 1);
*p = AX25_P_TEXT;
@@ -201,209 +138,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb)
skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */
}
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_kick(ax25);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- /*
- * A DAMA slave is _required_ to work as normal AX.25L2V2
- * if no DAMA master is available.
- */
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25);
- break;
-#endif
- }
-}
-
-/*
- * This procedure is passed a buffer descriptor for an iframe. It builds
- * the rest of the control part of the frame and then writes it out.
- */
-static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit)
-{
- unsigned char *frame;
-
- if (skb == NULL)
- return;
-
- skb->nh.raw = skb->data;
-
- if (ax25->modulus == AX25_MODULUS) {
- frame = skb_push(skb, 1);
-
- *frame = AX25_I;
- *frame |= (poll_bit) ? AX25_PF : 0;
- *frame |= (ax25->vr << 5);
- *frame |= (ax25->vs << 1);
- } else {
- frame = skb_push(skb, 2);
-
- frame[0] = AX25_I;
- frame[0] |= (ax25->vs << 1);
- frame[1] = (poll_bit) ? AX25_EPF : 0;
- frame[1] |= (ax25->vr << 1);
- }
-
- ax25_start_idletimer(ax25);
-
- ax25_transmit_buffer(ax25, skb, AX25_COMMAND);
-}
-
-void ax25_kick(ax25_cb *ax25)
-{
- struct sk_buff *skb, *skbn;
- int last = 1;
- unsigned short start, end, next;
-
- if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4)
- return;
-
- if (ax25->condition & AX25_COND_PEER_RX_BUSY)
- return;
-
- if (skb_peek(&ax25->write_queue) == NULL)
- return;
-
- start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs;
- end = (ax25->va + ax25->window) % ax25->modulus;
-
- if (start == end)
- return;
-
- ax25->vs = start;
-
- /*
- * Transmit data until either we're out of data to send or
- * the window is full. Send a poll on the final I frame if
- * the window is filled.
- */
-
- /*
- * Dequeue the frame and copy it.
- */
- skb = skb_dequeue(&ax25->write_queue);
-
- do {
- if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) {
- skb_queue_head(&ax25->write_queue, skb);
- break;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
-
- next = (ax25->vs + 1) % ax25->modulus;
- last = (next == end);
-
- /*
- * Transmit the frame copy.
- * bke 960114: do not set the Poll bit on the last frame
- * in DAMA mode.
- */
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF);
- break;
-
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- ax25_send_iframe(ax25, skbn, AX25_POLLOFF);
- break;
-#endif
- }
-
- ax25->vs = next;
-
- /*
- * Requeue the original data frame.
- */
- skb_queue_tail(&ax25->ack_queue, skb);
-
- } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-
- if (!ax25_t1timer_running(ax25)) {
- ax25_stop_t3timer(ax25);
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- }
-}
-
-void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- struct sk_buff *skbn;
- unsigned char *ptr;
- int headroom;
-
- if (ax25->ax25_dev == NULL) {
- ax25_disconnect(ax25, ENETUNREACH);
- return;
- }
-
- headroom = ax25_addr_size(ax25->digipeat);
-
- if (skb_headroom(skb) < headroom) {
- if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) {
- printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n");
- kfree_skb(skb);
- return;
- }
-
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
-
- kfree_skb(skb);
- skb = skbn;
- }
-
- ptr = skb_push(skb, headroom);
-
- ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus);
-
- skb->dev = ax25->ax25_dev->dev;
-
- ax25_queue_xmit(skb);
-}
-
-/*
- * A small shim to dev_queue_xmit to add the KISS control byte, and do
- * any packet forwarding in operation.
- */
-void ax25_queue_xmit(struct sk_buff *skb)
-{
- unsigned char *ptr;
-
- skb->protocol = htons(ETH_P_AX25);
- skb->dev = ax25_fwd_dev(skb->dev);
-
- ptr = skb_push(skb, 1);
- *ptr = 0x00; /* KISS */
-
- dev_queue_xmit(skb);
-}
-
-int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr)
-{
- if (ax25->vs == nr) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- return 1;
- } else {
- if (ax25->va != nr) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- return 1;
- }
- }
- return 0;
+ ax25_kick(ax25);
}
diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c
index 254ff36fb..cf468c65f 100644
--- a/net/ax25/ax25_route.c
+++ b/net/ax25/ax25_route.c
@@ -1,278 +1,246 @@
/*
- * AX.25 release 037
+ * ax25_route.c: Routing table management for NEW-AX.25
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4K
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * 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.
+ * Comment:
*
- * Other kernels modules in this kit are generally BSD derived. See the copyright headers.
+ * Changelog:
*
- *
- * History
- * AX.25 020 Jonathan(G4KLX) First go.
- * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list.
- * AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure. Device removal now
- * removes the heard structure.
- * AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping.
- * Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry.
- * AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and
- * ioctls to manipulate them. Added port
- * configuration.
- * AX.25 031 Jonathan(G4KLX) Added concept of default route.
- * Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by
- * destination call. Needed for IP routing via digipeater
- * Jonathan(G4KLX) Added routing for IP datagram packets.
- * Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default
- * route if available. Does not overwrite default routes
- * on route-table overflow anymore.
- * Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl()
- * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag
- * on routes.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * Jonathan(G4KLX) Support for packet forwarding.
- * Arnaldo C. Melo s/suser/capable/
+ * License: 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.
*/
-#include <linux/errno.h>
+#include <linux/config.h>
#include <linux/types.h>
#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
+#include <linux/spinlock.h>
#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
+#include <net/sock.h>
+#include <net/ax25_uid.h>
-static ax25_route *ax25_route_list;
+#include "af_ax25.h"
+#include "ax25_core.h"
+#include "ax25_route.h"
-static ax25_route *ax25_find_route(ax25_address *, struct net_device *);
+static struct ax25_route *ax25_route = NULL;
+rwlock_t ax25_rt_lock = RW_LOCK_UNLOCKED;
/*
- * small macro to drop non-digipeated digipeaters and reverse path
+ * delete all routes on a given device
*/
-static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out)
+void ax25_rt_device_down(struct net_device *dev)
{
- int k;
+ struct ax25_route *this, *last;
- for (k = 0; k < in->ndigi; k++)
- if (!in->repeated[k])
- break;
+ write_lock(&ax25_rt_lock);
+
+ this = ax25_route;
+ last = NULL;
- in->ndigi = k;
+ while (this) {
+ if (this->dev != dev) {
+ last = this;
+ this = this->next;
+ continue;
+ }
+ if (!last) {
+ ax25_route = this->next;
+ kfree(this);
+ this = ax25_route;
+ continue;
+ }
+ last->next = this->next;
+ kfree(this);
+ this = last->next;
+ }
- ax25_digi_invert(in, out);
+ write_unlock(&ax25_rt_lock);
}
-void ax25_rt_device_down(struct net_device *dev)
+int ax25_add_route(ax25_path_t *path, struct net_device *dev)
{
- ax25_route *s, *t, *ax25_rt = ax25_route_list;
-
- while (ax25_rt != NULL) {
- s = ax25_rt;
- ax25_rt = ax25_rt->next;
+ struct ax25_route *this;
+
+ write_lock(&ax25_rt_lock);
+
+ for (this = ax25_route; this; this = this->next) {
+ if (ax25cmp(&this->path.addr, &path->addr) == 0) {
+ this->path = *path;
+ this->dev = dev;
+ return 0;
+ }
+ }
+
+ if ((this = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_KERNEL)) == NULL)
+ return -ENOMEM;
+ this->path = *path;
+ this->dev = dev;
+ this->ip_mode = ' ';
+ this->next = ax25_route;
+ ax25_route = this;
+
+ write_unlock(&ax25_rt_lock);
+
+ return 0;
+}
+
+int ax25_del_route(ax25_address *addr)
+{
+ struct ax25_route *this, *last;
+
+ write_lock(&ax25_rt_lock);
+
+ this = ax25_route;
+ last = NULL;
+ while (this != NULL) {
+ if (ax25cmp(&this->path.addr, addr) != 0) {
+ last = this;
+ this = this->next;
+ continue;
+ }
+ if (!last) {
+ ax25_route = this->next;
+ kfree(this);
+ return 0;
+ }
+ last->next = this->next;
+ kfree(this);
+ return 0;
+ }
+
+ write_unlock(&ax25_rt_lock);
+
+ return -EINVAL;
+}
+
+int ax25_ipopt_route(ax25_address *addr, unsigned char opt)
+{
+ struct ax25_route *this;
+ int err = -EINVAL;
+
+ write_lock(&ax25_rt_lock);
- if (s->dev == dev) {
- if (ax25_route_list == s) {
- ax25_route_list = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- } else {
- for (t = ax25_route_list; t != NULL; t = t->next) {
- if (t->next == s) {
- t->next = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- break;
- }
- }
+ for (this = ax25_route; this; this = this->next) {
+ if (ax25cmp(&this->path.addr, addr) == 0) {
+ switch(opt) {
+ case ' ':
+ case 'D':
+ case 'V':
+ case 'C':
+ this->ip_mode = opt;
+ err = 0;
+ break;
}
}
}
+
+ write_unlock(&ax25_rt_lock);
+
+ return err;
}
int ax25_rt_ioctl(unsigned int cmd, void *arg)
{
- unsigned long flags;
- ax25_route *s, *t, *ax25_rt;
struct ax25_routes_struct route;
struct ax25_route_opt_struct rt_option;
- ax25_dev *ax25_dev;
+ struct net_device *dev;
+ ax25_path_t ax25_path;
+ int err = -EINVAL;
int i;
switch (cmd) {
- case SIOCADDRT:
- if (copy_from_user(&route, arg, sizeof(route)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
- return -EINVAL;
- if (route.digi_count > AX25_MAX_DIGIS)
- return -EINVAL;
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) {
- if (ax25_rt->digipeat != NULL) {
- kfree(ax25_rt->digipeat);
- ax25_rt->digipeat = NULL;
- }
- if (route.digi_count != 0) {
- if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- ax25_rt->digipeat->lastrepeat = -1;
- ax25_rt->digipeat->ndigi = route.digi_count;
- for (i = 0; i < route.digi_count; i++) {
- ax25_rt->digipeat->repeated[i] = 0;
- ax25_rt->digipeat->calls[i] = route.digi_addr[i];
- }
- }
- return 0;
- }
- }
- if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- ax25_rt->callsign = route.dest_addr;
- ax25_rt->dev = ax25_dev->dev;
- ax25_rt->digipeat = NULL;
- ax25_rt->ip_mode = ' ';
- if (route.digi_count != 0) {
- if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
- kfree(ax25_rt);
- return -ENOMEM;
- }
- ax25_rt->digipeat->lastrepeat = -1;
- ax25_rt->digipeat->ndigi = route.digi_count;
- for (i = 0; i < route.digi_count; i++) {
- ax25_rt->digipeat->repeated[i] = 0;
- ax25_rt->digipeat->calls[i] = route.digi_addr[i];
- }
- }
- save_flags(flags); cli();
- ax25_rt->next = ax25_route_list;
- ax25_route_list = ax25_rt;
- restore_flags(flags);
+ case SIOCADDRT:
+ /* do some sanity checks */
+ if (copy_from_user(&route, arg, sizeof(route))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
+ break;
+ if (route.digi_count > AX25_MAX_DIGIS)
break;
- case SIOCDELRT:
- if (copy_from_user(&route, arg, sizeof(route)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL)
- return -EINVAL;
- ax25_rt = ax25_route_list;
- while (ax25_rt != NULL) {
- s = ax25_rt;
- ax25_rt = ax25_rt->next;
- if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) {
- if (ax25_route_list == s) {
- ax25_route_list = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- } else {
- for (t = ax25_route_list; t != NULL; t = t->next) {
- if (t->next == s) {
- t->next = s->next;
- if (s->digipeat != NULL)
- kfree(s->digipeat);
- kfree(s);
- break;
- }
- }
- }
- }
- }
+ ax25_path.addr = route.dest_addr;
+ for (i = 0; i < route.digi_count; i++)
+ ax25_path.digipeater[i] = route.digi_addr[i];
+ ax25_path.dcount = route.digi_count;
+ err = ax25_add_route(&ax25_path, dev);
+ break;
+
+ case SIOCDELRT:
+ /* sanity checks */
+ if (copy_from_user(&route, arg, sizeof(route))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL)
break;
- case SIOCAX25OPTRT:
- if (copy_from_user(&rt_option, arg, sizeof(rt_option)))
- return -EFAULT;
- if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL)
- return -EINVAL;
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) {
- switch (rt_option.cmd) {
- case AX25_SET_RT_IPMODE:
- switch (rt_option.arg) {
- case ' ':
- case 'D':
- case 'V':
- ax25_rt->ip_mode = rt_option.arg;
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
- return -EINVAL;
- }
- }
- }
+ err = ax25_del_route(&route.dest_addr);
+ break;
+
+ case SIOCAX25OPTRT:
+ /* sanity checks */
+ if (copy_from_user(&rt_option, arg, sizeof(rt_option))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL)
break;
- default:
- return -EINVAL;
+ switch (rt_option.cmd) {
+ case AX25_SET_RT_IPMODE:
+ err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg);
+ break;
+ }
}
- return 0;
+ return err;
}
int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
{
- ax25_route *ax25_rt;
+ struct ax25_route *ax25_rt;
int len = 0;
off_t pos = 0;
off_t begin = 0;
char *callsign;
int i;
- cli();
+ read_lock(&ax25_rt_lock);
len += sprintf(buffer, "callsign dev mode digipeaters\n");
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0)
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0)
callsign = "default";
else
- callsign = ax2asc(&ax25_rt->callsign);
+ callsign = ax2asc(&ax25_rt->path.addr);
len += sprintf(buffer + len, "%-9s %-4s",
- callsign,
- ax25_rt->dev ? ax25_rt->dev->name : "???");
+ callsign,
+ ax25_rt->dev ? ax25_rt->dev->name : "???");
switch (ax25_rt->ip_mode) {
- case 'V':
- len += sprintf(buffer + len, " vc");
- break;
- case 'D':
- len += sprintf(buffer + len, " dg");
- break;
- default:
- len += sprintf(buffer + len, " *");
- break;
+ case 'V':
+ len += sprintf(buffer + len, " vc");
+ break;
+ case 'D':
+ len += sprintf(buffer + len, " dg");
+ break;
+ case 'C':
+ len += sprintf(buffer + len, " vj");
+ break;
+ default:
+ len += sprintf(buffer + len, " *");
+ break;
}
- if (ax25_rt->digipeat != NULL)
- for (i = 0; i < ax25_rt->digipeat->ndigi; i++)
- len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i]));
+ for (i = 0; i < ax25_rt->path.dcount; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->path.digipeater[i]));
len += sprintf(buffer + len, "\n");
@@ -287,7 +255,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
break;
}
- sti();
+ read_unlock(&ax25_rt_lock);
*start = buffer + (offset - begin);
len -= (offset - begin);
@@ -300,153 +268,149 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
/*
* Find AX.25 route
*/
-static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev)
+struct ax25_route *ax25_find_route(ax25_address *addr)
{
- ax25_route *ax25_spe_rt = NULL;
- ax25_route *ax25_def_rt = NULL;
- ax25_route *ax25_rt;
+ struct ax25_route *ax25_spe_rt = NULL;
+ struct ax25_route *ax25_def_rt = NULL;
+ struct ax25_route *ax25_rt;
- /*
- * Bind to the physical interface we heard them on, or the default
- * route if none is found;
- */
- for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
- if (dev == NULL) {
- if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL)
- ax25_spe_rt = ax25_rt;
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL)
- ax25_def_rt = ax25_rt;
- } else {
- if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev)
- ax25_spe_rt = ax25_rt;
- if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev)
- ax25_def_rt = ax25_rt;
- }
+ read_lock(&ax25_rt_lock);
+
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) {
+ if (ax25cmp(&ax25_rt->path.addr, addr) == 0)
+ ax25_spe_rt = ax25_rt;
+ if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0)
+ ax25_def_rt = ax25_rt;
}
if (ax25_spe_rt != NULL)
return ax25_spe_rt;
+ read_unlock(&ax25_rt_lock);
+
return ax25_def_rt;
}
/*
- * Adjust path: If you specify a default route and want to connect
- * a target on the digipeater path but w/o having a special route
- * set before, the path has to be truncated from your target on.
+ * MW: This is the core of the digipeating stuff. For a given
+ * src/dest it finds the appropriate device and digipeaterpath
+ * to route to.
*/
-static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat)
+struct net_device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct net_device *dev, struct net_device *out_dev)
{
- int k;
+ struct ax25_route *ax25rt;
+ ax25_address *next_dest;
+ ax25_address *dptr;
+ int more_digis;
- for (k = 0; k < digipeat->ndigi; k++) {
- if (ax25cmp(addr, &digipeat->calls[k]) == 0)
- break;
- }
+ /*
+ * find out where to go next. we route the packet either
+ * to the next digi behind us or to the destination. We
+ * NEVER route to the destination if there are digipeaters
+ * left.
+ */
+ more_digis = in->dcount - (in->lastrepeat+2);
+ if (more_digis > 0)
+ next_dest = &in->digipeater[in->lastrepeat+2];
+ else
+ next_dest = &in->dest;
- digipeat->ndigi = k;
-}
-
+ /*
+ * check for a route.
+ */
+ if ((ax25rt = ax25_find_route(next_dest)) != NULL)
+ out_dev = ax25rt->dev;
-/*
- * Find which interface to use.
- */
-int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
-{
- ax25_route *ax25_rt;
- ax25_address *call;
+ /*
+ * set up the digipeater path.
+ * for now we just copy the path of the incoming SABM
+ * up to the digipeater before us, if any.
+ */
+ out->dest = in->dest;
+ out->src = in->src;
- if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL)
- return -EHOSTUNREACH;
+ if (in->lastrepeat >= 0)
+ memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1));
- if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL)
- return -EHOSTUNREACH;
+ /*
+ * then we fill in the callsign of the device the frame
+ * came in.
+ */
+ out->lastrepeat = in->lastrepeat+1;
+ out->dcount = out->lastrepeat+1;
+ dptr = &out->digipeater[(int) out->lastrepeat];
+ *dptr++ = *((ax25_address *)dev->dev_addr);
- if ((call = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
- return -EPERM;
- call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
+ /*
+ * insert the route to next_dest, if any.
+ */
+ if (ax25rt != NULL && ax25rt->path.dcount != 0) {
+ memcpy(dptr, ax25rt->path.digipeater, sizeof(ax25_address) * ax25rt->path.dcount);
+ out->dcount += ax25rt->path.dcount;
+ dptr += ax25rt->path.dcount;
}
- ax25->source_addr = *call;
-
- if (ax25_rt->digipeat != NULL) {
- if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL)
- return -ENOMEM;
- memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
- ax25_adjust_path(addr, ax25->digipeat);
+ /*
+ * append next_dest if we route to a waypoint
+ */
+ while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) {
+ *dptr++ = *next_dest++;
+ out->dcount++;
}
-
- if (ax25->sk != NULL)
- ax25->sk->zapped = 0;
-
- return 0;
+ return out_dev;
}
/*
- * dl1bke 960117: build digipeater path
- * dl1bke 960301: use the default route if it exists
+ * Find the device to use
*/
-ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev)
+int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr)
{
- static ax25_route route;
- ax25_route *ax25_rt;
-
- if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) {
- route.next = NULL;
- route.callsign = *addr;
- route.dev = dev;
- route.digipeat = NULL;
- route.ip_mode = ' ';
- return &route;
- }
+ struct ax25_route *ax25_rt;
- return ax25_rt;
-}
+ if ((ax25_rt = ax25_find_route(addr)) == NULL)
+ return -EHOSTUNREACH;
-struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi)
-{
- struct sk_buff *skbn;
- unsigned char *bp;
- int len;
+/* ax25_remove_cb(ax25); */
+ ax25_fillin_cb(ax25, ax25_rt->dev);
+/* ax25_insert_cb(ax25); */
- len = digi->ndigi * AX25_ADDR_LEN;
+ return 0;
+}
- if (skb_headroom(skb) < len) {
- if ((skbn = skb_realloc_headroom(skb, len)) == NULL) {
- printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n");
- return NULL;
- }
+/*
+ * Return the IP mode of a given callsign/device pair.
+ */
+char ax25_rt_mode_get(ax25_address *callsign)
+{
+ struct ax25_route *ax25_rt;
- if (skb->sk != NULL)
- skb_set_owner_w(skbn, skb->sk);
+ read_lock(&ax25_rt_lock);
- kfree_skb(skb);
+ for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next)
+ if (ax25cmp(&ax25_rt->path.addr, callsign) == 0)
+ return ax25_rt->ip_mode;
- skb = skbn;
- }
+ read_unlock(&ax25_rt_lock);
- bp = skb_push(skb, len);
+ return ' ';
+}
- ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS);
- return skb;
-}
/*
- * Free all memory associated with routing structures.
+ * Free all memory associated with routing and device structures.
*/
-void __exit ax25_rt_free(void)
+void ax25_rt_free(void)
{
- ax25_route *s, *ax25_rt = ax25_route_list;
+ struct ax25_route *s, *ax25_rt = ax25_route;
+
+ write_lock(&ax25_rt_lock);
while (ax25_rt != NULL) {
s = ax25_rt;
ax25_rt = ax25_rt->next;
-
- if (s->digipeat != NULL)
- kfree(s->digipeat);
-
kfree(s);
}
+
+ write_unlock(&ax25_rt_lock);
}
diff --git a/net/ax25/ax25_route.h b/net/ax25/ax25_route.h
new file mode 100644
index 000000000..edcb41a6e
--- /dev/null
+++ b/net/ax25/ax25_route.h
@@ -0,0 +1,25 @@
+#ifndef _AX25_ROUTE_H
+#define _AX25_ROUTE_H
+
+struct ax25_route {
+ struct ax25_route *next;
+ ax25_path_t path;
+ struct net_device *dev;
+ char ip_mode;
+};
+
+extern void ax25_rt_device_down(struct net_device*);
+extern int ax25_rt_ioctl(unsigned int, void*);
+extern int ax25_rt_get_info(char*, char**, off_t, int);
+extern struct net_device* ax25_rt_set_addr(ax25_addr_t*, ax25_addr_t*, struct net_device*, struct net_device*);
+extern int ax25_rt_autobind(ax25_cb*, ax25_address*);
+extern int ax25_rt_fillin_dev(ax25_cb*, ax25_address*);
+extern char ax25_rt_mode_get(ax25_address*);
+extern struct ax25_route* ax25_find_route(ax25_address*);
+
+extern int ax25_add_route(ax25_path_t*, struct net_device*);
+extern int ax25_del_route(ax25_address*);
+extern int ax25_ipopt_route(ax25_address*, unsigned char);
+void ax25_rt_free(void);
+
+#endif
diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c
deleted file mode 100644
index d1cfc3ff9..000000000
--- a/net/ax25/ax25_std_in.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from
- * the sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * Jonathan(G4KLX) Added IP mode registration.
- * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception.
- * Upgraded state machine for SABME.
- * Added arbitrary protocol id support.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * HaJo(DD8NE) Added Idle Disc Timer T5
- * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly
- * different behaviour. Fixed defrag
- * routine (I hope)
- * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed.
- * AX.25 033 Jonathan(G4KLX) Remove auto-router.
- * Modularisation changes.
- * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer.
- * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <net/ip.h> /* For ip_rcv */
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * State machine for state 1, Awaiting Connection State.
- * The handling of the timer(s) is in file ax25_std_timer.c.
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_SABME:
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_UA:
- if (pf) {
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_ESTABLISHED;
- /* For WAIT_SABM connections we will produce an accept ready socket here */
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- }
- }
- break;
-
- case AX25_DM:
- if (pf) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ECONNREFUSED);
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- }
- }
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 2, Awaiting Release State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type)
-{
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- case AX25_UA:
- if (pf) ax25_disconnect(ax25, 0);
- break;
-
- case AX25_I:
- case AX25_REJ:
- case AX25_RNR:
- case AX25_RR:
- if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- break;
-
- default:
- break;
- }
-
- return 0;
-}
-
-/*
- * State machine for state 3, Connected State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25_requeue_frames(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_check_iframes_acked(ax25, nr);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- ax25_calculate_rtt(ax25);
- ax25_stop_t1timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_requeue_frames(ax25);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- if (ax25->condition & AX25_COND_PEER_RX_BUSY) {
- ax25_frames_acked(ax25, nr);
- } else {
- ax25_check_iframes_acked(ax25, nr);
- }
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_std_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_std_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_std_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * State machine for state 4, Timer Recovery State.
- * The handling of the timer(s) is in file ax25_std_timer.c
- * Handling of state 0 and connection release is in ax25.c.
- */
-static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type)
-{
- int queued = 0;
-
- switch (frametype) {
- case AX25_SABM:
- case AX25_SABME:
- if (frametype == AX25_SABM) {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- } else {
- ax25->modulus = AX25_EMODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW];
- }
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t3timer(ax25);
- ax25_start_idletimer(ax25);
- ax25->condition = 0x00;
- ax25->vs = 0;
- ax25->va = 0;
- ax25->vr = 0;
- ax25->state = AX25_STATE_3;
- ax25->n2count = 0;
- ax25_requeue_frames(ax25);
- break;
-
- case AX25_DISC:
- ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE);
- ax25_disconnect(ax25, 0);
- break;
-
- case AX25_DM:
- ax25_disconnect(ax25, ECONNRESET);
- break;
-
- case AX25_RR:
- case AX25_RNR:
- if (frametype == AX25_RR)
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- else
- ax25->condition |= AX25_COND_PEER_RX_BUSY;
- if (type == AX25_RESPONSE && pf) {
- ax25_stop_t1timer(ax25);
- ax25->n2count = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25_start_t3timer(ax25);
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_REJ:
- ax25->condition &= ~AX25_COND_PEER_RX_BUSY;
- if (pf && type == AX25_RESPONSE) {
- ax25_stop_t1timer(ax25);
- ax25->n2count = 0;
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- if (ax25->vs == ax25->va) {
- ax25_start_t3timer(ax25);
- ax25->state = AX25_STATE_3;
- } else {
- ax25_requeue_frames(ax25);
- }
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
- }
- if (type == AX25_COMMAND && pf)
- ax25_std_enquiry_response(ax25);
- if (ax25_validate_nr(ax25, nr)) {
- ax25_frames_acked(ax25, nr);
- ax25_requeue_frames(ax25);
- } else {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- }
- break;
-
- case AX25_I:
- if (!ax25_validate_nr(ax25, nr)) {
- ax25_std_nr_error_recovery(ax25);
- ax25->state = AX25_STATE_1;
- break;
- }
- ax25_frames_acked(ax25, nr);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY) {
- if (pf) ax25_std_enquiry_response(ax25);
- break;
- }
- if (ns == ax25->vr) {
- ax25->vr = (ax25->vr + 1) % ax25->modulus;
- queued = ax25_rx_iframe(ax25, skb);
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25->vr = ns; /* ax25->vr - 1 */
- ax25->condition &= ~AX25_COND_REJECT;
- if (pf) {
- ax25_std_enquiry_response(ax25);
- } else {
- if (!(ax25->condition & AX25_COND_ACK_PENDING)) {
- ax25->condition |= AX25_COND_ACK_PENDING;
- ax25_start_t2timer(ax25);
- }
- }
- } else {
- if (ax25->condition & AX25_COND_REJECT) {
- if (pf) ax25_std_enquiry_response(ax25);
- } else {
- ax25->condition |= AX25_COND_REJECT;
- ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE);
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- }
- }
- break;
-
- case AX25_FRMR:
- case AX25_ILLEGAL:
- ax25_std_establish_data_link(ax25);
- ax25->state = AX25_STATE_1;
- break;
-
- default:
- break;
- }
-
- return queued;
-}
-
-/*
- * Higher level upcall for a LAPB frame
- */
-int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type)
-{
- int queued = 0, frametype, ns, nr, pf;
-
- frametype = ax25_decode(ax25, skb, &ns, &nr, &pf);
-
- switch (ax25->state) {
- case AX25_STATE_1:
- queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_2:
- queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type);
- break;
- case AX25_STATE_3:
- queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- case AX25_STATE_4:
- queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type);
- break;
- }
-
- ax25_kick(ax25);
-
- return queued;
-}
diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c
deleted file mode 100644
index c868e0507..000000000
--- a/net/ax25/ax25_std_subr.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
- *
- * History
- * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-/*
- * The following routines are taken from page 170 of the 7th ARRL Computer
- * Networking Conference paper, as is the whole state machine.
- */
-
-void ax25_std_nr_error_recovery(ax25_cb *ax25)
-{
- ax25_std_establish_data_link(ax25);
-}
-
-void ax25_std_establish_data_link(ax25_cb *ax25)
-{
- ax25->condition = 0x00;
- ax25->n2count = 0;
-
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
-
- ax25_calculate_t1(ax25);
- ax25_stop_idletimer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_start_t1timer(ax25);
-}
-
-void ax25_std_transmit_enquiry(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
-
-void ax25_std_enquiry_response(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-}
-
-void ax25_std_timeout_response(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_OWN_RX_BUSY)
- ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE);
- else
- ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
-
- ax25->condition &= ~AX25_COND_ACK_PENDING;
-}
diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c
deleted file mode 100644
index 5a2d8771c..000000000
--- a/net/ax25/ax25_std_timer.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * AX.25 release 037
- *
- * This code REQUIRES 2.1.15 or higher/ NET3.038
- *
- * 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.
- *
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
- * AX.25 033 Jonathan(G4KLX) Modularisation functions.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- */
-
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-void ax25_std_heartbeat_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
-
- case AX25_STATE_0:
- /* Magic here: If we listen() and a new link dies before it
- is accepted() it isn't 'dead' so doesn't get removed. */
- if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) {
- ax25_destroy_socket(ax25);
- return;
- }
- break;
-
- case AX25_STATE_3:
- case AX25_STATE_4:
- /*
- * Check the state of the receive buffer.
- */
- if (ax25->sk != NULL) {
- if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) &&
- (ax25->condition & AX25_COND_OWN_RX_BUSY)) {
- ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE);
- break;
- }
- }
- }
-
- ax25_start_heartbeat(ax25);
-}
-
-void ax25_std_t2timer_expiry(ax25_cb *ax25)
-{
- if (ax25->condition & AX25_COND_ACK_PENDING) {
- ax25->condition &= ~AX25_COND_ACK_PENDING;
- ax25_std_timeout_response(ax25);
- }
-}
-
-void ax25_std_t3timer_expiry(ax25_cb *ax25)
-{
- ax25->n2count = 0;
- ax25_std_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
-}
-
-void ax25_std_idletimer_expiry(ax25_cb *ax25)
-{
- ax25_clear_queues(ax25);
-
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25->state = AX25_STATE_2;
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
-
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = 0;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
-}
-
-void ax25_std_t1timer_expiry(ax25_cb *ax25)
-{
- switch (ax25->state) {
- case AX25_STATE_1:
- if (ax25->n2count == ax25->n2) {
- if (ax25->modulus == AX25_MODULUS) {
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->modulus = AX25_MODULUS;
- ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW];
- ax25->n2count = 0;
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- }
- } else {
- ax25->n2count++;
- if (ax25->modulus == AX25_MODULUS)
- ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND);
- else
- ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_2:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND);
- }
- break;
-
- case AX25_STATE_3:
- ax25->n2count = 1;
- ax25_std_transmit_enquiry(ax25);
- ax25->state = AX25_STATE_4;
- break;
-
- case AX25_STATE_4:
- if (ax25->n2count == ax25->n2) {
- ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE);
- ax25_disconnect(ax25, ETIMEDOUT);
- return;
- } else {
- ax25->n2count++;
- ax25_std_transmit_enquiry(ax25);
- }
- break;
- }
-
- ax25_calculate_t1(ax25);
- ax25_start_t1timer(ax25);
-}
diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c
index 669789646..f8700bd8f 100644
--- a/net/ax25/ax25_subr.c
+++ b/net/ax25/ax25_subr.c
@@ -1,223 +1,77 @@
/*
- * AX.25 release 037
+ * ax25_subr.c: Subroutines for NEW-AX.25 state machine
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * 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.
+ * Comment: Most of this code is based on the SDL diagrams published in the
+ * ARRL Computer Networking Conference papers. The diagrams have mi
+ * in them, but are mostly correct. Before you modify the code coul
+ * read the SDL diagrams as the code is not obvious and probably ve
+ * easy to break;
*
- * Most of this code is based on the SDL diagrams published in the 7th
- * ARRL Computer Networking Conference papers. The diagrams have mistakes
- * in them, but are mostly correct. Before you modify the code could you
- * read the SDL diagrams as the code is not obvious and probably very
- * easy to break;
+ * Changelog:
*
- * History
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. Removed
- * old BSD code.
- * AX.25 030 Jonathan(G4KLX) Added support for extended AX.25.
- * Added fragmentation support.
- * Darryl(G7LED) Added function ax25_requeue_frames() to split
- * it up from ax25_frames_acked().
- * AX.25 031 Joerg(DL1BKE) DAMA needs KISS Fullduplex ON/OFF.
- * Thus we have ax25_kiss_cmd() now... ;-)
- * Dave Brown(N2RJT)
- * Killed a silly bug in the DAMA code.
- * Joerg(DL1BKE) Found the real bug in ax25.h, sri.
- * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of
- * enqueued buffers of a socket..
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
+ * License: 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.
*/
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
+#include <linux/tcp.h>
#include <linux/mm.h>
-#include <linux/interrupt.h>
+#include <net/sock.h>
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_core.h"
+#include "ax25_vj.h"
+#include "ax25_timer.h"
/*
* This routine purges all the queues of frames.
*/
void ax25_clear_queues(ax25_cb *ax25)
{
- struct sk_buff *skb;
-
- while ((skb = skb_dequeue(&ax25->write_queue)) != NULL)
- kfree_skb(skb);
+ int i;
- while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL)
- kfree_skb(skb);
+ skb_queue_purge(&ax25->frag_queue);
+ skb_queue_purge(&ax25->rcv_queue);
+ skb_queue_purge(&ax25->write_queue);
+ skb_queue_purge(&ax25->ack_queue);
- while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL)
- kfree_skb(skb);
+ for (i = 0; i <= AX25_SEQMASK; i++) {
+ struct sk_buff *skb;
- while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL)
- kfree_skb(skb);
-}
-
-/*
- * This routine purges the input queue of those frames that have been
- * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the
- * SDL diagram.
- */
-void ax25_frames_acked(ax25_cb *ax25, unsigned short nr)
-{
- struct sk_buff *skb;
-
- /*
- * Remove all the ack-ed frames from the ack queue.
- */
- if (ax25->va != nr) {
- while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) {
- skb = skb_dequeue(&ax25->ack_queue);
+ if ((skb = ax25->reseq[i].skb) != NULL) {
+ ax25->reseq[i].skb = NULL;
+ ax25->reseq[i].csum = 0;
kfree_skb(skb);
- ax25->va = (ax25->va + 1) % ax25->modulus;
}
}
}
-void ax25_requeue_frames(ax25_cb *ax25)
-{
- struct sk_buff *skb, *skb_prev = NULL;
-
- /*
- * Requeue all the un-ack-ed frames on the output queue to be picked
- * up by ax25_kick called from the timer. This arrangement handles the
- * possibility of an empty output queue.
- */
- while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) {
- if (skb_prev == NULL)
- skb_queue_head(&ax25->write_queue, skb);
- else
- skb_append(skb_prev, skb);
- skb_prev = skb;
- }
-}
-
/*
- * Validate that the value of nr is between va and vs. Return true or
- * false for testing.
+ * this is new
*/
-int ax25_validate_nr(ax25_cb *ax25, unsigned short nr)
+void ax25_tx_command(ax25_cb *ax25, int frametype, int poll_bit)
{
- unsigned short vc = ax25->va;
-
- while (vc != ax25->vs) {
- if (nr == vc) return 1;
- vc = (vc + 1) % ax25->modulus;
- }
-
- if (nr == ax25->vs) return 1;
-
- return 0;
+ if (poll_bit)
+ frametype |= 0x100;
+ ax25->tx_cmd = frametype;
+ ax25_kick(ax25);
}
-/*
- * This routine is the centralised routine for parsing the control
- * information for the different frame formats.
- */
-int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf)
+void ax25_tx_response(ax25_cb *ax25, int frametype, int poll_bit)
{
- unsigned char *frame;
- int frametype = AX25_ILLEGAL;
-
- frame = skb->data;
- *ns = *nr = *pf = 0;
-
- if (ax25->modulus == AX25_MODULUS) {
- if ((frame[0] & AX25_S) == 0) {
- frametype = AX25_I; /* I frame - carries NR/NS/PF */
- *ns = (frame[0] >> 1) & 0x07;
- *nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & AX25_PF;
- } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
- frametype = frame[0] & 0x0F;
- *nr = (frame[0] >> 5) & 0x07;
- *pf = frame[0] & AX25_PF;
- } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~AX25_PF;
- *pf = frame[0] & AX25_PF;
- }
- skb_pull(skb, 1);
- } else {
- if ((frame[0] & AX25_S) == 0) {
- frametype = AX25_I; /* I frame - carries NR/NS/PF */
- *ns = (frame[0] >> 1) & 0x7F;
- *nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & AX25_EPF;
- skb_pull(skb, 2);
- } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */
- frametype = frame[0] & 0x0F;
- *nr = (frame[1] >> 1) & 0x7F;
- *pf = frame[1] & AX25_EPF;
- skb_pull(skb, 2);
- } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */
- frametype = frame[0] & ~AX25_PF;
- *pf = frame[0] & AX25_PF;
- skb_pull(skb, 1);
- }
- }
-
- return frametype;
-}
-
-/*
- * This routine is called when the HDLC layer internally generates a
- * command or response for the remote machine ( eg. RR, UA etc. ).
- * Only supervisory or unnumbered frames are processed.
- */
-void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
-{
- struct sk_buff *skb;
- unsigned char *dptr;
-
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL)
- return;
-
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat));
-
- skb->nh.raw = skb->data;
-
- /* Assume a response - address structure for DTE */
- if (ax25->modulus == AX25_MODULUS) {
- dptr = skb_put(skb, 1);
- *dptr = frametype;
- *dptr |= (poll_bit) ? AX25_PF : 0;
- if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */
- *dptr |= (ax25->vr << 5);
- } else {
- if ((frametype & AX25_U) == AX25_U) {
- dptr = skb_put(skb, 1);
- *dptr = frametype;
- *dptr |= (poll_bit) ? AX25_PF : 0;
- } else {
- dptr = skb_put(skb, 2);
- dptr[0] = frametype;
- dptr[1] = (ax25->vr << 1);
- dptr[1] |= (poll_bit) ? AX25_EPF : 0;
- }
- }
-
- ax25_transmit_buffer(ax25, skb, type);
+ if (poll_bit)
+ frametype |= 0x100;
+ ax25->tx_rsp = frametype;
+ ax25_kick(ax25);
}
/*
@@ -225,44 +79,39 @@ void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type)
*
* Note: src here is the sender, thus it's the target of the DM
*/
-void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi)
+void ax25_return_dm(struct net_device *dev, ax25_pktinfo *pkt)
{
struct sk_buff *skb;
- char *dptr;
- ax25_digi retdigi;
+ unsigned char *dptr;
+ int sizeof_addr;
+ ax25_addr_t addr;
- if (dev == NULL)
- return;
+ sizeof_addr = ax25_sizeof_addr(&pkt->addr);
- if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL)
+ if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + sizeof_addr + 1, GFP_ATOMIC)) == NULL)
return; /* Next SABM will get DM'd */
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi));
- skb->nh.raw = skb->data;
-
- ax25_digi_invert(digi, &retdigi);
-
- dptr = skb_put(skb, 1);
+ skb_reserve(skb, AX25_BPQ_HEADER_LEN + sizeof_addr);
+ ax25_invert_addr(&pkt->addr, &addr);
- *dptr = AX25_DM | AX25_PF;
+ *skb_put(skb, 1) = AX25_DM|AX25_PF;
/*
* Do the address ourselves
*/
- dptr = skb_push(skb, ax25_addr_size(digi));
- dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS);
-
- skb->dev = dev;
-
- ax25_queue_xmit(skb);
+ skb->nh.raw = skb->data;
+ dptr = skb_push(skb, sizeof_addr);
+ dptr += ax25_build_addr(dptr, &addr, AX25_RESPONSE, AX25_SEQMASK);
+ ax25_send_unproto(skb, dev);
}
/*
* Exponential backoff for AX.25
*/
-void ax25_calculate_t1(ax25_cb *ax25)
+unsigned short ax25_calculate_t1(ax25_cb *ax25)
{
- int n, t = 2;
+ int n;
+ int t = 1;
switch (ax25->backoff) {
case 0:
@@ -273,52 +122,103 @@ void ax25_calculate_t1(ax25_cb *ax25)
break;
case 2:
- for (n = 0; n < ax25->n2count; n++)
- t *= 2;
- if (t > 8) t = 8;
+ t <<= (ax25->n2count < 8 ? ax25->n2count : 8);
break;
}
- ax25->t1 = t * ax25->rtt;
-}
+ n = (t * ax25->rtt);
-/*
- * Calculate the Round Trip Time
- */
-void ax25_calculate_rtt(ax25_cb *ax25)
-{
- if (ax25->backoff == 0)
- return;
+ if (n > AX25_T1CLAMPHI)
+ return AX25_T1CLAMPHI;
- if (ax25_t1timer_running(ax25) && ax25->n2count == 0)
- ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10;
+ if (n < AX25_T1CLAMPLO)
+ return AX25_T1CLAMPLO;
- if (ax25->rtt < AX25_T1CLAMPLO)
- ax25->rtt = AX25_T1CLAMPLO;
+ return n;
+}
- if (ax25->rtt > AX25_T1CLAMPHI)
- ax25->rtt = AX25_T1CLAMPHI;
+void ax25_close_socket(struct sock *sk, int reason)
+{
+ sk->err = reason;
+ sk->shutdown = SHUTDOWN_MASK;
+ sk->state = TCP_CLOSE;
+ sk->state_change(sk);
+ sk->dead = 1;
}
void ax25_disconnect(ax25_cb *ax25, int reason)
{
ax25_clear_queues(ax25);
+ ax25_link_failed(ax25, reason);
+ write_lock(&ax25->timer_lock);
+ ax25->wrt_timer = 0;
+ ax25->ack_timer = 0;
+ ax25->idletimer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25->killtimer = 90 * AX25_SLOWHZ;
+ ax25->state = AX25_STATE_0;
+ write_unlock(&ax25->timer_lock);
+ if (ax25->peer && ax25->peer->state > AX25_STATE_2)
+ ax25_set_cond(ax25->peer, AX25_COND_RELEASE);
+ if (ax25->slcomp) {
+ axhc_free(ax25->slcomp);
+ ax25->slcomp = NULL;
+ }
+}
- ax25_stop_t1timer(ax25);
- ax25_stop_t2timer(ax25);
- ax25_stop_t3timer(ax25);
- ax25_stop_idletimer(ax25);
+void ax25_nr_error_recovery(ax25_cb *ax25)
+{
+ if (ax25->peer != NULL)
+ ax25->peer->condition |= AX25_COND_RELEASE;
- ax25->state = AX25_STATE_0;
+ ax25_clear_queues(ax25);
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_2;
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+}
- ax25_link_failed(ax25, reason);
+void ax25_establish_data_link(ax25_cb *ax25)
+{
+ ax25->timer_lock= RW_LOCK_UNLOCKED;
+ ax25->state = AX25_STATE_1;
+ ax25->n2count = 0;
+ ax25->ack_timer = 0;
+ ax25->vs = 0;
+ ax25->va = 0;
+ ax25->vr = 0;
+ ax25->vl = 0;
+ ax25_set_cond(ax25, AX25_COND_SETUP);
+ ax25_start_t1(ax25);
+
+ if (ax25->seqmask == AX25_SEQMASK)
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(ax25, AX25_SABME, AX25_POLLON);
- if (ax25->sk != NULL) {
- ax25->sk->state = TCP_CLOSE;
- ax25->sk->err = reason;
- ax25->sk->shutdown |= SEND_SHUTDOWN;
- if (!ax25->sk->dead)
- ax25->sk->state_change(ax25->sk);
- ax25->sk->dead = 1;
- }
+}
+
+void ax25_transmit_enquiry(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_command(ax25, ft, AX25_POLLON);
+ ax25_start_t1(ax25);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+ if (DAMA_STATE(ax25) & DAMA_SLAVE) AX25_PTR(ax25->device)->dama_polled = 1;
+}
+
+void ax25_enquiry_response(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_response(ax25, ft, AX25_POLLON);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
+}
+
+void ax25_timeout_response(ax25_cb *ax25)
+{
+ int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR;
+ ax25_tx_response(ax25, ft, AX25_POLLOFF);
+ ax25_clr_cond(ax25, AX25_COND_ACK_PENDING);
}
diff --git a/net/ax25/ax25_subr.h b/net/ax25/ax25_subr.h
new file mode 100644
index 000000000..026217ae1
--- /dev/null
+++ b/net/ax25/ax25_subr.h
@@ -0,0 +1,38 @@
+#ifndef _AX25_SUBR_H
+#define _AX25_SUBR_H
+
+#include <net/ax25.h>
+#include <net/ax25dev.h>
+
+extern unsigned char *ax25_parse_addr(unsigned char*, int, ax25_pktinfo*);
+extern unsigned short ax25_calculate_t1(ax25_cb*);
+extern void ax25_return_dm(struct net_device*, ax25_pktinfo*);
+extern void ax25_tx_command(ax25_cb*, int, int);
+extern void ax25_tx_response(ax25_cb*, int, int);
+extern void ax25_clear_queues(ax25_cb*);
+extern void ax25_disconnect(ax25_cb*, int);
+extern void ax25_nr_error_recovery(ax25_cb*);
+extern void ax25_establish_data_link(ax25_cb*);
+extern void ax25_transmit_enquiry(ax25_cb*);
+extern void ax25_enquiry_response(ax25_cb*);
+extern void ax25_timeout_response(ax25_cb*);
+extern void ax25_close_socket(struct sock*, int);
+
+
+extern inline void ax25_set_cond(ax25_cb* ax25, unsigned int cond)
+{
+ ax25->condition |= cond;
+}
+
+extern inline void ax25_clr_cond(ax25_cb* ax25, unsigned int cond)
+{
+ ax25->condition &= ~cond;
+}
+
+extern inline void ax25_start_t1(ax25_cb* ax25)
+{
+ ax25_set_cond(ax25, AX25_COND_START_T1);
+ ax25->wrt_timer = 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c
index 01e7596f3..fdc871bc1 100644
--- a/net/ax25/ax25_timer.c
+++ b/net/ax25/ax25_timer.c
@@ -1,256 +1,455 @@
/*
- * AX.25 release 037
+ * ax25_timer.c: timer subroutines for NEW-AX.25
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KLX
+ * Alan Cox (GW4PTS), Joerg (DL1BKE), et al
*
- * 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.
+ * Comment:
*
- * History
- * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams.
- * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the
- * sock structure.
- * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names.
- * AX.25 031 Joerg(DL1BKE) Added DAMA support
- * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug
- * AX.25 033 Jonathan(G4KLX) Modularisation functions.
- * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating.
- * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files.
- * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with
- * standard AX.25 mode.
- * AX.25 037 Jonathan(G4KLX) New timer architecture.
- * Tomi(OH2BNS) Fixed heartbeat expiry (check ax25_dev).
+ * Changelog:
+ *
+ * License: 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.
*/
+
#include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
#include <linux/netdevice.h>
-#include <linux/skbuff.h>
+#include <linux/tcp.h>
#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-
-static void ax25_heartbeat_expiry(unsigned long);
-static void ax25_t1timer_expiry(unsigned long);
-static void ax25_t2timer_expiry(unsigned long);
-static void ax25_t3timer_expiry(unsigned long);
-static void ax25_idletimer_expiry(unsigned long);
-
-void ax25_start_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
+#include <net/ax25dev.h>
+#include <net/ax25.h>
- ax25->timer.data = (unsigned long)ax25;
- ax25->timer.function = &ax25_heartbeat_expiry;
- ax25->timer.expires = jiffies + 5 * HZ;
+#include "ax25_core.h"
+#include "ax25_ddi.h"
+#include "ax25_in.h"
+#include "ax25_subr.h"
+#include "ax25_timer.h"
- add_timer(&ax25->timer);
-}
+static void ax25_wrt_timeout(ax25_cb *);
-void ax25_start_t1timer(ax25_cb *ax25)
+/*
+ * AX.25 TIMER
+ *
+ * This routine is called every 100ms. Decrement timer by this
+ * amount - if expired then process the event.
+ */
+void ax25_timer(ax25_cb *ax25)
{
- del_timer(&ax25->t1timer);
+ int wrt_timeout;
+
+ switch (ax25->state) {
+ case AX25_LISTEN:
+ /*
+ * never kill listening sockets. let the be moved to
+ * AX25_STATE_0 first. ax25_release() does this.
+ */
+ return;
+
+ case AX25_STATE_0:
+ /*
+ * don't kill if a frame signalling a state change is
+ * still pending
+ */
+ if (ax25->condition & AX25_COND_STATE_CHANGE)
+ break;
+
+ if (ax25->sk) {
+ /*
+ * ax25_release() sets ax25->sk = NULL, releasing the
+ * connection between the socket and the underlying control
+ * structure, we handle that further down
+ */
+
+ /* almost dead, notify socket */
+ if (!ax25->sk->dead)
+ ax25_close_socket(ax25->sk, 0);
+ break;
+ }
+
+ /*
+ * wait a certain time before destroying the control block
+ */
+ if (ax25->killtimer > 0 && --ax25->killtimer > 0)
+ break;
+
+ /*
+ * if a peer exists in STATE [12], disconnect it. this handles
+ * "connection timed out" for digipeated connections.
+ *
+ * else if no peer exists, destroy this control block.
+ */
+ if (ax25->peer) {
+ if (ax25->peer->state < AX25_STATE_3)
+ ax25_destroy_cb(ax25->peer);
+ else
+ break;
+ }
+
+ ax25_destroy_cb(ax25);
+ return;
+
+ case AX25_STATE_3:
+ write_lock(&ax25->timer_lock);
+ if (!ax25->peer && !ax25->sk && ax25->idletimer > 0 && --ax25->idletimer == 0)
+ ax25_set_cond(ax25, AX25_COND_RELEASE);
+ write_unlock(&ax25->timer_lock);
+ /* fall through */
+ case AX25_STATE_4:
+ write_lock(&ax25->timer_lock);
+ if (ax25->condition & AX25_COND_ACK_PENDING) {
+ if (ax25->ack_timer > 0)
+ ax25->ack_timer--;
+ else
+ ax25_timeout_response(ax25);
+ }
+
+ /*
+ * Our peer connection has seen a DISC or DM and we're about to change to
+ * state 2. We don't DISC until we've delivered all queued data
+ */
+ if (ax25->condition & AX25_COND_RELEASE) {
+ if (!skb_queue_len(&ax25->write_queue) && !skb_queue_len(&ax25->ack_queue)) {
+ ax25->n2count = 1;
+ ax25->ack_timer = 0;
+ ax25_start_t1(ax25);
+ ax25_clr_cond(ax25, AX25_COND_RELEASE);
+ ax25->state = AX25_STATE_2;
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+
+ /*
+ * Check the state of the receive buffer. This is part of the flow control
+ * and should be done in ax25_rcvmsg();
+ */
+ } else if (ax25->sk && skb_queue_len(&ax25->rcv_queue)) {
+ struct sk_buff *skb;
+
+ while ((skb = skb_peek(&ax25->rcv_queue)) != NULL) {
+ if (ax25->sk->shutdown & RCV_SHUTDOWN)
+ break;
+ if (atomic_read(&ax25->sk->rmem_alloc) + skb->truesize < ax25->sk->rcvbuf) {
+ skb_dequeue(&ax25->rcv_queue);
+ sock_queue_rcv_skb(ax25->sk, skb);
+ } else
+ break;
+ }
+ if (skb == NULL) {
+ ax25_clr_cond(ax25, AX25_COND_OWN_RX_BUSY);
+ ax25_set_cond(ax25, AX25_COND_STATE_CHANGE);
+ ax25->state = AX25_STATE_4;
+ ax25_transmit_enquiry(ax25);
+ }
+ }
+ write_unlock(&ax25->timer_lock);
+ break;
+
+ default: /* state 1/2 */
+ break;
+ }
+
+ /* ax25_wrt_timout() must be called unlocked as ax25_disconnect()
+ * sets ax25->timer_lock
+ */
+
+ write_lock(&ax25->timer_lock);
+ wrt_timeout = (ax25->wrt_timer > 0 && --ax25->wrt_timer == 0);
+ write_unlock(&ax25->timer_lock);
+
+ if (wrt_timeout)
+ ax25_wrt_timeout(ax25);
- ax25->t1timer.data = (unsigned long)ax25;
- ax25->t1timer.function = &ax25_t1timer_expiry;
- ax25->t1timer.expires = jiffies + ax25->t1;
- add_timer(&ax25->t1timer);
}
-void ax25_start_t2timer(ax25_cb *ax25)
+static void ax25_wrt_timeout(ax25_cb *ax25)
{
- del_timer(&ax25->t2timer);
+ ax25_cb *peer = ax25->peer;
+ switch (ax25->state) {
+ case AX25_STATE_1:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->seqmask == AX25_SEQMASK) {
+ ax25_disconnect(ax25, ETIMEDOUT);
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ else if (peer) {
+ ax25_disconnect(peer, 0);
+ }
+ } else {
+ ax25->seqmask = AX25_SEQMASK;
+ ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW);
+ ax25->n2count = 0;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ }
+ } else {
+ ax25->n2count++;
+ ax25_start_t1(ax25);
+ if (ax25->seqmask == AX25_SEQMASK)
+ ax25_tx_command(ax25, AX25_SABM, AX25_POLLON);
+ else
+ ax25_tx_command(ax25, AX25_SABME, AX25_POLLON);
+ }
+ break;
+
+ case AX25_STATE_2:
+ if (ax25->n2count == ax25->n2) {
+ if (ax25->sk)
+ ax25_close_socket(ax25->sk, 0);
+ ax25_disconnect(ax25, 0);
+ } else {
+ ax25->n2count++;
+ ax25_start_t1(ax25);
+ ax25_tx_command(ax25, AX25_DISC, AX25_POLLON);
+ }
+ break;
+
+ case AX25_STATE_3:
+ ax25->vs_rtt = -1;
+ ax25->n2count = 1;
+ ax25_start_t1(ax25);
+ ax25->state = AX25_STATE_4;
+
+ /*
+ * We are angry now.
+ * Use CSMA. If master responds to poll, circuit will be saved
+ * and dama_mode will be re-enabled automagically in ax25_in.c
+ */
+ ax25_dev_set_dama(ax25->device, 0);
+ ax25_transmit_enquiry(ax25);
+ break;
+
+ case AX25_STATE_4:
+ if (ax25->n2count == ax25->n2) {
+ if (peer) {
+ ax25_set_cond(peer, AX25_COND_RELEASE);
+ ax25->killtimer = 90 * AX25_SLOWHZ;
+ } else if (ax25->sk)
+ ax25_close_socket(ax25->sk, ETIMEDOUT);
+ ax25_disconnect(ax25, ETIMEDOUT);
+ } else {
+ ax25->n2count++;
+ ax25_transmit_enquiry(ax25);
+ }
+ break;
+ }
+}
- ax25->t2timer.data = (unsigned long)ax25;
- ax25->t2timer.function = &ax25_t2timer_expiry;
- ax25->t2timer.expires = jiffies + ax25->t2;
- add_timer(&ax25->t2timer);
-}
+/* -------------------------------------------------------------------- */
+
+/************************************************************************/
+/* Module support functions follow. */
+/************************************************************************/
+
+static struct protocol_struct {
+ struct protocol_struct *next;
+ unsigned int pid;
+ int (*func)(struct sk_buff *, ax25_cb *);
+} *protocol_list = NULL;
+
+static struct linkfail_struct {
+ struct linkfail_struct *next;
+ void (*func)(ax25_cb *, int);
+} *linkfail_list = NULL;
-void ax25_start_t3timer(ax25_cb *ax25)
+static struct listen_struct {
+ struct listen_struct *next;
+ ax25_address callsign;
+ struct net_device *dev;
+} *listen_list = NULL;
+
+int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *))
{
- del_timer(&ax25->t3timer);
+ struct protocol_struct *protocol;
+ unsigned long flags;
- if (ax25->t3 > 0) {
- ax25->t3timer.data = (unsigned long)ax25;
- ax25->t3timer.function = &ax25_t3timer_expiry;
- ax25->t3timer.expires = jiffies + ax25->t3;
+ if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT)
+ return 0;
- add_timer(&ax25->t3timer);
- }
+ if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ protocol->pid = pid;
+ protocol->func = func;
+
+ save_flags(flags);
+ cli();
+
+ protocol->next = protocol_list;
+ protocol_list = protocol;
+
+ restore_flags(flags);
+
+ return 1;
}
-void ax25_start_idletimer(ax25_cb *ax25)
+void ax25_protocol_release(unsigned int pid)
{
- del_timer(&ax25->idletimer);
+ struct protocol_struct *s, *protocol = protocol_list;
+ unsigned long flags;
+
+ if (protocol == NULL)
+ return;
- if (ax25->idle > 0) {
- ax25->idletimer.data = (unsigned long)ax25;
- ax25->idletimer.function = &ax25_idletimer_expiry;
- ax25->idletimer.expires = jiffies + ax25->idle;
+ save_flags(flags);
+ cli();
- add_timer(&ax25->idletimer);
+ if (protocol->pid == pid) {
+ protocol_list = protocol->next;
+ restore_flags(flags);
+ kfree(protocol);
+ return;
}
-}
-void ax25_stop_heartbeat(ax25_cb *ax25)
-{
- del_timer(&ax25->timer);
-}
+ while (protocol != NULL && protocol->next != NULL) {
+ if (protocol->next->pid == pid) {
+ s = protocol->next;
+ protocol->next = protocol->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
-void ax25_stop_t1timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t1timer);
-}
+ protocol = protocol->next;
+ }
-void ax25_stop_t2timer(ax25_cb *ax25)
-{
- del_timer(&ax25->t2timer);
+ restore_flags(flags);
}
-void ax25_stop_t3timer(ax25_cb *ax25)
+int ax25_linkfail_register(void (*func)(ax25_cb *, int))
{
- del_timer(&ax25->t3timer);
-}
+ struct linkfail_struct *linkfail;
+ unsigned long flags;
-void ax25_stop_idletimer(ax25_cb *ax25)
-{
- del_timer(&ax25->idletimer);
+ if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL)
+ return 0;
+
+ linkfail->func = func;
+
+ save_flags(flags);
+ cli();
+
+ linkfail->next = linkfail_list;
+ linkfail_list = linkfail;
+
+ restore_flags(flags);
+
+ return 1;
}
-int ax25_t1timer_running(ax25_cb *ax25)
+void ax25_linkfail_release(void (*func)(ax25_cb *, int))
{
- return timer_pending(&ax25->t1timer);
+ struct linkfail_struct *s, *linkfail = linkfail_list;
+ unsigned long flags;
+
+ if (linkfail == NULL)
+ return;
+
+ save_flags(flags);
+ cli();
+
+ if (linkfail->func == func) {
+ linkfail_list = linkfail->next;
+ restore_flags(flags);
+ kfree(linkfail);
+ return;
+ }
+
+ while (linkfail != NULL && linkfail->next != NULL) {
+ if (linkfail->next->func == func) {
+ s = linkfail->next;
+ linkfail->next = linkfail->next->next;
+ restore_flags(flags);
+ kfree(s);
+ return;
+ }
+
+ linkfail = linkfail->next;
+ }
+
+ restore_flags(flags);
}
-unsigned long ax25_display_timer(struct timer_list *timer)
+static char empty_addr[AX25_ADDR_LEN] = {0, 0, 0, 0, 0, 0, 0};
+
+int ax25_listen_register(ax25_address *callsign, struct net_device *dev)
{
- if (!timer_pending(timer))
+ ax25_cb *ax25;
+
+ ax25_addr_t addr;
+
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
+
+ if (ax25_find_cb(&addr, dev) != NULL)
+ return 0;
+
+ if ((ax25 = ax25_create_cb()) == NULL)
return 0;
- return timer->expires - jiffies;
+ ax25->addr.src = *callsign;
+
+ ax25_fillin_cb(ax25, dev);
+ ax25_insert_cb(ax25);
+
+ return 1;
}
-static void ax25_heartbeat_expiry(unsigned long param)
+void ax25_listen_release(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
- int proto = AX25_PROTO_STD_SIMPLEX;
+ ax25_cb *ax25;
- if (ax25->ax25_dev)
- proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
+ ax25_addr_t addr;
- switch (proto) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_heartbeat_expiry(ax25);
- break;
+ addr.dcount = 0;
+ addr.src = *callsign;
+ memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN);
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_heartbeat_expiry(ax25);
- else
- ax25_std_heartbeat_expiry(ax25);
- break;
-#endif
- }
+ if ((ax25 = ax25_find_cb(&addr, dev)) != NULL)
+ ax25_destroy_cb(ax25);
}
-static void ax25_t1timer_expiry(unsigned long param)
+int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t1timer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return protocol->func;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t1timer_expiry(ax25);
- break;
-#endif
- }
+ return NULL;
}
-static void ax25_t2timer_expiry(unsigned long param)
+int ax25_listen_mine(ax25_address *callsign, struct net_device *dev)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct listen_struct *listen;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t2timer_expiry(ax25);
- break;
+ for (listen = listen_list; listen != NULL; listen = listen->next)
+ if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL))
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (!ax25->ax25_dev->dama.slave)
- ax25_std_t2timer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}
-static void ax25_t3timer_expiry(unsigned long param)
+void ax25_link_failed(ax25_cb *ax25, int reason)
{
- ax25_cb *ax25 = (ax25_cb *)param;
-
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_t3timer_expiry(ax25);
- break;
+ struct linkfail_struct *linkfail;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_t3timer_expiry(ax25);
- else
- ax25_std_t3timer_expiry(ax25);
- break;
-#endif
- }
+ for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next)
+ (linkfail->func)(ax25, reason);
}
-static void ax25_idletimer_expiry(unsigned long param)
+int ax25_protocol_is_registered(unsigned int pid)
{
- ax25_cb *ax25 = (ax25_cb *)param;
+ struct protocol_struct *protocol;
- switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
- case AX25_PROTO_STD_SIMPLEX:
- case AX25_PROTO_STD_DUPLEX:
- ax25_std_idletimer_expiry(ax25);
- break;
+ for (protocol = protocol_list; protocol != NULL; protocol = protocol->next)
+ if (protocol->pid == pid)
+ return 1;
-#ifdef CONFIG_AX25_DAMA_SLAVE
- case AX25_PROTO_DAMA_SLAVE:
- if (ax25->ax25_dev->dama.slave)
- ax25_ds_idletimer_expiry(ax25);
- else
- ax25_std_idletimer_expiry(ax25);
- break;
-#endif
- }
+ return 0;
}
diff --git a/net/ax25/ax25_timer.h b/net/ax25/ax25_timer.h
new file mode 100644
index 000000000..010e8e876
--- /dev/null
+++ b/net/ax25/ax25_timer.h
@@ -0,0 +1,11 @@
+/*
+ * Interface declaration for timer functions
+ *
+ * Joerg Reuter DL1BKE 2000-07-06
+ *
+ */
+
+
+#ifndef _AX25_TIMER_H
+#define _AX25_TIMER_H
+#endif
diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c
index 603d8b8cc..4a41313de 100644
--- a/net/ax25/ax25_uid.c
+++ b/net/ax25/ax25_uid.c
@@ -1,124 +1,146 @@
/*
- * AX.25 release 037
+ * ax25_uid.c: Callsign/UID mapper. This is in kernel space for security on mul
*
- * This code REQUIRES 2.1.15 or higher/ NET3.038
+ * Authors: Matthias Welwarsky (DG2FEF)
+ * Joerg Reuter (DL1BKE)
*
- * 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.
+ * Comment:
*
- * History
- * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c.
+ * Changelog:
+ * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de>
+ * extended policy scheme implemented
+ *
+ * License: 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.
*/
-#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
+#include <linux/errno.h>
#include <linux/sockios.h>
-#include <linux/net.h>
-#include <net/ax25.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <linux/fcntl.h>
#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/netfilter.h>
-#include <linux/sysctl.h>
-#include <net/ip.h>
-#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/ax25.h>
-/*
- * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines.
- */
+#include <net/ax25_uid.h>
+
+#include "af_ax25.h"
+t_ax25_uid_policy ax25_uid_policy = AX25_UID_POLICY_ARBITRARY_CALLSIGN;
static ax25_uid_assoc *ax25_uid_list;
-int ax25_uid_policy = 0;
+ax25_address *ax25_find_by_uid(uid_t uid)
+{
+ ax25_uid_assoc *a;
-ax25_address *ax25_findbyuid(uid_t uid)
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (a->uid == uid)
+ return &a->call;
+ }
+
+ return NULL;
+}
+
+ax25_address *ax25_find_match_for_uid(uid_t uid, ax25_address *provided, char *device)
{
- ax25_uid_assoc *ax25_uid;
+ int res;
+ ax25_uid_assoc *a;
+
+ /* no callsign given? find one! */
+ if (ax25cmp(provided, &null_ax25_address) == 0)
+ {
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (a->uid == uid)
+ {
+ if (device != NULL && strcmp(device, a->device))
+ continue;
+ return &a->call;
+ }
+ }
+ }
+
+ /* any user can choose an arbitrary callsign? */
+ if (ax25_uid_policy == 0)
+ return provided;
+
+ /* walk the list... */
+ for (a = ax25_uid_list; a != NULL ; a = a->next) {
+ if (a->uid == uid)
+ {
+ /* limited to a device? */
+ if (device != NULL && strcmp(device, a->device))
+ continue;
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25_uid->uid == uid)
- return &ax25_uid->call;
+ /* user can choose any callsign? */
+ if (ax25cmp(&a->call, &null_ax25_address) == 0)
+ return provided;
+
+ res = ax25cmp(provided, &a->call);
+
+ /* exact match, or only SSID differ (when allowed): okay */
+ if ( res == 0 ||
+ (res == 2 && ax25_uid_policy == AX25_UID_POLICY_ANY_SSID) )
+ return provided;
+ }
}
return NULL;
}
+/*
+ * TODO: move this stuff to procfs
+ * general idea: echo "add 503 dl1bke scc0" >/proc/net/ax25_calls
+ * echo "del 502 dk0tux *" >/proc/net/ax25_calls
+ */
+
int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
{
- ax25_uid_assoc *s, *ax25_uid;
- unsigned long flags;
+ ax25_uid_assoc *a;
switch (cmd) {
case SIOCAX25GETUID:
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
- return ax25_uid->uid;
+ for (a = ax25_uid_list; a != NULL; a = a->next) {
+ if (ax25cmp(&sax->sax25_call, &a->call) == 0)
+ return a->uid;
}
return -ENOENT;
case SIOCAX25ADDUID:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- if (ax25_findbyuid(sax->sax25_uid))
+ if (ax25_find_by_uid(sax->sax25_uid))
return -EEXIST;
if (sax->sax25_uid == 0)
return -EINVAL;
- if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL)
+ a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL);
+ if (a == NULL)
return -ENOMEM;
- ax25_uid->uid = sax->sax25_uid;
- ax25_uid->call = sax->sax25_call;
- save_flags(flags); cli();
- ax25_uid->next = ax25_uid_list;
- ax25_uid_list = ax25_uid;
- restore_flags(flags);
+ a->uid = sax->sax25_uid;
+ a->call = sax->sax25_call;
+ a->device[0] = '\0';
+ a->next = ax25_uid_list;
+ ax25_uid_list = a;
return 0;
- case SIOCAX25DELUID:
+ case SIOCAX25DELUID: {
+ ax25_uid_assoc **l;
+
if (!capable(CAP_NET_ADMIN))
return -EPERM;
- for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) {
- if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0)
- break;
- }
- if (ax25_uid == NULL)
- return -ENOENT;
- save_flags(flags); cli();
- if ((s = ax25_uid_list) == ax25_uid) {
- ax25_uid_list = s->next;
- restore_flags(flags);
- kfree(ax25_uid);
- return 0;
- }
- while (s != NULL && s->next != NULL) {
- if (s->next == ax25_uid) {
- s->next = ax25_uid->next;
- restore_flags(flags);
- kfree(ax25_uid);
+ l = &ax25_uid_list;
+ while ((*l) != NULL) {
+ if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) {
+ a = *l;
+ *l = (*l)->next;
+ kfree(a);
return 0;
}
- s = s->next;
+
+ l = &((*l)->next);
}
- restore_flags(flags);
return -ENOENT;
+ }
default:
return -EINVAL;
@@ -127,19 +149,17 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax)
return -EINVAL; /*NOTREACHED */
}
-int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
+int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length)
{
ax25_uid_assoc *pt;
int len = 0;
off_t pos = 0;
off_t begin = 0;
- cli();
-
len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy);
for (pt = ax25_uid_list; pt != NULL; pt = pt->next) {
- len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call));
+ len += sprintf(buffer + len, "%6d %-9s %s\n", pt->uid, ax2asc(&pt->call), pt->device);
pos = begin + len;
@@ -152,7 +172,6 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
break;
}
- sti();
*start = buffer + (offset - begin);
len -= offset - begin;
@@ -162,17 +181,3 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length)
return len;
}
-/*
- * Free all memory associated with UID/Callsign structures.
- */
-void __exit ax25_uid_free(void)
-{
- ax25_uid_assoc *s, *ax25_uid = ax25_uid_list;
-
- while (ax25_uid != NULL) {
- s = ax25_uid;
- ax25_uid = ax25_uid->next;
-
- kfree(s);
- }
-}
diff --git a/net/ax25/ax25_vj.c b/net/ax25/ax25_vj.c
new file mode 100644
index 000000000..4fa2dc1ff
--- /dev/null
+++ b/net/ax25/ax25_vj.c
@@ -0,0 +1,724 @@
+/*
+ * ax25_vj.c: VJ compression routines for VJ-compressed IP via NEW-AX.25
+ *
+ * Authors: Matthias Welwarsky (DG2FEF)
+ *
+ * Comment: Routines to compress TCP/IP packets according to RFC 1144 and to
+ * suppress redundant retransmissions on a reliable virtual circuit
+ * Largely based on code by Van Jacobson, Phil Karn et.al. Directly
+ * derived from WAMPES' slhc.c written by Dieter Deyke, DK5SG.
+ *
+ * Changelog:
+ * 1998-02-25 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * Adapted to Linux. contains code from drivers/net/slhc.c
+ *
+ * 1998-03-04 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem with nonatomically calling kmalloc from interrupt
+ *
+ * 1998-03-08 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de>
+ * fixed problem in axhc_recv_vjc() that lead to a system panic when
+ * the incoming sk_buff didn't contain enough headroom to rebuild the
+ * TCP/IP header after decompression
+ *
+ * License: 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.
+ */
+
+#include <linux/config.h>
+#if defined(CONFIG_INET)
+#include <linux/skbuff.h>
+#include <asm/unaligned.h>
+#include <linux/kernel.h>
+
+#include "ax25_vj.h"
+
+#ifdef DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x)
+#endif
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+static int axhc_toss(struct axvj_slcomp *);
+
+/* Put a short in host order into a char array in network order */
+static inline unsigned char *
+put16(unsigned char *cp, unsigned short x)
+{
+ *cp++ = x >> 8;
+ *cp++ = x;
+
+ return cp;
+}
+
+/* Pull a 16-bit integer in host order from buffer in network byte order */
+static unsigned short pull16(unsigned char **cpp)
+{
+ short rval;
+
+ rval = *(*cpp)++;
+ rval <<= 8;
+ rval |= *(*cpp)++;
+ return rval;
+}
+
+/* Encode a number */
+static unsigned char *
+encode(unsigned char *cp, unsigned short n)
+{
+ if (n >= 256 || n == 0) {
+ *cp++ = 0;
+ cp = put16(cp, n);
+ } else {
+ *cp++ = n;
+ }
+ return cp;
+}
+
+/* Decode a number */
+static long decode(unsigned char **cpp)
+{
+ register int x;
+
+ x = *(*cpp)++;
+ if (x == 0) {
+ return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */
+ } else {
+ return x & 0xff; /* -1 if PULLCHAR returned error */
+ }
+}
+
+
+/* Initialize compression data structure
+ * slots must be in range 0 to 255 (zero meaning no compression)
+ */
+struct axvj_slcomp *
+axhc_init(int rslots, int tslots)
+{
+ register short i;
+ register struct axvj_cstate *ts;
+ struct axvj_slcomp *comp;
+
+ comp = (struct axvj_slcomp *)kmalloc(sizeof(struct axvj_slcomp),
+ GFP_ATOMIC);
+ if (! comp)
+ return NULL;
+
+ memset(comp, 0, sizeof(struct axvj_slcomp));
+
+ if ( rslots > 0 && rslots < 256 ) {
+ size_t rsize = rslots * sizeof(struct axvj_cstate);
+ comp->rstate = (struct axvj_cstate *) kmalloc(rsize, GFP_ATOMIC);
+ if (! comp->rstate)
+ {
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->rstate, 0, rsize);
+ comp->rslot_limit = rslots - 1;
+ }
+
+ if ( tslots > 0 && tslots < 256 ) {
+ size_t tsize = tslots * sizeof(struct axvj_cstate);
+ comp->tstate = (struct axvj_cstate *) kmalloc(tsize, GFP_ATOMIC);
+ if (! comp->tstate)
+ {
+ kfree((unsigned char *)comp->rstate);
+ kfree((unsigned char *)comp);
+ return NULL;
+ }
+ memset(comp->tstate, 0, tsize);
+ comp->tslot_limit = tslots - 1;
+ }
+
+ comp->xmit_oldest = 0;
+ comp->xmit_current = 255;
+ comp->recv_current = 255;
+ /*
+ * don't accept any packets with implicit index until we get
+ * one with an explicit index. Otherwise the uncompress code
+ * will try to use connection 255, which is almost certainly
+ * out of range
+ */
+ comp->flags |= SLF_TOSS;
+
+ if ( tslots > 0 ) {
+ ts = comp->tstate;
+ for(i = comp->tslot_limit; i > 0; --i){
+ ts[i].cs_this = i;
+ ts[i].next = &(ts[i - 1]);
+ }
+ ts[0].next = &(ts[comp->tslot_limit]);
+ ts[0].cs_this = 0;
+ }
+ return comp;
+}
+
+
+/* Free a compression data structure */
+void
+axhc_free(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return;
+
+ if ( comp->rstate != NULL )
+ kfree( comp->rstate );
+
+ if ( comp->tstate != NULL )
+ kfree( comp->tstate );
+
+ kfree( comp );
+}
+
+/* Dear hacker. I assume that you have read and understood RFC 1144
+ * and the original slhc_compress() procedure before tinkering with
+ * this code.
+ *
+ * procedure is as follows:
+ * 1. check if packet is TCP. return AX25_P_IP if not.
+ * 2. check if SYN, FIN, MSS, WSCALE, TSTAMP or RST is set, or if ACK is not
+ * set. deny compression for these packets (do_compression = 0).
+ * 3. try to find the appopriate slot, reuse an old one if no match is found
+ * 4. attempt to compress the packet and check the following rules:
+ * - if the packet contains an old (outdated) seq and no new ack or
+ * window or urgent data, drop it (return 0).
+ * - if nothing changed since the last frame sent (no new seq, ack,
+ * window, urgent data, or changing TCP flags), drop it.
+ * - before dropping a packet, check if any packet made it through the
+ * filter within the last 120sec. If not, assume a packet loss and
+ * transmit the packet.
+ * 5. transmit a compressed, uncompressed or regular packet, depending
+ * on do_compression and cs->deny_compression.
+ */
+
+int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression)
+{
+ struct axvj_cstate *ocs = &(comp->tstate[comp->xmit_oldest]);
+ struct axvj_cstate *lcs = ocs;
+ struct axvj_cstate *cs = lcs->next;
+ unsigned int hlen;
+ struct tcphdr *th, *oth;
+ struct iphdr *iph;
+ unsigned long deltaS, deltaA;
+ unsigned short changes = 0;
+ unsigned char new_seq[16];
+ unsigned char *cp = new_seq;
+
+ /* Peek at IP header */
+ iph = (struct iphdr *) skb->data;
+
+ /* Bail if this packet isn't TCP, or is an IP fragment */
+ if (iph->protocol != IPPROTO_TCP ||
+ (ntohs(iph->frag_off) & 0x1fff) ||
+ (iph->frag_off & 32)) {
+ /* Send as regular IP */
+ if (iph->protocol != IPPROTO_TCP)
+ comp->sls_o_nontcp++;
+ else
+ comp->sls_o_tcp++;
+ return AX25_P_IP;
+ }
+ /* Extract TCP header */
+ th = (struct tcphdr *) (((unsigned char *) iph) + iph->ihl * 4);
+ hlen = iph->ihl * 4 + th->doff * 4;
+
+ PRINTK((KERN_DEBUG "ax25_vj.c: th.seq=%0x\n", ntohl(th->seq)));
+ /*
+ * check if packet may be compressed.
+ */
+ if (th->syn || th->fin || th->rst || !th->ack) {
+ comp->sls_o_tcp++;
+ do_compression = 0;
+ }
+ /*
+ * locate the connection state slot
+ */
+ for (;;) {
+ if (iph->saddr == cs->cs_ip.saddr
+ && iph->daddr == cs->cs_ip.daddr
+ && th->source == cs->cs_tcp.source
+ && th->dest == cs->cs_tcp.dest)
+ goto found;
+
+ /* if current equal oldest, at end of list */
+ if (cs == ocs)
+ break;
+ lcs = cs;
+ cs = cs->next;
+ comp->sls_o_searches++;
+ }
+ /*
+ * Didn't find it -- re-use oldest axvj_cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ *
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * xmit_oldest to update the lru linkage.
+ */
+ comp->sls_o_misses++;
+ comp->xmit_oldest = lcs->cs_this;
+ cs->deny_compression = 0;
+ cs->lastdropped = 0;
+ PRINTK((KERN_DEBUG "ax25_vj.c: new slot %d\n", cs->cs_this));
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (lcs == ocs) {
+ /* found at most recently used */
+ } else if (cs == ocs) {
+ /* found at least recently used */
+ comp->xmit_oldest = lcs->cs_this;
+ } else {
+ /* more than 2 elements */
+ lcs->next = cs->next;
+ cs->next = ocs->next;
+ ocs->next = cs;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: found slot %d\n", cs->cs_this));
+ /*
+ * Make sure that only what we expect to change changed.
+ * Check the following:
+ * IP protocol version, header length & type of service.
+ * The "Don't fragment" bit.
+ * The time-to-live field.
+ * The TCP header length.
+ * IP options, if any.
+ * TCP options, if any.
+ * If any of these things are different between the previous &
+ * current datagram, we send the current datagram `uncompressed'.
+ */
+ oth = &cs->cs_tcp;
+
+ if (iph->version != cs->cs_ip.version || iph->ihl != cs->cs_ip.ihl
+ || iph->tos != cs->cs_ip.tos
+ || (iph->frag_off & 64) != (cs->cs_ip.frag_off & 64)
+ || iph->ttl != cs->cs_ip.ttl
+ || th->doff != cs->cs_tcp.doff
+ || (iph->ihl > 5 && memcmp(iph + 1, cs->cs_ipopt, ((iph->ihl) - 5) * 4) != 0)
+ || (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) * 4) != 0)) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: packet uncompressable\n"));
+ goto uncompressed;
+ }
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (th->urg) {
+ deltaS = ntohs(th->urg_ptr);
+ cp = encode(cp, deltaS);
+ changes |= NEW_U;
+ } else if (th->urg_ptr != oth->urg_ptr) {
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+ }
+ if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_W;
+ }
+ if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) {
+ if (deltaA > 0x0000ffff)
+ goto uncompressed;
+ cp = encode(cp, deltaA);
+ changes |= NEW_A;
+ }
+ if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) {
+ if (deltaS > 0x0000ffff) {
+
+ /*
+ * - if the packet contains an old (outdated) seq and no
+ * new ack or window or urgent data, drop it (return 0)
+ */
+ if (before(ntohl(th->seq), ntohl(oth->seq)) && !changes) {
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: old packet, dS=%0x th.seq=%0x oth.seq=%0x\n", deltaS, ntohl(th->seq), ntohl(oth->seq)));
+ return 0;
+ }
+ goto uncompressed;
+ }
+ cp = encode(cp, deltaS);
+ changes |= NEW_S;
+ }
+ switch (changes) {
+ case 0: /* Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version. */
+ if (iph->tot_len != cs->cs_ip.tot_len
+ && ntohs(cs->cs_ip.tot_len) == hlen) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: data following ack\n"));
+ break;
+ }
+ /*
+ * MW: drop retransmitted packet. seq and ack did not change,
+ * check if flags have changed.
+ */
+ if (th->fin != oth->fin || th->syn != oth->syn || th->rst != oth->rst
+ || th->ack != oth->ack) {
+ PRINTK((KERN_DEBUG "ax25_vj.c: tcp flags changed\n"));
+ goto uncompressed;
+ }
+ if (cs->lastdropped != 0) {
+ if (jiffies - cs->lastdropped > 120 * HZ) {
+ goto uncompressed;
+ }
+ } else {
+ cs->lastdropped = jiffies;
+ }
+ PRINTK((KERN_DEBUG "ax25_vj.c: no changes detected\n"));
+ return 0;
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /* actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+ case NEW_S | NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.tot_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ /*
+ * The Packet contains new information, it has not been dropped
+ * until here. But compression has been denied, so we transmit an
+ * uncompressed packet instead.
+ */
+ if (cs->deny_compression) {
+ goto uncompressed;
+ }
+ deltaS = ntohs(iph->id) - ntohs(cs->cs_ip.id);
+ if (deltaS != 1) {
+ cp = encode(cp, deltaS);
+ changes |= NEW_I;
+ }
+ if (th->psh)
+ changes |= TCP_PUSH_BIT;
+ /* Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->check);
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ cs->lastdropped = 0;
+
+ /*
+ * MW: We don't actually perform the compression if we run on an
+ * uncompressible stream.
+ */
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ /* We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed.
+ */
+ deltaS = cp - new_seq;
+ skb_pull(skb, hlen); /* Strip TCP/IP headers */
+ if (comp->xmit_current != cs->cs_this) {
+ cp = skb_push(skb, deltaS + 4);
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_this;
+ comp->xmit_current = cs->cs_this;
+ } else {
+ cp = skb_push(skb, deltaS + 3);
+ *cp++ = changes;
+ }
+ cp = put16(cp, (short) deltaA); /* Write TCP checksum */
+ memcpy(cp, new_seq, deltaS); /* Write list of deltas */
+ comp->sls_o_compressed++;
+ return AX25_P_VJCOMP;
+
+ /* Update connection state cs & send uncompressed packet (i.e.,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+ uncompressed:
+ memcpy(&cs->cs_ip, iph, 20);
+ memcpy(&cs->cs_tcp, th, 20);
+ if (iph->ihl > 5)
+ memcpy(cs->cs_ipopt, iph + 1, ((iph->ihl) - 5) * 4);
+ if (th->doff > 5)
+ memcpy(cs->cs_tcpopt, th + 1, ((th->doff) - 5) * 4);
+ comp->xmit_current = cs->cs_this;
+ cs->lastdropped = 0;
+
+ if (!do_compression) {
+ cs->deny_compression = 1;
+ return AX25_P_IP;
+ }
+ iph->protocol = cs->cs_this;
+ cs->deny_compression = 0;
+ comp->sls_o_uncompressed++;
+ return AX25_P_VJUNCOMP;
+}
+
+int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register int changes;
+ long x;
+ register struct tcphdr *thp;
+ register struct iphdr *ip;
+ register struct axvj_cstate *cs;
+ int len, hdrlen;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+ unsigned char *cp = icp;
+
+ /* We've got a compressed packet; read the change byte */
+ comp->sls_i_compressed++;
+ if (isize < 3) {
+ comp->sls_i_error++;
+ return 0;
+ }
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /* Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag.
+ */
+ x = *cp++; /* Read conn index */
+ if (x < 0 || x > comp->rslot_limit)
+ goto bad;
+
+ comp->flags &= ~SLF_TOSS;
+ comp->recv_current = x;
+ } else {
+ /* this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet. */
+ if (comp->flags & SLF_TOSS) {
+ comp->sls_i_tossed++;
+ return 0;
+ }
+ }
+ cs = &comp->rstate[comp->recv_current];
+ thp = &cs->cs_tcp;
+ ip = &cs->cs_ip;
+
+ if ((x = pull16(&cp)) == -1) { /* Read the TCP checksum */
+ goto bad;
+ }
+ thp->check = htons(x);
+
+ thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0;
+/*
+ * we can use the same number for the length of the saved header and
+ * the current one, because the packet wouldn't have been sent
+ * as compressed unless the options were the same as the previous one
+ */
+
+ hdrlen = ip->ihl * 4 + thp->doff * 4;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I: /* Echoed terminal traffic */
+ {
+ register short i;
+ i = ntohs(ip->tot_len) - hdrlen;
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + i);
+ thp->seq = htonl(ntohl(thp->seq) + i);
+ }
+ break;
+
+ case SPECIAL_D: /* Unidirectional data */
+ thp->seq = htonl(ntohl(thp->seq) +
+ ntohs(ip->tot_len) - hdrlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ thp->urg = 1;
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->urg_ptr = htons(x);
+ } else
+ thp->urg = 0;
+ if (changes & NEW_W) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->window = htons(ntohs(thp->window) + x);
+ }
+ if (changes & NEW_A) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->ack_seq = htonl(ntohl(thp->ack_seq) + x);
+ }
+ if (changes & NEW_S) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ thp->seq = htonl(ntohl(thp->seq) + x);
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ if ((x = decode(&cp)) == -1) {
+ goto bad;
+ }
+ ip->id = htons(ntohs(ip->id) + x);
+ } else
+ ip->id = htons(ntohs(ip->id) + 1);
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Put the reconstructed TCP and IP headers back on the
+ * packet. Recalculate IP checksum (but not TCP checksum).
+ */
+
+ len = isize - (cp - icp);
+ if (len < 0)
+ goto bad;
+ len += hdrlen;
+ ip->tot_len = htons(len);
+ ip->check = 0;
+
+ /*
+ * MW:
+ * we are working on sk_buffs here, so we can spare the memmove()
+ * and simply skb_push() the hdrlen
+ */
+
+ skb_push(skb, hdrlen - (cp - icp));
+
+ cp = icp = skb->data;
+ memcpy(cp, ip, 20);
+ cp += 20;
+
+ if (ip->ihl > 5) {
+ memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4);
+ cp += (ip->ihl - 5) * 4;
+ }
+ put_unaligned(ip_fast_csum(icp, ip->ihl),
+ &((struct iphdr *) icp)->check);
+
+ memcpy(cp, thp, 20);
+ cp += 20;
+
+ if (thp->doff > 5) {
+ memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4);
+ cp += ((thp->doff) - 5) * 4;
+ }
+ return len;
+ bad:
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+}
+
+
+int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb)
+{
+ register struct axvj_cstate *cs;
+ unsigned ihl;
+
+ unsigned char index;
+
+ int isize = skb->len;
+ unsigned char *icp = skb->data;
+
+ if (isize < 20) {
+ /* The packet is shorter than a legal IP header */
+ printk(KERN_DEBUG "axhc_remember: short packet from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ /* Peek at the IP header's IHL field to find its length */
+ ihl = icp[0] & 0xf;
+ if (ihl < 20 / 4) {
+ /* The IP header length field is too small */
+ printk(KERN_DEBUG "axhc_remember: ihl too small from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_runt++;
+ return axhc_toss(comp);
+ }
+ index = icp[9];
+ icp[9] = IPPROTO_TCP;
+
+ if (ip_fast_csum(icp, ihl)) {
+ /* Bad IP header checksum; discard */
+ printk(KERN_DEBUG "axhc_remember: bad ip header checksum from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_badcheck++;
+ return axhc_toss(comp);
+ }
+ if (index > comp->rslot_limit) {
+ printk(KERN_DEBUG "axhc_remember: illegal slot from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr));
+ comp->sls_i_error++;
+ return axhc_toss(comp);
+ }
+ /* Update local state */
+ cs = &comp->rstate[comp->recv_current = index];
+ comp->flags &= ~SLF_TOSS;
+ memcpy(&cs->cs_ip, icp, 20);
+ memcpy(&cs->cs_tcp, icp + ihl * 4, 20);
+ if (ihl > 5)
+ memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4);
+ if (cs->cs_tcp.doff > 5)
+ memcpy(cs->cs_tcpopt, icp + ihl * 4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4);
+ cs->cs_hsize = ihl * 2 + cs->cs_tcp.doff * 2;
+ /* Put headers back on packet
+ * Neither header checksum is recalculated
+ */
+ comp->sls_i_uncompressed++;
+ return isize;
+}
+
+static int
+axhc_toss(struct axvj_slcomp *comp)
+{
+ if ( comp == NULL )
+ return 0;
+
+ comp->flags |= SLF_TOSS;
+ return 0;
+}
+
+#endif
diff --git a/net/ax25/ax25_vj.h b/net/ax25/ax25_vj.h
new file mode 100644
index 000000000..da059392c
--- /dev/null
+++ b/net/ax25/ax25_vj.h
@@ -0,0 +1,73 @@
+/*
+ * Interface declaration of the VJ compression code
+ *
+ * Matthias Welwarsky (DG2FEF) 05/25/98
+ */
+
+#ifndef _AX25_VJ_H
+#define _AX25_VJ_H
+
+#include <net/ip.h>
+#include <net/tcp.h>
+
+/*
+ * MW: copied this into ax25.h to unclobber the original struct cstate.
+ * "state" data for each active tcp conversation on the "wire". This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct axvj_cstate {
+ unsigned char cs_this; /* connection id number (xmit) */
+ struct axvj_cstate *next; /* next in ring (xmit) */
+ struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
+ struct tcphdr cs_tcp;
+ unsigned char cs_ipopt[64];
+ unsigned char cs_tcpopt[64];
+ int cs_hsize;
+ unsigned char deny_compression; /* MW: for vj compression via AX.25 */
+ unsigned long lastdropped;
+};
+
+/*
+ * all the state data for one VC (we need one of these per VC).
+ */
+struct axvj_slcomp {
+ struct axvj_cstate *tstate; /* transmit connection states (array)*/
+ struct axvj_cstate *rstate; /* receive connection states (array)*/
+
+ unsigned char tslot_limit; /* highest transmit slot id (0-l)*/
+ unsigned char rslot_limit; /* highest receive slot id (0-l)*/
+
+ unsigned char xmit_oldest; /* oldest xmit in ring */
+ unsigned char xmit_current; /* most recent xmit id */
+ unsigned char recv_current; /* most recent rcvd id */
+
+ unsigned char flags;
+#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
+
+ int sls_o_nontcp; /* outbound non-TCP packets */
+ int sls_o_tcp; /* outbound TCP packets */
+ int sls_o_uncompressed; /* outbound uncompressed packets */
+ int sls_o_compressed; /* outbound compressed packets */
+ int sls_o_searches; /* searches for connection state */
+ int sls_o_misses; /* times couldn't find conn. state */
+
+ int sls_i_uncompressed; /* inbound uncompressed packets */
+ int sls_i_compressed; /* inbound compressed packets */
+ int sls_i_error; /* inbound error packets */
+ int sls_i_tossed; /* inbound packets tossed because of error */
+
+ int sls_i_runt;
+ int sls_i_badcheck;
+};
+
+extern struct axvj_slcomp* axhc_init(int, int);
+extern void axhc_free(struct axvj_slcomp*);
+extern int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression);
+extern void ax25_vj_init(void);
+extern void ax25_vj_cleanup(void);
+extern int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb);
+extern int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb);
+
+#endif
diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c
index e6016f93d..416f4cc80 100644
--- a/net/ax25/sysctl_net_ax25.c
+++ b/net/ax25/sysctl_net_ax25.c
@@ -1,14 +1,31 @@
-/* -*- linux-c -*-
- * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem.
+/*
+ * sysctl_net_ax25.c: sysctl interface for NEW-AX.25
*
- * Begun April 1, 1996, Mike Shaver.
- * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS]
+ * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Mike Shaver
+ *
+ * Comment: Needs cleanup, most parameters are not needed and/or should be au
+ * adjusting.
+ *
+ * Changelog:
+ *
+ * License: 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.
*/
-#include <linux/config.h>
#include <linux/mm.h>
#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/if_arp.h>
#include <net/ax25.h>
+#include <net/ax25dev.h>
+#include "ax25_ddi.h"
+
+int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp);
+int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context);
static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1};
static int min_axdefmode[] = {0}, max_axdefmode[] = {1};
@@ -16,22 +33,29 @@ static int min_backoff[] = {0}, max_backoff[] = {2};
static int min_conmode[] = {0}, max_conmode[] = {2};
static int min_window[] = {1}, max_window[] = {7};
static int min_ewindow[] = {1}, max_ewindow[] = {63};
-static int min_t1[] = {1}, max_t1[] = {30 * HZ};
-static int min_t2[] = {1}, max_t2[] = {20 * HZ};
-static int min_t3[] = {0}, max_t3[] = {3600 * HZ};
-static int min_idle[] = {0}, max_idle[] = {65535 * HZ};
+static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ * 10};
+static int min_t2[] = {0}, max_t2[] = {10 * AX25_SLOWHZ * 10};
+static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ * 10};
+static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ * 10};
static int min_n2[] = {1}, max_n2[] = {31};
static int min_paclen[] = {1}, max_paclen[] = {512};
-static int min_proto[] = {0}, max_proto[] = {3};
-static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ};
+static int min_protocol[] = {0}, max_protocol[] = {2};
+static int min_dama_slave_timeout[] = {0}, max_dama_slave_timeout[] = {3600 * AX25_SLOWHZ};
+static int min_media_duplex[] = {0}, max_media_duplex[] = {2};
+static int min_media_txdelay[] = {0}, max_media_txdelay[] = {10000};
+static int min_media_txtail[] = {0}, max_media_txtail[] = {10000};
+static int min_media_txbitrate[] = {0}, max_media_txbitrate[] = {80000000};
+static int min_media_rxbitrate[] = {0}, max_media_rxbitrate[] = {80000000};
+static int min_media_slottime[] = {0}, max_media_slottime[] = {1000};
+static int min_media_ppersist[] = {0}, max_media_ppersist[] = {255};
+static int min_media_autoadj[] = {0}, max_media_autoadj[] = {1};
static struct ctl_table_header *ax25_table_header;
-static ctl_table *ax25_table;
-static int ax25_table_size;
+static ctl_table ax25_table[AX25_MAX_DEVICES + 1];
static ctl_table ax25_dir_table[] = {
- {NET_AX25, "ax25", NULL, 0, 0555, NULL},
+ {NET_AX25, "ax25", NULL, 0, 0555, ax25_table},
{0}
};
@@ -92,71 +116,121 @@ static const ctl_table ax25_param_table[] = {
{NET_AX25_PROTOCOL, "protocol",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
- &min_proto, &max_proto},
+ &min_protocol, &max_protocol},
{NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
- &min_ds_timeout, &max_ds_timeout},
+ &min_dama_slave_timeout, &max_dama_slave_timeout},
+ {NET_AX25_MEDIA_DUPLEX, "media_duplex",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_duplex, &max_media_duplex},
+ {NET_AX25_MEDIA_TXDELAY, "media_txdelay",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txdelay, &max_media_txdelay},
+ {NET_AX25_MEDIA_TXTAIL, "media_txtail",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txtail, &max_media_txtail},
+ {NET_AX25_MEDIA_TXBITRATE, "media_txbitrate",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_txbitrate, &max_media_txbitrate},
+ {NET_AX25_MEDIA_RXBITRATE, "media_rxbitrate",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_rxbitrate, &max_media_rxbitrate},
+ {NET_AX25_MEDIA_SLOTTIME, "media_slottime",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_slottime, &max_media_slottime},
+ {NET_AX25_MEDIA_PPERSISTENCE, "media_ppersistence",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_ppersist, &max_media_ppersist},
+ {NET_AX25_MEDIA_AUTO_ADJUST, "media_autoadjust",
+ NULL, sizeof(int), 0644, NULL,
+ &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL,
+ &min_media_autoadj, &max_media_autoadj},
{0} /* that's all, folks! */
};
-void ax25_register_sysctl(void)
+int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp,
+ void *buffer, size_t *lenp)
{
- ax25_dev *ax25_dev;
- int n, k;
+ int id = table->ctl_name;
+ int ret;
+ int oldval, newval;
+ struct net_device *dev;
+
+ oldval = * (int *) table->data;
+ ret = proc_dointvec_minmax(table, write, filp, buffer, lenp);
+ newval = * (int *) table->data;
+ if (oldval != newval) {
+ dev = dev_get(table->de->parent->name);
+ if (!dev) return ret; /* paranoia */
+ ax25_notify_dispatcher(dev, id-1, oldval, newval);
+ }
+ return ret;
+}
+
+int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval,
+ size_t *oldlenp, void *newval, size_t newlen, void **context)
+{
+ int id = table->ctl_name;
+ int ret;
+ struct net_device *dev;
+
+ ret = sysctl_intvec(table, name, nlen, oldval, oldlenp, newval,
+ newlen, context);
+ if (* (int *) oldval != * (int *) newval) {
+ dev = dev_get(table->de->parent->name);
+ if (!dev) return ret; /* paranoia */
+ ax25_notify_dispatcher(dev, id-1, * (int *) oldval, * (int *) newval);
+ }
+ return ret;
+}
- for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
- ax25_table_size += sizeof(ctl_table);
+void ax25_register_sysctl(void)
+{
+ struct net_device *dev;
+ struct ax25_dev *ax25_device;
- if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL)
- return;
+ int n, k, i;
- memset(ax25_table, 0x00, ax25_table_size);
+ memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table));
- for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) {
- ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC);
- if (!child) {
- while (n--)
- kfree(ax25_table[n].child);
- kfree(ax25_table);
- return;
- }
- memcpy(child, ax25_param_table, sizeof(ax25_param_table));
- ax25_table[n].child = ax25_dev->systable = child;
- ax25_table[n].ctl_name = n + 1;
- ax25_table[n].procname = ax25_dev->dev->name;
- ax25_table[n].mode = 0555;
+ n = 0;
-#ifndef CONFIG_AX25_DAMA_SLAVE
- /*
- * We do not wish to have a representation of this parameter
- * in /proc/sys/ when configured *not* to include the
- * AX.25 DAMA slave code, do we?
- */
+ for (i = 0; i < AX25_MAX_DEVICES; i++) {
+ if ((dev = ax25_devices[i]) != NULL) {
+ ax25_device = AX25_PTR(dev);
+ if (n <= AX25_MAX_DEVICES) {
+ ax25_table[n].ctl_name = n + 1;
+ ax25_table[n].procname = dev->name;
+ ax25_table[n].data = NULL;
+ ax25_table[n].maxlen = 0;
+ ax25_table[n].mode = 0555;
+ ax25_table[n].child = ax25_device->systable;
+ ax25_table[n].proc_handler = NULL;
- child[AX25_VALUES_DS_TIMEOUT].procname = NULL;
-#endif
+ memcpy(ax25_device->systable, ax25_param_table, sizeof(ax25_device->systable));
- child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
+ ax25_device->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */
- for (k = 0; k < AX25_MAX_VALUES; k++)
- child[k].data = &ax25_dev->values[k];
+ for (k = 0; k < AX25_MAX_VALUES; k++)
+ ax25_device->systable[k].data = &ax25_device->values[k];
- n++;
+ n++;
+ }
+ }
}
- ax25_dir_table[0].child = ax25_table;
-
ax25_table_header = register_sysctl_table(ax25_root_table, 1);
}
void ax25_unregister_sysctl(void)
{
- ctl_table *p;
unregister_sysctl_table(ax25_table_header);
-
- ax25_dir_table[0].child = NULL;
- for (p = ax25_table; p->ctl_name; p++)
- kfree(p->child);
- kfree(ax25_table);
}
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index c43892ea5..e3f98e671 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -257,33 +257,6 @@ static int arp_constructor(struct neighbour *neigh)
in old paradigm.
*/
-#if 1
- /* So... these "amateur" devices are hopeless.
- The only thing, that I can say now:
- It is very sad that we need to keep ugly obsolete
- code to make them happy.
-
- They should be moved to more reasonable state, now
- they use rebuild_header INSTEAD OF hard_start_xmit!!!
- Besides that, they are sort of out of date
- (a lot of redundant clones/copies, useless in 2.1),
- I wonder why people believe that they work.
- */
- switch (dev->type) {
- default:
- break;
- case ARPHRD_ROSE:
-#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE)
- case ARPHRD_AX25:
-#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE)
- case ARPHRD_NETROM:
-#endif
- neigh->ops = &arp_broken_ops;
- neigh->output = neigh->ops->output;
- return 0;
-#endif
- ;}
-#endif
if (neigh->type == RTN_MULTICAST) {
neigh->nud_state = NUD_NOARP;
arp_mc_map(addr, neigh->ha, dev, 1);
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index a4b89d294..3ba0b8c81 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -47,6 +47,7 @@
#include <linux/net.h>
#include <linux/stat.h>
#include <net/ax25.h>
+#include <net/ax25_uid.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
@@ -347,6 +348,8 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ char devname[IFNAMSIZ];
+ struct net_device *dev;
int opt;
if (level != SOL_NETROM)
@@ -389,6 +392,21 @@ static int nr_setsockopt(struct socket *sock, int level, int optname,
sk->protinfo.nr->idle = opt * 60 * HZ;
return 0;
+ case SO_BINDTODEVICE:
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ dev = dev_get_by_name(devname);
+ if (dev == NULL) return -ENODEV;
+
+ if (sk->type == SOCK_SEQPACKET &&
+ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+ return -EADDRNOTAVAIL;
+
+ sk->protinfo.nr->device=dev;
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -398,15 +416,24 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
+ void *valptr;
int val = 0;
- int len;
+ int maxlen, length;
if (level != SOL_NETROM)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
+ if (get_user(maxlen, optlen))
return -EFAULT;
+ if (maxlen < 1)
+ return -EFAULT;
+
+ valptr = (void *) &val;
+ length = min(maxlen, sizeof(int));
+
switch (optname) {
case NETROM_T1:
val = sk->protinfo.nr->t1 / HZ;
@@ -428,16 +455,33 @@ static int nr_getsockopt(struct socket *sock, int level, int optname,
val = sk->protinfo.nr->idle / (60 * HZ);
break;
+ case SO_BINDTODEVICE:
+ dev = sk->protinfo.nr->device;
+
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
+ devname[length-1] = '\0';
+ } else {
+ *devname = '\0';
+ length = 1;
+ }
+
+ valptr = (void *) devname;
+ break;
+
+
default:
return -ENOPROTOOPT;
}
- len = min(len, sizeof(int));
+ if (put_user(length, optlen))
+ return -EFAULT;
- if (put_user(len, optlen))
+ if (copy_to_user(optval, valptr, length))
return -EFAULT;
- return copy_to_user(optval, &val, len) ? -EFAULT : 0;
+ return 0;
}
static int nr_listen(struct socket *sock, int backlog)
@@ -589,13 +633,12 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr;
struct net_device *dev;
- ax25_address *user, *source;
+ ax25_address *user = NULL, *source = NULL;
if (sk->zapped == 0)
return -EINVAL;
- if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct
-full_sockaddr_ax25))
+ if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25))
return -EINVAL;
if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25)))
@@ -604,32 +647,42 @@ full_sockaddr_ax25))
if (addr->fsa_ax25.sax25_family != AF_NETROM)
return -EINVAL;
- if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) {
- SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n");
- return -EADDRNOTAVAIL;
- }
-
/*
- * Only the super user can set an arbitrary user callsign.
+ * User did not set the interfave with SO_BINDTODEVICE,
+ * use compatibility code.
*/
- if (addr->fsa_ax25.sax25_ndigis == 1) {
- if (!capable(CAP_NET_BIND_SERVICE))
- return -EACCES;
- sk->protinfo.nr->user_addr = addr->fsa_digipeater[0];
- sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call;
- } else {
- source = &addr->fsa_ax25.sax25_call;
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
- return -EPERM;
- user = source;
+ dev = sk->protinfo.nr->device;
+ if (dev == NULL) {
+ if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) {
+ /* device callsign provided */
+ if(ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) &&
+ (dev = nr_dev_get(&addr->fsa_digipeater[0])) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_digipeater[0];
+ } else {
+ if ((dev = nr_dev_get(&addr->fsax25_call)) == NULL)
+ return -EADDRNOTAVAIL;
+ source = &addr->fsa_ax25.sax25_call;
}
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
+ sk->protinfo.nr->device = dev;
+ } else {
+ source = (ax25_address *) dev->dev_addr;
}
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ /* FIXME: should not be coupled with AX.25 */
+ user = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name);
+ if (user == NULL && ax25_uid_policy)
+ return -EACCES;
+ }
+
+ if (user == NULL) user = &addr->fsax25_call;
+ sk->protinfo.nr->user_addr = *user;
+ sk->protinfo.nr->source_addr = *source;
+
sk->protinfo.nr->device = dev;
nr_insert_socket(sk);
@@ -643,7 +696,6 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
{
struct sock *sk = sock->sk;
struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr;
- ax25_address *user, *source = NULL;
struct net_device *dev;
if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
@@ -668,26 +720,8 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr,
if (addr->sax25_family != AF_NETROM)
return -EINVAL;
- if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
- sk->zapped = 0;
-
- if ((dev = nr_dev_first()) == NULL)
- return -ENETUNREACH;
-
- source = (ax25_address *)dev->dev_addr;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_ADMIN))
- return -EPERM;
- user = source;
- }
-
- sk->protinfo.nr->user_addr = *user;
- sk->protinfo.nr->source_addr = *source;
- sk->protinfo.nr->device = dev;
-
- nr_insert_socket(sk); /* Finish the bind */
- }
+ if (sk->zapped)
+ return -EADDRNOTAVAIL;
sk->protinfo.nr->dest_addr = addr->sax25_call;
diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c
index 5290ae024..519ca55da 100644
--- a/net/netrom/nr_loopback.c
+++ b/net/netrom/nr_loopback.c
@@ -44,10 +44,10 @@ int nr_loopback_queue(struct sk_buff *skb)
{
struct sk_buff *skbn;
- if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) {
- memcpy(skb_put(skbn, skb->len), skb->data, skb->len);
- skbn->h.raw = skbn->data;
+ skbn = skb_clone(skb, GFP_ATOMIC);
+ kfree(skb);
+ if (skbn != NULL) {
skb_queue_tail(&loopback_queue, skbn);
if (!nr_loopback_running())
diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c
index ec0578b51..e6793fb0b 100644
--- a/net/netrom/nr_route.c
+++ b/net/netrom/nr_route.c
@@ -60,8 +60,8 @@ static void nr_remove_neigh(struct nr_neigh *);
* Add a new route to a node, and in the process add the node and the
* neighbour if it is new.
*/
-static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25,
- ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count)
+static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_addr_t *ax25_addr,
+ struct net_device *dev, int quality, int obs_count)
{
struct nr_node *nr_node;
struct nr_neigh *nr_neigh;
@@ -77,7 +77,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
break;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
/*
@@ -106,8 +106,8 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- nr_neigh->callsign = *ax25;
- nr_neigh->digipeat = NULL;
+ nr_neigh->addr = *ax25_addr;
+ nr_neigh->addr.src = *((ax25_address *) dev->dev_addr);
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = sysctl_netrom_default_path_quality;
@@ -116,13 +116,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
- kfree(nr_neigh);
- return -ENOMEM;
- }
- memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
- }
+ /* FIXME: not SMP safe! */
save_flags(flags);
cli();
@@ -133,7 +127,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2
restore_flags(flags);
}
- if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked)
+ if (quality != 0 && ax25cmp(nr, &ax25_addr->dest) == 0 && !nr_neigh->locked)
nr_neigh->quality = quality;
if (nr_node == NULL) {
@@ -294,8 +288,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
if ((s = nr_neigh_list) == nr_neigh) {
nr_neigh_list = nr_neigh->next;
restore_flags(flags);
- if (nr_neigh->digipeat != NULL)
- kfree(nr_neigh->digipeat);
kfree(nr_neigh);
return;
}
@@ -304,8 +296,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh)
if (s->next == nr_neigh) {
s->next = nr_neigh->next;
restore_flags(flags);
- if (nr_neigh->digipeat != NULL)
- kfree(nr_neigh->digipeat);
kfree(nr_neigh);
return;
}
@@ -333,7 +323,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
if (nr_node == NULL) return -EINVAL;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(neighbour, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
if (nr_neigh == NULL) return -EINVAL;
@@ -370,13 +360,13 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n
/*
* Lock a neighbour with a quality.
*/
-static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality)
+static int nr_add_neigh(ax25_addr_t *ax25_addr, struct net_device *dev, unsigned int quality)
{
struct nr_neigh *nr_neigh;
unsigned long flags;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
- if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) {
+ if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) {
nr_neigh->quality = quality;
nr_neigh->locked = 1;
return 0;
@@ -386,8 +376,8 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net
if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- nr_neigh->callsign = *callsign;
- nr_neigh->digipeat = NULL;
+ nr_neigh->addr = *ax25_addr;
+ nr_neigh->addr.src = *((ax25_address *) dev->dev_addr);
nr_neigh->ax25 = NULL;
nr_neigh->dev = dev;
nr_neigh->quality = quality;
@@ -396,14 +386,6 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net
nr_neigh->number = nr_neigh_no++;
nr_neigh->failed = 0;
- if (ax25_digi != NULL && ax25_digi->ndigi > 0) {
- if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) {
- kfree(nr_neigh);
- return -ENOMEM;
- }
- memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi));
- }
-
save_flags(flags);
cli();
@@ -424,7 +406,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned
struct nr_neigh *nr_neigh;
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next)
- if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev)
+ if (ax25cmp(callsign, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev)
break;
if (nr_neigh == NULL) return -EINVAL;
@@ -586,31 +568,26 @@ struct net_device *nr_dev_get(ax25_address *addr)
for (dev = dev_base; dev != NULL; dev = dev->next) {
if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) {
dev_hold(dev);
- goto out;
+ break;
}
}
-out:
+
read_unlock(&dev_base_lock);
return dev;
}
-static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters)
+static ax25_addr_t *nr_neigh_to_addr(ax25_address *callsign, int ndigis, ax25_address *digipeater)
{
- static ax25_digi ax25_digi;
+ static ax25_addr_t ax25_addr;
int i;
- if (ndigis == 0)
- return NULL;
+ ax25_addr.dest = *callsign;
+ ax25_addr.dcount = ndigis;
- for (i = 0; i < ndigis; i++) {
- ax25_digi.calls[i] = digipeaters[i];
- ax25_digi.repeated[i] = 0;
- }
+ for (i = 0; i < ndigis; i++)
+ ax25_addr.digipeater[i] = digipeater[i];
- ax25_digi.ndigi = ndigis;
- ax25_digi.lastrepeat = -1;
-
- return &ax25_digi;
+ return &ax25_addr;
}
/*
@@ -632,16 +609,13 @@ int nr_rt_ioctl(unsigned int cmd, void *arg)
return -EINVAL;
switch (nr_route.type) {
case NETROM_NODE:
- return nr_add_node(&nr_route.callsign,
- nr_route.mnemonic,
- &nr_route.neighbour,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
+ return nr_add_node(&nr_route.callsign, nr_route.mnemonic,
+ nr_neigh_to_addr(&nr_route.neighbour, nr_route.ndigis, nr_route.digipeaters),
dev, nr_route.quality,
nr_route.obs_count);
case NETROM_NEIGH:
- return nr_add_neigh(&nr_route.callsign,
- nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters),
- dev, nr_route.quality);
+ return nr_add_neigh(nr_neigh_to_addr(&nr_route.callsign, nr_route.ndigis, nr_route.digipeaters),
+ dev, nr_route.quality);
default:
return -EINVAL;
}
@@ -713,8 +687,8 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
nr_dest = (ax25_address *)(skb->data + 7);
if (ax25 != NULL)
- nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat,
- ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser);
+ nr_add_node(nr_src, "", &ax25->addr, ax25->device, 0,
+ sysctl_netrom_obsolescence_count_initialiser);
if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */
if (ax25 == NULL) /* Its from me */
@@ -745,7 +719,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25)
dptr = skb_push(skb, 1);
*dptr = AX25_P_NETROM;
- nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev);
+ /* The next line is dirty hotfix. There is a bug in nr_add_neigh() -
+ * src callsign is taken from dev->dev_addr of device on which the
+ * broadcast was received. But this is AX25 device (and callsign),
+ * not the netrom device, so the callsign is bad.
+ */
+ nr_neigh->addr.src = *((ax25_address*)dev->dev_addr);
+ nr_neigh->ax25 = ax25_send_frame(skb, 256, &nr_neigh->addr, nr_neigh->dev);
return (nr_neigh->ax25 != NULL);
}
@@ -814,16 +794,16 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length)
for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) {
len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d",
nr_neigh->number,
- ax2asc(&nr_neigh->callsign),
+ ax2asc(&nr_neigh->addr.dest),
nr_neigh->dev ? nr_neigh->dev->name : "???",
nr_neigh->quality,
nr_neigh->locked,
nr_neigh->count,
nr_neigh->failed);
- if (nr_neigh->digipeat != NULL) {
- for (i = 0; i < nr_neigh->digipeat->ndigi; i++)
- len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i]));
+ if (nr_neigh->addr.dcount) {
+ for (i = 0; i < nr_neigh->addr.dcount; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->addr.digipeater[i]));
}
len += sprintf(buffer + len, "\n");
diff --git a/net/netsyms.c b/net/netsyms.c
index 8adcef849..5d56062f3 100644
--- a/net/netsyms.c
+++ b/net/netsyms.c
@@ -246,6 +246,7 @@ EXPORT_SYMBOL(ip_dev_find);
EXPORT_SYMBOL(inetdev_by_index);
EXPORT_SYMBOL(in_dev_finish_destroy);
EXPORT_SYMBOL(ip_defrag);
+EXPORT_SYMBOL(ip_options_fragment);
/* Route manipulation */
EXPORT_SYMBOL(ip_rt_ioctl);
diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
index a92bf86f5..07acd3946 100644
--- a/net/rose/af_rose.c
+++ b/net/rose/af_rose.c
@@ -39,6 +39,7 @@
#include <linux/net.h>
#include <linux/stat.h>
#include <net/ax25.h>
+#include <net/ax25_uid.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
@@ -404,6 +405,8 @@ static int rose_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
int opt;
if (level != SOL_ROSE)
@@ -454,6 +457,22 @@ static int rose_setsockopt(struct socket *sock, int level, int optname,
sk->protinfo.rose->qbitincl = opt ? 1 : 0;
return 0;
+
+ case SO_BINDTODEVICE:
+ if (optlen > IFNAMSIZ) optlen = IFNAMSIZ;
+ if (copy_from_user(devname, optval, optlen))
+ return -EFAULT;
+
+ dev = dev_get_by_name(devname);
+ if (dev == NULL) return -ENODEV;
+
+ if (sk->type == SOCK_SEQPACKET &&
+ (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN))
+ return -EADDRNOTAVAIL;
+
+ sk->protinfo.rose->device = dev;
+ return 0;
+
default:
return -ENOPROTOOPT;
}
@@ -463,14 +482,23 @@ static int rose_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->sk;
+ struct net_device *dev;
+ char devname[IFNAMSIZ];
+ void *valptr;
int val = 0;
- int len;
+ int maxlen, length;
if (level != SOL_ROSE)
return -ENOPROTOOPT;
- if (get_user(len, optlen))
+ if (get_user(maxlen, optlen))
return -EFAULT;
+
+ if (maxlen < 1)
+ return -EFAULT;
+
+ valptr = (void *) &val;
+ length = min(maxlen, sizeof(int));
switch (optname) {
case ROSE_DEFER:
@@ -501,16 +529,32 @@ static int rose_getsockopt(struct socket *sock, int level, int optname,
val = sk->protinfo.rose->qbitincl;
break;
+ case SO_BINDTODEVICE:
+ dev = sk->protinfo.rose->device;
+
+ if (dev != NULL) {
+ strncpy(devname, dev->name, IFNAMSIZ);
+ length = min(strlen(dev->name)+1, maxlen);
+ devname[length-1] = '\0';
+ } else {
+ *devname = '\0';
+ length = 1;
+ }
+
+ valptr = (void *) devname;
+ break;
+
default:
return -ENOPROTOOPT;
}
- len = min(len, sizeof(int));
+ if (put_user(length, optlen))
+ return -EFAULT;
- if (put_user(len, optlen))
+ if (copy_to_user(optval, valptr, length))
return -EFAULT;
- return copy_to_user(optval, &val, len) ? -EFAULT : 0;
+ return 0;
}
static int rose_listen(struct socket *sock, int backlog)
@@ -666,7 +710,8 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
struct sock *sk = sock->sk;
struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr;
struct net_device *dev;
- ax25_address *user, *source;
+ ax25_address *user = NULL;
+ rose_address *source = NULL;
int n;
if (sk->zapped == 0)
@@ -683,21 +728,36 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (addr->srose_ndigis > ROSE_MAX_DIGIS)
return -EINVAL;
+ /*
+ * User did not set interface with SO_BINDTODEVICE
+ * thus we'll use the compatibility code
+ */
+
+ dev = sk->protinfo.rose->device;
+ if (dev == NULL) {
+ if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
+ SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
+ return -EADDRNOTAVAIL;
+ }
- if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) {
- SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n");
- return -EADDRNOTAVAIL;
+ source = &addr->srose_addr;
+ sk->protinfo.rose->device = dev;
+ } else {
+ source = (rose_address *) dev->dev_addr;
}
- source = &addr->srose_call;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL) {
- if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE))
+ /* root can do whatever (s)he likes, but anyone else... */
+ if (!capable(CAP_NET_BIND_SERVICE))
+ {
+ /* FIXME: strictly speaking this has nothing to do with AX.25 */
+ user = ax25_find_match_for_uid(current->euid, &addr->srose_call, dev->name);
+ if (user == NULL && ax25_uid_policy)
return -EACCES;
- user = source;
}
- sk->protinfo.rose->source_addr = addr->srose_addr;
+ if (user == NULL) user = &addr->srose_call;
+
+ sk->protinfo.rose->source_addr = *source;
sk->protinfo.rose->source_call = *user;
sk->protinfo.rose->device = dev;
sk->protinfo.rose->source_ndigis = addr->srose_ndigis;
@@ -766,21 +826,8 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le
if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0)
return -ENETUNREACH;
- if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */
- sk->zapped = 0;
-
- if ((dev = rose_dev_first()) == NULL)
- return -ENETUNREACH;
-
- if ((user = ax25_findbyuid(current->euid)) == NULL)
- return -EINVAL;
-
- memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN);
- sk->protinfo.rose->source_call = *user;
- sk->protinfo.rose->device = dev;
-
- rose_insert_socket(sk); /* Finish the bind */
- }
+ if (sk->zapped)
+ return -EINVAL;
sk->protinfo.rose->dest_addr = addr->srose_addr;
sk->protinfo.rose->dest_call = addr->srose_call;
diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c
index 2c793e4e6..a4e5f71bd 100644
--- a/net/rose/rose_link.c
+++ b/net/rose/rose_link.c
@@ -104,14 +104,12 @@ static void rose_t0timer_expiry(unsigned long param)
*/
static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
{
- ax25_address *rose_call;
-
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
- rose_call = (ax25_address *)neigh->dev->dev_addr;
+ neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr);
else
- rose_call = &rose_callsign;
+ neigh->addr.src = rose_callsign;
- neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+ neigh->ax25 = ax25_send_frame(skb, 260, &neigh->addr, neigh->dev);
return (neigh->ax25 != NULL);
}
@@ -123,14 +121,12 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh)
*/
static int rose_link_up(struct rose_neigh *neigh)
{
- ax25_address *rose_call;
-
if (ax25cmp(&rose_callsign, &null_ax25_address) == 0)
- rose_call = (ax25_address *)neigh->dev->dev_addr;
+ neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr);
else
- rose_call = &rose_callsign;
+ neigh->addr.src = rose_callsign;
- neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev);
+ neigh->ax25 = ax25_find_cb(&neigh->addr, neigh->dev);
return (neigh->ax25 != NULL);
}
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
index 1bedfdf0d..4de549586 100644
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -77,23 +77,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
return -EINVAL;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
- if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+ if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev)
break;
if (rose_neigh == NULL) {
if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- rose_neigh->callsign = rose_route->neighbour;
- rose_neigh->digipeat = NULL;
- rose_neigh->ax25 = NULL;
- rose_neigh->dev = dev;
- rose_neigh->count = 0;
- rose_neigh->use = 0;
- rose_neigh->dce_mode = 0;
- rose_neigh->loopback = 0;
- rose_neigh->number = rose_neigh_no++;
- rose_neigh->restarted = 0;
+ rose_neigh->addr.dest = rose_route->neighbour;
+ rose_neigh->addr.dcount = 0;
+ rose_neigh->ax25 = NULL;
+ rose_neigh->dev = dev;
+ rose_neigh->count = 0;
+ rose_neigh->use = 0;
+ rose_neigh->dce_mode = 0;
+ rose_neigh->loopback = 0;
+ rose_neigh->number = rose_neigh_no++;
+ rose_neigh->restarted = 0;
skb_queue_head_init(&rose_neigh->queue);
@@ -101,18 +101,11 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device
init_timer(&rose_neigh->t0timer);
if (rose_route->ndigis != 0) {
- if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) {
- kfree(rose_neigh);
- return -ENOMEM;
- }
-
- rose_neigh->digipeat->ndigi = rose_route->ndigis;
- rose_neigh->digipeat->lastrepeat = -1;
+ rose_neigh->addr.dcount = rose_route->ndigis;
+ rose_neigh->addr.lastrepeat = -1;
- for (i = 0; i < rose_route->ndigis; i++) {
- rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i];
- rose_neigh->digipeat->repeated[i] = 0;
- }
+ for (i = 0; i < rose_route->ndigis; i++)
+ rose_neigh->addr.digipeater[i] = rose_route->digipeaters[i];
}
save_flags(flags); cli();
@@ -234,8 +227,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
if ((s = rose_neigh_list) == rose_neigh) {
rose_neigh_list = rose_neigh->next;
restore_flags(flags);
- if (rose_neigh->digipeat != NULL)
- kfree(rose_neigh->digipeat);
kfree(rose_neigh);
return;
}
@@ -244,8 +235,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh)
if (s->next == rose_neigh) {
s->next = rose_neigh->next;
restore_flags(flags);
- if (rose_neigh->digipeat != NULL)
- kfree(rose_neigh->digipeat);
kfree(rose_neigh);
return;
}
@@ -309,7 +298,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device
if (rose_node->loopback) return -EINVAL;
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
- if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev)
+ if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev)
break;
if (rose_neigh == NULL) return -EINVAL;
@@ -353,16 +342,16 @@ int rose_add_loopback_neigh(void)
if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL)
return -ENOMEM;
- rose_loopback_neigh->callsign = null_ax25_address;
- rose_loopback_neigh->digipeat = NULL;
- rose_loopback_neigh->ax25 = NULL;
- rose_loopback_neigh->dev = NULL;
- rose_loopback_neigh->count = 0;
- rose_loopback_neigh->use = 0;
- rose_loopback_neigh->dce_mode = 1;
- rose_loopback_neigh->loopback = 1;
- rose_loopback_neigh->number = rose_neigh_no++;
- rose_loopback_neigh->restarted = 1;
+ rose_loopback_neigh->addr.dest = null_ax25_address;
+ rose_loopback_neigh->addr.dcount = 0;
+ rose_loopback_neigh->ax25 = NULL;
+ rose_loopback_neigh->dev = NULL;
+ rose_loopback_neigh->count = 0;
+ rose_loopback_neigh->use = 0;
+ rose_loopback_neigh->dce_mode = 1;
+ rose_loopback_neigh->loopback = 1;
+ rose_loopback_neigh->number = rose_neigh_no++;
+ rose_loopback_neigh->restarted = 1;
skb_queue_head_init(&rose_loopback_neigh->queue);
@@ -787,11 +776,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25)
dest_addr = (rose_address *)(skb->data + 4);
for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next)
- if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev)
+ if (ax25cmp(&ax25->addr.dest, &rose_neigh->addr.dest) == 0 && ax25->device == rose_neigh->dev)
break;
if (rose_neigh == NULL) {
- printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr));
+ printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->addr.dest));
return 0;
}
@@ -1034,7 +1023,7 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length)
/* if (!rose_neigh->loopback) { */
len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu",
rose_neigh->number,
- (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign),
+ (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->addr.dest),
rose_neigh->dev ? rose_neigh->dev->name : "???",
rose_neigh->count,
rose_neigh->use,
@@ -1043,9 +1032,9 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length)
ax25_display_timer(&rose_neigh->t0timer) / HZ,
ax25_display_timer(&rose_neigh->ftimer) / HZ);
- if (rose_neigh->digipeat != NULL) {
- for (i = 0; i < rose_neigh->digipeat->ndigi; i++)
- len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i]));
+ if (rose_neigh->addr.dcount != 0) {
+ for (i = 0; i < rose_neigh->addr.dcount; i++)
+ len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->addr.digipeater[i]));
}
len += sprintf(buffer + len, "\n");
diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c
index 384347a0e..f881e5da5 100644
--- a/net/rose/rose_subr.c
+++ b/net/rose/rose_subr.c
@@ -119,8 +119,9 @@ void rose_write_internal(struct sock *sk, int frametype)
unsigned char lci1, lci2;
char buffer[100];
int len, faclen = 0;
+ int ax25_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1;
- len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1;
+ len = ax25_header_len + ROSE_MIN_LEN;
switch (frametype) {
case ROSE_CALL_REQUEST:
@@ -141,9 +142,9 @@ void rose_write_internal(struct sock *sk, int frametype)
/*
* Space for AX.25 header and PID.
*/
- skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1);
+ skb_reserve(skb, ax25_header_len);
- dptr = skb_put(skb, skb_tailroom(skb));
+ dptr = skb_put(skb, len - ax25_header_len);
lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F;
lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF;