summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2015-06-24 04:23:46 +0200
committerRalf Baechle <ralf@linux-mips.org>2015-06-24 10:03:18 +0200
commite5067d7cd967cb17067de24a162306b79f432b20 (patch)
tree541f101762df32a5742bec354009986a96d8e564 /drivers
parent86a981e836404006efc35881ebf3d5ae36925e82 (diff)
Import newax25-2.4.3.patch.1.bz2HEADnewax25-2.4.3-1
And cleanup the *.orig and *.rej files and whitespace errors that are part of the original patch. Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers')
-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
30 files changed, 8812 insertions, 9111 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);