diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 04:23:46 +0200 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-06-24 10:03:18 +0200 |
commit | e5067d7cd967cb17067de24a162306b79f432b20 (patch) | |
tree | 541f101762df32a5742bec354009986a96d8e564 /drivers/net | |
parent | 86a981e836404006efc35881ebf3d5ae36925e82 (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/net')
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); |