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 | |
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>
98 files changed, 16587 insertions, 14879 deletions
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c index 98b941224..9d483d1ea 100644 --- a/drivers/net/hamradio/6pack.c +++ b/drivers/net/hamradio/6pack.c @@ -1,11 +1,12 @@ /* * 6pack.c This module implements the 6pack protocol for kernel-based * devices like TTY. It interfaces between a raw TTY and the - * kernel's AX.25 protocol layers. + * kernel's new AX.25 protocol layer. * - * Version: @(#)6pack.c 0.3.0 04/07/98 + * Version: @(#)6pack.c 0.4.0 03/05/2000 * * Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de> + * Jens David (rework) <dg1kjd@afthd.tu-darmstadt.de> * * Quite a lot of stuff "stolen" by Jörg Reuter from slip.c, written by * @@ -28,145 +29,65 @@ #include <linux/netdevice.h> #include <linux/timer.h> #include <net/ax25.h> +#include <net/ax25dev.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/rtnetlink.h> #include <linux/if_arp.h> +#include <linux/if_slip.h> #include <linux/init.h> #include <linux/ip.h> #include <linux/tcp.h> -#define SIXPACK_VERSION "Revision: 0.3.0" - -/* sixpack priority commands */ -#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ -#define SIXP_TX_URUN 0x48 /* transmit overrun */ -#define SIXP_RX_ORUN 0x50 /* receive overrun */ -#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ - -#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ - -/* masks to get certain bits out of the status bytes sent by the TNC */ - -#define SIXP_CMD_MASK 0xC0 -#define SIXP_CHN_MASK 0x07 -#define SIXP_PRIO_CMD_MASK 0x80 -#define SIXP_STD_CMD_MASK 0x40 -#define SIXP_PRIO_DATA_MASK 0x38 -#define SIXP_TX_MASK 0x20 -#define SIXP_RX_MASK 0x10 -#define SIXP_RX_DCD_MASK 0x18 -#define SIXP_LEDS_ON 0x78 -#define SIXP_LEDS_OFF 0x60 -#define SIXP_CON 0x08 -#define SIXP_STA 0x10 - -#define SIXP_FOUND_TNC 0xe9 -#define SIXP_CON_ON 0x68 -#define SIXP_DCD_MASK 0x08 -#define SIXP_DAMA_OFF 0 - -/* default level 2 parameters */ -#define SIXP_TXDELAY 25 /* in 10 ms */ -#define SIXP_PERSIST 50 /* in 256ths */ -#define SIXP_SLOTTIME 10 /* in 10 ms */ -#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */ -#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */ - -/* 6pack configuration. */ -#define SIXP_NRUNIT 31 /* MAX number of 6pack channels */ -#define SIXP_MTU 256 /* Default MTU */ - -enum sixpack_flags { - SIXPF_INUSE, /* Channel in use */ - SIXPF_ERROR, /* Parity, etc. error */ -}; +#include "6pack.h" -struct sixpack { - int magic; - - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - - /* These are pointers to the malloc()ed frame buffers. */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - unsigned char raw_buf[4]; - unsigned char cooked_buf[400]; - - unsigned int rx_count; - unsigned int rx_count_cooked; - - /* 6pack interface statistics. */ - struct net_device_stats stats; - - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - - unsigned long flags; /* Flag values/ mode etc */ - unsigned char mode; /* 6pack mode */ - - /* 6pack stuff */ - unsigned char tx_delay; - unsigned char persistance; - unsigned char slottime; - unsigned char duplex; - unsigned char led_state; - unsigned char status; - unsigned char status1; - unsigned char status2; - unsigned char tx_enable; - unsigned char tnc_ok; - - struct timer_list tx_t; - struct timer_list resync_t; -}; +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) -#define AX25_6PACK_HEADER_LEN 0 -#define SIXPACK_MAGIC 0x5304 +/* ----------------------------------------------------------------------- */ -typedef struct sixpack_ctrl { - struct sixpack ctrl; /* 6pack things */ - struct net_device dev; /* the device */ -} sixpack_ctrl_t; -static sixpack_ctrl_t **sixpack_ctrls; +static struct sixpack_ctrl_t **sixpack_ctrls = NULL; +int sixpack_maxdev = SIXP_NRUNIT; /* override with insmod */ -int sixpack_maxdev = SIXP_NRUNIT; /* Can be overridden with insmod! */ -MODULE_PARM(sixpack_maxdev, "i"); -MODULE_PARM_DESC(sixpack_maxdev, "number of 6PACK devices"); +/* ----------------------------------------------------------------------- */ -static void sp_start_tx_timer(struct sixpack *); -static void sp_xmit_on_air(unsigned long); +/* prototypes */ static void resync_tnc(unsigned long); static void sixpack_decode(struct sixpack *, unsigned char[], int); -static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned char); -static int sixpack_init(struct net_device *dev); - +static int encode_sixpack(unsigned char *, unsigned char *, int, unsigned int); static void decode_prio_command(unsigned char, struct sixpack *); static void decode_std_command(unsigned char, struct sixpack *); static void decode_data(unsigned char, struct sixpack *); - static int tnc_init(struct sixpack *); +static unsigned int sixpack_ddi_report_dcd(struct net_device *dev); +static unsigned int sixpack_ddi_report_ptt(struct net_device *dev); +static void sixpack_parameter_change_notify(struct net_device *dev, int valueno, + int old, int new); + +static int sixpack_init(struct net_device *dev); + +/* ----------------------------------------------------------------------- */ /* Find a free 6pack channel, and link in this `tty' line. */ -static inline struct sixpack *sp_alloc(void) +static inline struct sixpack *sp_alloc(struct tty_struct *tty) { - sixpack_ctrl_t *spp = NULL; + struct sixpack_ctrl_t *spp = NULL; int i; - for (i = 0; i < sixpack_maxdev; i++) { + if (sixpack_ctrls == NULL) return NULL; /* Master array missing ! */ + + for (i = 0; i < sixpack_maxdev; i++) + { spp = sixpack_ctrls[i]; if (spp == NULL) break; - if (!test_and_set_bit(SIXPF_INUSE, &spp->ctrl.flags)) + if (!spp->ctrl.flags.in_use) + { + spp->ctrl.flags.in_use = 1; break; + } } /* Too many devices... */ @@ -174,42 +95,42 @@ static inline struct sixpack *sp_alloc(void) return NULL; /* If no channels are available, allocate one */ - if (!spp && - (sixpack_ctrls[i] = (sixpack_ctrl_t *)kmalloc(sizeof(sixpack_ctrl_t), - GFP_KERNEL)) != NULL) { + if (!spp && (sixpack_ctrls[i] = (struct sixpack_ctrl_t *) kmalloc(sizeof(struct sixpack_ctrl_t), GFP_KERNEL)) != NULL) { spp = sixpack_ctrls[i]; - memset(spp, 0, sizeof(sixpack_ctrl_t)); + memset(spp, 0, sizeof(struct sixpack_ctrl_t)); /* Initialize channel control data */ - set_bit(SIXPF_INUSE, &spp->ctrl.flags); - spp->ctrl.tty = NULL; + spp->ctrl.flags.in_use= 1; sprintf(spp->dev.name, "sp%d", i); spp->dev.base_addr = i; - spp->dev.priv = (void *) &spp->ctrl; + spp->dev.priv = (void*)&(spp->ctrl); spp->dev.next = NULL; spp->dev.init = sixpack_init; } - if (spp != NULL) { - /* register device so that it can be ifconfig'ed */ - /* sixpack_init() will be called as a side-effect */ - /* SIDE-EFFECT WARNING: sixpack_init() CLEARS spp->ctrl ! */ - - if (register_netdev(&spp->dev) == 0) { - set_bit(SIXPF_INUSE, &spp->ctrl.flags); - spp->ctrl.dev = &spp->dev; - spp->dev.priv = (void *) &spp->ctrl; - SET_MODULE_OWNER(&spp->dev); - return &spp->ctrl; + if (spp != NULL) + { + spp->ctrl.tty = tty; + /* + * register device so that it can be ifconfig'ed + * sixpack_init() will be called as a side-effect + * SIDE-EFFECT WARNING: sixpack_init() CLEARS spp->ctrl ! + */ + if (register_netdev(&(spp->dev)) == 0) + { + spp->ctrl.flags.in_use = 1; + spp->ctrl.dev = &(spp->dev); + spp->dev.priv = (void*)&(spp->ctrl); + return (&(spp->ctrl)); } else { - clear_bit(SIXPF_INUSE, &spp->ctrl.flags); + spp->ctrl.flags.in_use = 0; printk(KERN_WARNING "sp_alloc() - register_netdev() failure.\n"); } } - return NULL; } +/* ----------------------------------------------------------------------- */ /* Free a 6pack channel. */ static inline void sp_free(struct sixpack *sp) @@ -218,48 +139,40 @@ static inline void sp_free(struct sixpack *sp) if (sp->rbuff) kfree(sp->rbuff); sp->rbuff = NULL; - if (sp->xbuff) + if (sp->xbuff) { kfree(sp->xbuff); + } sp->xbuff = NULL; - - if (!test_and_clear_bit(SIXPF_INUSE, &sp->flags)) - printk(KERN_WARNING "%s: sp_free for already free unit.\n", sp->dev->name); } +/* ----------------------------------------------------------------------- */ /* Send one completely decapsulated IP datagram to the IP layer. */ - -/* This is the routine that sends the received data to the kernel AX.25. - 'cmd' is the KISS command. For AX.25 data, it is zero. */ - static void sp_bump(struct sixpack *sp, char cmd) { struct sk_buff *skb; int count; unsigned char *ptr; - count = sp->rcount+1; - + count = sp->rcount; sp->stats.rx_bytes += count; - if ((skb = dev_alloc_skb(count)) == NULL) { + skb = dev_alloc_skb(count); + if (skb == NULL) + { printk(KERN_DEBUG "%s: memory squeeze, dropping packet.\n", sp->dev->name); sp->stats.rx_dropped++; return; } - skb->dev = sp->dev; ptr = skb_put(skb, count); - *ptr++ = cmd; /* KISS command */ - memcpy(ptr, (sp->cooked_buf)+1, count); - skb->mac.raw = skb->data; - skb->protocol = htons(ETH_P_AX25); + skb->mac.raw=skb->data; + skb->protocol=htons(ETH_P_AX25); netif_rx(skb); sp->stats.rx_packets++; } - /* ----------------------------------------------------------------------- */ /* Encapsulate one AX.25 frame and stuff into a TTY queue. */ @@ -267,70 +180,38 @@ static void sp_encaps(struct sixpack *sp, unsigned char *icp, int len) { unsigned char *p; int actual, count; + int tx_delay; - if (len > sp->mtu) { /* sp->mtu = AX25_MTU = max. PACLEN = 256 */ - printk(KERN_DEBUG "%s: truncating oversized transmit packet!\n", sp->dev->name); + if (len > sp->mtu) { + len = sp->mtu; + printk(KERN_DEBUG "%s: dropping oversized transmit packet!\n", sp->dev->name); sp->stats.tx_dropped++; - netif_start_queue(sp->dev); + netif_wake_queue(sp->dev); return; } p = icp; - if (p[0] > 5) { - printk(KERN_DEBUG "%s: invalid KISS command -- dropped\n", sp->dev->name); - netif_start_queue(sp->dev); - return; - } + tx_delay = ax25_dev_get_value(sp->dev, AX25_VALUES_MEDIA_TXDELAY); + if (tx_delay > 2550) tx_delay = 2550; - if ((p[0] != 0) && (len > 2)) { - printk(KERN_DEBUG "%s: KISS control packet too long -- dropped\n", sp->dev->name); - netif_start_queue(sp->dev); - return; - } - - if ((p[0] == 0) && (len < 15)) { - printk(KERN_DEBUG "%s: bad AX.25 packet to transmit -- dropped\n", sp->dev->name); - netif_start_queue(sp->dev); - sp->stats.tx_dropped++; - return; - } - - count = encode_sixpack(p, (unsigned char *) sp->xbuff, len, sp->tx_delay); + count = encode_sixpack(p, (unsigned char *) sp->xbuff, len, tx_delay); sp->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - switch (p[0]) { - case 1: sp->tx_delay = p[1]; return; - case 2: sp->persistance = p[1]; return; - case 3: sp->slottime = p[1]; return; - case 4: /* ignored */ return; - case 5: sp->duplex = p[1]; return; - } + sp->led_state = 0x70; + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); - if (p[0] == 0) { - /* in case of fullduplex or DAMA operation, we don't take care - about the state of the DCD or of any timers, as the determination - of the correct time to send is the job of the AX.25 layer. We send - immediately after data has arrived. */ - if (sp->duplex == 1) { - sp->led_state = 0x70; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, count); - sp->xleft = count - actual; - sp->xhead = sp->xbuff + actual; - sp->led_state = 0x60; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); - } else { - sp->xleft = count; - sp->xhead = sp->xbuff; - sp->status2 = count; - if (sp->duplex == 0) - sp_start_tx_timer(sp); - } - } + sp->tx_counter++; /* TX counter +1 */ + sp->tx_enable = 1; + actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, count); + sp->xleft = count - actual; + sp->xhead = sp->xbuff + actual; + sp->led_state = 0x60; + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); } +/* ----------------------------------------------------------------------- */ + /* * Called by the TTY driver when there's room for more data. If we have * more packets to send, we send them here. @@ -341,9 +222,9 @@ static void sixpack_write_wakeup(struct tty_struct *tty) struct sixpack *sp = (struct sixpack *) tty->disc_data; /* First make sure we're connected. */ - if (!sp || sp->magic != SIXPACK_MAGIC || - !netif_running(sp->dev)) + if (!sp || !netif_running(sp->dev)) { return; + } if (sp->xleft <= 0) { /* Now serial buffer is almost free & we can start @@ -365,83 +246,36 @@ static void sixpack_write_wakeup(struct tty_struct *tty) /* ----------------------------------------------------------------------- */ /* Encapsulate an IP datagram and kick it into a TTY queue. */ - static int sp_xmit(struct sk_buff *skb, struct net_device *dev) { struct sixpack *sp = (struct sixpack *) dev->priv; /* We were not busy, so we are now... :-) */ - netif_stop_queue(dev); - sp->stats.tx_bytes += skb->len; - sp_encaps(sp, skb->data, skb->len); - dev_kfree_skb(skb); - return 0; -} - - -/* perform the persistence/slottime algorithm for CSMA access. If the persistence - check was successful, write the data to the serial driver. Note that in case - of DAMA operation, the data is not sent here. */ - -static void sp_xmit_on_air(unsigned long channel) -{ - struct sixpack *sp = (struct sixpack *) channel; - int actual; - static unsigned char random; - - random = random * 17 + 41; - - if (((sp->status1 & SIXP_DCD_MASK) == 0) && (random < sp->persistance)) { - sp->led_state = 0x70; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); - sp->tx_enable = 1; - actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, sp->status2); - sp->xleft -= actual; - sp->xhead += actual; - sp->led_state = 0x60; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); - sp->status2 = 0; - } else - sp_start_tx_timer(sp); -} - - -/* Return the frame type ID */ -static int sp_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len) -{ -#ifdef CONFIG_INET - if (type != htons(ETH_P_AX25)) - return ax25_encapsulate(skb, dev, type, daddr, saddr, len); -#endif - return 0; -} - - -static int sp_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - return ax25_rebuild_header(skb); -#else + if (skb != NULL) { + netif_stop_queue(dev); + sp->stats.tx_bytes += skb->len; + sp_encaps(sp, skb->data, skb->len); + dev_kfree_skb(skb); + } return 0; -#endif } +/* ----------------------------------------------------------------------- */ /* Open the low-level part of the 6pack channel. */ static int sp_open(struct net_device *dev) { - struct sixpack *sp = (struct sixpack *) dev->priv; + struct sixpack *sp = (struct sixpack*)(dev->priv); unsigned long len; if (sp->tty == NULL) return -ENODEV; - /* * Allocate the 6pack frame buffers: * * rbuff Receive buffer. * xbuff Transmit buffer. + * cbuff Temporary compression buffer. */ /* !!! length of the buffers. MTU is IP MTU, not PACLEN! @@ -449,62 +283,62 @@ static int sp_open(struct net_device *dev) len = dev->mtu * 2; - if ((sp->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) + sp->rbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL); + if (sp->rbuff == NULL) return -ENOMEM; - if ((sp->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) { + sp->xbuff = (unsigned char *) kmalloc(len + 4, GFP_KERNEL); + if (sp->xbuff == NULL) + { kfree(sp->rbuff); return -ENOMEM; } - sp->mtu = AX25_MTU + 73; - sp->buffsize = len; - sp->rcount = 0; - sp->rx_count = 0; + sp->mtu = SIXP_MTU; + sp->buffsize = len; + sp->rcount = 0; + sp->rx_count = 0; sp->rx_count_cooked = 0; - sp->xleft = 0; - - sp->flags &= (1 << SIXPF_INUSE); /* Clear ESCAPE & ERROR flags */ - - sp->duplex = 0; - sp->tx_delay = SIXP_TXDELAY; - sp->persistance = SIXP_PERSIST; - sp->slottime = SIXP_SLOTTIME; + sp->xleft = 0; + sp->flags.error = 0; sp->led_state = 0x60; sp->status = 1; sp->status1 = 1; sp->status2 = 0; sp->tnc_ok = 0; sp->tx_enable = 0; + sp->tx_counter = 0; netif_start_queue(dev); - init_timer(&sp->tx_t); init_timer(&sp->resync_t); return 0; } +/* ----------------------------------------------------------------------- */ /* Close the low-level part of the 6pack channel. */ static int sp_close(struct net_device *dev) { - struct sixpack *sp = (struct sixpack *) dev->priv; + struct sixpack *sp = (struct sixpack*)(dev->priv); - if (sp->tty == NULL) + if (sp->tty == NULL) { return -EBUSY; - + } sp->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); netif_stop_queue(dev); return 0; } +/* ----------------------------------------------------------------------- */ + static int sixpack_receive_room(struct tty_struct *tty) { return 65536; /* We can handle an infinite amount of data. :-) */ } -/* !!! receive state machine */ +/* ----------------------------------------------------------------------- */ /* * Handle the 'receiver data ready' interrupt. @@ -517,38 +351,41 @@ static void sixpack_receive_buf(struct tty_struct *tty, const unsigned char *cp, unsigned char buf[512]; unsigned long flags; int count1; - struct sixpack *sp = (struct sixpack *) tty->disc_data; - if (!sp || sp->magic != SIXPACK_MAGIC || - !netif_running(sp->dev) || !count) + if (!sp || !netif_running(sp->dev) || !count) return; save_flags(flags); cli(); - memcpy(buf, cp, count<sizeof(buf)? count:sizeof(buf)); + memcpy(buf, cp, min(count, sizeof(buf))); restore_flags(flags); /* Read the characters out of the buffer */ - count1 = count; - while (count) { + while(count) + { count--; if (fp && *fp++) { - if (!test_and_set_bit(SIXPF_ERROR, &sp->flags)) + if (!sp->flags.error) + { + sp->flags.error = 1; sp->stats.rx_errors++; + } continue; } } sixpack_decode(sp, buf, count1); } +/* ----------------------------------------------------------------------- */ + /* * Open the high-level part of the 6pack channel. * This function is called by the TTY module when the * 6pack line discipline is called for. Because we are * sure the tty line exists, we only have to link it to - * a free 6pcack channel... + * a free 6pack channel... */ static int sixpack_open(struct tty_struct *tty) { @@ -556,35 +393,32 @@ static int sixpack_open(struct tty_struct *tty) int err; /* First make sure we're not already connected. */ - - if (sp && sp->magic == SIXPACK_MAGIC) - return -EEXIST; + if (sp) return -EEXIST; /* OK. Find a free 6pack channel to use. */ - if ((sp = sp_alloc()) == NULL) + if ((sp = sp_alloc(tty)) == NULL) return -ENFILE; sp->tty = tty; + tty->disc_data = sp; + if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); - /* Restore default settings */ sp->dev->type = ARPHRD_AX25; - /* Perform the low-level 6pack initialization. */ if ((err = sp_open(sp->dev))) return err; + MOD_INC_USE_COUNT; /* Done. We have linked the TTY line to a channel. */ - tnc_init(sp); - return sp->dev->base_addr; } +/* ----------------------------------------------------------------------- */ /* * Close down a 6pack channel. @@ -597,14 +431,11 @@ static void sixpack_close(struct tty_struct *tty) struct sixpack *sp = (struct sixpack *) tty->disc_data; /* First make sure we're connected. */ - if (!sp || sp->magic != SIXPACK_MAGIC) - return; - + if (!sp) return; rtnl_lock(); - dev_close(sp->dev); - - del_timer(&sp->tx_t); - del_timer(&sp->resync_t); + if (sp->dev->flags & IFF_UP) + dev_close(sp->dev); + del_timer(&(sp->resync_t)); tty->disc_data = 0; sp->tty = NULL; @@ -612,28 +443,37 @@ static void sixpack_close(struct tty_struct *tty) sp_free(sp); unregister_netdevice(sp->dev); rtnl_unlock(); + MOD_DEC_USE_COUNT; } +/* ----------------------------------------------------------------------- */ static struct net_device_stats *sp_get_stats(struct net_device *dev) { - struct sixpack *sp = (struct sixpack *) dev->priv; - return &sp->stats; + struct sixpack *sp = (struct sixpack*) (dev->priv); + return (&sp->stats); } +/* ----------------------------------------------------------------------- */ -static int sp_set_mac_address(struct net_device *dev, void *addr) +static int sixpack_set_mac_address(struct net_device *dev, void *addr) { - return copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN) ? -EFAULT : 0; + if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN)) + return -EFAULT; + return 0; } -static int sp_set_dev_mac_address(struct net_device *dev, void *addr) +/* ----------------------------------------------------------------------- */ + +static int sixpack_dev_set_mac_address(struct net_device *dev, void *addr) { struct sockaddr *sa = addr; + memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); return 0; } +/* ----------------------------------------------------------------------- */ /* Perform I/O control on an active 6pack channel. */ static int sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) @@ -642,29 +482,28 @@ static int sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) unsigned int tmp; /* First make sure we're connected. */ - if (!sp || sp->magic != SIXPACK_MAGIC) + if (!sp) return -EINVAL; switch(cmd) { case SIOCGIFNAME: - return copy_to_user(arg, sp->dev->name, strlen(sp->dev->name) + 1) ? -EFAULT : 0; + if (copy_to_user(arg, sp->dev->name, IFNAMSIZ)) + return -EFAULT; + return 0; case SIOCGIFENCAP: - return put_user(0, (int *)arg); + if (put_user(sp->mode, (int *)arg)) + return -EFAULT; + return 0; case SIOCSIFENCAP: - if (get_user(tmp, (int *) arg)) + if (get_user(tmp,(int *)arg)) return -EFAULT; - sp->mode = tmp; - sp->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */ - sp->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; - sp->dev->type = ARPHRD_AX25; - return 0; - case SIOCSIFHWADDR: - return sp_set_mac_address(sp->dev, arg); + case SIOCSIFHWADDR: + return sixpack_set_mac_address(sp->dev, arg); /* Allow stty to read, but not set, the serial port */ case TCGETS: @@ -676,155 +515,115 @@ static int sixpack_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) } } -static int sp_open_dev(struct net_device *dev) +/* ----------------------------------------------------------------------- */ + +/* report DCD state to DDI layer */ +static unsigned int sixpack_ddi_report_dcd(struct net_device *dev) { struct sixpack *sp = (struct sixpack *) dev->priv; - if (sp->tty == NULL) - return -ENODEV; - return 0; + return (sp->status1 & SIXP_DCD_MASK); } -/* Fill in our line protocol discipline */ -static struct tty_ldisc sp_ldisc = { - magic: TTY_LDISC_MAGIC, - name: "6pack", - open: sixpack_open, - close: sixpack_close, - ioctl: (int (*)(struct tty_struct *, struct file *, - unsigned int, unsigned long)) sixpack_ioctl, - receive_buf: sixpack_receive_buf, - receive_room: sixpack_receive_room, - write_wakeup: sixpack_write_wakeup, -}; - -/* Initialize 6pack control device -- register 6pack line discipline */ - -static const char msg_banner[] __initdata = KERN_INFO "AX.25: 6pack driver, " SIXPACK_VERSION " (dynamic channels, max=%d)\n"; -static const char msg_invparm[] __initdata = KERN_ERR "6pack: sixpack_maxdev parameter too large.\n"; -static const char msg_nomem[] __initdata = KERN_ERR "6pack: can't allocate sixpack_ctrls[] array! No 6pack available.\n"; -static const char msg_regfail[] __initdata = KERN_ERR "6pack: can't register line discipline (err = %d)\n"; +/* ----------------------------------------------------------------------- */ -static int __init sixpack_init_driver(void) +/* report PTT state to DDI layer */ +static unsigned int sixpack_ddi_report_ptt(struct net_device *dev) { - int status; - - /* Do sanity checks on maximum device parameter. */ - if (sixpack_maxdev < 4) - sixpack_maxdev = 4; - if (sixpack_maxdev * sizeof(void*) >= KMALLOC_MAXSIZE) { - printk(msg_invparm); - return -ENFILE; - } - - printk(msg_banner, sixpack_maxdev); - - sixpack_ctrls = (sixpack_ctrl_t **) kmalloc(sizeof(void*)*sixpack_maxdev, GFP_KERNEL); - if (sixpack_ctrls == NULL) { - printk(msg_nomem); - return -ENOMEM; - } + struct sixpack *sp = (struct sixpack *) dev->priv; + return !!sp->tx_counter; +} - /* Clear the pointer array, we allocate devices when we need them */ - memset(sixpack_ctrls, 0, sizeof(void*)*sixpack_maxdev); /* Pointers */ +/* ----------------------------------------------------------------------- */ - /* Register the provided line protocol discipline */ - if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) { - printk(msg_regfail, status); - kfree(sixpack_ctrls); +/* this function if called by DDI layer whenever a parameter changes. */ +static void sixpack_parameter_change_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct sixpack *sp = (struct sixpack *) dev->priv; + int br; + + switch (valueno) { + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + /* + * If anybody knows how to set TTY's baud rate + * from kernel mode please add it here. We currently + * reject baud rate change requests. + */ + br = tty_get_baud_rate(sp->tty); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, br); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, br); + break; + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + if (new >= 2500) ax25_dev_set_value(dev, valueno, 2490); + break; + case AX25_VALUES_MEDIA_DUPLEX: + default: + /* just let them do it. */ + break; } - - return status; + return; } -static const char msg_unregfail[] __exitdata = KERN_ERR "6pack: can't unregister line discipline (err = %d)\n"; +/* ----------------------------------------------------------------------- */ -static void __exit sixpack_exit_driver(void) +static int sp_open_dev(struct net_device *dev) { - int i; + struct sixpack *sp = (struct sixpack*) (dev->priv); - if ((i = tty_register_ldisc(N_6PACK, NULL))) - printk(msg_unregfail, i); - - for (i = 0; i < sixpack_maxdev; i++) { - if (sixpack_ctrls[i]) { - /* - * VSV = if dev->start==0, then device - * unregistered while close proc. - */ - if (netif_running(&sixpack_ctrls[i]->dev)) - unregister_netdev(&sixpack_ctrls[i]->dev); - - kfree(sixpack_ctrls[i]); - } - } - kfree(sixpack_ctrls); + if (sp->tty == NULL) return -ENODEV; + return 0; } +/* ----------------------------------------------------------------------- */ -/* Initialize the 6pack driver. Called by DDI. */ +/* Initialize the 6pack driver. Called by core/dev.c on registration */ static int sixpack_init(struct net_device *dev) { struct sixpack *sp = (struct sixpack *) dev->priv; - static char ax25_bcast[AX25_ADDR_LEN] = - {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; - static char ax25_test[AX25_ADDR_LEN] = - {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; - - if (sp == NULL) /* Allocation failed ?? */ + if (sp == NULL) return -ENODEV; /* Set up the "6pack Control Block". (And clear statistics) */ - - memset(sp, 0, sizeof (struct sixpack)); - sp->magic = SIXPACK_MAGIC; - sp->dev = dev; + sp->dev = dev; /* Finish setting up the DEVICE info. */ dev->mtu = SIXP_MTU; dev->hard_start_xmit = sp_xmit; dev->open = sp_open_dev; dev->stop = sp_close; - dev->hard_header = sp_header; + dev->hard_header = NULL; dev->get_stats = sp_get_stats; - dev->set_mac_address = sp_set_dev_mac_address; + dev->set_mac_address = sixpack_dev_set_mac_address; dev->hard_header_len = AX25_MAX_HEADER_LEN; dev->addr_len = AX25_ADDR_LEN; dev->type = ARPHRD_AX25; dev->tx_queue_len = 10; - dev->rebuild_header = sp_rebuild_header; + dev->rebuild_header = NULL; dev->tx_timeout = NULL; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); /* Only activated in AX.25 mode */ - memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); /* "" "" "" "" */ + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + AX25_PTR(dev) = &sp->ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + AX25_PTR(dev)->hw.fast = 0; + AX25_PTR(dev)->hw.dcd = sixpack_ddi_report_dcd; + AX25_PTR(dev)->hw.ptt = sixpack_ddi_report_ptt; + AX25_PTR(dev)->hw.cts = NULL; + AX25_PTR(dev)->hw.rts = NULL; + AX25_PTR(dev)->hw.parameter_change_notify = sixpack_parameter_change_notify; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 200); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 20); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 1); dev_init_buffers(dev); - - /* New-style flags. */ - dev->flags = 0; - return 0; } - - - -/* ----> 6pack timer interrupt handler and friends. <---- */ -static void sp_start_tx_timer(struct sixpack *sp) -{ - int when = sp->slottime; - - del_timer(&sp->tx_t); - sp->tx_t.data = (unsigned long) sp; - sp->tx_t.function = sp_xmit_on_air; - sp->tx_t.expires = jiffies + ((when+1)*HZ)/100; - add_timer(&sp->tx_t); -} - +/* ----------------------------------------------------------------------- */ /* encode an AX.25 packet into 6pack */ - -static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay) +int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned int tx_delay) { int count = 0; unsigned char checksum = 0, buf[400]; @@ -833,37 +632,39 @@ static int encode_sixpack(unsigned char *tx_buf, unsigned char *tx_buf_raw, int tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK; tx_buf_raw[raw_count++] = SIXP_SEOF; - buf[0] = tx_delay; - for (count = 1; count < length; count++) - buf[count] = tx_buf[count]; + buf[0] = tx_delay/10; /* 10 ms units */ + memcpy(&buf[1], tx_buf, length); + length++; - for (count = 0; count < length; count++) + for(count = 0; count < length; count++) checksum += buf[count]; buf[length] = (unsigned char) 0xff - checksum; - for (count = 0; count <= length; count++) { - if ((count % 3) == 0) { + for(count = 0; count <= length; count++) { + if((count % 3) == 0) { tx_buf_raw[raw_count++] = (buf[count] & 0x3f); tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30); - } else if ((count % 3) == 1) { + } + else if((count % 3) == 1) { tx_buf_raw[raw_count++] |= (buf[count] & 0x0f); - tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x3c); + tx_buf_raw[raw_count] = + ((buf[count] >> 2) & 0x3c); } else { tx_buf_raw[raw_count++] |= (buf[count] & 0x03); - tx_buf_raw[raw_count++] = (buf[count] >> 2); + tx_buf_raw[raw_count++] = + (buf[count] >> 2); } } if ((length % 3) != 2) raw_count++; tx_buf_raw[raw_count++] = SIXP_SEOF; - return raw_count; + return(raw_count); } +/* ----------------------------------------------------------------------- */ /* decode a 6pack packet */ - -static void -sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count) +void sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count) { unsigned char inbyte; int count1; @@ -873,134 +674,147 @@ sixpack_decode(struct sixpack *sp, unsigned char pre_rbuff[], int count) if (inbyte == SIXP_FOUND_TNC) { printk(KERN_INFO "6pack: TNC found.\n"); sp->tnc_ok = 1; - del_timer(&sp->resync_t); + sp->tx_counter = 0; + del_timer(&(sp->resync_t)); } - if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) + if ((inbyte & SIXP_PRIO_CMD_MASK) != 0) { decode_prio_command(inbyte, sp); - else if ((inbyte & SIXP_STD_CMD_MASK) != 0) + } else if((inbyte & SIXP_STD_CMD_MASK) != 0) { decode_std_command(inbyte, sp); - else if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) + } else { decode_data(inbyte, sp); + } } } +/* ----------------------------------------------------------------------- */ + static int tnc_init(struct sixpack *sp) { - unsigned char inbyte = 0xe8; + static unsigned char inbyte = 0xe8; sp->tty->driver.write(sp->tty, 0, &inbyte, 1); - del_timer(&sp->resync_t); + del_timer(&(sp->resync_t)); sp->resync_t.data = (unsigned long) sp; sp->resync_t.function = resync_tnc; sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + add_timer(&(sp->resync_t)); return 0; } +/* ----------------------------------------------------------------------- */ /* identify and execute a 6pack priority command byte */ - -static void decode_prio_command(unsigned char cmd, struct sixpack *sp) +void decode_prio_command(unsigned char cmd, struct sixpack *sp) { unsigned char channel; int actual; + int duplex; + struct net_device *dev = sp->dev; + duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX); channel = cmd & SIXP_CHN_MASK; - if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */ - - /* RX and DCD flags can only be set in the same prio command, - if the DCD flag has been set without the RX flag in the previous - prio command. If DCD has not been set before, something in the - transmission has gone wrong. In this case, RX and DCD are - cleared in order to prevent the decode_data routine from - reading further data that might be corrupt. */ - + if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { + /* + * not idle. + * RX and DCD flags can only be set in the same prio command, + * if the DCD flag has been set without the RX flag in the previous + * prio command. If DCD has not been set before, something in the + * transmission has gone wrong. In this case, RX and DCD are + * cleared in order to prevent the decode_data routine from + * reading further data that might be corrupt. + */ if (((sp->status & SIXP_DCD_MASK) == 0) && ((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) { - if (sp->status != 1) + if (sp->status != 1) { printk(KERN_DEBUG "6pack: protocol violation\n"); - else + sp->tnc_ok = 0; + } else { sp->status = 0; + } cmd &= !SIXP_RX_DCD_MASK; } sp->status = cmd & SIXP_PRIO_DATA_MASK; - } - else { /* output watchdog char if idle */ - if ((sp->status2 != 0) && (sp->duplex == 1)) { + } else { + /* output watchdog char if idle */ + if ((sp->status2 != 0) && (duplex == 1)) { sp->led_state = 0x70; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); sp->tx_enable = 1; actual = sp->tty->driver.write(sp->tty, 0, sp->xbuff, sp->status2); sp->xleft -= actual; sp->xhead += actual; sp->led_state = 0x60; sp->status2 = 0; - } } - /* needed to trigger the TNC watchdog */ - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); + if (((cmd & 0xc0) == 0x80) && (cmd & 0x20)) { + /* dirty hack for now */ + if (--sp->tx_counter < 0) + sp->tnc_ok = 0; + } - /* if the state byte has been received, the TNC is present, - so the resync timer can be reset. */ + /* needed to trigger the TNC watchdog */ + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); + /* + * if the state byte has been received, the TNC is present, + * so the resync timer can be reset. + */ if (sp->tnc_ok == 1) { - del_timer(&sp->resync_t); + del_timer(&(sp->resync_t)); sp->resync_t.data = (unsigned long) sp; sp->resync_t.function = resync_tnc; sp->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + add_timer(&(sp->resync_t)); } sp->status1 = cmd & SIXP_PRIO_DATA_MASK; } -/* try to resync the TNC. Called by the resync timer defined in - decode_prio_command */ +/* ----------------------------------------------------------------------- */ +/* + * try to resync the TNC. Called by the resync timer defined in + * decode_prio_command + */ static void resync_tnc(unsigned long channel) { static char resync_cmd = 0xe8; struct sixpack *sp = (struct sixpack *) channel; - printk(KERN_INFO "6pack: resyncing TNC\n"); + printk(KERN_INFO "6pack: resyncing TNC...\n"); /* clear any data that might have been received */ - - sp->rx_count = 0; - sp->rx_count_cooked = 0; + sp->rx_count = 0; + sp->rx_count_cooked = 0; /* reset state machine */ - - sp->status = 1; - sp->status1 = 1; - sp->status2 = 0; - sp->tnc_ok = 0; + sp->status = 1; + sp->status1 = 1; + sp->status2 = 0; + sp->tnc_ok = 0; /* resync the TNC */ - sp->led_state = 0x60; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); sp->tty->driver.write(sp->tty, 0, &resync_cmd, 1); - /* Start resync timer again -- the TNC might be still absent */ - - del_timer(&sp->resync_t); + del_timer(&(sp->resync_t)); sp->resync_t.data = (unsigned long) sp; sp->resync_t.function = resync_tnc; sp->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT; - add_timer(&sp->resync_t); + add_timer(&(sp->resync_t)); } - +/* ----------------------------------------------------------------------- */ /* identify and execute a standard 6pack command byte */ - -static void decode_std_command(unsigned char cmd, struct sixpack *sp) +void decode_std_command(unsigned char cmd, struct sixpack *sp) { unsigned char checksum = 0, rest = 0, channel; short i; @@ -1009,27 +823,27 @@ static void decode_std_command(unsigned char cmd, struct sixpack *sp) switch (cmd & SIXP_CMD_MASK) { /* normal command */ case SIXP_SEOF: if ((sp->rx_count == 0) && (sp->rx_count_cooked == 0)) { - if ((sp->status & SIXP_RX_DCD_MASK) == - SIXP_RX_DCD_MASK) { + if ((sp->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK) { sp->led_state = 0x68; - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); } } else { sp->led_state = 0x60; /* fill trailing bytes with zeroes */ - sp->tty->driver.write(sp->tty, 0, &sp->led_state, 1); + sp->tty->driver.write(sp->tty, 0, &(sp->led_state), 1); rest = sp->rx_count; if (rest != 0) - for (i = rest; i <= 3; i++) + for(i=rest; i<=3; i++) decode_data(0, sp); - if (rest == 2) + if (rest == 2) { sp->rx_count_cooked -= 2; - else if (rest == 3) + } else if (rest == 3) { sp->rx_count_cooked -= 1; - for (i = 0; i < sp->rx_count_cooked; i++) - checksum += sp->cooked_buf[i]; + } + for (i=0; i<sp->rx_count_cooked; i++) + checksum+=sp->cooked_buf[i]; if (checksum != SIXP_CHKSUM) { - printk(KERN_DEBUG "6pack: bad checksum %2.2x\n", checksum); + sp->stats.rx_crc_errors++; } else { sp->rcount = sp->rx_count_cooked-2; sp_bump(sp, 0); @@ -1037,24 +851,30 @@ static void decode_std_command(unsigned char cmd, struct sixpack *sp) sp->rx_count_cooked = 0; } break; - case SIXP_TX_URUN: printk(KERN_DEBUG "6pack: TX underrun\n"); + + case SIXP_TX_URUN: + sp->stats.tx_fifo_errors++; break; - case SIXP_RX_ORUN: printk(KERN_DEBUG "6pack: RX overrun\n"); + + case SIXP_RX_ORUN: + sp->stats.rx_over_errors++; break; + case SIXP_RX_BUF_OVL: - printk(KERN_DEBUG "6pack: RX buffer overflow\n"); + sp->stats.rx_length_errors++; } } -/* decode 4 sixpack-encoded bytes into 3 data bytes */ +/* ----------------------------------------------------------------------- */ -static void decode_data(unsigned char inbyte, struct sixpack *sp) +/* decode 4 sixpack-encoded bytes into 3 data bytes */ +void decode_data(unsigned char inbyte, struct sixpack *sp) { unsigned char *buf; - if (sp->rx_count != 3) + if (sp->rx_count != 3) { sp->raw_buf[sp->rx_count++] = inbyte; - else { + } else { buf = sp->raw_buf; sp->cooked_buf[sp->rx_count_cooked++] = buf[0] | ((buf[1] << 2) & 0xc0); @@ -1066,9 +886,81 @@ static void decode_data(unsigned char inbyte, struct sixpack *sp) } } +/* ----------------------------------------------------------------------- */ + +/* Fill in our line protocol discipline */ +static struct tty_ldisc sp_ldisc = { + magic: TTY_LDISC_MAGIC, + name: "6pack", + open: sixpack_open, + close: sixpack_close, + ioctl: (int (*)(struct tty_struct *, struct file *, + unsigned int, unsigned long)) sixpack_ioctl, + receive_buf: sixpack_receive_buf, + receive_room: sixpack_receive_room, + write_wakeup: sixpack_write_wakeup, +}; + + +/* Initialize 6pack control device -- register 6pack line discipline */ +int __init sixpack_init_ctrl_dev(void) +{ + int status; + + /* Do sanity checks on maximum device parameter. */ + if (sixpack_maxdev < 4) sixpack_maxdev = 4; + + printk(KERN_INFO "AX.25: 6pack driver, %s (dynamic channels, max=%d)\n", + SIXPACK_VERSION, sixpack_maxdev); + + sixpack_ctrls = (struct sixpack_ctrl_t **) kmalloc(sizeof(void *) *sixpack_maxdev, GFP_KERNEL); + if (sixpack_ctrls == NULL) + { + printk(KERN_WARNING "6pack: Can't allocate sixpack_ctrls[] array! Uaargh! (-> No 6pack available)\n"); + return -ENOMEM; + } + + /* Clear the pointer array, we allocate devices when we need them */ + memset(sixpack_ctrls, 0, sizeof(void*)*sixpack_maxdev); + + if ((status = tty_register_ldisc(N_6PACK, &sp_ldisc)) != 0) { + printk(KERN_WARNING "6pack: can't register line discipline (err = %d)\n", status); + kfree(sixpack_ctrls); + } + + return 0; +} + +void __exit sixpack_exit_driver(void) +{ + int i; + + if (sixpack_ctrls != NULL) { + if ((i = tty_register_ldisc(N_6PACK, NULL))) + printk(KERN_WARNING "6pack: can't unregister line discipline (err = %d)\n", i); + + for (i = 0; i < sixpack_maxdev; i++) { + if (sixpack_ctrls[i]) { + /* + * VSV = if netif_running(), then device + * unregistered while close proc. + */ + if (netif_running(&(sixpack_ctrls[i]->dev))) + unregister_netdev(&(sixpack_ctrls[i]->dev)); + + kfree(sixpack_ctrls[i]); + sixpack_ctrls[i] = NULL; + } + } + kfree(sixpack_ctrls); + sixpack_ctrls = NULL; + } +} MODULE_AUTHOR("Andreas Könsgen <ajk@ccac.rwth-aachen.de>"); MODULE_DESCRIPTION("6pack driver for AX.25"); +MODULE_PARM(sixpack_maxdev, "i"); +MODULE_PARM_DESC(sixpack_maxdev, "number of 6PACK devices"); -module_init(sixpack_init_driver); +module_init(sixpack_init_ctrl_dev); module_exit(sixpack_exit_driver); diff --git a/drivers/net/hamradio/6pack.h b/drivers/net/hamradio/6pack.h new file mode 100644 index 000000000..ac4ad0052 --- /dev/null +++ b/drivers/net/hamradio/6pack.h @@ -0,0 +1,106 @@ +/* + * 6pack.h Define the 6pack device driver interface and constants. + * + * Version: @(#)6pack.h 0.4.0 03/05/2000 + * + * Author: Andreas Könsgen <ajk@iehk.rwth-aachen.de> + * Jens David <dg1kjd@afthd.tu-darmstadt.de> + * + * This file is derived from slip.h, written by + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + */ + +#ifndef _LINUX_6PACK_H +#define _LINUX_6PACK_H + +#include <net/ax25dev.h> + +#define SIXPACK_VERSION "Revision: 0.4.0" + +#ifdef __KERNEL__ + +/* sixpack priority commands */ +#define SIXP_SEOF 0x40 /* start and end of a 6pack frame */ +#define SIXP_TX_URUN 0x48 /* transmit overrun */ +#define SIXP_RX_ORUN 0x50 /* receive overrun */ +#define SIXP_RX_BUF_OVL 0x58 /* receive buffer overflow */ +#define SIXP_CHKSUM 0xFF /* valid checksum of a 6pack frame */ + +/* masks to get certain bits out of the status bytes sent by the TNC */ +#define SIXP_CMD_MASK 0xC0 +#define SIXP_CHN_MASK 0x07 +#define SIXP_PRIO_CMD_MASK 0x80 +#define SIXP_STD_CMD_MASK 0x40 +#define SIXP_PRIO_DATA_MASK 0x38 +#define SIXP_TX_MASK 0x20 +#define SIXP_RX_MASK 0x10 +#define SIXP_RX_DCD_MASK 0x18 +#define SIXP_LEDS_ON 0x78 +#define SIXP_LEDS_OFF 0x60 +#define SIXP_CON 0x08 +#define SIXP_STA 0x10 +#define SIXP_FOUND_TNC 0xe9 +#define SIXP_CON_ON 0x68 +#define SIXP_DCD_MASK 0x08 +#define SIXP_DAMA_OFF 0 + +/* default level 2 parameters */ +#define SIXP_TXDELAY 250 /* in ms */ +#define SIXP_INIT_RESYNC_TIMEOUT 1500 /* in ms */ +#define SIXP_RESYNC_TIMEOUT 5000 /* in ms */ + +/* 6pack configuration. */ +#define SIXP_NRUNIT 256 /* MAX number of 6pack channels */ +#define SIXP_MTU (256+MAX_HEADER) + +struct sixpack { + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* easy for intr handling */ + struct ax25_dev ax25dev; /* AX.25 control structure */ + struct net_device_stats stats; /* statistics */ + + spinlock_t lock; + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + + unsigned char raw_buf[4]; + unsigned char cooked_buf[400]; + + unsigned int rx_count; + unsigned int rx_count_cooked; + + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + + struct { + int in_use:1; + int error:1; + } flags; + + unsigned char mode; /* 6pack mode */ + + /* 6pack stuff */ + unsigned char led_state; + unsigned char status; + unsigned char status1; + unsigned char status2; + int tx_counter; /* counts "unack'ed" TX packets */ + unsigned char tx_enable; + unsigned char tnc_ok; + + struct timer_list resync_t; +}; + +struct sixpack_ctrl_t { + char if_name[8]; /* "sp0\0" .. "sp99999\0" */ + struct sixpack ctrl; /* 6pack things */ + struct net_device dev; /* the device */ +}; + +#endif /* __KERNEL__ */ +#endif /* _LINUX_6PACK.H */ diff --git a/drivers/net/hamradio/Config.in b/drivers/net/hamradio/Config.in index 1d45998f4..877169c27 100644 --- a/drivers/net/hamradio/Config.in +++ b/drivers/net/hamradio/Config.in @@ -1,6 +1,7 @@ comment 'AX.25 network device drivers' -dep_tristate 'Serial port KISS driver' CONFIG_MKISS $CONFIG_AX25 +dep_tristate 'Userspace device driver support' CONFIG_AX25_USERDEV $CONFIG_AX25 +dep_tristate 'Serial port KISS driver' CONFIG_KISS $CONFIG_AX25 dep_tristate 'Serial port 6PACK driver' CONFIG_6PACK $CONFIG_AX25 dep_tristate 'BPQ Ethernet driver' CONFIG_BPQETHER $CONFIG_AX25 diff --git a/drivers/net/hamradio/Makefile b/drivers/net/hamradio/Makefile index 964a184c4..0af115315 100644 --- a/drivers/net/hamradio/Makefile +++ b/drivers/net/hamradio/Makefile @@ -10,28 +10,22 @@ # Christoph Hellwig <hch@caldera.de> # - O_TARGET := hamradio.o export-objs = hdlcdrv.o - obj-$(CONFIG_DMASCC) += dmascc.o obj-$(CONFIG_SCC) += scc.o -obj-$(CONFIG_MKISS) += mkiss.o +obj-$(CONFIG_KISS) += kiss.o obj-$(CONFIG_6PACK) += 6pack.o obj-$(CONFIG_YAM) += yam.o +obj-$(CONFIG_PI) += pi2.o +obj-$(CONFIG_PT) += pt.o obj-$(CONFIG_BPQETHER) += bpqether.o +obj-$(CONFIG_AX25_USERDEV) += ax25_userdev.o obj-$(CONFIG_BAYCOM_SER_FDX) += baycom_ser_fdx.o hdlcdrv.o obj-$(CONFIG_BAYCOM_SER_HDX) += baycom_ser_hdx.o hdlcdrv.o obj-$(CONFIG_BAYCOM_PAR) += baycom_par.o hdlcdrv.o obj-$(CONFIG_BAYCOM_EPP) += baycom_epp.o hdlcdrv.o -obj-$(CONFIG_SOUNDMODEM) += hdlcdrv.o - -subdir-$(CONFIG_SOUNDMODEM) += soundmodem - -ifeq ($(CONFIG_SOUNDMODEM),y) -obj-y += soundmodem/soundmodem.o -endif include $(TOPDIR)/Rules.make diff --git a/drivers/net/hamradio/ax25_userdev.c b/drivers/net/hamradio/ax25_userdev.c new file mode 100644 index 000000000..198df9134 --- /dev/null +++ b/drivers/net/hamradio/ax25_userdev.c @@ -0,0 +1,596 @@ +/* + * AX.25 user space device driver + * Portions Copyright 2001, by Joerg Reuter <jreuter@yaina.de> + * + * based on TUN - Universal TUN/TAP device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/major.h> +#include <linux/sched.h> +#include <linux/malloc.h> +#include <linux/poll.h> +#include <linux/fcntl.h> +#include <linux/init.h> +#include <linux/random.h> + +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/miscdevice.h> +#include <linux/rtnetlink.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/if_ether.h> +#include <linux/ax25_userdev.h> + +#include <asm/system.h> +#include <asm/uaccess.h> + + +/* Network device part of the driver */ + +static unsigned int ax25_udev_ddi_report_dcd(struct net_device *dev); +static unsigned int ax25_udev_ddi_report_ptt(struct net_device *dev); +static unsigned int ax25_udev_ddi_report_cts(struct net_device *dev); +static void ax25_udev_ddi_set_rts(struct net_device *dev); +static void ax25_udev_ddi_parameter_change_notify(struct net_device *dev, int item, int old, int new); + +/* Net device open. */ +static int ax25_udev_net_open(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + +#ifdef notdef + /* I'm not quite sure if these are the right semantics: I think + this will make the MAC code believe that we actually are in + full duplex mode... + */ + + if (ax25_udev->capabilities.duplex) + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 1); + else + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); +#endif + + AX25_PTR(dev)->hw.dcd = ax25_udev_ddi_report_dcd; + AX25_PTR(dev)->hw.ptt = ax25_udev_ddi_report_ptt; + AX25_PTR(dev)->hw.fast = 0; + + switch(ax25_udev->capabilities.arbitration) + { + case AX25_UDEV_CAP_ADVANCED_ARBITRATION: + AX25_PTR(dev)->hw.cts = ax25_udev_ddi_report_cts; + AX25_PTR(dev)->hw.rts = ax25_udev_ddi_set_rts; + break; + case AX25_UDEV_CAP_OWN_ARBITRATION: + AX25_PTR(dev)->hw.fast = 1; + /* fall through */ + default: + AX25_PTR(dev)->hw.cts = NULL; + AX25_PTR(dev)->hw.rts = NULL; + } + + AX25_PTR(dev)->hw.parameter_change_notify = ax25_udev_ddi_parameter_change_notify; + + netif_start_queue(dev); + return 0; +} + +/* Net device close. */ +static int ax25_udev_net_close(struct net_device *dev) +{ + netif_stop_queue(dev); + return 0; +} + +/* Net device start xmit */ + +static int ax25_udev_net_do_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv; + + /* Queue frame */ + skb_queue_tail(&ax25_udev->txq, skb); + if (skb_queue_len(&ax25_udev->txq) >= AX25_UDEV_TXQ_SIZE) + netif_stop_queue(dev); + + if (ax25_udev->flags & AX25_UDEV_FASYNC) + kill_fasync(&ax25_udev->fasync, SIGIO, POLL_IN); + + /* Wake up process */ + wake_up_interruptible(&ax25_udev->read_wait); + + return 0; +} + + +static int ax25_udev_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv; + DBG(KERN_INFO "%s: ax25_udev_net_xmit %d\n", ax25_udev->name, skb->len); + + /* prepend packet type */ + *((ax25_udev_pt_t *) skb_push(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_DATA; + return ax25_udev_net_do_xmit(skb, dev); +} + +static void ax25_udev_net_mclist(struct net_device *dev) +{ + /* Nothing to do for multicast filters. + * We always accept all frames. + */ + return; +} + +static int ax25_udev_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *) addr; + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; +} + +static struct net_device_stats *ax25_udev_net_stats(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv; + + return &ax25_udev->stats; +} + +/* Initialize net device. */ +int ax25_udev_net_init(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)dev->priv; + + DBG(KERN_INFO "%s: ax25_udev_net_init\n", ax25_udev->name); + + SET_MODULE_OWNER(dev); + dev->open = ax25_udev_net_open; + dev->stop = ax25_udev_net_close; + dev->hard_start_xmit = ax25_udev_net_xmit; + dev->get_stats = ax25_udev_net_stats; + dev->set_mac_address = ax25_udev_set_mac_address; + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + AX25_PTR(dev) = &ax25_udev->ax25_dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + + ax25_udev->capabilities.duplex = AX25_UDEV_CAP_HALF_DUPLEX; + ax25_udev->capabilities.arbitration = AX25_UDEV_CAP_SIMPLE_ARBITRATION; + + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 160); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 10); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 0); + + return 0; +} + +static unsigned int ax25_udev_ddi_report_dcd(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + return ax25_udev->status.dcd; +} + +static unsigned int ax25_udev_ddi_report_ptt(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + return ax25_udev->status.ptt; +} + +static unsigned int ax25_udev_ddi_report_cts(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + return ax25_udev->status.cts; +} + +static void ax25_udev_ddi_set_rts(struct net_device *dev) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + struct sk_buff *skb; + + skb = dev_alloc_skb(sizeof(ax25_udev_pt_t)); + if (skb == NULL) return; /* Ouch! */ + + skb->dev = &ax25_udev->dev; + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + + *((ax25_udev_pt_t *) skb_put(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_REQUEST_RTS; + + /* question: is this safe or must we protect it with a spin lock? */ + ax25_udev_net_do_xmit(skb, dev); +} + +static void ax25_udev_ddi_parameter_change_notify(struct net_device *dev, int item, int old, int new) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *) dev->priv; + struct sk_buff *skb; + + skb = dev_alloc_skb(sizeof(ax25_udev_pt_t)+3*sizeof(ax25_udev_val_t)); + if (skb == NULL) return; /* Ouch! */ + + skb->dev = &ax25_udev->dev; + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + + *((ax25_udev_pt_t *) skb_put(skb, sizeof(ax25_udev_pt_t))) = AX25_UDEV_SET_MAC_VALUE; + *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = item; + *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = old; + *((ax25_udev_val_t *) skb_put(skb, sizeof(ax25_udev_val_t))) = new; + + /* question: is this safe or must we protect it with a spin lock? */ + ax25_udev_net_do_xmit(skb, dev); +} + + +/* Character device part */ + +/* Poll */ +static unsigned int ax25_udev_chr_poll(struct file *file, poll_table * wait) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + + DBG(KERN_INFO "%s: ax25_udev_chr_poll\n", ax25_udev->name); + + poll_wait(file, &ax25_udev->read_wait, wait); + + if (skb_queue_len(&ax25_udev->txq)) + return POLLIN | POLLRDNORM; + + return POLLOUT | POLLWRNORM; +} + + +/* Write to netdevice (ie. read from daemon) */ + +static ssize_t ax25_udev_chr_write(struct file * file, const char * buf, + size_t count, loff_t *pos) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + struct sk_buff *skb; + ax25_udev_pt_t packet_type; + ax25_udev_val_t * arg_ptr; + ax25_udev_val_t item, value; + + DBG(KERN_INFO "%s: ax25_udev_chr_write %d\n", ax25_udev->name, count); + + if (!(ax25_udev->flags & AX25_UDEV_IFF_SET)) + return -EBUSY; + + if (verify_area(VERIFY_READ, buf, count)) + return -EFAULT; + + if (count > AX25_UDEV_MAX_FRAME) + return -EINVAL; + + skb = dev_alloc_skb(count + MAX_HEADER); + if (skb == NULL) + return -ENOMEM; + + skb_reserve(skb, MAX_HEADER); + skb->dev = &ax25_udev->dev; + copy_from_user(skb_put(skb, count), buf, count); + + packet_type = *((ax25_udev_pt_t *) skb->data); + arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_pt_t)); + + switch(packet_type) + { + case AX25_UDEV_DATA: + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + netif_rx(skb); + ax25_udev->stats.rx_packets++; + ax25_udev->stats.rx_bytes += count; + return count; + + case AX25_UDEV_CAPABILITIES: + while (skb->len >= sizeof(ax25_udev_val_t)) + { + switch (*arg_ptr) + { + case AX25_UDEV_CAP_HALF_DUPLEX: + case AX25_UDEV_CAP_FULL_DUPLEX: + ax25_udev->capabilities.duplex = *arg_ptr; + break; + case AX25_UDEV_CAP_ADVANCED_ARBITRATION: + case AX25_UDEV_CAP_OWN_ARBITRATION: + case AX25_UDEV_CAP_SIMPLE_ARBITRATION: + ax25_udev->capabilities.arbitration = *arg_ptr; + break; + default: + break; + + } + arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t)); + } + break; + + case AX25_UDEV_DCD_STATUS: + ax25_udev->status.dcd = *arg_ptr; + break; + + case AX25_UDEV_CTS_STATUS: + ax25_udev->status.cts = *arg_ptr; + break; + case AX25_UDEV_PTT_STATUS: + ax25_udev->status.ptt = *arg_ptr; + break; + case AX25_UDEV_SET_MAC_VALUE: + while (skb->len >= sizeof(ax25_udev_val_t) * 2) + { + item = *arg_ptr; + arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t)); + value = *arg_ptr; + arg_ptr = (ax25_udev_val_t *) skb_pull(skb, sizeof(ax25_udev_val_t)); + + if (item < 0 || item > AX25_MAX_VALUES) + { + kfree_skb(skb); + return -EINVAL; + } + + ax25_dev_set_value(skb->dev, item, value); + } + + break; + default: + break; + } + + kfree_skb(skb); + + return count; +} + +/* Put packet to user space buffer(already verified) */ +static __inline__ ssize_t ax25_udev_put_user(struct ax25_udev_struct *ax25_udev, + struct sk_buff *skb, + char *buf, int count) +{ + int len = count, total = 0; + char *ptr = buf; + + len = (skb->len < len)? skb->len:len; + copy_to_user(ptr, skb->data, len); + total += len; + + ax25_udev->stats.tx_packets++; + ax25_udev->stats.tx_bytes += len; + + return total; +} + + +/* Read */ +static ssize_t ax25_udev_chr_read(struct file * file, char * buf, + size_t count, loff_t *pos) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + struct sk_buff *skb; + ssize_t ret = 0; + + DBG(KERN_INFO "%s: ax25_udev_chr_read\n", ax25_udev->name); + + add_wait_queue(&ax25_udev->read_wait, &wait); + while (count) { + current->state = TASK_INTERRUPTIBLE; + + /* Read frames from device queue */ + if (!(skb=skb_dequeue(&ax25_udev->txq))) { + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + + /* Nothing to read, let's sleep */ + schedule(); + continue; + } + netif_start_queue(&ax25_udev->dev); + + if (!verify_area(VERIFY_WRITE, buf, count)) + ret = ax25_udev_put_user(ax25_udev, skb, buf, count); + else + ret = -EFAULT; + + kfree_skb(skb); + break; + } + + current->state = TASK_RUNNING; + remove_wait_queue(&ax25_udev->read_wait, &wait); + + return ret; +} + +static loff_t ax25_udev_chr_lseek(struct file * file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int ax25_udev_set_iff(struct ax25_udev_struct *ax25_udev, unsigned long arg) +{ + struct ifreq ifr; + char *mask; + + if (copy_from_user(&ifr, (void *)arg, sizeof(ifr))) + return -EFAULT; + ifr.ifr_name[IFNAMSIZ-1] = '\0'; + + if (ax25_udev->flags & AX25_UDEV_IFF_SET) + return -EEXIST; + + mask = "ax%d"; + if (*ifr.ifr_name) + strcpy(ax25_udev->dev.name, ifr.ifr_name); + else + strcpy(ax25_udev->dev.name, mask); + + /* Register net device */ + if (register_netdev(&ax25_udev->dev)) + return -EBUSY; + + ax25_udev->flags |= AX25_UDEV_IFF_SET; + strcpy(ax25_udev->name, ax25_udev->dev.name); + + /* Return iface info to the user space */ + strcpy(ifr.ifr_name, ax25_udev->dev.name); + copy_to_user((void *)arg, &ifr, sizeof(ifr)); + + return 0; +} + +static int ax25_udev_chr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + + DBG(KERN_INFO "%s: ax25_udev_chr_ioctl\n", ax25_udev->name); + + switch (cmd) { + case AX25_UDEV_SETIFF: + return ax25_udev_set_iff(ax25_udev, arg); + +#ifdef AX25_UDEV_DEBUG + case AX25_UDEV_SETDEBUG: + ax25_udev->debug = arg; + break; +#endif + + default: + return -EINVAL; + }; + + return 0; +} + +static int ax25_udev_chr_fasync(int fd, struct file *file, int on) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + int ret; + + DBG(KERN_INFO "%s: ax25_udev_chr_fasync %d\n", ax25_udev->name, on); + + if ((ret = fasync_helper(fd, file, on, &ax25_udev->fasync)) < 0) + return ret; + + if (on) + ax25_udev->flags |= AX25_UDEV_FASYNC; + else + ax25_udev->flags &= ~AX25_UDEV_FASYNC; + + return 0; +} + +static int ax25_udev_chr_open(struct inode *inode, struct file * file) +{ + struct ax25_udev_struct *ax25_udev = NULL; + +// DBG1(KERN_INFO "ax25_udevX: ax25_udev_chr_open\n"); + + ax25_udev = kmalloc(sizeof(struct ax25_udev_struct), GFP_KERNEL); + if (ax25_udev == NULL) + return -ENOMEM; + + memset(ax25_udev, 0, sizeof(struct ax25_udev_struct)); + file->private_data = ax25_udev; + + skb_queue_head_init(&ax25_udev->txq); + init_waitqueue_head(&ax25_udev->read_wait); + + sprintf(ax25_udev->name, "ax25"); + + ax25_udev->dev.init = ax25_udev_net_init; + ax25_udev->dev.priv = ax25_udev; + + return 0; +} + +static int ax25_udev_chr_close(struct inode *inode, struct file *file) +{ + struct ax25_udev_struct *ax25_udev = (struct ax25_udev_struct *)file->private_data; + + DBG(KERN_INFO "%s: ax25_udev_chr_close\n", ax25_udev->name); + + if (ax25_udev->flags & AX25_UDEV_IFF_SET) { + rtnl_lock(); + dev_close(&ax25_udev->dev); + rtnl_unlock(); + + /* Drop TX queue */ + skb_queue_purge(&ax25_udev->txq); + + unregister_netdev(&ax25_udev->dev); + } + + kfree(ax25_udev); + file->private_data = NULL; + + return 0; +} + +static struct file_operations ax25_udev_fops = { + owner: THIS_MODULE, + llseek: ax25_udev_chr_lseek, + read: ax25_udev_chr_read, + write: ax25_udev_chr_write, + poll: ax25_udev_chr_poll, + ioctl: ax25_udev_chr_ioctl, + open: ax25_udev_chr_open, + release:ax25_udev_chr_close, + fasync: ax25_udev_chr_fasync +}; + +static struct miscdevice ax25_udev_miscdev= +{ + AX25_UDEV_MINOR, + "net/ax25", + &ax25_udev_fops +}; + +int __init ax25_udev_init(void) +{ + printk(KERN_INFO "AX.25: userspace network device support driver version %s\n", AX25_UDEV_VER); + + if (misc_register(&ax25_udev_miscdev)) { + printk(KERN_ERR "ax25_udev: Can't register misc device %d\n", AX25_UDEV_MINOR); + return -EIO; + } + + return 0; +} + +void ax25_udev_cleanup(void) +{ + misc_deregister(&ax25_udev_miscdev); +} + +module_init(ax25_udev_init); +module_exit(ax25_udev_cleanup); diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 0e41265fe..cd6815df7 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -814,7 +814,7 @@ static int receive(struct net_device *dev, int cnt) #ifdef __i386__ #define GETTICK(x) \ ({ \ - if (cpu_has_tsc) \ + if (current_cpu_data.x86_capability & X86_FEATURE_TSC) \ __asm__ __volatile__("rdtsc" : "=a" (x) : : "dx");\ }) #else /* __i386__ */ @@ -1023,8 +1023,7 @@ static int epp_open(struct net_device *dev) struct baycom_state *bc; struct parport *pp; const struct tq_struct run_bh = { - routine: (void *)(void *)epp_bh, - data: dev + 0, 0, (void *)(void *)epp_bh, dev }; unsigned int i, j; unsigned char tmp[128]; @@ -1507,7 +1506,7 @@ module_exit(cleanup_baycomepp); static int __init baycom_epp_setup(char *str) { - static unsigned __initdata nr_dev = 0; + static unsigned __initlocaldata nr_dev = 0; int ints[2]; if (nr_dev >= NR_PORTS) diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index 75539ecef..db96de7b9 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -1,5 +1,5 @@ /* - * G8BPQ compatible "AX.25 via ethernet" driver release 004 + * G8BPQ compatible "AX.25 via ethernet" driver release 003 * * This code REQUIRES 2.0.0 or higher/ NET3.029 * @@ -62,10 +62,9 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/net.h> -#include <net/ax25.h> +#include <net/ax25dev.h> #include <linux/inet.h> #include <linux/netdevice.h> -#include <linux/if_ether.h> #include <linux/if_arp.h> #include <linux/skbuff.h> #include <net/sock.h> @@ -77,25 +76,16 @@ #include <linux/notifier.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/netfilter.h> #include <linux/module.h> #include <linux/init.h> #include <linux/rtnetlink.h> - #include <net/ip.h> #include <net/arp.h> - #include <linux/bpqether.h> -static const char banner[] __initdata = KERN_INFO "AX.25: bpqether driver version 004\n"; - -static unsigned char ax25_bcast[AX25_ADDR_LEN] = - {'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static unsigned char ax25_defaddr[AX25_ADDR_LEN] = - {'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; +/* ------------------------------------------------------------------------ */ static char bcast_addr[6]={0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - static char bpq_eth_addr[6]; static int bpq_rcv(struct sk_buff *, struct net_device *, struct packet_type *); @@ -103,56 +93,62 @@ static int bpq_device_event(struct notifier_block *, unsigned long, void *); static char *bpq_print_ethaddr(unsigned char *); static struct packet_type bpq_packet_type = { - type: __constant_htons(ETH_P_BPQ), - func: bpq_rcv, + 0, /* ntohs(ETH_P_BPQ),*/ + 0, /* copy */ + bpq_rcv, + NULL, + NULL, }; static struct notifier_block bpq_dev_notifier = { - notifier_call: bpq_device_event, + bpq_device_event, + 0 }; - #define MAXBPQDEV 100 static struct bpqdev { struct bpqdev *next; char ethname[14]; /* ether device name */ - struct net_device *ethdev; /* link to ethernet device */ - struct net_device axdev; /* bpq device (bpq#) */ + struct net_device *ethdev; /* link to ethernet device */ + struct net_device axdev; /* bpq device (bpq#) */ struct net_device_stats stats; /* some statistics */ char dest_addr[6]; /* ether destination address */ char acpt_addr[6]; /* accept ether frames from this address only */ -} *bpq_devices; - + struct ax25_dev ax25dev; /* AX.25 device control structure */ +} *bpq_devices = NULL; /* ------------------------------------------------------------------------ */ - /* * Get the ethernet device for a BPQ device */ -static inline struct net_device *bpq_get_ether_dev(struct net_device *dev) +static __inline__ struct net_device *bpq_get_ether_dev(struct net_device *dev) { - struct bpqdev *bpq = (struct bpqdev *) dev->priv; + struct bpqdev *bpq; - return bpq ? bpq->ethdev : NULL; + bpq = (struct bpqdev *)dev->priv; + return (bpq != NULL) ? bpq->ethdev : NULL; } +/* ------------------------------------------------------------------------ */ + /* * Get the BPQ device for the ethernet device */ -static inline struct net_device *bpq_get_ax25_dev(struct net_device *dev) +static __inline__ struct net_device *bpq_get_ax25_dev(struct net_device *dev) { struct bpqdev *bpq; for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) if (bpq->ethdev == dev) return &bpq->axdev; - return NULL; } -static inline int dev_is_ethdev(struct net_device *dev) +/* ------------------------------------------------------------------------ */ + +static __inline__ int dev_is_ethdev(struct net_device *dev) { return ( dev->type == ARPHRD_ETHER @@ -160,6 +156,8 @@ static inline int dev_is_ethdev(struct net_device *dev) ); } +/* ------------------------------------------------------------------------ */ + /* * Sanity check: remove all devices that ceased to exists and * return '1' if the given BPQ device was affected. @@ -174,7 +172,6 @@ static int bpq_check_devices(struct net_device *dev) cli(); bpq_prev = NULL; - for (bpq = bpq_devices; bpq != NULL; bpq = bpq->next) { if (!dev_get(bpq->ethname)) { if (bpq_prev) @@ -195,23 +192,18 @@ static int bpq_check_devices(struct net_device *dev) bpq_prev = bpq; } - restore_flags(flags); - return result; } - /* ------------------------------------------------------------------------ */ - /* * Receive an AX.25 frame via an ethernet interface. */ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) { int len; - char * ptr; struct ethhdr *eth = (struct ethhdr *)skb->mac.raw; struct bpqdev *bpq; @@ -242,11 +234,8 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty skb_pull(skb, 2); /* Remove the length bytes */ skb_trim(skb, len); /* Set the length of the data */ - bpq->stats.rx_packets++; - bpq->stats.rx_bytes += len; - - ptr = skb_push(skb, 1); - *ptr = 0; + ((struct bpqdev *)dev->priv)->stats.rx_packets++; + ((struct bpqdev *)dev->priv)->stats.rx_bytes+=len; skb->dev = dev; skb->protocol = htons(ETH_P_AX25); @@ -254,10 +243,11 @@ static int bpq_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_ty skb->pkt_type = PACKET_HOST; netif_rx(skb); - return 0; } +/* ------------------------------------------------------------------------ */ + /* * Send an AX.25 frame via an ethernet interface */ @@ -265,7 +255,8 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) { struct sk_buff *newskb; unsigned char *ptr; - struct bpqdev *bpq; + struct sk_buff *skb_cp; + struct bpqdev *bpq = (struct bpqdev *) dev->priv; int size; /* @@ -278,63 +269,72 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) return -ENODEV; } - skb_pull(skb, 1); - size = skb->len; + /* NOTE non-obvious race condition prevention here */ + skb_cp = skb_copy(skb, GFP_ATOMIC); + kfree_skb(skb); + if (skb_cp == NULL) { /* out of memory */ + bpq->stats.tx_dropped++; + return 0; + } + + size = skb_cp->len; /* * The AX.25 code leaves enough room for the ethernet header, but * sendto() does not. */ - if (skb_headroom(skb) < AX25_BPQ_HEADER_LEN) { /* Ough! */ - if ((newskb = skb_realloc_headroom(skb, AX25_BPQ_HEADER_LEN)) == NULL) { + if (skb_headroom(skb_cp) < AX25_BPQ_HEADER_LEN) { /* Ough! */ + if ((newskb = skb_realloc_headroom(skb_cp, AX25_BPQ_HEADER_LEN)) == NULL) { printk(KERN_WARNING "bpqether: out of memory\n"); - kfree_skb(skb); + kfree_skb(skb_cp); return -ENOMEM; } - if (skb->sk != NULL) - skb_set_owner_w(newskb, skb->sk); + if (skb_cp->sk != NULL) + skb_set_owner_w(newskb, skb_cp->sk); - kfree_skb(skb); - skb = newskb; + kfree_skb(skb_cp); + skb_cp = newskb; } - skb->protocol = htons(ETH_P_AX25); + skb_cp->protocol = htons(ETH_P_AX25); - ptr = skb_push(skb, 2); + ptr = skb_push(skb_cp, 2); *ptr++ = (size + 5) % 256; *ptr++ = (size + 5) / 256; - bpq = (struct bpqdev *)dev->priv; - if ((dev = bpq_get_ether_dev(dev)) == NULL) { bpq->stats.tx_dropped++; - kfree_skb(skb); + kfree_skb(skb_cp); return -ENODEV; } - skb->dev = dev; - skb->nh.raw = skb->data; - dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); + skb_cp->dev = dev; + skb_cp->nh.raw = skb_cp->data; + dev->hard_header(skb_cp, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); bpq->stats.tx_packets++; - bpq->stats.tx_bytes+=skb->len; + bpq->stats.tx_bytes+=skb_cp->len; - dev_queue_xmit(skb); - netif_wake_queue(dev); + dev_queue_xmit(skb_cp); return 0; } +/* ------------------------------------------------------------------------ */ + /* * Statistics */ static struct net_device_stats *bpq_get_stats(struct net_device *dev) { - struct bpqdev *bpq = (struct bpqdev *) dev->priv; + struct bpqdev *bpq; + bpq = (struct bpqdev *)dev->priv; return &bpq->stats; } +/* ------------------------------------------------------------------------ */ + /* * Set AX.25 callsign */ @@ -343,10 +343,11 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr) struct sockaddr *sa = (struct sockaddr *)addr; memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); - return 0; } +/* ------------------------------------------------------------------------ */ + /* Ioctl commands * * SIOCSBPQETHOPT reserved for enhancements @@ -356,11 +357,12 @@ static int bpq_set_mac_address(struct net_device *dev, void *addr) */ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { + int err; struct bpq_ethaddr *ethaddr = (struct bpq_ethaddr *)ifr->ifr_data; struct bpqdev *bpq = dev->priv; struct bpq_req req; - if (!capable(CAP_NET_ADMIN)) + if (!suser()) return -EPERM; if (bpq == NULL) /* woops! */ @@ -368,8 +370,9 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCSBPQETHOPT: - if (copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req))) - return -EFAULT; + if ((err = verify_area(VERIFY_WRITE, ifr->ifr_data, sizeof(struct bpq_req))) != 0) + return err; + copy_from_user(&req, ifr->ifr_data, sizeof(struct bpq_req)); switch (req.cmd) { case SIOCGBPQETHPARAM: case SIOCSBPQETHPARAM: @@ -380,19 +383,37 @@ static int bpq_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) break; case SIOCSBPQETHADDR: - if (copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN)) - return -EFAULT; - if (copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN)) - return -EFAULT; + if ((err = verify_area(VERIFY_READ, ethaddr, sizeof(struct bpq_ethaddr))) != 0) + return err; + copy_from_user(bpq->dest_addr, ethaddr->destination, ETH_ALEN); + copy_from_user(bpq->acpt_addr, ethaddr->accept, ETH_ALEN); break; default: return -EINVAL; } - return 0; } +/* ------------------------------------------------------------------------ */ + +void bpq_param_notify(struct net_device *dev, int value, int old, int new) +{ + /* we reject some parameters because they are not implemented (yet) */ + switch (value) { + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + case AX25_VALUES_MEDIA_TXBITRATE: + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_DUPLEX: + ax25_dev_set_value(dev, value, old); + default: break; + } + return; +} + +/* ------------------------------------------------------------------------ */ + /* * open/close a device */ @@ -400,32 +421,60 @@ static int bpq_open(struct net_device *dev) { if (bpq_check_devices(dev)) return -ENODEV; /* oops, it's gone */ - + MOD_INC_USE_COUNT; netif_start_queue(dev); return 0; } +/* ------------------------------------------------------------------------ */ + static int bpq_close(struct net_device *dev) { netif_stop_queue(dev); + MOD_DEC_USE_COUNT; return 0; } +/* ------------------------------------------------------------------------ */ + /* * currently unused */ static int bpq_dev_init(struct net_device *dev) { + struct bpqdev *bpqdev = (struct bpqdev *) dev->priv; + + dev->hard_start_xmit = bpq_xmit; + dev->open = bpq_open; + dev->stop = bpq_close; + dev->set_mac_address = bpq_set_mac_address; + dev->get_stats = bpq_get_stats; + dev->do_ioctl = bpq_ioctl; + dev->flags = 0; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + AX25_PTR(dev) = &bpqdev->ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + AX25_PTR(dev)->hw.fast = 1; + AX25_PTR(dev)->hw.parameter_change_notify = bpq_param_notify; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 1); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, 10000000); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, 10000000); + + dev_init_buffers(dev); return 0; } - /* ------------------------------------------------------------------------ */ - /* * Proc filesystem */ @@ -435,10 +484,11 @@ static char * bpq_print_ethaddr(unsigned char *e) sprintf(buf, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", e[0], e[1], e[2], e[3], e[4], e[5]); - return buf; } +/* ------------------------------------------------------------------------ */ + static int bpq_get_info(char *buffer, char **start, off_t offset, int length) { struct bpqdev *bpqdev; @@ -475,14 +525,11 @@ static int bpq_get_info(char *buffer, char **start, off_t offset, int length) len -= (offset - begin); if (len > length) len = length; - return len; } - /* ------------------------------------------------------------------------ */ - /* * Setup a new device. */ @@ -524,36 +571,11 @@ static int bpq_new_device(struct net_device *dev) dev->init = bpq_dev_init; /* We should be locked, call register_netdevice() directly. */ - if (register_netdevice(dev) != 0) { kfree(bpq); return -EIO; } - dev_init_buffers(dev); - - dev->hard_start_xmit = bpq_xmit; - dev->open = bpq_open; - dev->stop = bpq_close; - dev->set_mac_address = bpq_set_mac_address; - dev->get_stats = bpq_get_stats; - dev->do_ioctl = bpq_ioctl; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_defaddr, AX25_ADDR_LEN); - - dev->flags = 0; - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#endif - - dev->type = ARPHRD_AX25; - dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; - dev->mtu = AX25_DEF_PACLEN; - dev->addr_len = AX25_ADDR_LEN; - cli(); if (bpq_devices == NULL) { @@ -564,10 +586,10 @@ static int bpq_new_device(struct net_device *dev) } sti(); - return 0; } +/* ------------------------------------------------------------------------ */ /* * Handle device status changes. @@ -595,32 +617,30 @@ static int bpq_device_event(struct notifier_block *this,unsigned long event, voi default: break; } - return NOTIFY_DONE; } - /* ------------------------------------------------------------------------ */ /* * Initialize driver. To be called from af_ax25 if not compiled as a * module */ -static int __init bpq_init_driver(void) +int __init bpq_init_driver(void) { struct net_device *dev; + bpq_packet_type.type = htons(ETH_P_BPQ); dev_add_pack(&bpq_packet_type); - register_netdevice_notifier(&bpq_dev_notifier); - - printk(banner); + printk(KERN_INFO "AX.25: bqpether driver version 0.02\n"); proc_net_create("bpqether", 0, bpq_get_info); read_lock_bh(&dev_base_lock); for (dev = dev_base; dev != NULL; dev = dev->next) { - if (dev_is_ethdev(dev)) { + if (dev_is_ethdev(dev)) + { read_unlock_bh(&dev_base_lock); bpq_new_device(dev); read_lock_bh(&dev_base_lock); @@ -630,12 +650,13 @@ static int __init bpq_init_driver(void) return 0; } -static void __exit bpq_cleanup_driver(void) +/* ------------------------------------------------------------------------ */ + +void __exit bpq_cleanup_driver(void) { struct bpqdev *bpq; dev_remove_pack(&bpq_packet_type); - unregister_netdevice_notifier(&bpq_dev_notifier); proc_net_remove("bpqether"); diff --git a/drivers/net/hamradio/dmascc.h b/drivers/net/hamradio/dmascc.h new file mode 100644 index 000000000..bd156e665 --- /dev/null +++ b/drivers/net/hamradio/dmascc.h @@ -0,0 +1,191 @@ +/* + * $Id: dmascc.h,v 1.0 + * + * Driver for high-speed SCC boards (those with DMA support) + * + * These are #define, type declarations and prototypes. + * + * Cleaned up on 03/11/2000 Jens David, <dg1kjd@afthd.tu-darmstadt.de> + * + * Copyright (C) 1997 Klaus Kudielka + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* ------------------------------------------------------------------------ */ + +/* Number of buffers per channel */ +#define NUM_TX_BUF 2 /* NUM_TX_BUF >= 1 (2 recommended) */ +#define NUM_RX_BUF 2 /* NUM_RX_BUF >= 1 (2 recommended) */ +#define BUF_SIZE 2016 + +/* Cards supported */ +#define HW_PI { "Ottawa PI", 0x300, 0x20, 0x10, 8, \ + 0, 8, 1843200, 3686400 } +#define HW_PI2 { "Ottawa PI2", 0x300, 0x20, 0x10, 8, \ + 0, 8, 3686400, 7372800 } +#define HW_TWIN { "Gracilis PackeTwin", 0x200, 0x10, 0x10, 32, \ + 0, 4, 6144000, 6144000 } + +#define HARDWARE { HW_PI, HW_PI2, HW_TWIN } + +#define TYPE_PI 0 +#define TYPE_PI2 1 +#define TYPE_TWIN 2 +#define NUM_TYPES 3 +#define MAX_NUM_DEVS 32 + +/* SCC chips supported */ +#define Z8530 0 +#define Z85C30 1 +#define Z85230 2 + +#define CHIPNAMES { "Z8530", "Z85C30", "Z85230" } + +/* I/O registers */ +/* 8530 registers relative to card base */ +#define SCCB_CMD 0x00 +#define SCCB_DATA 0x01 +#define SCCA_CMD 0x02 +#define SCCA_DATA 0x03 + +/* 8253/8254 registers relative to card base */ +#define TMR_CNT0 0x00 +#define TMR_CNT1 0x01 +#define TMR_CNT2 0x02 +#define TMR_CTRL 0x03 + +/* Additional PI/PI2 registers relative to card base */ +#define PI_DREQ_MASK 0x04 + +/* Additional PackeTwin registers relative to card base */ +#define TWIN_INT_REG 0x08 +#define TWIN_CLR_TMR1 0x09 +#define TWIN_CLR_TMR2 0x0a +#define TWIN_SPARE_1 0x0b +#define TWIN_DMA_CFG 0x08 +#define TWIN_SERIAL_CFG 0x09 +#define TWIN_DMA_CLR_FF 0x0a +#define TWIN_SPARE_2 0x0b + +/* PackeTwin I/O register values */ +/* INT_REG */ +#define TWIN_SCC_MSK 0x01 +#define TWIN_TMR1_MSK 0x02 +#define TWIN_TMR2_MSK 0x04 +#define TWIN_INT_MSK 0x07 + +/* SERIAL_CFG */ +#define TWIN_DTRA_ON 0x01 +#define TWIN_DTRB_ON 0x02 +#define TWIN_EXTCLKA 0x04 +#define TWIN_EXTCLKB 0x08 +#define TWIN_LOOPA_ON 0x10 +#define TWIN_LOOPB_ON 0x20 +#define TWIN_EI 0x80 + +/* DMA_CFG */ +#define TWIN_DMA_HDX_T1 0x08 +#define TWIN_DMA_HDX_R1 0x0a +#define TWIN_DMA_HDX_T3 0x14 +#define TWIN_DMA_HDX_R3 0x16 +#define TWIN_DMA_FDX_T3R1 0x1b +#define TWIN_DMA_FDX_T1R3 0x1d + +/* Status values */ +/* tx_state */ +#define TX_IDLE 0 +#define TX_OFF 1 +#define TX_TXDELAY 2 +#define TX_ACTIVE 3 +#define TX_SQDELAY 4 + +/* ------------------------------------------------------------------------ */ + +/* Data types */ +struct scc_hardware { + char *name; + int io_region; + int io_delta; + int io_size; + int num_devs; + int scc_offset; + int tmr_offset; + int tmr_hz; + int pclk_hz; +}; + +struct scc_priv { + struct ax25_dev ax25dev; + struct enet_statistics stats; + struct scc_info *info; + int channel; + int cmd, data, tmr; + struct scc_param param; + char rx_buf[NUM_RX_BUF][BUF_SIZE]; + int rx_len[NUM_RX_BUF]; + int rx_ptr; + struct tq_struct rx_task; + int rx_head, rx_tail, rx_count; + int rx_over; + char tx_buf[NUM_TX_BUF][BUF_SIZE]; + int tx_len[NUM_TX_BUF]; + int tx_ptr; + int tx_head, tx_tail, tx_count; + int tx_state; + unsigned long tx_start; + int status; +}; + +struct scc_info { + int type; + int chip; + int open; + int scc_base; + int tmr_base; + int twin_serial_cfg; + struct net_device dev[2]; + struct scc_priv priv[2]; + struct scc_info *next; +}; + +/* ------------------------------------------------------------------------ */ + +/* Prototypes */ +int dmascc_init(void) __init; +static int setup_adapter(int io, int h, int n) __init; +static inline void write_scc(int ctl, int reg, int val); +static inline int read_scc(int ctl, int reg); +static int scc_open(struct net_device *dev); +static int scc_close(struct net_device *dev); +static int scc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int scc_send_packet(struct sk_buff *skb, struct net_device *dev); +static struct enet_statistics *scc_get_stats(struct net_device *dev); +static int scc_set_mac_address(struct net_device *dev, void *sa); +static void scc_isr(int irq, void *dev_id, struct pt_regs * regs); +static inline void z8530_isr(struct scc_info *info); +static void rx_isr(struct net_device *dev); +static void special_condition(struct net_device *dev, int rc); +static void rx_bh(void *arg); +static void tx_isr(struct net_device *dev); +static void es_isr(struct net_device *dev); +static void tm_isr(struct net_device *dev); +static inline void delay(struct net_device *dev, int t); +static unsigned int report_dcd(struct net_device *dev); +static unsigned int report_ptt(struct net_device *dev); +static void parameter_change_notify(struct net_device *dev, int valueno, + int old, int new); + +/* ------------------------------------------------------------------------ */ diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index e1af65373..1726c8ed3 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -287,7 +287,7 @@ void hdlcdrv_receiver(struct net_device *dev, struct hdlcdrv_state *s) /* ---------------------------------------------------------------------- */ -static inline void do_kiss_params(struct hdlcdrv_state *s, +static void inline do_kiss_params(struct hdlcdrv_state *s, unsigned char *data, unsigned long len) { @@ -889,7 +889,14 @@ EXPORT_SYMBOL(hdlcdrv_unregister_hdlcdrv); /* --------------------------------------------------------------------- */ -static int __init hdlcdrv_init_driver(void) +#ifdef MODULE + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); +MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); + +/* --------------------------------------------------------------------- */ + +int __init init_module(void) { printk(KERN_INFO "hdlcdrv: (C) 1996-2000 Thomas Sailer HB9JNX/AE4WA\n"); printk(KERN_INFO "hdlcdrv: version 0.8 compiled " __TIME__ " " __DATE__ "\n"); @@ -898,16 +905,10 @@ static int __init hdlcdrv_init_driver(void) /* --------------------------------------------------------------------- */ -static void __exit hdlcdrv_cleanup_driver(void) +void cleanup_module(void) { printk(KERN_INFO "hdlcdrv: cleanup\n"); } -/* --------------------------------------------------------------------- */ - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Packet Radio network interface HDLC encoder/decoder"); -module_init(hdlcdrv_init_driver); -module_exit(hdlcdrv_cleanup_driver); - +#endif /* MODULE */ /* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/kiss.c b/drivers/net/hamradio/kiss.c new file mode 100644 index 000000000..f3cce6429 --- /dev/null +++ b/drivers/net/hamradio/kiss.c @@ -0,0 +1,1276 @@ +/* + * kiss.c This module implements the KISS protocol for kernel-based + * devices like TTY. It interfaces between a raw TTY, and the + * kernel's AX.25 protocol layers. + * + * Shamelessly copied from slip.c on 99/01/04: + * Matthias Welwarsky <dg2fef@afthd.tu-darmstadt.de> + * + * Original Authors: + * Laurence Culhane, <loz@holmes.demon.co.uk> + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * + * See slip.c for additional credits and fixes. + * + * 2000-09-21 adapted for kernel 2.4.0, it however still crashes + * badly when killing kissattach and trying to re- + * start the interface. Does the original SLIP driver + * do the same? + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/tty.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include <linux/if_arp.h> +#include <linux/init.h> +#include <net/ax25.h> +#include <net/ax25dev.h> +#ifdef CONFIG_INET +#include <linux/ip.h> +#include <linux/tcp.h> +#endif +#include "kiss.h" + + +#define KISS_VERSION "KISS-NET3.019-NEWTTY" + +static int maxdev = KISS_NRUNIT; +static kiss_ctrl_t **kiss_ctrls = NULL; +static struct tty_ldisc kiss_ldisc; + +static int kiss_esc(unsigned char *p, unsigned char *d, int len); +static void kiss_unesc(struct kiss *ks, unsigned char c); +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len); +static int kiss_init(struct net_device *dev); +static int kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg); +static int kiss_dev_ioctl(struct net_device *dev,struct ifreq *rq,int cmd); +static int kiss_dev_set_mac_address(struct net_device *dev, void *addr); +static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new); +static void kiss_update_parameters(struct net_device *dev); +static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value); + +static spinlock_t ks_ctrl_lock = SPIN_LOCK_UNLOCKED; + +/*---------------------------------------------------------------------------*/ + +static const unsigned short Crc_16_table[] = { + 0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241, + 0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440, + 0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40, + 0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841, + 0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40, + 0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41, + 0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641, + 0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040, + 0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240, + 0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441, + 0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41, + 0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840, + 0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41, + 0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40, + 0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640, + 0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041, + 0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240, + 0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441, + 0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41, + 0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840, + 0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41, + 0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40, + 0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640, + 0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041, + 0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241, + 0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440, + 0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40, + 0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841, + 0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40, + 0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41, + 0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641, + 0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040 +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short +calc_crc_16(unsigned char *cp, int size) +{ + unsigned short crc = 0; + + while (size--) + crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff]; + + return crc; +} + +/*---------------------------------------------------------------------------*/ + +static int +check_crc_16(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + if (size < 3) + return -1; + + while (size--) + crc = (crc >> 8) ^ Crc_16_table[(crc ^ *cp++) & 0xff]; + + if (crc != 0) + return -1; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static const unsigned short Crc_flex_table[] = { + 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, + 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, + 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, + 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, + 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, + 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, + 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, + 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, + 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, + 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, + 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, + 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, + 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, + 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, + 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, + 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, + 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, + 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, + 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, + 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, + 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, + 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, + 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, + 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, + 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, + 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, + 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, + 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, + 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, + 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, + 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, + 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short +calc_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + return crc; +} + +/*---------------------------------------------------------------------------*/ + +static int +check_crc_flex(unsigned char *cp, int size) +{ + unsigned short crc = 0xffff; + + if (size < 3) + return -1; + + while (size--) + crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; + + if ((crc & 0xffff) != 0x7070) + return -1; + + return 0; +} + +/******************************** +* Buffer administration routines: +* kiss_alloc_bufs() +* kiss_free_bufs() +* kiss_realloc_bufs() +* +* NOTE: kiss_realloc_bufs != kiss_free_bufs + kiss_alloc_bufs, because +* kiss_realloc_bufs provides strong atomicity and reallocation +* on actively running device. +*********************************/ + +/* + Allocate channel buffers. + */ + +static int +kiss_alloc_bufs(struct kiss *ks, int mtu) +{ + int err = -ENOBUFS; + unsigned long len; + char * rbuff = NULL; + char * xbuff = NULL; + + /* + * Allocate the KISS frame buffers: + * + * rbuff Receive buffer. + * xbuff Transmit buffer. + */ + len = mtu * 2; + + /* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + rbuff = kmalloc(len + 4, GFP_KERNEL); + if (rbuff == NULL) + goto err_exit; + xbuff = kmalloc(len + 4, GFP_KERNEL); + if (xbuff == NULL) + goto err_exit; + + spin_lock_bh(&ks->lock); + if (ks->tty == NULL) { + err = -ENODEV; + goto err_exit; + } + ks->mtu = mtu; + ks->buffsize = len; + ks->rcount = 0; + ks->xleft = 0; + rbuff = xchg(&ks->rbuff, rbuff); + xbuff = xchg(&ks->xbuff, xbuff); + err = 0; + + /* Cleanup */ +err_exit: + spin_unlock_bh(&ks->lock); + if (xbuff) + kfree(xbuff); + if (rbuff) + kfree(rbuff); + return err; +} + +/* Free a KISS channel buffers. */ +static void +kiss_free_bufs(struct kiss *ks) +{ + void * tmp; + + /* Free all KISS frame buffers. */ + if ((tmp = xchg(&ks->rbuff, NULL)) != NULL) + kfree(tmp); + if ((tmp = xchg(&ks->xbuff, NULL)) != NULL) + kfree(tmp); +} + +/* + Reallocate kiss channel buffers. + */ + +static int kiss_realloc_bufs(struct kiss *ks, int mtu) +{ + int err = 0; + struct net_device *dev = ks->dev; + unsigned char *xbuff, *rbuff; + int len = (mtu + MAX_HEADER) * 2; + +/* + * allow for arrival of larger UDP packets, even if we say not to + * also fixes a bug in which SunOS sends 512-byte packets even with + * an MSS of 128 + */ + if (len < 576 * 2) + len = 576 * 2; + + xbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); + rbuff = (unsigned char *) kmalloc (len + 4, GFP_ATOMIC); + + if (xbuff == NULL || rbuff == NULL) { + if (mtu >= dev->mtu) { + printk("%s: unable to grow kiss buffers, MTU change cancelled.\n", + dev->name); + err = -ENOBUFS; + } + goto done; + } + + spin_lock_bh(&ks->lock); + + err = -ENODEV; + if (ks->tty == NULL) + goto done_on_bh; + + xbuff = xchg(&ks->xbuff, xbuff); + rbuff = xchg(&ks->rbuff, rbuff); + if (ks->xleft) { + if (ks->xleft <= len) { + memcpy(ks->xbuff, ks->xhead, ks->xleft); + } else { + ks->xleft = 0; + ks->stats.tx_dropped++; + } + } + ks->xhead = ks->xbuff; + + if (ks->rcount) { + if (ks->rcount <= len) { + memcpy(ks->rbuff, rbuff, ks->rcount); + } else { + ks->rcount = 0; + ks->stats.rx_over_errors++; + ks->flags.error=1; + } + } + ks->mtu = mtu + MAX_HEADER;; + dev->mtu = mtu; + ks->buffsize = len; + err = 0; + +done_on_bh: + spin_unlock_bh(&ks->lock); + +done: + if (xbuff) + kfree(xbuff); + if (rbuff) + kfree(rbuff); + return err; +} + +/* Send one completely decapsulated IP datagram to the IP layer. */ +static void +kiss_bump(struct kiss *ks) +{ + struct sk_buff *skb; + int count; + + if (ks->mode == KISS_MODE_ADAPTIVE) { + if (ks->rbuff[0] & 0x20) { + ks->mode = KISS_MODE_FLEX; + } else + if (ks->rbuff[0] & 0x80) { + ks->mode = KISS_MODE_SMACK; + } + } + + if (ks->mode == KISS_MODE_FLEX) { + if (check_crc_flex(ks->rbuff, ks->rcount) < 0) { + ks->stats.rx_errors++; + return; + } + ks->rcount -= 2; + } else if (ks->mode == KISS_MODE_SMACK) { + if (check_crc_16(ks->rbuff, ks->rcount) < 0) { + ks->stats.rx_errors++; + return; + } + ks->rcount -= 2; + } + + count = ks->rcount-1; + ks->stats.rx_bytes += count; + + skb = dev_alloc_skb(count+MAX_HEADER); + if (skb == NULL) { + printk("%s: memory squeeze, dropping packet.\n", ks->dev->name); + ks->stats.rx_dropped++; + return; + } + skb_reserve(skb, MAX_HEADER); + skb->dev = ks->dev; + memcpy(skb_put(skb,count), ks->rbuff+1, count); + skb->mac.raw = skb->data; + skb->protocol = __constant_htons(ETH_P_AX25); + netif_rx(skb); + ks->stats.rx_packets++; +} + +/* Encapsulate one IP datagram and stuff into a TTY queue. */ +static void +kiss_encaps(struct kiss *ks, unsigned char *icp, int len) +{ + unsigned char *p; + int actual, count; + + if (len > ks->mtu) { /* Sigh, shouldn't occur BUT ... */ + printk ("%s: dropping oversized transmit packet!\n", ks->dev->name); + ks->stats.tx_dropped++; + netif_wake_queue(ks->dev); + return; + } + + p = icp; + + if (*p == 0) { + /* KISS data */ + switch (ks->mode) { + unsigned short crc; + + case KISS_MODE_FLEX: + *p |= 0x20; + crc = calc_crc_flex(p, len); + count = kiss_esc_crc(p, ks->xbuff, crc, len+2); + break; + case KISS_MODE_SMACK: + *p |= 0x80; + crc = calc_crc_16(p, len); + count = kiss_esc_crc(p, ks->xbuff, crc, len+2); + break; + default: + count = kiss_esc(p, ks->xbuff, len); + break; + } + } else { + /* KISS parameter */ + ks->xbuff[0] = END; + memcpy(&ks->xbuff[1], p, len); + ks->xbuff[len+1] = END; + count = len+2; + } + + /* Order of next two lines is *very* important. + * When we are sending a little amount of data, + * the transfer may be completed inside driver.write() + * routine, because it's running with interrupts enabled. + * In this case we *never* got WRITE_WAKEUP event, + * if we did not request it before write operation. + * 14 Oct 1994 Dmitry Gorodchanin. + */ + ks->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); + actual = ks->tty->driver.write(ks->tty, 0, ks->xbuff, count); + ks->dev->trans_start = jiffies; + ks->xleft = count - actual; + ks->xhead = ks->xbuff + actual; +} + +/* + * Called by the driver when there's room for more data. If we have + * more packets to send, we send them here. + */ +static void kiss_write_wakeup(struct tty_struct *tty) +{ + int actual; + struct kiss *ks = (struct kiss *) tty->disc_data; + + /* First make sure we're connected. */ + if (!ks || !netif_running(ks->dev)) { + return; + } + if (ks->xleft <= 0) { + /* Now serial buffer is almost free & we can start + * transmission of another packet */ + ks->stats.tx_packets++; + tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + netif_wake_queue(ks->dev); + return; + } + + actual = tty->driver.write(tty, 0, ks->xhead, ks->xleft); + ks->xleft -= actual; + ks->xhead += actual; +} + +/* Encapsulate an IP datagram and kick it into a TTY queue. */ +static int +kiss_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + spin_lock(&ks->lock); + + /* ldisc disappeared under our feet, ouch! */ + if (ks->tty == NULL) + goto done; + + /* rant by dl1bke: + * There used to be a completely useless copy of + * the skb here. If there really _is_ a race condition + * fix the upper protocols, dammit! Masquearading these + * problems doesn't help at all, they'll rear their ugly + * head somewhere else again anyway! + */ + + /* We were not busy, so we are now... :-) */ + if (skb_headroom(skb) > 0) + { + *skb_push(skb, 1) = 0; /* prepend KISS header */ + netif_stop_queue(dev); + ks->stats.tx_bytes+=skb->len; + kiss_encaps(ks, skb->data, skb->len); + } else { + ks->stats.tx_dropped++; + } + +done: + dev_kfree_skb(skb); + spin_unlock(&ks->lock); + return 0; +} + + +/****************************************** + * Routines looking at netdevice side. + ******************************************/ + +/* Netdevice UP -> DOWN routine */ + +static int +kiss_dev_close(struct net_device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + spin_lock_bh(&ks->lock); + if (ks->tty) { + /* TTY discipline is running. */ + ks->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); + } + + ks->rcount = 0; + ks->xleft = 0; + netif_stop_queue(dev); + spin_unlock_bh(&ks->lock); + + MOD_DEC_USE_COUNT; + return 0; +} + +/* Netdevice DOWN -> UP routine */ + +static int kiss_dev_open(struct net_device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + if (ks->tty==NULL) + return -ENODEV; + + ks->flags.in_use = 1; + ks->flags.error = 0; + ks->flags.escape = 0; + + MOD_INC_USE_COUNT; + + netif_start_queue(dev); + return 0; +} + +/* Netdevice change MTU request */ + +static int kiss_change_mtu(struct net_device *dev, int new_mtu) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + if (new_mtu < 68 || new_mtu > 65534) + return -EINVAL; + + if (new_mtu != dev->mtu) + return kiss_realloc_bufs(ks, new_mtu); + return 0; +} + +/* Netdevice get statistics request */ + +static struct net_device_stats * +kiss_get_stats(struct net_device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + return (&ks->stats); +} + +/* Netdevice register callback */ + +static int kiss_init(struct net_device *dev) +{ + struct kiss *ks = (struct kiss*)(dev->priv); + + /* + * Finish setting up the DEVICE info. + */ + + dev->mtu = ks->mtu - MAX_HEADER; + dev->hard_start_xmit = kiss_xmit; + dev->open = kiss_dev_open; + dev->stop = kiss_dev_close; + dev->get_stats = kiss_get_stats; + dev->change_mtu = kiss_change_mtu; + dev->do_ioctl = kiss_dev_ioctl; + dev->set_mac_address = kiss_dev_set_mac_address; + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->addr_len = AX25_ADDR_LEN; + dev->type = ARPHRD_AX25; + dev->tx_queue_len = 10; + + dev_init_buffers(dev); + + /* New-style flags. */ + dev->flags = IFF_BROADCAST | IFF_MULTICAST; + + /* initialise DDI interface values */ + AX25_PTR(dev) = &ks->ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + AX25_PTR(dev)->hw.fast = 1; + AX25_PTR(dev)->hw.dcd = NULL; + AX25_PTR(dev)->hw.ptt = NULL; + AX25_PTR(dev)->hw.cts = NULL; + AX25_PTR(dev)->hw.rts = NULL; + AX25_PTR(dev)->hw.parameter_change_notify = kiss_parameter_change_notify; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, 200); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, 20); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_SLOTTIME, 10); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE, 64); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, 0); + return 0; +} + +static void kiss_send_parameter(struct net_device *dev, unsigned char param, unsigned char value) +{ + struct kiss *ks = (struct kiss *) dev->priv; + unsigned char buf[2] = {param, value}; + + kiss_encaps(ks, buf, 2); + return; +} + +static void kiss_update_parameters(struct net_device *dev) +{ + kiss_send_parameter(dev, KISS_PARAM_TXDELAY, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY)/10); + kiss_send_parameter(dev, KISS_PARAM_PPERS, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE)); + kiss_send_parameter(dev, KISS_PARAM_SLOT, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME)/10); + kiss_send_parameter(dev, KISS_PARAM_DUPLEX, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX)); + kiss_send_parameter(dev, KISS_PARAM_TXTAIL, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)/10); + return; +} + +static void kiss_parameter_change_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct kiss *ks = (struct kiss *) dev->priv; + int br; + + switch (valueno) { + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + /* + * If anybody knows how to set TTY's baud rate + * from kernel mode please add it here. We currently + * reject baud rate change requests. + */ + br = tty_get_baud_rate(ks->tty); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, br); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, br); + break; + case AX25_VALUES_MEDIA_DUPLEX: + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + case AX25_VALUES_MEDIA_SLOTTIME: + case AX25_VALUES_MEDIA_PPERSISTENCE: + kiss_update_parameters(dev); + case AX25_VALUES_MEDIA_AUTO_ADJUST: + default: + break; + } + return; +} + +/****************************************** + Routines looking at TTY side. + ******************************************/ + + +static int kiss_receive_room(struct tty_struct *tty) +{ + return 65536; /* We can handle an infinite amount of data. :-) */ +} + +/* + * Handle the 'receiver data ready' interrupt. + * This function is called by the 'tty_io' module in the kernel when + * a block of KISS data has been received, which can now be decapsulated + * and sent on to some IP layer for further processing. + */ + +static void kiss_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + + if (!ks || !netif_running(ks->dev)) + return; + + /* Read the characters out of the buffer */ + while (count--) { + if (fp && *fp++) { + if (!ks->flags.error) + { + ks->flags.error = 1; + ks->stats.rx_errors++; + } + cp++; + continue; + } + kiss_unesc(ks, *cp++); + } +} + +/************************************ + * kiss_open helper routines. + ************************************/ + +/* Collect hanged up channels */ + +static void kiss_sync(void) +{ + int i; + + spin_lock_bh(&ks_ctrl_lock); + for (i = 0; i < maxdev; i++) { + kiss_ctrl_t *ksp = kiss_ctrls[i]; + if (ksp == NULL) + break; + if (ksp->ctrl.tty) + continue; + if (ksp->dev.flags & IFF_UP) + dev_close(&ksp->dev); + } + spin_unlock_bh(&ks_ctrl_lock); +} + +/* Find a free KISS channel, and link in this `tty' line. */ +static struct kiss * +kiss_alloc(kdev_t line) +{ + struct kiss *ks; + kiss_ctrl_t *ksp = NULL; + int i; + int sel = -1; + int score = -1; + + if (kiss_ctrls == NULL) + return NULL; /* Master array missing ! */ + + spin_lock_bh(&ks_ctrl_lock); + + for (i = 0; i < maxdev; i++) { + ksp = kiss_ctrls[i]; + if (ksp == NULL) + break; + + if (ksp->ctrl.tty) + continue; + + if (current->pid == ksp->ctrl.pid) { + if (ksp->ctrl.line == line && score < 3) { + sel = i; + score = 3; + continue; + } + if (score < 2) { + sel = i; + score = 2; + } + continue; + } + if (ksp->ctrl.line == line && score < 1) { + sel = i; + score = 1; + continue; + } + if (score < 0) { + sel = i; + score = 0; + } + } + + if (sel >= 0) { + i = sel; + ksp = kiss_ctrls[i]; + if (score > 1) { + ksp->ctrl.flags.in_use = 1; + ksp->ctrl.flags.error = 0; + ksp->ctrl.flags.escape = 0; + goto done; + } + } + + /* Sorry, too many, all slots in use */ + if (i >= maxdev) + goto outahere; + + if (ksp) { + if (ksp->ctrl.flags.in_use) { + unregister_netdevice(&ksp->dev); + kiss_free_bufs(&ksp->ctrl); + } + } else if ((ksp = (kiss_ctrl_t *)kmalloc(sizeof(kiss_ctrl_t),GFP_KERNEL)) == NULL) + goto outahere; + + memset(ksp, 0, sizeof(kiss_ctrl_t)); + + ks = &ksp->ctrl; + /* Initialize channel control data */ + ks->dev = &ksp->dev; + ks->mode = KISS_MODE_ADAPTIVE; + sprintf(ksp->dev.name, "ax%d", i); + ksp->dev.base_addr = i; + ksp->dev.priv = (void*)ks; + ksp->dev.init = kiss_init; + kiss_ctrls[i] = ksp; + +done: + spin_lock_init(&ksp->ctrl.lock); + spin_unlock_bh(&ks_ctrl_lock); + return &ksp->ctrl; + +outahere: + spin_unlock_bh(&ks_ctrl_lock); + return NULL; +} + +/* + * Open the high-level part of the KISS channel. + * This function is called by the TTY module when the + * KISS line discipline is called for. Because we are + * sure the tty line exists, we only have to link it to + * a free KISS channel... + */ +static int +kiss_open(struct tty_struct *tty) +{ + struct kiss *ks; + int err; + + MOD_INC_USE_COUNT; + + /* Collect hanged up channels. */ + kiss_sync(); + + ks = (struct kiss *) tty->disc_data; + err = -EEXIST; + /* First make sure we're not already connected. */ + if (ks) goto err_exit; + + /* OK. Find a free KISS channel to use. */ + err = -ENFILE; + if ((ks = kiss_alloc(tty->device)) == NULL) + goto err_exit; + + ks->tty = tty; + tty->disc_data = ks; + ks->line = tty->device; + ks->pid = current->pid; + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + + if (! ks->flags.in_use) { + /* Perform the low-level KISS initialization. */ + if ((err = kiss_alloc_bufs(ks, KISS_MTU)) != 0) + goto err_free_chan; + if (register_netdevice(ks->dev)) { + kiss_free_bufs(ks); + goto err_free_chan; + } + + ks->flags.in_use = 1; + } + + return ks->dev->base_addr; + +err_free_chan: + + ks->tty = NULL; + tty->disc_data = NULL; + ks->flags.in_use = 0; + +err_exit: + printk(KERN_DEBUG "kiss_open: err:%d\n", err); + + /* Count references from TTY module */ + MOD_DEC_USE_COUNT; + return err; +} + +/* + Let me to blame a bit. + 1. TTY module calls this funstion on soft interrupt. + 2. TTY module calls this function WITH MASKED INTERRUPTS! + 3. TTY module does not notify us about line discipline + shutdown, + + Seems, now it is clean. The solution is to consider netdevice and + line discipline sides as two independent threads. + + By-product (not desired): sl? does not feel hangups and remains open. + It is supposed, that user level program (dip, diald, slattach...) + will catch SIGHUP and make the rest of work. + + I see no way to make more with current tty code. --ANK + */ + +/* + * Close down a KISS channel. + * This means flushing out any pending queues, and then restoring the + * TTY line discipline to what it was before it got hooked to KISS + * (which usually is TTY again). + */ +static void +kiss_close(struct tty_struct *tty) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + + /* First make sure we're connected. */ + if (!ks || ks->tty != tty) + return; + + tty->disc_data = 0; + ks->tty = NULL; + ks->line = 0; + + /* Count references from TTY module */ + MOD_DEC_USE_COUNT; +} + + /************************************************************************ + * STANDARD KISS ENCAPSULATION * + ************************************************************************/ + +int +kiss_esc(unsigned char *s, unsigned char *d, int len) +{ + unsigned char *ptr = d; + unsigned char c; + + /* + * Send an initial END character to flush out any + * data that may have accumulated in the receiver + * due to line noise. + */ + + *ptr++ = END; + + /* + * For each byte in the packet, send the appropriate + * character sequence, according to the KISS protocol. + */ + + while (len-- > 0) { + switch(c = *s++) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return (ptr - d); +} + +/* + * MW: + * OK its ugly, but tell me a better solution without copying the + * packet to a temporary buffer :-) + */ +static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, unsigned int len) +{ + unsigned char *ptr = d; + unsigned char c = 0; + + *ptr++ = END; + while (len > 0) { + if (len > 2) + c = *s++; + else if (len > 1) + c = crc >> 8; + else if (len > 0) + c = crc & 0xff; + + len--; + + switch (c) { + case END: + *ptr++ = ESC; + *ptr++ = ESC_END; + break; + case ESC: + *ptr++ = ESC; + *ptr++ = ESC_ESC; + break; + default: + *ptr++ = c; + break; + } + } + *ptr++ = END; + return ptr - d; +} + +static void kiss_unesc(struct kiss *ks, unsigned char s) +{ + + switch(s) { + case END: + if (! ks->flags.error && (ks->rcount > 2)) + kiss_bump(ks); + + ks->flags.error = 0; + ks->flags.escape = 0; + ks->rcount = 0; + return; + + case ESC: + ks->flags.escape = 1; + return; + case ESC_ESC: + if (ks->flags.escape) + { + s = ESC; + ks->flags.escape = 0; + } + break; + case ESC_END: + if (ks->flags.escape) { + s = END; + ks->flags.escape = 0; + } + break; + } + if (!ks->flags.error) { + if (ks->rcount < ks->buffsize) { + ks->rbuff[ks->rcount++] = s; + } else { + ks->stats.rx_over_errors++; + ks->flags.error = 1; + } + } +} + + +static int kiss_set_mac_address(struct net_device *dev, void *addr) +{ + if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN)) + return -EFAULT; + return 0; +} + +static int kiss_dev_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); + return 0; +} + +/* Perform I/O control on an active KISS channel. */ +static int +kiss_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) +{ + struct kiss *ks = (struct kiss *) tty->disc_data; + unsigned int tmp; + + /* First make sure we're connected. */ + if (!ks) return -EINVAL; + + switch(cmd) { + case SIOCGIFNAME: + tmp = strlen(ks->dev->name) + 1; + if (copy_to_user(arg, ks->dev->name, tmp)) + return -EFAULT; + return 0; + + case SIOCSIFHWADDR: + return kiss_set_mac_address(ks->dev, arg); + + case SIOCGIFENCAP: + if (put_user(ks->mode, (int *) arg)) + return -EFAULT; + return 0; + case SIOCSIFENCAP: + if (get_user(tmp, (int *) arg)) + return -EFAULT; + if (tmp < 0 || tmp > KISS_MODE_TNN_TOKENRING) + return -EINVAL; + if (tmp == KISS_MODE_COMPAT) tmp = KISS_MODE_ADAPTIVE; + ks->mode = tmp; + return 0; + + /* Allow stty to read, but not set, the serial port */ + case TCGETS: + case TCGETA: + return n_tty_ioctl(tty, (struct file *) file, cmd, (unsigned long) arg); + + default: + return -ENOIOCTLCMD; + } +} + +static int kiss_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + int retval = -ENOIOCTLCMD; + struct kiss *ks = (struct kiss *)dev->priv; + + if (ks == NULL) + return -ENODEV; + + spin_lock_bh(&ks->lock); + + if (!ks->tty) { + retval = -ENODEV; + goto done; + } + + printk(KERN_DEBUG "ioctl %d called for dev %s\n", cmd, dev->name); + + switch(cmd) { + default: + retval = 0; + break; + } + +done: + spin_unlock_bh(&ks->lock); + return retval; +} + +/* Initialize KISS control device -- register KISS line discipline */ + +int __init kiss_init_driver(void) +{ + int status; + + if (maxdev < KISS_NRUNIT) maxdev = KISS_NRUNIT; /* Sanity */ + + printk(KERN_INFO "KISS: version %s (dynamic channels, max=%d)\n", + KISS_VERSION, maxdev); + + kiss_ctrls = (kiss_ctrl_t **) kmalloc(sizeof(void*)*maxdev, GFP_KERNEL); + if (kiss_ctrls == NULL) + { + printk("KISS: Can't allocate kiss_ctrls[] array! Uaargh! (-> No KISS available)\n"); + return -ENOMEM; + } + + /* Clear the pointer array, we allocate devices when we need them */ + memset(kiss_ctrls, 0, sizeof(void*)*maxdev); /* Pointers */ + + /* Fill in our line protocol discipline, and register it */ + memset(&kiss_ldisc, 0, sizeof(kiss_ldisc)); + kiss_ldisc.magic = TTY_LDISC_MAGIC; + kiss_ldisc.name = "kiss"; + kiss_ldisc.flags = 0; + kiss_ldisc.open = kiss_open; + kiss_ldisc.close = kiss_close; + kiss_ldisc.read = NULL; + kiss_ldisc.write = NULL; + kiss_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, + unsigned int, unsigned long)) kiss_ioctl; + kiss_ldisc.poll = NULL; + kiss_ldisc.receive_buf = kiss_receive_buf; + kiss_ldisc.receive_room = kiss_receive_room; + kiss_ldisc.write_wakeup = kiss_write_wakeup; + if ((status = tty_register_ldisc(N_KISS, &kiss_ldisc)) != 0) { + printk("KISS: can't register line discipline (err = %d)\n", status); + } + + + return status; + + /* Return "not found", so that dev_init() will unlink + * the placeholder device entry for us. + */ + return -ENODEV; + } + + +void __exit kiss_exit_driver(void) +{ + int i; + + if (kiss_ctrls != NULL) { + unsigned long start = jiffies; + int busy = 0; + + /* First of all: check for active disciplines and hangup them. + */ + do { + if (busy) { + current->counter = 0; + schedule(); + } + + busy = 0; + spin_lock_bh(&ks_ctrl_lock); + for (i = 0; i < maxdev; i++) { + struct kiss_ctrl *ksc = kiss_ctrls[i]; + if (ksc && ksc->ctrl.tty) { + busy++; + tty_hangup(ksc->ctrl.tty); + } + } + spin_unlock_bh(&ks_ctrl_lock); + } while (busy && jiffies - start < 1*HZ); + + busy = 0; + for (i = 0; i < maxdev; i++) { + struct kiss_ctrl *ksc = kiss_ctrls[i]; + if (ksc) { + unregister_netdev(&ksc->dev); + if (ksc->ctrl.tty) { + printk("%s: tty discipline is still running\n", ksc->dev.name); + /* Pin module forever */ + MOD_INC_USE_COUNT; + busy++; + continue; + } + kiss_free_bufs(&ksc->ctrl); + kfree(ksc); + kiss_ctrls[i] = NULL; + } + } + if (!busy) { + kfree(kiss_ctrls); + kiss_ctrls = NULL; + } + } + if ((i = tty_register_ldisc(N_KISS, NULL))) + { + printk("KISS: can't unregister line discipline (err = %d)\n", i); + } +} + +MODULE_PARM(maxdev, "i"); +MODULE_AUTHOR("Matthias Welwarsky <dg2fef@afthd.tu-darmstadt.de>"); +MODULE_DESCRIPTION("KISS driver for AX.25"); +module_init(kiss_init_driver); +module_exit(kiss_exit_driver); diff --git a/drivers/net/hamradio/kiss.h b/drivers/net/hamradio/kiss.h new file mode 100644 index 000000000..f4df5720d --- /dev/null +++ b/drivers/net/hamradio/kiss.h @@ -0,0 +1,90 @@ +/* + * kiss.h Define the KISS device driver interface and constants. + * + * NOTE: THIS FILE WILL BE MOVED TO THE LINUX INCLUDE DIRECTORY + * AS SOON AS POSSIBLE! + * + * Version: @(#)kiss.h 1.2.0 03/28/93 + * + * Fixes: + * Alan Cox : Added slip mtu field. + * Matt Dillon : Printable slip (borrowed from net2e) + * Alan Cox : Added SL_SLIP_LOTS + * Dmitry Gorodchanin : A lot of changes in the 'struct slip' + * Dmitry Gorodchanin : Added CSLIP statistics. + * Stanislav Voronyi : Make line checking as created by + * Igor Chechik, RELCOM Corp. + * Craig Schlenter : Fixed #define bug that caused + * CSLIP telnets to hang in 1.3.61-6 + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + */ +#ifndef _LINUX_KISS_H +#define _LINUX_KISS_H + +#include <linux/config.h> +#include <linux/netdevice.h> + +#define KISS_NRUNIT 16 +#define KISS_MTU (256+MAX_HEADER) + +/* KISS protocol characters. */ +#define END 0300 /* indicates end of frame */ +#define ESC 0333 /* indicates byte stuffing */ +#define ESC_END 0334 /* ESC ESC_END means END 'data' */ +#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ + +typedef enum { + KISS_MODE_NORMAL, + KISS_MODE_ADAPTIVE, + KISS_MODE_FLEX, + KISS_MODE_SMACK, + KISS_MODE_COMPAT, + KISS_MODE_TNN_TOKENRING +} kiss_crcmode_t; + +typedef enum { + KISS_PARAM_TXDELAY=1, + KISS_PARAM_PPERS, + KISS_PARAM_SLOT, + KISS_PARAM_TXTAIL, + KISS_PARAM_DUPLEX +} kiss_command_t; + +struct kiss { + struct tty_struct *tty; /* ptr to TTY structure */ + struct net_device *dev; /* easy for intr handling */ + + spinlock_t lock; + + /* These are pointers to the malloc()ed frame buffers. */ + unsigned char *rbuff; /* receiver buffer */ + int rcount; /* received chars counter */ + unsigned char *xbuff; /* transmitter buffer */ + unsigned char *xhead; /* pointer to next byte to XMIT */ + int xleft; /* bytes left in XMIT queue */ + int mtu; /* Our mtu (to spot changes!) */ + int buffsize; /* Max buffers sizes */ + + struct { + int in_use:1; + int escape:1; + int error:1; + } flags; + + kiss_crcmode_t mode; /* KISS mode */ + + kdev_t line; + pid_t pid; + + struct net_device_stats stats; /* statistics */ + struct ax25_dev ax25dev; +}; + +typedef struct kiss_ctrl { + struct kiss ctrl; /* KISS things */ + struct net_device dev; /* the device */ +} kiss_ctrl_t; + + +#endif /* _LINUX_KISS.H */ diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c deleted file mode 100644 index 3ec09be5a..000000000 --- a/drivers/net/hamradio/mkiss.c +++ /dev/null @@ -1,1016 +0,0 @@ -/* - * MKISS Driver - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * This module implements the AX.25 protocol for kernel-based - * devices like TTYs. It interfaces between a raw TTY, and the - * kernel's AX.25 protocol layers, just like slip.c. - * AX.25 needs to be separated from slip.c while slip.c is no - * longer a static kernel device since it is a module. - * This method clears the way to implement other kiss protocols - * like mkiss smack g8bpq ..... so far only mkiss is implemented. - * - * Hans Alblas <hans@esrac.ele.tue.nl> - * - * History - * Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15. - * Matthias (DG2FEF) Added support for FlexNet CRC (on special request) - * Fixed bug in ax25_close(): dev_lock_wait() was - * called twice, causing a deadlock. - */ - -#include <linux/config.h> -#include <linux/module.h> -#include <asm/system.h> -#include <asm/segment.h> -#include <asm/bitops.h> -#include <asm/uaccess.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/in.h> -#include <linux/inet.h> -#include <linux/tty.h> -#include <linux/errno.h> -#include <linux/netdevice.h> -#include <linux/major.h> -#include <linux/init.h> -#include <linux/rtnetlink.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/if_arp.h> - -#include <net/ax25.h> - -#include "mkiss.h" - -#ifdef CONFIG_INET -#include <linux/ip.h> -#include <linux/tcp.h> -#endif - -static const char banner[] __initdata = KERN_INFO "mkiss: AX.25 Multikiss, Hans Albas PE1AYX\n"; - -#define NR_MKISS 4 -#define MKISS_SERIAL_TYPE_NORMAL 1 - -struct mkiss_channel { - int magic; /* magic word */ - int init; /* channel exists? */ - struct tty_struct *tty; /* link to tty control structure */ -}; - -typedef struct ax25_ctrl { - struct ax_disp ctrl; /* */ - struct net_device dev; /* the device */ -} ax25_ctrl_t; - -static ax25_ctrl_t **ax25_ctrls; - -int ax25_maxdev = AX25_MAXDEV; /* Can be overridden with insmod! */ - -static struct tty_ldisc ax_ldisc; - -static int ax25_init(struct net_device *); -static int kiss_esc(unsigned char *, unsigned char *, int); -static int kiss_esc_crc(unsigned char *, unsigned char *, unsigned short, int); -static void kiss_unesc(struct ax_disp *, unsigned char); - -/*---------------------------------------------------------------------------*/ - -static const unsigned short Crc_flex_table[] = { - 0x0f87, 0x1e0e, 0x2c95, 0x3d1c, 0x49a3, 0x582a, 0x6ab1, 0x7b38, - 0x83cf, 0x9246, 0xa0dd, 0xb154, 0xc5eb, 0xd462, 0xe6f9, 0xf770, - 0x1f06, 0x0e8f, 0x3c14, 0x2d9d, 0x5922, 0x48ab, 0x7a30, 0x6bb9, - 0x934e, 0x82c7, 0xb05c, 0xa1d5, 0xd56a, 0xc4e3, 0xf678, 0xe7f1, - 0x2e85, 0x3f0c, 0x0d97, 0x1c1e, 0x68a1, 0x7928, 0x4bb3, 0x5a3a, - 0xa2cd, 0xb344, 0x81df, 0x9056, 0xe4e9, 0xf560, 0xc7fb, 0xd672, - 0x3e04, 0x2f8d, 0x1d16, 0x0c9f, 0x7820, 0x69a9, 0x5b32, 0x4abb, - 0xb24c, 0xa3c5, 0x915e, 0x80d7, 0xf468, 0xe5e1, 0xd77a, 0xc6f3, - 0x4d83, 0x5c0a, 0x6e91, 0x7f18, 0x0ba7, 0x1a2e, 0x28b5, 0x393c, - 0xc1cb, 0xd042, 0xe2d9, 0xf350, 0x87ef, 0x9666, 0xa4fd, 0xb574, - 0x5d02, 0x4c8b, 0x7e10, 0x6f99, 0x1b26, 0x0aaf, 0x3834, 0x29bd, - 0xd14a, 0xc0c3, 0xf258, 0xe3d1, 0x976e, 0x86e7, 0xb47c, 0xa5f5, - 0x6c81, 0x7d08, 0x4f93, 0x5e1a, 0x2aa5, 0x3b2c, 0x09b7, 0x183e, - 0xe0c9, 0xf140, 0xc3db, 0xd252, 0xa6ed, 0xb764, 0x85ff, 0x9476, - 0x7c00, 0x6d89, 0x5f12, 0x4e9b, 0x3a24, 0x2bad, 0x1936, 0x08bf, - 0xf048, 0xe1c1, 0xd35a, 0xc2d3, 0xb66c, 0xa7e5, 0x957e, 0x84f7, - 0x8b8f, 0x9a06, 0xa89d, 0xb914, 0xcdab, 0xdc22, 0xeeb9, 0xff30, - 0x07c7, 0x164e, 0x24d5, 0x355c, 0x41e3, 0x506a, 0x62f1, 0x7378, - 0x9b0e, 0x8a87, 0xb81c, 0xa995, 0xdd2a, 0xcca3, 0xfe38, 0xefb1, - 0x1746, 0x06cf, 0x3454, 0x25dd, 0x5162, 0x40eb, 0x7270, 0x63f9, - 0xaa8d, 0xbb04, 0x899f, 0x9816, 0xeca9, 0xfd20, 0xcfbb, 0xde32, - 0x26c5, 0x374c, 0x05d7, 0x145e, 0x60e1, 0x7168, 0x43f3, 0x527a, - 0xba0c, 0xab85, 0x991e, 0x8897, 0xfc28, 0xeda1, 0xdf3a, 0xceb3, - 0x3644, 0x27cd, 0x1556, 0x04df, 0x7060, 0x61e9, 0x5372, 0x42fb, - 0xc98b, 0xd802, 0xea99, 0xfb10, 0x8faf, 0x9e26, 0xacbd, 0xbd34, - 0x45c3, 0x544a, 0x66d1, 0x7758, 0x03e7, 0x126e, 0x20f5, 0x317c, - 0xd90a, 0xc883, 0xfa18, 0xeb91, 0x9f2e, 0x8ea7, 0xbc3c, 0xadb5, - 0x5542, 0x44cb, 0x7650, 0x67d9, 0x1366, 0x02ef, 0x3074, 0x21fd, - 0xe889, 0xf900, 0xcb9b, 0xda12, 0xaead, 0xbf24, 0x8dbf, 0x9c36, - 0x64c1, 0x7548, 0x47d3, 0x565a, 0x22e5, 0x336c, 0x01f7, 0x107e, - 0xf808, 0xe981, 0xdb1a, 0xca93, 0xbe2c, 0xafa5, 0x9d3e, 0x8cb7, - 0x7440, 0x65c9, 0x5752, 0x46db, 0x3264, 0x23ed, 0x1176, 0x00ff -}; - -/*---------------------------------------------------------------------------*/ - -static unsigned short calc_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - while (size--) - crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - return crc; -} - -/*---------------------------------------------------------------------------*/ - -static int check_crc_flex(unsigned char *cp, int size) -{ - unsigned short crc = 0xffff; - - if (size < 3) - return -1; - - while (size--) - crc = (crc << 8) ^ Crc_flex_table[((crc >> 8) ^ *cp++) & 0xff]; - - if ((crc & 0xffff) != 0x7070) - return -1; - - return 0; -} - -/*---------------------------------------------------------------------------*/ - -/* Find a free channel, and link in this `tty' line. */ -static inline struct ax_disp *ax_alloc(void) -{ - ax25_ctrl_t *axp=NULL; - int i; - - for (i = 0; i < ax25_maxdev; i++) { - axp = ax25_ctrls[i]; - - /* Not allocated ? */ - if (axp == NULL) - break; - - /* Not in use ? */ - if (!test_and_set_bit(AXF_INUSE, &axp->ctrl.flags)) - break; - } - - /* Sorry, too many, all slots in use */ - if (i >= ax25_maxdev) - return NULL; - - /* If no channels are available, allocate one */ - if (axp == NULL && (ax25_ctrls[i] = kmalloc(sizeof(ax25_ctrl_t), GFP_KERNEL)) != NULL) { - axp = ax25_ctrls[i]; - memset(axp, 0, sizeof(ax25_ctrl_t)); - - /* Initialize channel control data */ - set_bit(AXF_INUSE, &axp->ctrl.flags); - sprintf(axp->dev.name, "ax%d", i++); - axp->ctrl.tty = NULL; - axp->dev.base_addr = i; - axp->dev.priv = (void *)&axp->ctrl; - axp->dev.next = NULL; - axp->dev.init = ax25_init; - } - - if (axp != NULL) { - /* - * register device so that it can be ifconfig'ed - * ax25_init() will be called as a side-effect - * SIDE-EFFECT WARNING: ax25_init() CLEARS axp->ctrl ! - */ - if (register_netdev(&axp->dev) == 0) { - /* (Re-)Set the INUSE bit. Very Important! */ - set_bit(AXF_INUSE, &axp->ctrl.flags); - axp->ctrl.dev = &axp->dev; - axp->dev.priv = (void *) &axp->ctrl; - - return &axp->ctrl; - } else { - clear_bit(AXF_INUSE,&axp->ctrl.flags); - printk(KERN_ERR "mkiss: ax_alloc() - register_netdev() failure.\n"); - } - } - - return NULL; -} - -/* Free an AX25 channel. */ -static inline void ax_free(struct ax_disp *ax) -{ - /* Free all AX25 frame buffers. */ - if (ax->rbuff) - kfree(ax->rbuff); - ax->rbuff = NULL; - if (ax->xbuff) - kfree(ax->xbuff); - ax->xbuff = NULL; - if (!test_and_clear_bit(AXF_INUSE, &ax->flags)) - printk(KERN_ERR "mkiss: %s: ax_free for already free unit.\n", ax->dev->name); -} - -static void ax_changedmtu(struct ax_disp *ax) -{ - struct net_device *dev = ax->dev; - unsigned char *xbuff, *rbuff, *oxbuff, *orbuff; - int len; - unsigned long flags; - - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - xbuff = kmalloc(len + 4, GFP_ATOMIC); - rbuff = kmalloc(len + 4, GFP_ATOMIC); - - if (xbuff == NULL || rbuff == NULL) { - printk(KERN_ERR "mkiss: %s: unable to grow ax25 buffers, MTU change cancelled.\n", - ax->dev->name); - dev->mtu = ax->mtu; - if (xbuff != NULL) - kfree(xbuff); - if (rbuff != NULL) - kfree(rbuff); - return; - } - - save_flags(flags); - cli(); - - oxbuff = ax->xbuff; - ax->xbuff = xbuff; - orbuff = ax->rbuff; - ax->rbuff = rbuff; - - if (ax->xleft) { - if (ax->xleft <= len) { - memcpy(ax->xbuff, ax->xhead, ax->xleft); - } else { - ax->xleft = 0; - ax->tx_dropped++; - } - } - - ax->xhead = ax->xbuff; - - if (ax->rcount) { - if (ax->rcount <= len) { - memcpy(ax->rbuff, orbuff, ax->rcount); - } else { - ax->rcount = 0; - ax->rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } - } - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - - restore_flags(flags); - - if (oxbuff != NULL) - kfree(oxbuff); - if (orbuff != NULL) - kfree(orbuff); -} - - -/* Set the "sending" flag. This must be atomic. */ -static inline void ax_lock(struct ax_disp *ax) -{ - netif_stop_queue(ax->dev); -} - - -/* Clear the "sending" flag. This must be atomic. */ -static inline void ax_unlock(struct ax_disp *ax) -{ - netif_start_queue(ax->dev); -} - -/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */ -static void ax_bump(struct ax_disp *ax) -{ - struct ax_disp *tmp_ax; - struct sk_buff *skb; - struct mkiss_channel *mkiss; - int count; - - tmp_ax = ax; - - if (ax->rbuff[0] > 0x0f) { - if (ax->mkiss != NULL) { - mkiss= ax->mkiss->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) - tmp_ax = ax->mkiss; - } else if (ax->rbuff[0] & 0x20) { - ax->crcmode = CRC_MODE_FLEX; - if (check_crc_flex(ax->rbuff, ax->rcount) < 0) { - ax->rx_errors++; - return; - } - ax->rcount -= 2; - } - } - - count = ax->rcount; - - if ((skb = dev_alloc_skb(count)) == NULL) { - printk(KERN_ERR "mkiss: %s: memory squeeze, dropping packet.\n", ax->dev->name); - ax->rx_dropped++; - return; - } - - skb->dev = tmp_ax->dev; - memcpy(skb_put(skb,count), ax->rbuff, count); - skb->mac.raw = skb->data; - skb->protocol = htons(ETH_P_AX25); - netif_rx(skb); - tmp_ax->rx_packets++; -} - -/* Encapsulate one AX.25 packet and stuff into a TTY queue. */ -static void ax_encaps(struct ax_disp *ax, unsigned char *icp, int len) -{ - unsigned char *p; - int actual, count; - struct mkiss_channel *mkiss = ax->tty->driver_data; - - if (ax->mtu != ax->dev->mtu + 73) /* Someone has been ifconfigging */ - ax_changedmtu(ax); - - if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */ - len = ax->mtu; - printk(KERN_ERR "mkiss: %s: truncating oversized transmit packet!\n", ax->dev->name); - ax->tx_dropped++; - ax_unlock(ax); - return; - } - - p = icp; - - if (mkiss->magic != MKISS_DRIVER_MAGIC) { - switch (ax->crcmode) { - unsigned short crc; - - case CRC_MODE_FLEX: - *p |= 0x20; - crc = calc_crc_flex(p, len); - count = kiss_esc_crc(p, (unsigned char *)ax->xbuff, crc, len+2); - break; - - default: - count = kiss_esc(p, (unsigned char *)ax->xbuff, len); - break; - } - ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, count); - ax->tx_packets++; - ax->dev->trans_start = jiffies; - ax->xleft = count - actual; - ax->xhead = ax->xbuff + actual; - } else { - count = kiss_esc(p, (unsigned char *) ax->mkiss->xbuff, len); - ax->mkiss->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP); - actual = ax->mkiss->tty->driver.write(ax->mkiss->tty, 0, ax->mkiss->xbuff, count); - ax->tx_packets++; - ax->mkiss->dev->trans_start = jiffies; - ax->mkiss->xleft = count - actual; - ax->mkiss->xhead = ax->mkiss->xbuff + actual; - } -} - -/* - * Called by the driver when there's room for more data. If we have - * more packets to send, we send them here. - */ -static void ax25_write_wakeup(struct tty_struct *tty) -{ - int actual; - struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - struct mkiss_channel *mkiss; - - /* First make sure we're connected. */ - if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev)) - return; - if (ax->xleft <= 0) { - /* Now serial buffer is almost free & we can start - * transmission of another packet - */ - tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - - if (ax->mkiss != NULL) { - mkiss= ax->mkiss->tty->driver_data; - if (mkiss->magic == MKISS_DRIVER_MAGIC) - ax_unlock(ax->mkiss); - } - - netif_wake_queue(ax->dev); - return; - } - - actual = tty->driver.write(tty, 0, ax->xhead, ax->xleft); - ax->xleft -= actual; - ax->xhead += actual; -} - -/* Encapsulate an AX.25 packet and kick it into a TTY queue. */ -static int ax_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct ax_disp *ax = (struct ax_disp *) dev->priv; - struct mkiss_channel *mkiss = ax->tty->driver_data; - struct ax_disp *tmp_ax; - - tmp_ax = NULL; - - if (mkiss->magic == MKISS_DRIVER_MAGIC) { - if (skb->data[0] < 0x10) - skb->data[0] = skb->data[0] + 0x10; - tmp_ax = ax->mkiss; - } - - if (!netif_running(dev)) { - printk(KERN_ERR "mkiss: %s: xmit call when iface is down\n", dev->name); - return 1; - } - - if (tmp_ax != NULL) - if (netif_queue_stopped(tmp_ax->dev)) - return 1; - - if (tmp_ax != NULL) - if (netif_queue_stopped(dev)) { - printk(KERN_ERR "mkiss: dev busy while serial dev is free\n"); - ax_unlock(ax); - } - - if (netif_queue_stopped(dev)) { - /* - * May be we must check transmitter timeout here ? - * 14 Oct 1994 Dmitry Gorodchanin. - */ - if (jiffies - dev->trans_start < 20 * HZ) { - /* 20 sec timeout not reached */ - return 1; - } - - printk(KERN_ERR "mkiss: %s: transmit timed out, %s?\n", dev->name, - (ax->tty->driver.chars_in_buffer(ax->tty) || ax->xleft) ? - "bad line quality" : "driver error"); - - ax->xleft = 0; - ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - ax_unlock(ax); - } - - /* We were not busy, so we are now... :-) */ - if (skb != NULL) { - ax_lock(ax); - if (tmp_ax != NULL) - ax_lock(tmp_ax); - ax_encaps(ax, skb->data, skb->len); - kfree_skb(skb); - } - - return 0; -} - -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - -/* Return the frame type ID */ -static int ax_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, - void *daddr, void *saddr, unsigned len) -{ -#ifdef CONFIG_INET - if (type != htons(ETH_P_AX25)) - return ax25_encapsulate(skb, dev, type, daddr, saddr, len); -#endif - return 0; -} - - -static int ax_rebuild_header(struct sk_buff *skb) -{ -#ifdef CONFIG_INET - return ax25_rebuild_header(skb); -#else - return 0; -#endif -} - -#endif /* CONFIG_{AX25,AX25_MODULE} */ - -/* Open the low-level part of the AX25 channel. Easy! */ -static int ax_open(struct net_device *dev) -{ - struct ax_disp *ax = (struct ax_disp *) dev->priv; - unsigned long len; - - if (ax->tty == NULL) - return -ENODEV; - - /* - * Allocate the frame buffers: - * - * rbuff Receive buffer. - * xbuff Transmit buffer. - */ - len = dev->mtu * 2; - - /* - * allow for arrival of larger UDP packets, even if we say not to - * also fixes a bug in which SunOS sends 512-byte packets even with - * an MSS of 128 - */ - if (len < 576 * 2) - len = 576 * 2; - - if ((ax->rbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto norbuff; - - if ((ax->xbuff = kmalloc(len + 4, GFP_KERNEL)) == NULL) - goto noxbuff; - - ax->mtu = dev->mtu + 73; - ax->buffsize = len; - ax->rcount = 0; - ax->xleft = 0; - - ax->flags &= (1 << AXF_INUSE); /* Clear ESCAPE & ERROR flags */ - - netif_start_queue(dev); - return 0; - -noxbuff: - kfree(ax->rbuff); - -norbuff: - return -ENOMEM; -} - - -/* Close the low-level part of the AX25 channel. Easy! */ -static int ax_close(struct net_device *dev) -{ - struct ax_disp *ax = (struct ax_disp *) dev->priv; - - if (ax->tty == NULL) - return -EBUSY; - - ax->tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP); - - netif_stop_queue(dev); - - return 0; -} - -static int ax25_receive_room(struct tty_struct *tty) -{ - return 65536; /* We can handle an infinite amount of data. :-) */ -} - -/* - * Handle the 'receiver data ready' interrupt. - * This function is called by the 'tty_io' module in the kernel when - * a block of data has been received, which can now be decapsulated - * and sent on to the AX.25 layer for further processing. - */ -static void ax25_receive_buf(struct tty_struct *tty, const unsigned char *cp, char *fp, int count) -{ - struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - - if (ax == NULL || ax->magic != AX25_MAGIC || !netif_running(ax->dev)) - return; - - /* - * Argh! mtu change time! - costs us the packet part received - * at the change - */ - if (ax->mtu != ax->dev->mtu + 73) - ax_changedmtu(ax); - - /* Read the characters out of the buffer */ - while (count--) { - if (fp != NULL && *fp++) { - if (!test_and_set_bit(AXF_ERROR, &ax->flags)) - ax->rx_errors++; - cp++; - continue; - } - - kiss_unesc(ax, *cp++); - } -} - -static int ax25_open(struct tty_struct *tty) -{ - struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - struct ax_disp *tmp_ax; - struct mkiss_channel *mkiss; - int err, cnt; - - /* First make sure we're not already connected. */ - if (ax && ax->magic == AX25_MAGIC) - return -EEXIST; - - /* OK. Find a free AX25 channel to use. */ - if ((ax = ax_alloc()) == NULL) - return -ENFILE; - - ax->tty = tty; - tty->disc_data = ax; - - ax->mkiss = NULL; - tmp_ax = NULL; - - if (tty->driver.flush_buffer) - tty->driver.flush_buffer(tty); - if (tty->ldisc.flush_buffer) - tty->ldisc.flush_buffer(tty); - - /* Restore default settings */ - ax->dev->type = ARPHRD_AX25; - - /* Perform the low-level AX25 initialization. */ - if ((err = ax_open(ax->dev))) - return err; - - mkiss = ax->tty->driver_data; - - if (mkiss->magic == MKISS_DRIVER_MAGIC) { - for (cnt = 1; cnt < ax25_maxdev; cnt++) { - if (ax25_ctrls[cnt]) { - if (netif_running(&ax25_ctrls[cnt]->dev)) { - if (ax == &ax25_ctrls[cnt]->ctrl) { - cnt--; - tmp_ax = &ax25_ctrls[cnt]->ctrl; - break; - } - } - } - } - } - - if (tmp_ax != NULL) { - ax->mkiss = tmp_ax; - tmp_ax->mkiss = ax; - } - - MOD_INC_USE_COUNT; - - /* Done. We have linked the TTY line to a channel. */ - return ax->dev->base_addr; -} - -static void ax25_close(struct tty_struct *tty) -{ - struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - - /* First make sure we're connected. */ - if (ax == NULL || ax->magic != AX25_MAGIC) - return; - - unregister_netdev(ax->dev); - - tty->disc_data = 0; - ax->tty = NULL; - - ax_free(ax); - MOD_DEC_USE_COUNT; -} - - -static struct net_device_stats *ax_get_stats(struct net_device *dev) -{ - static struct net_device_stats stats; - struct ax_disp *ax = (struct ax_disp *) dev->priv; - - memset(&stats, 0, sizeof(struct net_device_stats)); - - stats.rx_packets = ax->rx_packets; - stats.tx_packets = ax->tx_packets; - stats.rx_dropped = ax->rx_dropped; - stats.tx_dropped = ax->tx_dropped; - stats.tx_errors = ax->tx_errors; - stats.rx_errors = ax->rx_errors; - stats.rx_over_errors = ax->rx_over_errors; - - return &stats; -} - - -/************************************************************************ - * STANDARD ENCAPSULATION * - ************************************************************************/ - -static int kiss_esc(unsigned char *s, unsigned char *d, int len) -{ - unsigned char *ptr = d; - unsigned char c; - - /* - * Send an initial END character to flush out any - * data that may have accumulated in the receiver - * due to line noise. - */ - - *ptr++ = END; - - while (len-- > 0) { - switch (c = *s++) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - - *ptr++ = END; - - return ptr - d; -} - -/* - * MW: - * OK its ugly, but tell me a better solution without copying the - * packet to a temporary buffer :-) - */ -static int kiss_esc_crc(unsigned char *s, unsigned char *d, unsigned short crc, int len) -{ - unsigned char *ptr = d; - unsigned char c=0; - - *ptr++ = END; - while (len > 0) { - if (len > 2) - c = *s++; - else if (len > 1) - c = crc >> 8; - else if (len > 0) - c = crc & 0xff; - - len--; - - switch (c) { - case END: - *ptr++ = ESC; - *ptr++ = ESC_END; - break; - case ESC: - *ptr++ = ESC; - *ptr++ = ESC_ESC; - break; - default: - *ptr++ = c; - break; - } - } - *ptr++ = END; - return ptr - d; -} - -static void kiss_unesc(struct ax_disp *ax, unsigned char s) -{ - switch (s) { - case END: - /* drop keeptest bit = VSV */ - if (test_bit(AXF_KEEPTEST, &ax->flags)) - clear_bit(AXF_KEEPTEST, &ax->flags); - - if (!test_and_clear_bit(AXF_ERROR, &ax->flags) && (ax->rcount > 2)) - ax_bump(ax); - - clear_bit(AXF_ESCAPE, &ax->flags); - ax->rcount = 0; - return; - - case ESC: - set_bit(AXF_ESCAPE, &ax->flags); - return; - case ESC_ESC: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = ESC; - break; - case ESC_END: - if (test_and_clear_bit(AXF_ESCAPE, &ax->flags)) - s = END; - break; - } - - if (!test_bit(AXF_ERROR, &ax->flags)) { - if (ax->rcount < ax->buffsize) { - ax->rbuff[ax->rcount++] = s; - return; - } - - ax->rx_over_errors++; - set_bit(AXF_ERROR, &ax->flags); - } -} - - -static int ax_set_mac_address(struct net_device *dev, void *addr) -{ - if (copy_from_user(dev->dev_addr, addr, AX25_ADDR_LEN)) - return -EFAULT; - return 0; -} - -static int ax_set_dev_mac_address(struct net_device *dev, void *addr) -{ - struct sockaddr *sa = addr; - - memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); - - return 0; -} - - -/* Perform I/O control on an active ax25 channel. */ -static int ax25_disp_ioctl(struct tty_struct *tty, void *file, int cmd, void *arg) -{ - struct ax_disp *ax = (struct ax_disp *) tty->disc_data; - unsigned int tmp; - - /* First make sure we're connected. */ - if (ax == NULL || ax->magic != AX25_MAGIC) - return -EINVAL; - - switch (cmd) { - case SIOCGIFNAME: - if (copy_to_user(arg, ax->dev->name, strlen(ax->dev->name) + 1)) - return -EFAULT; - return 0; - - case SIOCGIFENCAP: - return put_user(4, (int *)arg); - - case SIOCSIFENCAP: - if (get_user(tmp, (int *)arg)) - return -EFAULT; - ax->mode = tmp; - ax->dev->addr_len = AX25_ADDR_LEN; /* sizeof an AX.25 addr */ - ax->dev->hard_header_len = AX25_KISS_HEADER_LEN + AX25_MAX_HEADER_LEN + 3; - ax->dev->type = ARPHRD_AX25; - return 0; - - case SIOCSIFHWADDR: - return ax_set_mac_address(ax->dev, arg); - - default: - return -ENOIOCTLCMD; - } -} - -static int ax_open_dev(struct net_device *dev) -{ - struct ax_disp *ax = (struct ax_disp *) dev->priv; - - if (ax->tty == NULL) - return -ENODEV; - - return 0; -} - - -/* Initialize the driver. Called by network startup. */ -static int ax25_init(struct net_device *dev) -{ - struct ax_disp *ax = (struct ax_disp *) dev->priv; - - static char ax25_bcast[AX25_ADDR_LEN] = - {'Q'<<1,'S'<<1,'T'<<1,' '<<1,' '<<1,' '<<1,'0'<<1}; - static char ax25_test[AX25_ADDR_LEN] = - {'L'<<1,'I'<<1,'N'<<1,'U'<<1,'X'<<1,' '<<1,'1'<<1}; - - if (ax == NULL) /* Allocation failed ?? */ - return -ENODEV; - - /* Set up the "AX25 Control Block". (And clear statistics) */ - memset(ax, 0, sizeof (struct ax_disp)); - ax->magic = AX25_MAGIC; - ax->dev = dev; - - /* Finish setting up the DEVICE info. */ - dev->mtu = AX_MTU; - dev->hard_start_xmit = ax_xmit; - dev->open = ax_open_dev; - dev->stop = ax_close; - dev->get_stats = ax_get_stats; - dev->set_mac_address = ax_set_dev_mac_address; - dev->hard_header_len = 0; - dev->addr_len = 0; - dev->type = ARPHRD_AX25; - dev->tx_queue_len = 10; - dev->hard_header = ax_header; - dev->rebuild_header = ax_rebuild_header; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_test, AX25_ADDR_LEN); - - dev_init_buffers(dev); - - /* New-style flags. */ - dev->flags = 0; - - return 0; -} - - -/* ******************************************************************** */ -/* * Init MKISS driver * */ -/* ******************************************************************** */ - -static int __init mkiss_init_driver(void) -{ - int status; - - printk(banner); - - if (ax25_maxdev < 4) - ax25_maxdev = 4; /* Sanity */ - - if ((ax25_ctrls = kmalloc(sizeof(void *) * ax25_maxdev, GFP_KERNEL)) == NULL) { - printk(KERN_ERR "mkiss: Can't allocate ax25_ctrls[] array!\n"); - return -ENOMEM; - } - - /* Clear the pointer array, we allocate devices when we need them */ - memset(ax25_ctrls, 0, sizeof(void*) * ax25_maxdev); /* Pointers */ - - /* Fill in our line protocol discipline, and register it */ - ax_ldisc.magic = TTY_LDISC_MAGIC; - ax_ldisc.name = "mkiss"; - ax_ldisc.open = ax25_open; - ax_ldisc.close = ax25_close; - ax_ldisc.ioctl = (int (*)(struct tty_struct *, struct file *, - unsigned int, unsigned long))ax25_disp_ioctl; - ax_ldisc.receive_buf = ax25_receive_buf; - ax_ldisc.receive_room = ax25_receive_room; - ax_ldisc.write_wakeup = ax25_write_wakeup; - - if ((status = tty_register_ldisc(N_AX25, &ax_ldisc)) != 0) { - printk(KERN_ERR "mkiss: can't register line discipline (err = %d)\n", status); - kfree(ax25_ctrls); - } - return status; -} - -static void __exit mkiss_exit_driver(void) -{ - int i; - - for (i = 0; i < ax25_maxdev; i++) { - if (ax25_ctrls[i]) { - /* - * VSV = if dev->start==0, then device - * unregistered while close proc. - */ - if (netif_running(&ax25_ctrls[i]->dev)) - unregister_netdev(&ax25_ctrls[i]->dev); - kfree(ax25_ctrls[i]); - } - } - - kfree(ax25_ctrls); - ax25_ctrls = NULL; - - if ((i = tty_register_ldisc(N_AX25, NULL))) - printk(KERN_ERR "mkiss: can't unregister line discipline (err = %d)\n", i); -} - -MODULE_AUTHOR("Hans Albas PE1AYX <hans@esrac.ele.tue.nl>"); -MODULE_DESCRIPTION("KISS driver for AX.25 over TTYs"); -MODULE_PARM(ax25_maxdev, "i"); -MODULE_PARM_DESC(ax25_maxdev, "number of MKISS devices"); - -module_init(mkiss_init_driver); -module_exit(mkiss_exit_driver); - diff --git a/drivers/net/hamradio/mkiss.h b/drivers/net/hamradio/mkiss.h deleted file mode 100644 index 4a3d700cb..000000000 --- a/drivers/net/hamradio/mkiss.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** - * Defines for the Multi-KISS driver. - ****************************************************************************/ - -#define AX25_MAXDEV 16 /* MAX number of AX25 channels; - This can be overridden with - insmod -oax25_maxdev=nnn */ -#define AX_MTU 236 - -/* SLIP/KISS protocol characters. */ -#define END 0300 /* indicates end of frame */ -#define ESC 0333 /* indicates byte stuffing */ -#define ESC_END 0334 /* ESC ESC_END means END 'data' */ -#define ESC_ESC 0335 /* ESC ESC_ESC means ESC 'data' */ - -struct ax_disp { - int magic; - - /* Various fields. */ - struct tty_struct *tty; /* ptr to TTY structure */ - struct net_device *dev; /* easy for intr handling */ - struct ax_disp *mkiss; /* mkiss txport if mkiss channel*/ - - /* These are pointers to the malloc()ed frame buffers. */ - unsigned char *rbuff; /* receiver buffer */ - int rcount; /* received chars counter */ - unsigned char *xbuff; /* transmitter buffer */ - unsigned char *xhead; /* pointer to next byte to XMIT */ - int xleft; /* bytes left in XMIT queue */ - - /* SLIP interface statistics. */ - unsigned long rx_packets; /* inbound frames counter */ - unsigned long tx_packets; /* outbound frames counter */ - unsigned long rx_errors; /* Parity, etc. errors */ - unsigned long tx_errors; /* Planned stuff */ - unsigned long rx_dropped; /* No memory for skb */ - unsigned long tx_dropped; /* When MTU change */ - unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */ - - /* Detailed SLIP statistics. */ - int mtu; /* Our mtu (to spot changes!) */ - int buffsize; /* Max buffers sizes */ - - - unsigned long flags; /* Flag values/ mode etc */ - /* long req'd: used by set_bit --RR */ -#define AXF_INUSE 0 /* Channel in use */ -#define AXF_ESCAPE 1 /* ESC received */ -#define AXF_ERROR 2 /* Parity, etc. error */ -#define AXF_KEEPTEST 3 /* Keepalive test flag */ -#define AXF_OUTWAIT 4 /* is outpacket was flag */ - - int mode; - int crcmode; /* MW: for FlexNet, SMACK etc. */ -#define CRC_MODE_NONE 0 -#define CRC_MODE_FLEX 1 -#define CRC_MODE_SMACK 2 -}; - -#define AX25_MAGIC 0x5316 -#define MKISS_DRIVER_MAGIC 1215 diff --git a/drivers/net/hamradio/pciscc4.c b/drivers/net/hamradio/pciscc4.c new file mode 100644 index 000000000..c167bebb7 --- /dev/null +++ b/drivers/net/hamradio/pciscc4.c @@ -0,0 +1,2494 @@ +/***************************************************************************** + * + * pciscc4.c This is the device driver for the PCISCC-4 card or any other + * board based on the Siemens PEB-20534H (DSCC-4) communication + * controller. The PCISCC-4 is a four-channel medium-speed (up + * to 10 respectively 52 Mbps/channel) synchronous serial + * interface controller with HDLC protocol processor and + * busmaster-DMA facilities. + * + * Info: http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 + * + * Authors: (c) 1999 Jens David <dg1kjd@afthd.tu-darmstadt.de> + * + * Policy: Please contact me before making structural changes. + * Before applying changes to the communication with + * the DSCC-4 please read: + * - Data Sheet 09/98 PEB-20534 Version 2.0 + * - Delta Sheet Chip Rev. 2.0-2.1 + * - Addendum/Corrections to Data Sheet 09/98 as of 01/99 + * - DSCC-4 Errata Sheet (Book?) 03/99 Chip Rev. 2.1 + * - Sample driver source code as of 07/27/99 + * All these documents are available from Infineon on + * request or from http://www.infineon.de/... . + * At least the current version of this beast likes to be + * treated _very_ carefully. If you don't do this, it crashes + * itself or the system. I have made comments on these common + * traps where appropriate. No, there isn't such thing as a + * "master reset". + * + * CVS: $Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 dg1kjd Exp $ + * + * Changelog: Please log any changes here. + * | 08/23/99 Initial version Jens + * | 08/25/99 Reworked buffer concept to use last-mode Jens + * | policy and implemented Siemens' workarounds + * | 08/27/99 Reworked transmitter to use internal timers Jens + * | for better resolution at txdelay/txtail + * | 09/01/99 Ioctl()s implemented Jens + * | 09/10/99 Descriptor chain reworked. RX hold condition Jens + * | can't occur any more. TX descriptors are not Jens + * | re-initialized after transmit any more. + * | 09/12/99 TX reworked. TX-Timeout added. Jens + * | 09/13/99 TX timeout fixed Jens + * | 10/09/99 Cosmetic fixes and comments added Jens + * | 10/16/99 Cosmetic stuff and non-module mode fixed Jens + * | 10/21/99 TX-skbs are not freed in xmit()-statemachine Jens + * | 10/25/99 Default configuration more sensible now Jens + * | 02/13/00 Converted to new driver interface Jens + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + *---------------------------------------------------------------------------- + * + * | Please note that the GPL allows you to use the driver, NOT the radio. | + * | In order to use the radio, you need a license from the communications | + * | authority of your country. | + * + *---------------------------------------------------------------------------- + * + ***************************************************************************** + * + * Concept of Operation + * -------------------- + * + * I. SCCs + * We use all SCC cores in HDLC mode. Asyncronous and BiSync operation is not + * supported and probably never will. We do not make use of the built-in + * LAPB/LAPD protocol processor features (Auto Mode). Moreover can't use + * automatic address recognition either because it is implemented in a way + * which allows only operation with fixed header sizes and address fields on + * static positions. Thus we use HDLC address mode 0. As far as the clock modes + * are concerned we make use of mode 0a (for DF9IC-like modems, RX and TX clock + * from pin header), 0b (G3RUH-"like", external RX clock, internal TX clock + * from BRG but unfornately without oversampling), 6b (for TCM-3105-like simple + * modems, using on-chip DPLL for RX clock recovery. Internal TX clock from BRG + * which can optionaly be provided on TxClk and/or RTS pins. No oversampling.) + * and 4 (external RX and TX clock like DF9IC, but with clock gapping + * function, see Data Book 09/98 pp. 141 ff). Channel coding is user-selectable + * on a per-channel basis. DF9IC-type modems like NRZ (conversion NRZ->NRZI + * done internally), TCM-3105-like modems are usually used with NRZI coding. + * Moreover manchester, FM0 and FM1 can be selected (untested). + * The internal SCC-DMAC interface seem to obey the KISS-concept. The drawback + * of this fact is, that the chip fills our data buffers in memory completely + * sequential. If at the end of a frame the SCC realizes, that the FCS failed, + * it does not "discard" the frame. That is, it requests an interrupt and + * uses up a packet buffer as if the frame was valid. The frame is, however, + * marked invalid, but of cause the host processor still needs to clean the + * mess up, which costs time. Now consider that every six ones in series on a + * empty channel will cause an interrupt and work to the handler. The only + * way around this is to gate the receive data with the DCD signal. Of cause + * the modem's DCD needs to be very fast to accomplish this. The standard-DCD + * on DF9IC-type modems currently isn't. As far as modem handshake is concerned + * we program the DCD input of each channel as general purpose input and read + * its state whenever L2 requests us to do so. TX handshake can be used in two + * modes I called "hard" and "soft". Hard mode is reserved for future + * (smart) modems which tell the controller when they are ready to transmit + * using the CTS (clear to send) signal. In soft mode we use each channel's + * internal timer to generate txdelay and txtail. The advantage of this concept + * is, that we have a resolution of one bit since the timers are clocked with + * the effective TxClk, which also allows us to probe the TX-bitrate in external + * clock modes (L2 likes this information). The SCC cores have some other + * goodies, as preample transmission, one insertion after 7 consecutive zeros + * and stuff like this which we make user selectable. + * + * II. DMA Controller and IRQs + * For maximum performance and least host processor load, the design of the + * DMA controller is descriptor orientated. For both, RX and TX channels + * descriptor "queues" are set up on device initialization. Each descriptor + * contains a link to its subsequent desriptor, a pointer to the buffer + * associated with it and the buffer's size. The buffer itself is _not_ part + * of the descriptor, but can be located anywhere else in address space. + * Thus, in TX case all we have to do when a packet to be sent arrives from + * L2, is painting out a descriptor (pointer to the sk_buf's data buffer, + * length of the frame and so on) and telling the DMAC to process it. We do + * not have to move the data around in memory. When the descriptor is finished + * (i.e. packet sent out completely or at least data completely in FIFO), the + * DMAC sets a flag (C) in the descriptor and issues an IRQ. We check the flag + * and if it is set, we can skb_free up the packet. Both descriptor queues (RX + * and TX) are organized circular with a user setable size and allocated + * statically at device initialization. As far as the RX queue ("ring") is + * concerned we also already allocate the sk_buffs associated with them. + * Whenever the DMAC processed a RX descriptor (thus "filled" the buffer + * associated with it) we release the buffer to L2 and allocate a new one. + * No copying. The structure of the RX descriptor chain never changes either. + * It stays the same since inititalization on device initialization and + * descriptor memory itself is only freed when the device destructor is called. + * The fact that both descriptor queues are kept statically has two advantages: + * It is save, because the DMAC can not "escape" due to a race condition and + * mess up our memory and it works around a hardware bug in the DSCC-4. + * A few words on linux mm: + * When a device driver allocates memory using functions like malloc() or + * alloc_skb(), the returned address pointers are pointers to virtual memory. + * In case of access to this memory, the MMU, as part of the CPU translates + * the virtual addresses to physical ones, which are e.g. used to drive the + * RAM address bus lines. If a PCI bus master accesses the same memory, it + * needs to know the right address vom _its_ point of view, the so-called + * "bus" address. On most architectures this is the same as the physical + * address. We use the funktion virt_to_bus() and bus_to_virt() to translate + * them. The descriptor structures contain both types, just to make the code + * faster and more readable. The symbol names for "bus"-pointers end on + * "addr", for example rx_desc_t.next --(virt-to-bus)--> rx_desc_t.nextptr. + * When we accessed "common" memory (i.e. descriptor or buffer memory) we + * issue a flush_cache_all() due to the fact that some architectures (not PC) + * don't keep memory caches consistent on DMAs. Where it isn't apropriate gcc + * will optimize it away for us. + * Another word on IRQ management: + * The DMAC is not only responsible for moving around network data from/to + * the SCC cores, but also maintains 10 so-called "interrupt queues" (IQs). + * These are intended to help speeding up operation and minimize load on the + * host CPU. There is the configuration queue (IQCFG) which is responsible + * for answers to DMAC configuration commands, the peripheral queue (IQPER), + * which cares about interrupt sources on the local bus, SSC (not SCC!) or GPP + * if enabled, and one TX and one RX queue per channel (IQRX and IQTX + * respectively), which are responsible for RX and TX paths and not only + * indicate DMAC exceptions (packet finished etc.) but also SCC exceptions + * (FIFO over/underrun, frame length exceeded etc.). Each element in the + * queues is a dword. The queues are "naturally" organized circular as well. + * Naturally means, that there is no such thing as a "next pointer" as in + * the frame descriptors, but that we tell the DMAC the length of each queue + * (user configurable in 32 dword-steps) and it does the wrap-around + * automagically. Whenever an element is added to a queue an IRQ is issued. + * The IRQ handler acks all interrupts by writing back the global status + * register (GSTAR) and starts processing ALL queues, independent of who + * caused the IRQ. + * + * III. General Purpose Port (GPP) + * The general purpose port is used for status LED connection. We support + * only 2 LEDs per port. These can be controlled with an ioctl(). We do not + * care about it, this ought to be done by a user-space daemon. The SSC port + * is not used. The LBI can be configured with the global settings and + * controlled by an own ioctl(). We don't care any more. + * + * IV. Configuration + * We divide configuration into global (i.e. concerning all ports, chipwide) + * and local. We have one template for each, chipcfg_default and devcfg_default + * which is hardcoded and never changes. On module load it is copied for each + * chip and each device respectively (chipctl_t.cfg and devctl_t.cfg). The + * silicon is initialized with these values only in chip_open() and + * device_open() and the structures themselves can only be changed when the + * corresponding interface (or all interfaces for global config) is down. + * Changes take effect when the interface is brought up the next time. + * + * V. Initialization + * When module_init is called, the PCI driver already took care of assigning + * two pieces of memory space and an IRQ to each board. On module load we do + * nothing else than building up our internal structures (devctl_t and + * chipctl_t), grabbing the interface names and registering them with the + * network subsystem. Chip_open() and device_open() are called only upon uping + * a device and perform IRQ grabbing, memory mapping and allocation and + * hardware initialization. + * + * VI. RX Handling + * When a frame is received completely, the C flag in the corresponding + * descriptor is set by the DSCC-4, an interrupt vector transfered to the + * channel's RX IQ, and the IRQ line asserted. The IRQ handler takes control + * of the system. The first step it performs is reading the global status + * register and writing it back, thus ack'ing the IRQ. Then it is analyzed + * bit-by-bit to find out where it originated from. The channel's RX IQ + * is examined and the function pciscc_isr_receiver() called for the + * corresponding port. This functions processes the rx descriptor queue + * starting with the element (devctl_t.dq_rx_next) following the last element + * processed the last time the function was called. All descriptors with the + * C-flag ("C"omplete) set are processed. And at the end the dq_rx_next pointer + * is updated to the next to last element processed. During "processing" at + * first two pieces of information are examined: The status field of the + * descriptor, mainly containing the length of the received frame and a flag + * telling us wether the frame reception was aborted or not (RA), which + * was written by the DMAC and the so-called Receive Data Section Status Byte + * (RSTA) which was appended to the end of the data buffer by the channel's + * SCC core. Both are checked and if they yield a valid frame and we success- + * fully allocated a new skb we remove the old one from the descriptor and + * hand it off to pciscc_rx_skb() which paints out some of the skb's members + * and fires it up to the MAC layer. The descriptor's fields are re-initialized + * anyway to prepare it for the next reception. + * After all complete descriptors were processed we must tell the DMAC that the + * last ready-to-fill descriptor (LRDA, Last Receive Descriptor Address) is the + * one pre-previous to the last one processed. In fact experiments show that + * normally this is not neccessary since we get an interrupt for _every_ + * received frame so we can re-prepare the descriptor then. This is just to + * prevent the DMAC from "running around" uncontrolled in the circular + * structure, eventually losing sync with the devctl_t.dq_rx_next pointer in + * case of _very_ high bus/interrupt latency on heavy system load conditions. + * + * VII. TX Handling + * We assume we are in half duplex mode with software txdelay since everything + * else is a lot easier. The current TX state is kept track of in the + * devctl_t.txstate variable. When L2 hands us a frame, the first thing we + * do is check wether there is a free TX descriptor ready in the device's + * ring. The element dq_tx_last is a pointer to the last descriptor which + * is currently to be sent or already is sent. Thus, the element next to this + * element is the one we would like to fill. The variable dq_tx_cleanup + * of the device control structure tells us the next element to be "cleaned + * up" (free skb etc.) after transmission. If it points not to the element + * we like to fill, the element we like to fill is free. If it does, we must + * discard the new frame due to the full descriptor queue. Now that we are + * sure to have found our descriptor candidate will can paint it out, but + * we can't start transmission yet. We check what state the TX is in. If + * it is idle, we load the timer with the txdelay value and start it. Of cause + * we also need to manually assert the RTS line. If we were already + * transmitting, or sending trailing flags we immediately schedule the + * descriptor for process by the DMAC by pointing LTDA (Last Transmit + * Descriptor Address) to it. In the first case, the txdelay-timer will run + * out after the txdelay period is over, causing an IRQ. The interrupt handler + * can now schedule the transmission. During transmission we use the timer + * as some kind of software watchdog. When transmission finishes, we again + * program and start the timer, this time with the txtail value. The txstate + * variable is set to TX_TAIL and as soon as the timer runs out we can + * deassert the RTS line and reset txstate to TX_IDLE. The frame(s) were + * (hopefully) transmitted successfully. Anyway, each transmitted frame + * causes a ALLS TX IRQ. The only thing we need to do then, is to call + * tx_cleanup() which walks through the tx descriptor ring, cleaning up + * all finished entries (freeing up the skbs and things like this). + * + ***************************************************************************** + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/io.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/in.h> +#include <linux/errno.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/rtnetlink.h> +#include <linux/if_arp.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/ioctl.h> +#include <linux/delay.h> +#include <net/ax25.h> +#include <net/ax25dev.h> +#include <linux/pciscc4.h> + +/* ------------------------------------------------------------------------- */ +/* user serviceable area or module parameter */ + +static int xtal = 29491200; /* oscillator frequency in HZ */ +static int probebit = 9600; /* number of cycles for tx_bitrate test */ +static int txtimeout= 30; /* TX watchdog - timeout in seconds */ +#define PCISCC_DEBUG /* enable debugging */ +#undef PCISCC_VDEBUG /* enable verbose buffer debugging */ +#define FULLDUP_PTT /* pull PTT on full duplex */ + +/* ------------------------------------------------------------------------- */ +/* global variables */ + +static const char PCISCC_VERSION[] = "$Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 dg1kjd Exp $"; + +static int chipcnt; +static struct devctl_t *devctl[64]; +static struct chipctl_t *chipctl[16]; +static struct tq_struct txto_task; +static unsigned char *dummybuf; + +/* template for global configuration, copied on driver init */ +static struct chipcfg_t chipcfg_default = { + 0, /* LBI mode */ + 1, /* oscillator power */ + 16, /* number of RX descriptors and buffers per channel */ + 128, /* number of TX descriptors per channel */ + 32, /* interrupt queue length */ + -1, /* priority channel */ + 16, /* RX main fifo DMA threshold */ + 0 /* endian swap for data buffers */ +}; + +/* template for device configuration, copied on driver init */ +static struct devcfg_t devcfg_default = { + CFG_CHCODE_NRZ, /* channel coding */ + CFG_CM_DF9IC, /* clocking mode */ + CFG_DUPLEX_HALF, /* duplex mode */ + 0, /* DPLL */ + 10, /* BRG "M" */ + 0, /* BRG "N" (N+1)*2^M; M=10, N=0 => 9600 baud */ + 0, /* clock-out enable */ + 0, /* data inversion */ + CFG_TXDDRIVE_TP, /* TxD driver type */ + 0, /* carrier detect inversion */ + 0, /* test loop */ + 32, /* TX main fifo share */ + 24, /* TX main fifo refill threshold in dw */ + 20, /* TX main fifo forward */ + 24, /* RX channel fifo forward threshold */ + CFG_TXDEL_SOFT, /* TX-delay mode */ + 2000, /* software TX-delay in bitclk cycles => default 250 ms @9600 baud */ + 400, /* TX-tail, see above */ + 1, /* shared flags */ + 0, /* CRC mode */ + 0, /* preamble repeatitions */ + 0, /* preamble */ + 0 /* HDLC extensions */ +}; + +/* ------------------------------------------------------------------------- */ + +#ifdef PCISCC_DEBUG +/* dump DMAC's register to syslog */ +static void pciscc_dmac_regdump(struct chipctl_t *cctlp) +{ + printk(KERN_INFO "PCISCC: ---------- begin DMAC register dump ----------\n"); + printk(KERN_INFO "CH BRDA LRDA FRDA BTDA LTDA FTDA CFG\n"); + printk(KERN_INFO " 0 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n", + (unsigned long) readl(cctlp->io_base+CH0BRDA), + (unsigned long) readl(cctlp->io_base+CH0LRDA), + (unsigned long) readl(cctlp->io_base+CH0FRDA), + (unsigned long) readl(cctlp->io_base+CH0BTDA), + (unsigned long) readl(cctlp->io_base+CH0LTDA), + (unsigned long) readl(cctlp->io_base+CH0FTDA), + (unsigned long) readl(cctlp->io_base+CH0CFG)); + printk(KERN_INFO " 1 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n", + (unsigned long) readl(cctlp->io_base+CH1BRDA), + (unsigned long) readl(cctlp->io_base+CH1LRDA), + (unsigned long) readl(cctlp->io_base+CH1FRDA), + (unsigned long) readl(cctlp->io_base+CH1BTDA), + (unsigned long) readl(cctlp->io_base+CH1LTDA), + (unsigned long) readl(cctlp->io_base+CH1FTDA), + (unsigned long) readl(cctlp->io_base+CH1CFG)); + printk(KERN_INFO " 2 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n", + (unsigned long) readl(cctlp->io_base+CH2BRDA), + (unsigned long) readl(cctlp->io_base+CH2LRDA), + (unsigned long) readl(cctlp->io_base+CH2FRDA), + (unsigned long) readl(cctlp->io_base+CH2BTDA), + (unsigned long) readl(cctlp->io_base+CH2LTDA), + (unsigned long) readl(cctlp->io_base+CH2FTDA), + (unsigned long) readl(cctlp->io_base+CH2CFG)); + printk(KERN_INFO " 3 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n", + (unsigned long) readl(cctlp->io_base+CH3BRDA), + (unsigned long) readl(cctlp->io_base+CH3LRDA), + (unsigned long) readl(cctlp->io_base+CH3FRDA), + (unsigned long) readl(cctlp->io_base+CH3BTDA), + (unsigned long) readl(cctlp->io_base+CH3LTDA), + (unsigned long) readl(cctlp->io_base+CH3FTDA), + (unsigned long) readl(cctlp->io_base+CH3CFG)); + printk(KERN_INFO "PCISCC: ---------- end DMAC register dump ----------\n"); + return; +} +#endif /* PCISCC_DEBUG */ + +/* ------------------------------------------------------------------------- */ + +#ifdef PCISCC_DEBUG +/* dump descriptor rings to syslog */ +static void pciscc_queuedump(struct devctl_t *dctlp) +{ + unsigned int i; + struct rx_desc_t *rdp; + struct tx_desc_t *tdp; + + if (!dctlp) return; + printk(KERN_INFO "PCISCC: ---------- begin queue dump RX iface %s ----------\n", dctlp->name); + printk(KERN_INFO "# &desc &next &prev &nextptr &dataptr &skb &feptr flags result\n"); + for (rdp=dctlp->dq_rx,i=0; ((rdp!=dctlp->dq_rx) || (i==0)) && (i<256); rdp=rdp->next,i++) { + if (!rdp) break; + printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx B0x%08lx 0x%08lx 0x%08lx\n", + i, + (unsigned long) rdp, + (unsigned long) rdp->next, + (unsigned long) rdp->prev, + (unsigned long) rdp->nextptr, + (unsigned long) rdp->dataptr, + (unsigned long) rdp->skb, + (unsigned long) rdp->feptr, + (unsigned long) rdp->flags, + (unsigned long) rdp->result); + } + printk(KERN_INFO "PCISCC: ---------- end queue dump RX iface %s ----------\n", dctlp->name); + printk(KERN_INFO "PCISCC: ---------- begin queue dump TX iface %s ----------\n", dctlp->name); + printk(KERN_INFO "%s->dq_tx=V0x%08lx %s->dq_tx_cleanup=V0x%08lx %s->dq_tx_last=V0x%08lx.\n", + dctlp->name, (unsigned long) dctlp->dq_tx, + dctlp->name, (unsigned long) dctlp->dq_tx_cleanup, + dctlp->name, (unsigned long) dctlp->dq_tx_last); + printk(KERN_INFO "# &desc &next &prev &nextptr &dataptr &skb flags result\n"); + for (tdp=dctlp->dq_tx,i=0; ((tdp!=dctlp->dq_tx) || (i==0)) && (i<256); tdp=tdp->next,i++) { + if (!tdp) break; + printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx 0x%08lx 0x%08lx\n", + i, + (unsigned long) tdp, + (unsigned long) tdp->next, + (unsigned long) tdp->prev, + (unsigned long) tdp->nextptr, + (unsigned long) tdp->dataptr, + (unsigned long) tdp->skb, + (unsigned long) tdp->flags, + (unsigned long) tdp->result); + } + printk(KERN_INFO "PCISCC: ---------- end queue dump TX iface %s ----------\n", dctlp->name); + return; +} +#endif /* PCISCC_DEBUG */ + +/* ------------------------------------------------------------------------- */ + +/* + * initialize chip, called when first interface of a chip is brought up + * action sequency was carefully chosen, don't mess with it + */ +static int pciscc_chip_open(struct chipctl_t *cctlp) +{ + unsigned long start_time; + volatile unsigned long gcc_optimizer_safe; + int i; + int fifo_sum; + char pci_latency; + + if (cctlp->initialized) return 0; +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: chip_open().\n"); +#endif + /* + * make sure FIFO space requested by all devices is not larger than + * what's available on the silicon. + */ + cli(); + for (i=0, fifo_sum=0; i<4; i++) { + fifo_sum += cctlp->device[i]->cfg.mfifo_tx_p; + } + sti(); + if (fifo_sum > 128) { + printk(KERN_ERR "PCISCC: chip_open(): TX main_fifo misconfiguration.\n"); + return -EAGAIN; + } + /* map control and LBI memory */ + cctlp->io_base=ioremap_nocache(cctlp->pcidev->base_address[0], 2*1024); + cctlp->lbi_base=ioremap_nocache(cctlp->pcidev->base_address[1], 64*1024); + /* enable bus mastering */ + pci_set_master(cctlp->pcidev); + /* tweak latency */ + pcibios_read_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, &pci_latency); +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: chip_open(): Old PCI latency timer: %u.\n", (unsigned int) pci_latency); +#endif + pcibios_write_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, 255); + /* request IRQ */ + if (request_irq(cctlp->pcidev->irq, pciscc_isr, SA_SHIRQ, "pciscc", (void *) cctlp)) { + printk(KERN_ERR "PCISCC: chip_open(): Could not get IRQ #%u.\n", cctlp->pcidev->irq); + pciscc_chip_close(cctlp); + return -EAGAIN; + } + cctlp->irq=cctlp->pcidev->irq; + /* allocate and initialize peripheral queue */ + if (!(cctlp->iq_per = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating peripheral interrupt queue.\n"); + return -ENOMEM; + } + memset(cctlp->iq_per, 0, cctlp->cfg.iqlen*4); + cctlp->iq_per_next = cctlp->iq_per; + /* configuration interrupt queue */ + if (!(cctlp->iq_cfg = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating configuration interrupt queue.\n"); + return -ENOMEM; + } + memset(cctlp->iq_cfg, 0, cctlp->cfg.iqlen*4); + cctlp->iq_cfg_next = cctlp->iq_cfg; + /* global hardware initialization */ + cli(); + writel((cctlp->cfg.endianswap ? ENDIAN : 0) + | (cctlp->cfg.prichan != -1 ? (SPRI | (cctlp->cfg.prichan * CHN)) : 0) + | (4 * PERCFG) + | (3 * LCD) + | CMODE + | (cctlp->cfg.oscpwr ? 0 : OSCPD), cctlp->io_base+GMODE); + writel(virt_to_bus(cctlp->iq_per), cctlp->io_base+IQPBAR); + writel(virt_to_bus(cctlp->iq_cfg), cctlp->io_base+IQCFGBAR); + writel((((cctlp->cfg.iqlen/32)-1)*IQCFGLEN) | (((cctlp->cfg.iqlen/32)-1)*IQPLEN), cctlp->io_base+IQLENR2); + writel(((cctlp->device[0]->cfg.mfifo_tx_p/4)*TFSIZE0) + | ((cctlp->device[1]->cfg.mfifo_tx_p/4)*TFSIZE1) + | ((cctlp->device[2]->cfg.mfifo_tx_p/4)*TFSIZE2) + | ((cctlp->device[3]->cfg.mfifo_tx_p/4)*TFSIZE3), cctlp->io_base+FIFOCR1); + writel(((cctlp->device[0]->cfg.mfifo_tx_r/4)*TFRTHRES0) + | ((cctlp->device[1]->cfg.mfifo_tx_r/4)*TFRTHRES1) + | ((cctlp->device[2]->cfg.mfifo_tx_r/4)*TFRTHRES2) + | ((cctlp->device[3]->cfg.mfifo_tx_r/4)*TFRTHRES3) + | M4_0 | M4_1 | M4_2 | M4_3, cctlp->io_base+FIFOCR2); + writel(cctlp->cfg.mfifo_rx_t, cctlp->io_base+FIFOCR3); + writel(((cctlp->device[0]->cfg.mfifo_tx_f)*TFFTHRES0) + | ((cctlp->device[1]->cfg.mfifo_tx_f)*TFFTHRES1) + | ((cctlp->device[2]->cfg.mfifo_tx_f)*TFFTHRES2) + | ((cctlp->device[3]->cfg.mfifo_tx_f)*TFFTHRES3), cctlp->io_base+FIFOCR4); + /* mask out all DMAC interrupts */ + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG); + /* all SCC cores in reset state */ + writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0); + /* mask out all SCC interrupts */ + writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR); + /* peripheral configuration */ + writel((BTYP*3), cctlp->io_base+LCONF); + writel(0x00000000, cctlp->io_base+SSCCON); + writel(0x00000000, cctlp->io_base+SSCIM); + writel(0x000000ff, cctlp->io_base+GPDIR); + writel(0x00000000, cctlp->io_base+GPDATA); + writel(0x00000000, cctlp->io_base+GPIM); + sti(); + /* initialize configuration and peripheral IQs */ + start_time = jiffies; + cctlp->mailbox = MAILBOX_NONE; + writel(CFGIQCFG | CFGIQP | AR, cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: chip_open(): Success on IQ config request.\n"); +#endif + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: chip_open(): Timeout on IQ config request. Sync HDDs and hardware-reset NOW!\n"); + pciscc_chip_close(cctlp); + return -EIO; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: chip_open(): Failure on IQ config request. Sync HDDs and hardware-reset NOW!\n"); + pciscc_chip_close(cctlp); + return -EIO; + } + cctlp->initialized=1; + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* + * close chip, called when last device (channel) of a chip was closed. + * don't mess up. + */ +static int pciscc_chip_close(struct chipctl_t *cctlp) +{ + if (cctlp->usecnt) { + printk(KERN_ERR "PCISCC: chip_close() called while channels active.\n"); + return -EBUSY; + } +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: chip_close().\n"); +#endif + /* global configuration to reset state */ + cli(); + writel((cctlp->cfg.endianswap ? ENDIAN : 0) + | (4 * PERCFG) + | (3 * LCD) + | CMODE + | OSCPD, cctlp->io_base+GMODE); + /* mask all DMAC interrupts */ + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG); + writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG); + /* SCC cores to reset state */ + writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0); + writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0); + /* mask all SCC interrupts */ + writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR); + writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR); + sti(); + /* free IQs, free IRQ, unmap control address space */ + if (cctlp->iq_per) { + kfree(cctlp->iq_per); + cctlp->iq_per=0; + cctlp->iq_per_next=0; + } + if (cctlp->iq_cfg) { + kfree(cctlp->iq_cfg); + cctlp->iq_cfg=0; + cctlp->iq_cfg_next=0; + } + if (cctlp->irq) { + free_irq(cctlp->irq, (void *) cctlp); + cctlp->irq=0; + } + if (cctlp->io_base) { + iounmap(cctlp->io_base); + cctlp->io_base=0; + } + if (cctlp->lbi_base) { + iounmap(cctlp->lbi_base); + cctlp->lbi_base=0; + } + cctlp->initialized=0; + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* + * open one channel, chip must have been initialized by chip_open() before. + * the sequence of actions done here was carefully chosen, don't mess with + * it unless you know exactly what you are doing... + */ +static int pciscc_channel_open(struct devctl_t *dctlp) +{ + struct chipctl_t *cctlp = dctlp->chip; + struct net_device *dev = &dctlp->dev; + int channel = dctlp->channel; + struct rx_desc_t *rdp, *last_rdp; + struct tx_desc_t *tdp, *last_tdp; + unsigned long l; + unsigned long start_time; + volatile unsigned long gcc_optimizer_safe; + int i; + unsigned char *data; + + if (dctlp->dev.start) return 0; +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open().\n"); +#endif + /* allocate and initialize RX and TX IQs */ + if (!(dctlp->iq_rx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx interrupt queue.\n"); + return -ENOMEM; + } + memset(dctlp->iq_rx, 0, cctlp->cfg.iqlen*4); + dctlp->iq_rx_next = dctlp->iq_rx; + if (!(dctlp->iq_tx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx interrupt queue.\n"); + return -ENOMEM; + } + memset(dctlp->iq_tx, 0, cctlp->cfg.iqlen*4); + dctlp->iq_tx_next = dctlp->iq_tx; + cli(); + writel(0, cctlp->io_base+SCCBASE[channel]+CCR1); /* stop SCC */ + writel(0, cctlp->io_base+SCCBASE[channel]+CCR2); + writel(0, cctlp->io_base+SCCBASE[channel]+CCR0); + writel(0, cctlp->io_base+SCCBASE[channel]+TIMR); + /* set IQ lengths and base addresses */ + l = readl(cctlp->io_base+IQLENR1); + switch (channel) { + case 0: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG); + writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC0RXBAR); + writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC0TXBAR); + l &= 0x0fff0fff; + l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC0RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC0TXLEN); + break; + case 1: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG); + writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC1RXBAR); + writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC1TXBAR); + l &= 0xf0fff0ff; + l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC1RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC1TXLEN); + break; + case 2: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG); + writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC2RXBAR); + writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC2TXBAR); + l &= 0xff0fff0f; + l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC2RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC2TXLEN); + break; + case 3: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG); + writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC3RXBAR); + writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC3TXBAR); + l &= 0xfff0fff0; + l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC3RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC3TXLEN); + break; + } + writel(l, cctlp->io_base+IQLENR1); + sti(); + start_time = jiffies; + cctlp->mailbox = MAILBOX_NONE; + writel(AR /* Action Request */ + | (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0) + | (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0) + | (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0) + | (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open(): Success on IQSCC config request.\n"); +#endif + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: channel_open(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n"); + pciscc_channel_close(dctlp); + return -EIO; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: channel_open(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n"); + pciscc_channel_close(dctlp); + return -EIO; + } + /* initialize channel's SCC core */ + cli(); + l = PU + | ((dctlp->cfg.coding == CFG_CHCODE_NRZ) ? 0*SC : 0) + | ((dctlp->cfg.coding == CFG_CHCODE_NRZI) ? 2*SC : 0) + | ((dctlp->cfg.coding == CFG_CHCODE_FM0) ? 4*SC : 0) + | ((dctlp->cfg.coding == CFG_CHCODE_FM1) ? 5*SC : 0) + | ((dctlp->cfg.coding == CFG_CHCODE_MANCH) ? 6*SC : 0) + | VIS + | ((dctlp->cfg.dpll & CFG_DPLL_PS) ? 0 : PSD) + | ((dctlp->cfg.clkout & CFG_TXTXCLK) ? TOE : 0) + | ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? SSEL : 0) + | ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? SSEL : 0) + | ((dctlp->cfg.clockmode == CFG_CM_HS) ? HS : 0) + | ((dctlp->cfg.clockmode == CFG_CM_DF9IC) ? 0*CM : 0) /* clockmode 0a */ + | ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? 0*CM : 0) /* clockmode 0b */ + | ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? 6*CM : 0) /* clockmode 6b */ + | ((dctlp->cfg.clockmode == CFG_CM_HS) ? 4*CM : 0); /* clockmode 4 */ + writel(l, cctlp->io_base+SCCBASE[channel]+CCR0); + l = (dctlp->cfg.datainv ? DIV : 0) + | ((dctlp->cfg.txddrive & CFG_TXDDRIVE_TP) ? ODS : 0) + | (dctlp->cfg.cdinv ? 0 : ICD) + | ((dctlp->cfg.clkout & CFG_TXRTS) ? TCLKO : 0) + | ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) ? 0 : FCTS) + | MDS1 + | (dctlp->cfg.testloop ? TLP : 0) + | (dctlp->cfg.sharedflg ? SFLAG : 0) + | ((dctlp->cfg.crcmode & CFG_CRCMODE_RESET_0000) ? CRL : 0) + | ((dctlp->cfg.crcmode & CFG_CRCMODE_CRC32) ? C32 : 0); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + l = RAC + | ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCD) ? DRCRC : 0) + | ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCRCFWD) ? RCRC : 0) + | ((dctlp->cfg.crcmode & CFG_CRCMODE_TXNOCRC) ? XCRC : 0) + /* 1 and 2 dwords somehow don't work */ + | ((dctlp->cfg.cfifo_rx_t == 1) ? 1*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 2) ? 1*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 4) ? 1*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 16) ? 2*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 24) ? 3*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 32) ? 4*RFTH : 0) + | ((dctlp->cfg.cfifo_rx_t == 60) ? 5*RFTH : 0) + | (dctlp->cfg.preamble*PRE) + | (dctlp->cfg.preamb_rpt ? EPT : 0) + | ((dctlp->cfg.preamb_rpt == 2) ? PRE0 : 0) + | ((dctlp->cfg.preamb_rpt == 4) ? PRE1 : 0) + | ((dctlp->cfg.preamb_rpt == 8) ? PRE0|PRE1 : 0) + | ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEFILL) ? 0 : ITF) + | ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEINS) ? OIN : 0); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR2); + writel((dctlp->cfg.brate_m*BRM) | (dctlp->cfg.brate_n*BRN), cctlp->io_base+SCCBASE[channel]+BRR); + writel(RCE | (dctlp->dev.mtu*RL), cctlp->io_base+SCCBASE[channel]+RLCR); + /* + * all sent | tx device underrun | timer int | tx message repeat | + * tx pool ready | rx device overflow | receive FIFO overflow | + * frame length exceeded => interrupt mask register + */ + writel(~(ALLS | XDU | TIN | XMR | XPR | RDO | RFO | FLEX), cctlp->io_base+SCCBASE[channel]+IMR); + sti(); + /* wait until command_executing (CEC) is clear */ + start_time=jiffies; + do { + l=readl(cctlp->io_base+SCCBASE[channel]+STAR); + gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC); + } while (gcc_optimizer_safe); + if (l & CEC) { + /* not ready, but we will execute reset anyway */ + printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for reset.\n"); + } + /* execute channel's SCC core RX and TX reset */ + writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR); + start_time = jiffies; + dctlp->tx_mailbox = 0xffffffff; + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff); + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ + case 0x02001000: /* SCC XPR interrupt received */ +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open(): Success on SCC reset.\n"); +#endif + break; + case 0xffffffff: + printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC reset. Clocking problem?\n"); + break; + default: + printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); + break; + } + if (!(dctlp->tx_bitrate=pciscc_probe_txrate(dctlp))) dctlp->tx_bitrate=9600; + /* + * Prepare circular RX and TX descriptor queues ("FIFO" rings) + * Attention: + * This beast gets _very_ angry if you try to hand it a + * descriptor with a data length of 0. In fact it crashes + * the system by asserting /SERR or something. + */ + cli(); + rdp = last_rdp = NULL; + for (i=0; i<cctlp->cfg.rxbufcnt; i++, last_rdp=rdp) { + if (!(rdp=kmalloc(sizeof(struct rx_desc_t), GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx descriptor chain.\n"); + sti(); + pciscc_channel_close(dctlp); + return -ENOMEM; + } + memset(rdp, 0, sizeof(struct rx_desc_t)); + if (i==0) { + dctlp->dq_rx=rdp; /* queue (ring) "head" */ + } else { + rdp->prev=last_rdp; + last_rdp->next=rdp; + last_rdp->nextptr=(void *) virt_to_bus(rdp); + } + if (!(rdp->skb=alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating socket buffers.\n"); + sti(); + pciscc_channel_close(dctlp); + return -ENOMEM; + } + skb_reserve(rdp->skb, SKB_HEADROOM); + rdp->dataptr=(void *) virt_to_bus(data=skb_put(rdp->skb, dctlp->dev.mtu+10)); /* we will skb_trim() it after */ + rdp->flags=(dctlp->dev.mtu*NO); /* reception when we know frame length */ + } + rdp->next=dctlp->dq_rx; /* close ring structure */ + rdp->nextptr=(void *) virt_to_bus(dctlp->dq_rx); + dctlp->dq_rx->prev=rdp; + dctlp->dq_rx_next=dctlp->dq_rx; /* first descriptor to be processed = "first" descriptor in chain */ + /* TX queue */ + tdp = last_tdp = NULL; + for (i=0; i<cctlp->cfg.txbufcnt; i++, last_tdp=tdp) { + if (!(tdp=kmalloc(sizeof(struct tx_desc_t), GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx descriptor chain.\n"); + sti(); + pciscc_channel_close(dctlp); + return -ENOMEM; + } + memset(tdp, 0, sizeof(struct tx_desc_t)); + if (i==0) { + dctlp->dq_tx=tdp; + } else { + tdp->prev=last_tdp; + last_tdp->next=tdp; + last_tdp->nextptr=(void *) virt_to_bus(tdp); + } + tdp->skb=NULL; + tdp->dataptr=(void *) virt_to_bus(dummybuf); + tdp->flags=(8*NO) | FE; + } + tdp->next=dctlp->dq_tx; /* close ring structure */ + tdp->nextptr=(void *) virt_to_bus(dctlp->dq_tx); + dctlp->dq_tx->prev=tdp; + dctlp->dq_tx_last=dctlp->dq_tx; /* last descriptor to be transmitted */ + dctlp->dq_tx_cleanup=dctlp->dq_tx; /* first descriptor to be cleaned up after transmission */ + flush_cache_all(); + /* initialize DMAC channel's RX */ + switch (channel) { + case 0: writel(IDR, cctlp->io_base+CH0CFG); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0BRDA); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0FRDA); + writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH0LRDA); + break; + case 1: writel(IDR, cctlp->io_base+CH1CFG); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1BRDA); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1FRDA); + writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH1LRDA); + break; + case 2: writel(IDR, cctlp->io_base+CH2CFG); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2BRDA); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2FRDA); + writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH2LRDA); + break; + case 3: writel(IDR, cctlp->io_base+CH3CFG); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3BRDA); + writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3FRDA); + writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH3LRDA); + break; + } + sti(); + start_time=jiffies; + cctlp->mailbox=MAILBOX_NONE; + writel(AR, cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-RX config request.\n"); +#endif + dctlp->dmac_rx=DMAC_RX_INIT; + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n"); + break; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n"); + break; + } + /* mask all DMAC interrupts (needed) */ + switch (channel) { + case 0: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG); + break; + case 1: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG); + break; + case 2: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG); + break; + case 3: writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG); + break; + } + /* SCC core TX reset (again) */ + start_time=jiffies; + do { + l=readl(cctlp->io_base+SCCBASE[channel]+STAR); + gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC); + } while (gcc_optimizer_safe); + if (l & CEC) { + /* not ready, but we will execute reset anyway */ + printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for TX-reset.\n"); + } + writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR); + start_time = jiffies; + dctlp->tx_mailbox = 0xffffffff; + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff); + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ + case 0x02001000: /* SCC XPR interrupt received */ +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open(): Success on SCC TX-reset.\n"); +#endif + break; + case 0xffffffff: + printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC TX-reset. Clocking problem?\n"); + break; + default: + printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC TX-reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); + break; + } + /* + * initialize DMAC's TX channel, FI must stay masked all the time + * even during operation, see device errata 03/99 + */ + switch (channel) { + case 0: writel(IDT | MTFI, cctlp->io_base+CH0CFG); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0BTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0FTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0LTDA); + break; + case 1: writel(IDT | MTFI, cctlp->io_base+CH1CFG); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1BTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1FTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1LTDA); + break; + case 2: writel(IDT | MTFI, cctlp->io_base+CH2CFG); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2BTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2FTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2LTDA); + break; + case 3: writel(IDT | MTFI, cctlp->io_base+CH3CFG); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3BTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3FTDA); + writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3LTDA); + break; + } + start_time=jiffies; + cctlp->mailbox=MAILBOX_NONE; + writel(AR, cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-TX config request.\n"); +#endif + dctlp->txstate=TX_IDLE; + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n"); + break; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n"); + break; + } +#ifdef FULLDUP_PTT + if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { + l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l |= RTS; + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + } +#endif + flush_cache_all(); +#ifdef PCISCC_DEBUG + pciscc_dmac_regdump(cctlp); + pciscc_queuedump(dctlp); +#endif + dctlp->chip->usecnt++; + dctlp->dev.start=1; + /* clear statistics */ + mdelay(10); + memset(&dctlp->stats, 0, sizeof(struct net_device_stats)); + /* some housekeeping */ + pciscc_update_values(dev); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* close one channel - don't mess with it either */ +static void pciscc_channel_close(struct devctl_t *dctlp) +{ + struct chipctl_t *cctlp = dctlp->chip; + int channel = dctlp->channel; + struct rx_desc_t *rdp, *last_rdp; + struct tx_desc_t *tdp, *last_tdp; + unsigned long l; + unsigned long start_time; + volatile unsigned long gcc_optimizer_safe; + +#ifdef PCISCC_DEBUG + pciscc_dmac_regdump(cctlp); + pciscc_queuedump(dctlp); +#endif + /* at first stop timer */ + writel(0, cctlp->io_base+SCCBASE[channel]+TIMR); + /* wait until command_executing (CEC) is clear */ + start_time=jiffies; + do { + l=readl(cctlp->io_base+SCCBASE[channel]+STAR); + gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC); + } while (gcc_optimizer_safe); + if (l & CEC) { + /* not ready, but we will execute reset anyway */ + printk(KERN_ERR "PCISCC: channel_close(): Timeout waiting for SCC being ready for reset.\n"); + } + /* RX and TX SCC reset */ + writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR); + start_time = jiffies; + dctlp->tx_mailbox = 0xffffffff; + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff); + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ + case 0x02001000: /* SCC XPR interrupt received */ +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_close(): Success on SCC reset.\n"); +#endif + break; + case 0xffffffff: + printk(KERN_ERR "PCISCC: channel_close(): Timeout on SCC reset.\n"); + break; + default: + printk(KERN_ERR "PCISCC: channel_close(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); + break; + } + /* stop SCC core */ + writel(0, cctlp->io_base+SCCBASE[channel]+CCR1); + writel(0, cctlp->io_base+SCCBASE[channel]+CCR2); + writel(0, cctlp->io_base+SCCBASE[channel]+CCR0); + /* + * Give the isr some time to "refill" the rx-dq + * we _MUST_ guarantee that the DMAC-RX is _NOT_ in + * hold state when issuing the RESET command, otherwise the DMAC + * will crash. (DSCC-4 Rev. <= 2.1) + * In addition to that we may only issue a RESET if channel is + * currently really initialized, otherwise something horrible will + * result. + */ + start_time=jiffies; + do { + gcc_optimizer_safe=(jiffies-start_time)<5; + } while (gcc_optimizer_safe); + /* OK, now we should be ready to put the DMAC into reset state */ + switch (channel) { + case 0: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG); + break; + case 1: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG); + break; + case 2: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG); + break; + case 3: writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG); + break; + } + start_time = jiffies; + cctlp->mailbox = MAILBOX_NONE; + writel(AR, cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_close(): Success on DMAC reset channel %u.\n", channel); +#endif + dctlp->dmac_rx=DMAC_RX_RESET; + dctlp->txstate=TX_RESET; + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: channel_close(): Timeout on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel); + break; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: channel_close(): Failure on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel); + break; + } + /* clear IQs */ + l = readl(cctlp->io_base+IQLENR1); + switch (channel) { + case 0: l &= 0x0fff0fff; + writel(l, cctlp->io_base+IQLENR1); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0RXBAR); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0TXBAR); + break; + case 1: l &= 0xf0fff0ff; + writel(l, cctlp->io_base+IQLENR1); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1RXBAR); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1TXBAR); + break; + case 2: l &= 0xff0fff0f; + writel(l, cctlp->io_base+IQLENR1); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2RXBAR); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2TXBAR); + break; + case 3: l &= 0xfff0fff0; + writel(l, cctlp->io_base+IQLENR1); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3RXBAR); + writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3TXBAR); + break; + } + start_time = jiffies; + cctlp->mailbox = MAILBOX_NONE; + writel(AR + | (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0) + | (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0) + | (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0) + | (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR); + do { + gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox; + } while (gcc_optimizer_safe); /* timeout 20 jiffies */ + switch (cctlp->mailbox) { /* mailbox was written by isr */ + case MAILBOX_OK: +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: channel_close(): Success on IQSCC config request.\n"); +#endif + break; + case MAILBOX_NONE: + printk(KERN_ERR "PCISCC: channel_close(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n"); + break; + case MAILBOX_FAILURE: + printk(KERN_ERR "PCISCC: channel_close(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n"); + break; + } + if (dctlp->dq_rx) { + rdp=dctlp->dq_rx; /* free descriptor chains and buffers */ + do { + if (rdp->skb) { + kfree_skb(rdp->skb); + rdp->skb=NULL; + } + last_rdp=rdp; + rdp=rdp->next; + kfree(last_rdp); + } while (rdp!=dctlp->dq_rx); + dctlp->dq_rx=NULL; + } + dctlp->dq_rx_next=NULL; + if (dctlp->dq_tx) { + tdp=dctlp->dq_tx; + do { + if (tdp->skb) { + kfree_skb(tdp->skb); + tdp->skb=NULL; + } + last_tdp=tdp; + tdp=tdp->next; + kfree(last_tdp); + } while (tdp!=dctlp->dq_tx); + dctlp->dq_tx=NULL; + } + dctlp->dq_tx_cleanup=NULL; + dctlp->dq_tx_last=NULL; + if (dctlp->iq_rx) { /* free IQs */ + kfree(dctlp->iq_rx); + dctlp->iq_rx=NULL; + } + if (dctlp->iq_tx) { + kfree(dctlp->iq_tx); + dctlp->iq_tx=NULL; + } + dctlp->dev.start=0; + dctlp->chip->usecnt--; + return; +} + +/* ------------------------------------------------------------------------- */ + +/* interrupt handler root */ +static void pciscc_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct chipctl_t *cctlp = (struct chipctl_t *) dev_id; + struct devctl_t *dctlp; + unsigned long flags; + unsigned long status; + unsigned long l; + int channel; + unsigned long *iqp; + int processed; + + status = readl(cctlp->io_base+GSTAR); + writel(status, cctlp->io_base+GSTAR); /* ack' irq */ + if (!status) return; + /* do not disturb... */ + save_flags(flags); + cli(); + if (status & (IIPGPP | IIPLBI | IIPSSC)) { + /* process peripheral queue */ + processed = 0; + for (iqp=cctlp->iq_per_next; *iqp!=0; iqp=((iqp==(cctlp->iq_per+cctlp->cfg.iqlen-1)) ? cctlp->iq_per : iqp+1)) { /* note wrap-arround */ + /* I just love raping for-statements */ + printk(KERN_INFO "PCISCC: isr: IQPER vector: 0x%0lx.\n", *iqp); + processed++; + *iqp=0; + } + cctlp->iq_per_next=iqp; + } + if (status & IICFG) { + /* process configuration queue */ + cctlp->mailbox = MAILBOX_NONE; + processed = 0; + /* I love raping for-statements... */ + for (iqp=cctlp->iq_cfg_next; *iqp!=0; iqp=((iqp==(cctlp->iq_cfg+cctlp->cfg.iqlen-1)) ? cctlp->iq_cfg : iqp+1)) { /* note wrap-arround */ +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: isr: IQCFG vector: 0x%0lx.\n", *iqp); +#endif + if ((*iqp) & ARACK) { + cctlp->mailbox = MAILBOX_OK; + processed++; + } + if ((*iqp) & ARF) { + cctlp->mailbox = MAILBOX_FAILURE; + processed++; + } + *iqp=0; + } + cctlp->iq_cfg_next=iqp; + if (processed != 1) { + printk(KERN_ERR "PCISCC: isr: Something weird going on... IICFG:processed=%u.\n", processed); + } + } + for (channel=0; channel<4; channel++) if (status & (1<<(24+channel))) { + /* process TX queue */ + dctlp=cctlp->device[channel]; + if (!dctlp->iq_tx || !dctlp->iq_tx_next) continue; + processed = 0; + for (iqp=dctlp->iq_tx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1)) { /* note wrap-arround */ + if (*iqp & TIN) { + /* timer interrupt */ + writel(0, cctlp->io_base+SCCBASE[channel]+TIMR); + /* now, which state are we in? */ + switch (dctlp->txstate) { + case TX_DELAY: + /* data transmit */ + dctlp->txstate=TX_XMIT; + switch (channel) { + case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); + break; + case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); + break; + case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); + break; + case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); + break; + } + writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + break; + case TX_TAIL: + /* transmitting tail */ + dctlp->txstate=TX_IDLE; + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + break; + case TX_PROBE: + /* tx bitrate test going on */ + do_gettimeofday(&dctlp->tv); + dctlp->txstate=TX_RESET; + dctlp->tx_mailbox=1; + break; + case TX_CAL: + /* we are (i.e. were) calibrating */ + if (dctlp->dq_tx_last != dctlp->dq_tx_cleanup) { + /* we have something in the tx queue */ + dctlp->txstate = TX_XMIT; + switch (channel) { + case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); + break; + case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); + break; + case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); + break; + case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); + break; + } + writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + } else { + dctlp->txstate=TX_IDLE; +#ifdef FULLDUP_PTT + if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + } +#else + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); +#endif + } + break; + case TX_XMIT: + /* watchdog just ran out */ + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + dctlp->txstate=TX_IDLE; + txto_task.routine=pciscc_bh_txto; + txto_task.data=(void *) dctlp; + queue_task(&txto_task, &tq_scheduler); + break; + default: + printk(KERN_ERR "PCISCC: isr: Timer interrupt while txstate=%u.\n", dctlp->txstate); + dctlp->txstate=TX_IDLE; + } + } + if (*iqp & ALLS) { + /* a TX frame was just completed */ + pciscc_isr_txcleanup(dctlp); + if (dctlp->dq_tx_cleanup == dctlp->dq_tx_last) { + /* complete TX-queue sent out */ + if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { + /* just update txstate */ + dctlp->txstate=TX_IDLE; +#ifndef FULLDUP_PTT + /* release PTT */ + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); +#endif + } else if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) { + /* start txtail */ + dctlp->txstate=TX_TAIL; + writel(dctlp->cfg.txtailval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + } else if (dctlp->cfg.txdelmode == CFG_TXDEL_HARD) { + /* deassert RTS immediately */ + dctlp->txstate=TX_IDLE; + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + } + } + } + if (*iqp & XDU) { + /* + * TX stall - now we are _really_ in trouble. + * We must reset the SCC core and re-init DMAC-TX. + * This includes delay loops and we are in interrupt + * context, with all interrupts disabled... + */ +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: isr: TX data underrun occured iface=%s.\n", dctlp->name); +#endif + dctlp->stats.tx_fifo_errors++; + /* reset TX-DMAC */ + switch (channel) { + case 0: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH0CFG); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); + case 1: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH1CFG); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); + case 2: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH2CFG); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); + case 3: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH3CFG); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); + } + writel(AR, cctlp->io_base+GCMDR); + mdelay(10); + /* reset SCC core TX */ + writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR); + mdelay(30); + /* re-init TX-DMAC */ + switch (channel) { + case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); + writel(MTFI | IDT, cctlp->io_base+CH0CFG); + case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); + writel(MTFI | IDT, cctlp->io_base+CH1CFG); + case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); + writel(MTFI | IDT, cctlp->io_base+CH2CFG); + case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA); + writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); + writel(MTFI | IDT, cctlp->io_base+CH3CFG); + } + writel(AR, cctlp->io_base+GCMDR); + mdelay(10); + /* We can't do anything more since we are still + * in the interrupt handler. We can only hope that + * the reset succeeded. */ + } + if (*iqp & XMR) { + /* + * TX message repeat - not critical, since + * resolved automagically by abort sequence + * and retransmit. + */ +#ifdef PCISCC_VDEBUG + printk(KERN_INFO "PCISCC: isr: TX message repeat interrupt iface=%s.\n", dctlp->name); +#endif + } + dctlp->tx_mailbox=*iqp; + *iqp=0; + processed++; + } + dctlp->iq_tx_next=iqp; + } + for (channel=0; channel<4; channel++) if (status & (1<<(28+channel))) { + dctlp=cctlp->device[channel]; + /* process RX queue */ + if (!dctlp->iq_rx || !dctlp->iq_rx_next) { + printk(KERN_ERR "PCISCC: isr: IQCHAN%uRX interrupt for non-initialized queue!\n", channel); + continue; + } + processed = 0; + for (iqp=dctlp->iq_rx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_rx+cctlp->cfg.iqlen-1)) ? dctlp->iq_rx : iqp+1)) { /* note wrap-arround */ + /* statistics only */ + if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RDO)) { + dctlp->stats.rx_fifo_errors++; + } + if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RFO)) { + dctlp->stats.rx_over_errors++; + } + if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_FLEX)) { + dctlp->stats.rx_length_errors++; + } + if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_ERR)) { + dctlp->stats.rx_errors++; + } + if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_HI)) { + printk(KERN_ERR "PCISCC: isr: Weird... received HI interrupt.\n"); + } + if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_FI)) { + } + dctlp->rx_mailbox=*iqp; + *iqp=0; + processed++; + } + /* in any case check RX descriptor queue for received frames */ + if (dctlp->dev.start) pciscc_isr_receiver(dctlp); + dctlp->iq_rx_next=iqp; + } + restore_flags(flags); + return; +} + +/* ------------------------------------------------------------------------- */ + +/* called by interrupt handler root when RX interrupt occurred */ +static __inline__ void pciscc_isr_receiver(struct devctl_t *dctlp) +{ + struct chipctl_t *cctlp = dctlp->chip; + int channel = dctlp->channel; + struct rx_desc_t *rdp; + long status; + unsigned char rdsb; /* receive data status byte, generated by DMAC at buffer end */ + int bno; + int valid; + struct sk_buff *new_skb; + int processed; + +#ifdef PCISCC_DEBUG + if (!dctlp->dev.start) { + printk(KERN_INFO "PCISCC: isr_receiver: frame received while !dev->start.\n"); + } +#endif + for (rdp=dctlp->dq_rx_next, processed=0; (rdp->result & C); rdp=rdp->next, processed++) { +#ifdef PCISCC_DEBUG + if ((rdp->nextptr != (void *) virt_to_bus(rdp->next)) || (rdp->dataptr != (void *) virt_to_bus(rdp->skb->data))) { + panic("PCISCC: isr_receiver(): mm fucked with our buffers"); + } +#endif + status = rdp->result; + bno = (status >> 16) & 0x1fff; + valid = 1; /* we assume frame valid */ + if ((status & RA) || (bno <= 0) || (bno > dctlp->dev.mtu) || !(status & FE) || (rdp->feptr != (void *) virt_to_bus(rdp))) { + /* aborted or invalid length */ + valid = 0; + } else { + rdsb = rdp->skb->data[bno-1]; + if (!(rdsb & SB_VFR)) { /* incorrect bit length */ + valid = 0; + dctlp->stats.rx_frame_errors++; + } + if (rdsb & SB_RDO) { /* data overflow */ + valid = 0; /* areadly counted */ + } + if (!(rdsb & SB_CRC) && !(dctlp->cfg.crcmode & CFG_CRCMODE_RXCD)) { + /* CRC error */ + valid = 0; + dctlp->stats.rx_crc_errors++; + } + if (rdsb & SB_RAB) { /* receive message aborted */ + valid = 0; + } + } + /* OK, this is a little bit tricky. We have to make sure + * that every descriptor has a buffer assigned. Thus we + * can only release a buffer to the link layer if we get + * a new one in turn from mm before. */ + if (valid) { + if ((new_skb = alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_ATOMIC))) { + skb_reserve(new_skb, SKB_HEADROOM); + skb_trim(rdp->skb, bno-1); + pciscc_rx_skb(rdp->skb, dctlp); + rdp->skb = new_skb; + rdp->dataptr=(void *) virt_to_bus(skb_put(rdp->skb, dctlp->dev.mtu+10)); + } else { +#ifdef PCISCC_DEBUG + printk(KERN_INFO "PCISCC: isr_receiver: Out of memory allocating new skb!\n"); +#endif + } + } + rdp->flags=dctlp->dev.mtu*NO; /* prepare descriptor for next time */ + rdp->result=0; + rdp->feptr=0; + flush_cache_all(); + } +#ifdef PCISCC_VDEBUG + printk(KERN_INFO "PCISCC: isr_receiver: Processed %u frames at once.\n", processed); +#endif + dctlp->dq_rx_next = rdp; + /* + * tell DMAC last available descriptor - keep up one + * descriptor space for security (paranoia) (...->prev->prev) + */ + switch (channel) { + case 0: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH0LRDA); + break; + case 1: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH1LRDA); + break; + case 2: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH2LRDA); + break; + case 3: writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH3LRDA); + break; + } + return; +} + +/* ------------------------------------------------------------------------- */ + +/* called by IRQ handler root when a TX descriptor was completed */ +static __inline__ void pciscc_isr_txcleanup(struct devctl_t *dctlp) +{ + struct tx_desc_t *tdp; + int processed; + + processed=0; + tdp=dctlp->dq_tx_cleanup; + while ((tdp != dctlp->dq_tx_last) && (tdp->result & C)) { + /* clean up all (C)omplete descriptors */ + if (tdp->skb) { +#ifdef PCISCC_DEBUG + if ((tdp->nextptr != (void *) virt_to_bus(tdp->next)) || (tdp->dataptr != (void *) virt_to_bus(tdp->skb->data))) { + /* + * paranoia check - + * this should _never_ever_occur_ . + * if it does, the memory subsystem moved around + * our buffers in address space, and it's the + * last you will see. + */ + printk(KERN_ERR "PCISCC: isr_txcleanup(): mm fucked with our buffers"); + } +#endif + dctlp->stats.tx_packets++; + dctlp->stats.tx_bytes += tdp->skb->len; + kfree_skb(tdp->skb); + tdp->skb = NULL; + } + tdp->flags = (FE | (NO*8)); /* dummy */ + tdp->dataptr = (void *) virt_to_bus(dummybuf); /* paranoia */ + tdp->result = 0; + tdp = tdp->next; + processed++; +#ifdef PCISCC_DEBUG + if (processed>100) { + printk(KERN_ERR "PCISCC: trouble in isr_txcleanup or please reduce bit rate by 20 dB.\n"); + dctlp->dev.start=0; + break; + } +#endif + } + dctlp->dq_tx_cleanup = tdp; + flush_cache_all(); +#ifdef PCISCC_VDEBUG + printk(KERN_INFO "PCISCC: isr_txcleanup: Processed %u frames.\n", processed); +#endif + return; +} + +/* ------------------------------------------------------------------------- */ + +/* called by TIN ISR as bh when TX timeout has occured (watchdog) */ +static void pciscc_bh_txto(void *arg) +{ + struct devctl_t *dctlp = (struct devctl_t *) arg; + + printk(KERN_ERR "PCISCC: Taking interface %s down due to TX hang. Clocking problem?\n", dctlp->name); + dev_close(&dctlp->dev); + return; +} + +/* ------------------------------------------------------------------------- */ + +/* *REALLY* ugly work-around for timer race */ +static __inline__ void pciscc_clear_timer(struct devctl_t *dctlp) +{ + struct chipctl_t *cctlp = dctlp->chip; + unsigned long *iqp; + + /* walk through TX queue eliminating TINs FIXME */ + if (!dctlp->iq_tx || !dctlp->iq_tx_next) return; + for (iqp=dctlp->iq_tx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1)) { /* note wrap-arround */ + if (*iqp & TIN) *iqp = SCCIV_IGNORE; + } + return; +} + +/* ------------------------------------------------------------------------- */ + +/* + * probe TX bitrate of a channel, called from device_open() + * idea behind it: + * load the channels timer with a known value and measure how long it + * takes to reach zero, using the system timer + */ +static long pciscc_probe_txrate(struct devctl_t *dctlp) +{ + struct chipctl_t *cctlp = dctlp->chip; + int channel = dctlp->channel; + struct timeval tv_start; + volatile unsigned long gcc_optimizer_safe; + unsigned long start_time; + long delta_us; + unsigned long long tx_bitrate; + unsigned long l; + + dctlp->txstate = TX_PROBE; + dctlp->tx_mailbox = 0; + start_time = jiffies; + /* assert RTS line */ + l=readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); + l |= RTS; + writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); + writel((probebit*TVALUE), cctlp->io_base+SCCBASE[dctlp->channel]+TIMR); + do_gettimeofday(&tv_start); + writel(STI, cctlp->io_base+SCCBASE[dctlp->channel]+CMDR); + do { + gcc_optimizer_safe = !dctlp->tx_mailbox && ((jiffies-start_time)<1000); + } while (gcc_optimizer_safe); + dctlp->txstate = TX_RESET; + if (!dctlp->tx_mailbox) { + printk(KERN_ERR "PCISCC: probe_txrate(): Timeout probing %s-TxClk. Clocking problem?\n", dctlp->dev.name); + return 0; + + } else { + delta_us = (dctlp->tv.tv_sec - tv_start.tv_sec)*1000000+(dctlp->tv.tv_usec - tv_start.tv_usec); + } + tx_bitrate = 10000*probebit/(delta_us/100); + printk(KERN_INFO "PCISCC: probe_txrate(): tx_bitrate=%ld.\n", (long) tx_bitrate); + if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + } else { +#ifndef FULLDUP_PTT + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l=l & (~RTS); + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); +#endif + } + return tx_bitrate; +} + +/* ------------------------------------------------------------------------- */ + +/* DDI support: immediately assert RTS, downcall from MAC layer */ +static void pciscc_setptt(struct net_device *dev) +{ +#ifdef AX25_ARBITER_NOT_BUGGY + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = dctlp->chip; + unsigned long l; + + l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); + l |= RTS; + writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); +#endif + return; +} + +/* ------------------------------------------------------------------------- */ + +/* report DCD state to DDI layer */ +static unsigned int pciscc_getdcd(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = dctlp->chip; + unsigned long l; + unsigned int dcd; + + l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR); + dcd = dctlp->cfg.cdinv ? !!(l & CD) : !(l & CD); + return dcd; +} + +/* ------------------------------------------------------------------------- */ + +/* return CTS state to DDI layer */ +static unsigned int pciscc_getcts(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = dctlp->chip; + unsigned int cts; + + cts = !!(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS); + return cts; +} + +/* ------------------------------------------------------------------------- */ + +/* report PTT state to DDI layer */ +static unsigned int pciscc_getptt(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = dctlp->chip; + unsigned int ptt; + + ptt = !(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS); + ptt = (dctlp->cfg.cdinv ? ptt : !ptt); + return ptt; +} + +/* ------------------------------------------------------------------------- */ + +/* + * this function is called by DDI layer whenever a media parameter change + * was suggested by either proc/sysctl or MAC/LAP internal cause + */ +static void pciscc_parameter_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + int m,n; + int br, bits; + + printk(KERN_ERR "PCISCC: pciscc_parameter_notify(%s, %u).\n", dev->name, valueno); + switch (valueno) { + case AX25_VALUES_MEDIA_DUPLEX: + if (dev->start) goto reject; + dctlp->cfg.duplex = new; + break; + case AX25_VALUES_MEDIA_TXBITRATE: + case AX25_VALUES_MEDIA_RXBITRATE: + if (dev->start) goto reject; + pciscc_rate2brg(new, &m, &n); + dctlp->cfg.brate_m = m; + dctlp->cfg.brate_n = n; + break; + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + if (br == 0) goto reject; + bits = (br*new)/1000; + if (bits == 0) bits=16; /* two flags */ + if (valueno == AX25_VALUES_MEDIA_TXDELAY) { + dctlp->cfg.txdelval = bits; + } else { + dctlp->cfg.txtailval = bits; + } + break; + case AX25_VALUES_MEDIA_SLOTTIME: + case AX25_VALUES_MEDIA_PPERSISTENCE: + case AX25_VALUES_MEDIA_AUTO_ADJUST: + default: + /* + * We do not care about changes of + * those - they are somebody else's problem (now). + */ + return; + } + +reject: + /* + * (Re)store values from our (changed) + * configuration information + */ + pciscc_update_values(dev); + return; +} + +/* ------------------------------------------------------------------------- */ + +/* convert BRG N and M into bitrate in bps */ +static int pciscc_brg2rate(int m, int n) +{ + int br = xtal / ((n+1) * (1<<m)); + + if (br == 0) br=1; + return br; +} + +/* ------------------------------------------------------------------------- */ + +/* find best BRG N and M match for given bitrate */ +static void pciscc_rate2brg(int rate, int *m, int *n) +{ + int ratio = xtal/rate; + int brg_best_n = 0; + int brg_best_m = 0; + int brg_tmp_n = 0; + int brg_tmp = 0; + int brg_best = 0; + int i; + + *m = *n = 0; + if (ratio > 2097152) return; + for (i=0; i<16; i++) { + brg_tmp_n = (ratio/(1<<i))-1; + if (brg_tmp_n > 63 || brg_tmp_n < 0) continue; + brg_tmp = (brg_tmp_n+1)*(1<<i); + if (abs(brg_best-ratio) < abs(brg_tmp-ratio)) continue; + brg_best = brg_tmp; + brg_best_n = brg_tmp_n; + brg_best_m = i; + } + *m = brg_best_m; + *n = brg_best_n; + return; +} + +/* ------------------------------------------------------------------------- */ + +static void pciscc_update_values(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + int rx_br, tx_br; + int clk_ext; + + AX25_PTR(dev)->hw.fast = 0; + tx_br = rx_br = 0; + clk_ext = (dctlp->cfg.clockmode == CFG_CM_G3RUH + || dctlp->cfg.clockmode == CFG_CM_TCM3105); + if (clk_ext) { + tx_br = rx_br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n); + } + if (dev->start) { + tx_br = dctlp->tx_bitrate; + if (clk_ext) rx_br = tx_br; /* assumption; we have no way to find out */ + } + if (tx_br > 0) { + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, dctlp->cfg.duplex); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, tx_br); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, rx_br); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, dctlp->cfg.txdelval/tx_br); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, dctlp->cfg.txtailval/tx_br); + } + return; +} + +/* ------------------------------------------------------------------------- */ + +/* netdevice UP -> DOWN routine */ +static int pciscc_dev_close(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + + if (dctlp->dev.start) { + pciscc_channel_close(dctlp); + } + dctlp->dev.start=0; + pciscc_update_values(dev); + if (dctlp->chip->initialized && !dctlp->chip->usecnt) { + pciscc_chip_close(dctlp->chip); + } +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* netdevice DOWN -> UP routine */ +static int pciscc_dev_open(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + int res; + + if (!dctlp->chip->initialized) { + if ((res=pciscc_chip_open(dctlp->chip))) return res; + } + if (!dctlp->dev.start) { + if ((res=pciscc_channel_open(dctlp))) return res; + pciscc_update_values(dev); + } +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* netdevice change MTU request */ +static int pciscc_change_mtu(struct net_device *dev, int new_mtu) +{ + dev->mtu=new_mtu; + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* netdevice get statistics request */ +static struct net_device_stats *pciscc_get_stats(struct net_device *dev) +{ + struct devctl_t *dctlp; + + if (!dev || !dev->priv) return NULL; /* paranoia */ + dctlp = (struct devctl_t *) dev->priv; + return &dctlp->stats; +} + +/* ------------------------------------------------------------------------- */ + +/* netdevice register - finish painting netdev structure */ +static int pciscc_dev_init(struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + int br; + + dev->mtu = 1500; + dev->hard_start_xmit = pciscc_xmit; + dev->open = pciscc_dev_open; + dev->stop = pciscc_dev_close; + dev->get_stats = pciscc_get_stats; + dev->change_mtu = pciscc_change_mtu; + dev->do_ioctl = pciscc_dev_ioctl; + dev->set_mac_address = pciscc_dev_set_mac_address; + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->addr_len = AX25_ADDR_LEN; + dev->type = ARPHRD_AX25; + dev->tx_queue_len = 10; + dev->flags = (IFF_BROADCAST | IFF_MULTICAST); + AX25_PTR(dev) = &((struct devctl_t *) dev->priv)->ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + AX25_PTR(dev)->hw.dcd = pciscc_getdcd; + AX25_PTR(dev)->hw.ptt = pciscc_getptt; + AX25_PTR(dev)->hw.rts = pciscc_setptt; + AX25_PTR(dev)->hw.cts = pciscc_getcts; + AX25_PTR(dev)->hw.parameter_change_notify = pciscc_parameter_notify; + dev_init_buffers(dev); + memcpy(&dctlp->cfg, &devcfg_default, sizeof(struct devcfg_t)); + br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n); + pciscc_update_values(dev); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* set device's L2 address */ +static int pciscc_dev_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = addr; + + memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN); + return 0; +} + +/* ------------------------------------------------------------------------- */ +/* + * IOCTLs: + * + * SIOCPCISCCGCCFG PCISCC Get Chip ConFiG + * SIOCPCISCCSCCFG PCISCC Set Chip ConFiG + * SIOCPCISCCGDCFG PCISCC Get Device ConFiG + * SIOCPCISCCSDCFG PCISCC Set Device ConFiG + * SIOCPCISCCSLED PCISCC Set LEDs + * SIOCPCISCCGDSTAT PCISCC Get Device STATus + * SIOCPCISCCDCAL PCISCC Device CALibrate + * SIOCPCISCCLBI PCISCC DSCC-4 Local Bus Interface transaction + */ + +static int pciscc_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = dctlp->chip; + struct devcfg_t dcfg; + struct chipcfg_t ccfg; + struct lbi_xfer lbi; + int channel = dctlp->channel; + int i; + unsigned long l; + unsigned long status; + unsigned long time; + + switch (cmd) { + case SIOCPCISCCGCCFG: + /* return cctlp->cfg structure in user-provided buffer */ + if (copy_to_user(ifr->ifr_data, &cctlp->cfg, sizeof(struct chipcfg_t))) { + return -EFAULT; + } + return 0; + case SIOCPCISCCSCCFG: + /* copy user-provided data buffer to cctlp->cfg */ + if (!suser()) return -EPERM; + for (i=0; i<4; i++) { + if (cctlp->device[i]->dev.start) return -EBUSY; + } + if (copy_from_user(&ccfg, ifr->ifr_data, sizeof(struct chipcfg_t))) { + return -EFAULT; + } + if ((ccfg.rxbufcnt < 4) || (ccfg.rxbufcnt > 128)) return -EINVAL; + if ((ccfg.txbufcnt < 4) || (ccfg.txbufcnt > 256)) return -EINVAL; + if ((ccfg.iqlen < 32) || (ccfg.iqlen > 512) || (ccfg.iqlen % 32 != 0)) return -EINVAL; + if ((ccfg.prichan > 3) || (ccfg.prichan < -1)) return -EINVAL; + if ((ccfg.mfifo_rx_t > 124) || (ccfg.mfifo_rx_t < 4)) return -EINVAL; + memcpy((unsigned char *) &cctlp->cfg, (unsigned char *) &ccfg, sizeof(struct chipcfg_t)); + return 0; + case SIOCPCISCCGDCFG: + /* return dctlp->cfg structure in user-provided buffer */ + if (copy_to_user(ifr->ifr_data, &dctlp->cfg, sizeof(struct devcfg_t))) { + return -EFAULT; + } + return 0; + case SIOCPCISCCSDCFG: + /* copy user-provided data buffer to dctlp->cfg */ + if (!suser()) return -EPERM; + for (i=0; i<4; i++) { + if (cctlp->device[i]->dev.start) return -EBUSY; + } + if (copy_from_user(&dcfg, ifr->ifr_data, sizeof(struct devcfg_t))) { + return -EFAULT; + } + if ((dcfg.coding < CFG_CHCODE_MIN) || (dcfg.coding > CFG_CHCODE_MAX)) return -EINVAL; + if ((dcfg.clockmode < CFG_CM_MIN) || (dcfg.clockmode > CFG_CM_MAX)) return -EINVAL; + if ((dcfg.duplex < CFG_DUPLEX_MIN) || (dcfg.duplex > CFG_DUPLEX_MAX)) return -EINVAL; + if (dcfg.brate_m > CFG_BRATEM_MAX) return -EINVAL; + if (dcfg.brate_n > CFG_BRATEN_MAX) return -EINVAL; + if ((dcfg.txddrive < CFG_TXDDRIVE_MIN) || (dcfg.txddrive > CFG_TXDDRIVE_MAX)) return -EINVAL; + if ((dcfg.mfifo_tx_p < 4) || (dcfg.mfifo_tx_p > 124) || (dcfg.mfifo_tx_p % 4 != 0)) return -EINVAL; + if ((dcfg.mfifo_tx_r < 1) || (dcfg.mfifo_tx_r > dcfg.mfifo_tx_p) || (dcfg.mfifo_tx_r % 4 != 0)) return -EINVAL; + if (dcfg.mfifo_tx_f > (dcfg.mfifo_tx_p-1)) return -EINVAL; + if ((dcfg.cfifo_rx_t != 1) && (dcfg.cfifo_rx_t != 2) && (dcfg.cfifo_rx_t != 4) + && (dcfg.cfifo_rx_t != 16) && (dcfg.cfifo_rx_t != 24) && (dcfg.cfifo_rx_t != 32) + && (dcfg.cfifo_rx_t != 60)) return -EINVAL; + if ((dcfg.txdelmode < CFG_TXDEL_MIN) || (dcfg.txdelmode > CFG_TXDEL_MAX)) return -EINVAL; + if ((dcfg.preamb_rpt!=0) && (dcfg.preamb_rpt!=1) && (dcfg.preamb_rpt!=2) && (dcfg.preamb_rpt!=4) && (dcfg.preamb_rpt!=8)) return -EINVAL; + memcpy((unsigned char *) &dctlp->cfg, (unsigned char *) &dcfg, sizeof(struct devcfg_t)); + pciscc_update_values(dev); + return 0; + case SIOCPCISCCSLED: + /* set channel LEDs */ + if (!suser()) return -EPERM; + l = readl(cctlp->io_base+GPDATA); + switch (channel) { + case 0: l &= ~((1<<0) | (1<<1)); + l |= (((unsigned long) ifr->ifr_data & 3) << 0); + break; + case 1: l &= ~((1<<2) | (1<<3)); + l |= (((unsigned long) ifr->ifr_data & 3) << 2); + break; + case 2: l &= ~((1<<4) | (1<<5)); + l |= (((unsigned long) ifr->ifr_data & 3) << 4); + break; + case 3: l &= ~((1<<6) | (1<<7)); + l |= (((unsigned long) ifr->ifr_data & 3) << 6); + break; + } + writel(l, cctlp->io_base+GPDATA); + return 0; + case SIOCPCISCCGDSTAT: + /* get channel status */ + status = (dctlp->txstate & 0x0f); + l = readl(cctlp->io_base+SCCBASE[channel]+STAR); + if (l & DPLA) status |= STATUS_DPLA; + if (l & RLI) status |= STATUS_RLI; + if (dctlp->cfg.cdinv) { + if (l & CD) status |= STATUS_CD; + } else { + if (!(l & CD)) status |= STATUS_CD; + } + if (l & CTS) status |= STATUS_CTS; + if (readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1) & RTS) status |= STATUS_RTS; + ifr->ifr_data = (void *) status; + return 0; + case SIOCPCISCCDCAL: + /* calibrate */ + if (!suser()) return -EPERM; + if (!dev->start) return -EAGAIN; + if ((dctlp->txstate != TX_IDLE) && (dctlp->txstate != TX_IDLE)) return -EAGAIN; + time = (unsigned long) ifr->ifr_data; + if (time > 0xffffff) return -EINVAL; + writel((time*TVALUE), cctlp->io_base+SCCBASE[channel]+TIMR); + if (time == 0) { + dctlp->txstate = TX_IDLE; + if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { + l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l &= ~RTS; + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); + } else { + /* duplex */ +#ifndef FULLDUP_PTT + l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); + l &= ~RTS; + writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); +#endif + } + } else { + dctlp->txstate = TX_CAL; + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + } + return 0; + case SIOCPCISCCLBI: + /* local bus transaction */ + if (!suser()) return -EPERM; + if (copy_from_user(&lbi, ifr->ifr_data, sizeof(struct lbi_xfer))) { + return -EFAULT; + } + if (lbi.mode == LBI_WRITE) { + writew(lbi.data, cctlp->lbi_base+lbi.addr); + } else { + lbi.data = readl(cctlp->lbi_base+lbi.addr); + if (copy_to_user(ifr->ifr_data, &lbi, sizeof(struct lbi_xfer))) + return -EFAULT; + } + return 0; + default: + return -EINVAL; + } + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* transmit frame, downcall from MAC layer */ +static int pciscc_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct devctl_t *dctlp = (struct devctl_t *) dev->priv; + struct chipctl_t *cctlp = (struct chipctl_t *) dctlp->chip; + int channel = dctlp->channel; + unsigned long flags; + struct tx_desc_t *txdp; + unsigned long l; + + if (!dev->start) { + printk(KERN_ERR "PCISCC: xmit(): Call when iface %s is down\n", dev->name); + kfree_skb(skb); + return 0; + } + if (!skb) { + printk(KERN_ERR "PCISCC: xmit(): L2 handed us a NULL skb!\n"); + return 0; + } + if (!skb->len) { + printk(KERN_ERR "PCISCC: xmit(): L2 tried to trick us into sending a skb of len 0!\n"); + kfree_skb(skb); + return 0; + } + save_flags(flags); + cli(); + txdp=dctlp->dq_tx_last->next; + if ((txdp == dctlp->dq_tx_cleanup) || (txdp->next == dctlp->dq_tx_cleanup) || (txdp->result & C) || (txdp->next->result & C)) { + /* desriptor chain "full" */ +#ifdef PCISCC_VDEBUG + printk(KERN_INFO "PCISCC: xmit(): Dropping frame due to full TX queue interface %s.\n", dev->name); +#endif + dctlp->stats.tx_dropped++; + kfree_skb(skb); + restore_flags(flags); + return 0; + } + /* prepare TX descriptor */ + txdp->result=0; + txdp->skb=skb; + txdp->flags=FE | (BNO*skb->len); + txdp->dataptr=(void *) virt_to_bus(skb->data); + dctlp->dq_tx_last=txdp; + flush_cache_all(); + if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { + /* in full duplex mode we can start frame transmit at once */ + dctlp->txstate=TX_XMIT; + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); + switch (channel) { + case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA); + break; + case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA); + break; + case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA); + break; + case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA); + break; + } + } else if ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) || !dctlp->cfg.txdelval) { + /* Hardware TX-delay control using RTS/CTS or zero TX-delay */ + if (dctlp->txstate == TX_IDLE) { + writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + } + dctlp->txstate=TX_XMIT; + if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) { + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); + } + switch (channel) { + case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA); + break; + case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA); + break; + case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA); + break; + case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA); + break; + } + } else { + /* half duplex, software txdelay */ + switch (dctlp->txstate) { + case TX_RESET: + /* TX not initialized */ + printk(KERN_INFO "PCISCC: xmit(): %s: Cannot transmit frame since TX is not inititalized!\n", dev->name); + case TX_IDLE: + /* TX is idle, key up and start txdelay */ + dctlp->txstate=TX_DELAY; + l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); + writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); + writel(dctlp->cfg.txdelval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + break; + case TX_DELAY: + /* tx is already keyed but not yet ready */ + break; + case TX_TAIL: + /* tx is currently transmitting closing txtail sequence */ + writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); + pciscc_clear_timer(dctlp); + writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); + case TX_XMIT: /* note fall-through */ + /* tx is already transmitting preamble or data */ + dctlp->txstate=TX_XMIT; + switch (channel) { + case 0: writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA); + break; + case 1: writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA); + break; + case 2: writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA); + break; + case 3: writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA); + break; + } + break; + case TX_PROBE: + case TX_CAL: + /* we are busy with diagnostic stuff */ + break; + default: + /* should not occur */ + printk(KERN_ERR "PCISCC: Unhandled txstate in xmit() iface=%s.\n", dev->name); + } + } + /* skb will be kfree()d by isr_txcleanup after transmission */ + restore_flags(flags); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +/* called by receiver function - prepare received skb and fire up to L2 */ +static __inline__ void pciscc_rx_skb(struct sk_buff *skb, struct devctl_t *dctlp) +{ + if (!skb) { + printk(KERN_ERR "PCISCC: rx_skb(): Received NULL skb iface=%s.\n", dctlp->name); + return; + } + dctlp->stats.rx_packets++; + dctlp->stats.rx_bytes += skb->len; + skb->protocol = htons(ETH_P_AX25); + skb->dev = &dctlp->dev; + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + netif_rx(skb); + return; +} + +/* ------------------------------------------------------------------------- */ + +/* Initialize pciscc control device */ +#ifdef MODULE +int pciscc_init(void) +#else /* !MODULE */ +__initfunc(int pciscc_init(struct net_device *dummy)) +#endif /* !MODULE */ +{ + int i,j; + int devnum; + struct pci_dev *pcidev = NULL; + + printk(KERN_ERR "PCISCC: version %s\n", PCISCC_VERSION); + if (!(dummybuf = kmalloc(256, GFP_DMA | GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: init: Could not get memory for dummybuf.\n"); + return -ENOMEM; + } + chipcnt=0; + j=0; + while ((pcidev = pci_find_device(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_PEB20534H, pcidev))) { + if (!(chipctl[chipcnt]=kmalloc(sizeof(struct chipctl_t), GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: Out of memory allocating chipctl-structure\n"); +#ifdef MODULE + cleanup_module(); +#endif + return -ENOMEM; + } + memset(chipctl[chipcnt], 0, sizeof(struct chipctl_t)); + chipctl[chipcnt]->pcidev=pcidev; + memcpy(&chipctl[chipcnt]->cfg, &chipcfg_default, sizeof(struct chipcfg_t)); + for (i=0;i<4;i++) { + devnum = chipcnt*4+i; + if (!(devctl[devnum]=kmalloc(sizeof(struct devctl_t), GFP_KERNEL))) { + printk(KERN_ERR "PCISCC: Out of memory allocating devctl-structure.\n"); +#ifdef MODULE + cleanup_module(); +#endif + return -ENOMEM; + } + memset(devctl[devnum], 0, sizeof(struct devctl_t)); + do { + sprintf(devctl[devnum]->name, "dscc%u", devnum); + j++; + } while (dev_get(devctl[devnum]->name) && j<60); /* find free device name */ + if (j>=60) { /* none left */ + printk(KERN_ERR "PCISCC: Could not find free netdev name.\n"); +#ifdef MODULE + cleanup_module(); +#endif + return -EEXIST; + } + devctl[devnum]->dev.priv = (void *) devctl[devnum]; + devctl[devnum]->dev.name = devctl[devnum]->name; + devctl[devnum]->dev.init = pciscc_dev_init; + devctl[devnum]->chip = chipctl[chipcnt]; + devctl[devnum]->channel = i; + chipctl[chipcnt]->device[i] = devctl[devnum]; + register_netdev(&devctl[devnum]->dev); + } + chipcnt++; + } + printk(KERN_ERR "PCISCC: %u controller(s) found.\n", chipcnt); + return 0; +} + +/* ------------------------------------------------------------------------- */ + +#ifndef MODULE +__initfunc(void pciscc_setup(char *str, int *ints)) +{ + return; +} +#endif + +/* ------------------------------------------------------------------------- */ + + +/***************************************************************************** + * Module stuff. * + *****************************************************************************/ + +#ifdef MODULE +MODULE_AUTHOR("Jens David, DG1KJD <dg1kjd@afthd.tu-darmstadt.de>"); +MODULE_DESCRIPTION("AX.25 Device Driver for Siemens PEB-20534H (DSCC-4) based SCC cards"); +MODULE_SUPPORTED_DEVICE("pciscc"); +MODULE_PARM(xtal, "i"); +MODULE_PARM(probebit, "i"); +MODULE_PARM(txtimeout, "i"); + +int init_module(void) +{ + return pciscc_init(); +} + +void cleanup_module(void) +{ + int i; + struct chipctl_t *cctlp; + struct devctl_t *dctlp; + + for (i=0; i<4*chipcnt; i++) { + dctlp=devctl[i]; + pciscc_dev_close(&dctlp->dev); + if (dctlp) { + unregister_netdev(&dctlp->dev); + kfree(dctlp); + devctl[i]=NULL; + } + } + for (i=0; i<chipcnt; i++) { + cctlp=chipctl[i]; + if (cctlp) { + if (cctlp->irq) { + free_irq(cctlp->irq, (void *) cctlp); + cctlp->irq=0; + } + if (cctlp->io_base) { + iounmap(cctlp->io_base); + cctlp->io_base=0; + } + if (cctlp->lbi_base) { + iounmap(cctlp->lbi_base); + cctlp->lbi_base=0; + } + kfree(cctlp); + chipctl[i]=NULL; + } + } + if (dummybuf) { + kfree(dummybuf); + } + return; +} +#endif /* MODULE */ diff --git a/drivers/net/hamradio/scc.c b/drivers/net/hamradio/scc.c index d256ef600..4cf69753c 100644 --- a/drivers/net/hamradio/scc.c +++ b/drivers/net/hamradio/scc.c @@ -1,9 +1,10 @@ #define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" -#define VERSION "3.0" +#define VERSION "4.0" +#define BANNER "Z8530 SCC driver version "VERSION".dl1bke by DL1BKE\n" /* - * Please use z8530drv-utils-3.0 with this version. + * Please use z8530drv-utils-4.0 with this version. * ------------------ * * You can find a subset of the documentation in @@ -103,9 +104,8 @@ flags that aren't... Restarting the DPLL does not help either, it resynchronizes too slow and the first received frame gets lost. - 2000-02-13 Fixed for new network driver interface changes, still - does TX timeouts itself since it uses its own queue - scheme. + 1999-02-21 Started to implement the new AX.25 device interface + 2000-07-18 Ported to 2.4.x Thanks to all who contributed to this driver with ideas and bug reports! @@ -113,7 +113,7 @@ NB -- if you find errors, change something, please let me know first before you distribute it... And please don't touch the version number. Just replace my callsign in - "v3.0.dl1bke" with your own. Just to avoid confusion... + "v4.0.dl1bke" with your own. Just to avoid confusion... If you want to add your modification to the linux distribution please (!) contact me first. @@ -131,21 +131,23 @@ Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU Internet: jreuter@yaina.de - www : http://yaina.de/jreuter + www : http://yaina.de/jreuter/ */ /* ----------------------------------------------------------------------- */ -#undef SCC_LDELAY /* slow it even a bit more down */ #undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ -#define SCC_MAXCHIPS 4 /* number of max. supported chips */ #define SCC_BUFSIZE 384 /* must not exceed 4096 */ #undef SCC_DEBUG #define SCC_DEFAULT_CLOCK 4915200 /* default pclock if nothing is specified */ +#define SCC_SIMPLE_MAC /* no rts/cts control by DDI layer */ +#define SCC_WATCHDOG_TIMEOUT 10 + /* ten seconds */ + /* ----------------------------------------------------------------------- */ #include <linux/config.h> @@ -162,6 +164,7 @@ #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/spinlock.h> #include <linux/skbuff.h> #include <linux/netdevice.h> @@ -170,196 +173,199 @@ #include <linux/socket.h> #include <linux/init.h> -#include <linux/scc.h> -#include "z8530.h" +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> #include <net/ax25.h> +#include <net/ax25dev.h> + #include <asm/irq.h> + #include <asm/system.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/bitops.h> -#include <linux/ctype.h> -#include <linux/kernel.h> -#include <linux/proc_fs.h> +#include <linux/scc.h> +#include "z8530.h" -static const char banner[] __initdata = KERN_INFO "AX.25: Z8530 SCC driver version "VERSION".dl1bke\n"; +int scc_init(void); -static void t_dwait(unsigned long); -static void t_txdelay(unsigned long); -static void t_tail(unsigned long); -static void t_busy(unsigned long); -static void t_maxkeyup(unsigned long); -static void t_idle(unsigned long); static void scc_tx_done(struct scc_channel *); -static void scc_start_tx_timer(struct scc_channel *, void (*)(unsigned long), unsigned long); -static void scc_start_maxkeyup(struct scc_channel *); -static void scc_start_defer(struct scc_channel *); - -static void z8530_init(void); +static void scc_kick_tx(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(struct scc_channel *), unsigned long); +static void scc_tail(struct scc_channel *scc); +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc); +static void scc_set_rts(struct scc_channel *scc); +#endif static void init_channel(struct scc_channel *scc); static void scc_key_trx (struct scc_channel *scc, char tx); static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); static void scc_init_timer(struct scc_channel *scc); -static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev); +static unsigned int scc_ddi_report_dcd(struct net_device *); +static unsigned int scc_ddi_report_ptt(struct net_device *); +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *); +static void scc_ddi_set_rts(struct net_device *); +#endif +static void scc_ddi_set_bitrate(struct net_device *, unsigned int); +static void scc_ddi_param_update(struct net_device *); +static void scc_ddi_param_notify(struct net_device *, int, int, int); + +static int scc_net_setup(struct scc_channel *scc); static int scc_net_init(struct net_device *dev); static int scc_net_open(struct net_device *dev); static int scc_net_close(struct net_device *dev); static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); static int scc_net_tx(struct sk_buff *skb, struct net_device *dev); +static void scc_net_timeout(struct net_device *dev); static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); static int scc_net_set_mac_address(struct net_device *dev, void *addr); static struct net_device_stats * scc_net_get_stats(struct net_device *dev); -static unsigned char *SCC_DriverName = "scc"; +static int scc_proc_intvec_nchips(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_modem(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_port(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); +static int scc_proc_intvec_chip(ctl_table *, int *, int, void *, size_t *, void *, size_t, void **); -static struct irqflags { unsigned char used : 1; } Ivec[16]; - -static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ +struct scc_ctrl_proc_tables { + ctl_table parent[2]; /* /proc/sys/net/dev/ */ + ctl_table dir[2]; /* /.../dev/z8530drv/ */ + ctl_table chip[10]; /* /.../dev/z8530drv/chip<n> */ +}; + +static int scc_irq_used[16]; static struct scc_ctrl { - io_port chan_A; - io_port chan_B; - int irq; -} SCC_ctrl[SCC_MAXCHIPS+1]; + struct scc_channel channel_a; + struct scc_channel channel_b; + struct scc_ctrl_proc_tables proc_tables; + + struct ctl_table_header * proc_table_head; +} **scc_ctrl; + + +static struct ctl_table_header *scc_proc_table_header; +static int maxchips = 4; +static int Nchips = 0; +static long IO_Delay = 0; +static spinlock_t IO_Spinlock = SPIN_LOCK_UNLOCKED; + +static ctl_table scc_proc_parent_table[]; +static ctl_table scc_proc_nchips_table[]; -static unsigned char Driver_Initialized; -static int Nchips; -static io_port Vector_Latch; /* ******************************************************************** */ /* * Port Access Functions * */ /* ******************************************************************** */ +static inline unsigned char +Inb(io_port port) +{ + int r; + + r = inb(port); + if (IO_Delay) udelay(IO_Delay); + return r; +} + +static inline void +Outb(io_port port, unsigned char val) +{ + outb(val, port); + if (IO_Delay) udelay(IO_Delay); +} + /* These provide interrupt save 2-step access to the Z8530 registers */ -static inline unsigned char InReg(io_port port, unsigned char reg) +static unsigned char +InReg(io_port port, unsigned char reg) { unsigned long flags; unsigned char r; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); - udelay(SCC_LDELAY); - r=Inb(port); - udelay(SCC_LDELAY); -#else - Outb(port, reg); - r=Inb(port); -#endif - restore_flags(flags); + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + r=inb(port); + udelay(IO_Delay); + } else { + outb(reg, port); + r=inb(port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); return r; } -static inline void OutReg(io_port port, unsigned char reg, unsigned char val) +static void +OutReg(io_port port, unsigned char reg, unsigned char val) { unsigned long flags; - - save_flags(flags); - cli(); -#ifdef SCC_LDELAY - Outb(port, reg); udelay(SCC_LDELAY); - Outb(port, val); udelay(SCC_LDELAY); -#else - Outb(port, reg); - Outb(port, val); -#endif - restore_flags(flags); + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + outb(val, port); + udelay(IO_Delay); + } else { + outb(reg, port); + outb(val, port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); } -static inline void wr(struct scc_channel *scc, unsigned char reg, - unsigned char val) +static inline +void wr(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); } -static inline void or(struct scc_channel *scc, unsigned char reg, unsigned char val) +static inline void +or(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); } -static inline void cl(struct scc_channel *scc, unsigned char reg, unsigned char val) +static inline void +cl(struct scc_channel *scc, unsigned char reg, unsigned char val) { OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); } /* ******************************************************************** */ -/* * Some useful macros * */ -/* ******************************************************************** */ - -static inline void scc_discard_buffers(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); - - if (scc->tx_buff != NULL) - { - dev_kfree_skb(scc->tx_buff); - scc->tx_buff = NULL; - } - - while (skb_queue_len(&scc->tx_queue)) - dev_kfree_skb(skb_dequeue(&scc->tx_queue)); - - restore_flags(flags); -} - - - -/* ******************************************************************** */ /* * Interrupt Service Routines * */ /* ******************************************************************** */ -/* ----> subroutines for the interrupt handlers <---- */ - -static inline void scc_notify(struct scc_channel *scc, int event) -{ - struct sk_buff *skb; - char *bp; - - if (scc->kiss.fulldup != KISS_DUPLEX_OPTIMA) - return; - - skb = dev_alloc_skb(2); - if (skb != NULL) - { - bp = skb_put(skb, 2); - *bp++ = PARAM_HWEVENT; - *bp++ = event; - scc_net_rx(scc, skb); - } else - scc->stat.nospace++; -} - static inline void flush_rx_FIFO(struct scc_channel *scc) { int k; - for (k=0; k<3; k++) + for (k=0; k<(scc->enhanced? 8:4); k++) Inb(scc->data); if(scc->rx_buff != NULL) /* did we receive something? */ { - scc->stat.rxerrs++; /* then count it as an error */ - dev_kfree_skb_irq(scc->rx_buff); + scc->stat.rxerrs++; /* then count it as an error */ + kfree_skb(scc->rx_buff); scc->rx_buff = NULL; } } static void start_hunt(struct scc_channel *scc) { - if ((scc->modem.clocksrc != CLK_EXTERNAL)) + if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)) OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ - or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ + or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ } /* ----> four different interrupt handlers for Tx, Rx, changing of */ @@ -377,36 +383,9 @@ static inline void scc_txint(struct scc_channel *scc) if (skb == NULL) { - skb = skb_dequeue(&scc->tx_queue); - scc->tx_buff = skb; netif_wake_queue(scc->dev); - - if (skb == NULL) - { - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - if (skb->len == 0) /* Paranoia... */ - { - dev_kfree_skb_irq(skb); - scc->tx_buff = NULL; - scc_tx_done(scc); - Outb(scc->ctrl, RES_Tx_P); - return; - } - - scc->stat.tx_state = TXS_ACTIVE; - - OutReg(scc->ctrl, R0, RES_Tx_CRC); - /* reset CRC generator */ - or(scc,R10,ABUNDER); /* re-install underrun protection */ - Outb(scc->data,*skb->data); /* send byte */ - skb_pull(skb, 1); - - if (!scc->enhanced) /* reset EOM latch */ - Outb(scc->ctrl,RES_EOM_L); + scc_tx_done(scc); + Outb(scc->ctrl, RES_Tx_P); return; } @@ -416,11 +395,11 @@ static inline void scc_txint(struct scc_channel *scc) { Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ cl(scc, R10, ABUNDER); /* send CRC */ - dev_kfree_skb_irq(skb); + dev_kfree_skb(skb); scc->tx_buff = NULL; - scc->stat.tx_state = TXS_NEWFRAME; /* next frame... */ + scc_kick_tx(scc); /* next frame */ return; - } + } /* send octet */ @@ -447,25 +426,23 @@ static inline void scc_exint(struct scc_channel *scc) /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ - if ((changes & SYNC_HUNT) && scc->kiss.softdcd) + if ((changes & SYNC_HUNT) && scc->modem.softdcd) { if (status & SYNC_HUNT) { scc->dcd = 0; flush_rx_FIFO(scc); - if ((scc->modem.clocksrc != CLK_EXTERNAL)) + if ((scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL)) OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ } else { scc->dcd = 1; } - - scc_notify(scc, scc->dcd? HWEV_DCD_OFF:HWEV_DCD_ON); } /* DCD: on = start to receive packet, off = ABORT condition */ /* (a successfully received packet generates a special condition int) */ - if((changes & DCD) && !scc->kiss.softdcd) /* DCD input changed state */ + if((changes & DCD) && !scc->modem.softdcd) /* DCD input changed state */ { if(status & DCD) /* DCD is now ON */ { @@ -476,8 +453,6 @@ static inline void scc_exint(struct scc_channel *scc) flush_rx_FIFO(scc); scc->dcd = 0; } - - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); } #ifdef notdef @@ -489,24 +464,24 @@ static inline void scc_exint(struct scc_channel *scc) if (chg_and_stat & CTS) /* CTS is now ON */ { - if (scc->kiss.txdelay == 0) /* zero TXDELAY = wait for CTS */ - scc_start_tx_timer(scc, t_txdelay, 0); + if (scc->modem.tx_delay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_tx_delay, 0); } #endif if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) { - scc->stat.tx_under++; /* oops, an underrun! count 'em */ + scc->stat.tx_under++; /* oops, an underrun! count 'em */ Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ if (scc->tx_buff != NULL) { - dev_kfree_skb_irq(scc->tx_buff); + dev_kfree_skb(scc->tx_buff); scc->tx_buff = NULL; } or(scc,R10,ABUNDER); - scc_start_tx_timer(scc, t_txdelay, 0); /* restart transmission */ + scc_tx_done(scc); } scc->status = status; @@ -517,11 +492,12 @@ static inline void scc_exint(struct scc_channel *scc) /* Receiver interrupt handler */ static inline void scc_rxint(struct scc_channel *scc) { + unsigned char status; struct sk_buff *skb; scc->stat.rxints++; - if((scc->wreg[5] & RTS) && scc->kiss.fulldup == KISS_DUPLEX_HALF) + if((scc->wreg[5] & RTS) && (scc->modem.fullduplex == 0)) { Inb(scc->data); /* discard char */ or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ @@ -529,7 +505,6 @@ static inline void scc_rxint(struct scc_channel *scc) } skb = scc->rx_buff; - if (skb == NULL) { skb = dev_alloc_skb(scc->stat.bufsize); @@ -541,167 +516,116 @@ static inline void scc_rxint(struct scc_channel *scc) or(scc, R3, ENT_HM); return; } - - scc->rx_buff = skb; - *(skb_put(skb, 1)) = 0; /* KISS data */ - } - if (skb->len >= scc->stat.bufsize) - { -#ifdef notdef - printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); -#endif - dev_kfree_skb_irq(skb); - scc->rx_buff = NULL; - Inb(scc->data); - or(scc, R3, ENT_HM); - return; + scc->rx_buff = skb; } - *(skb_put(skb, 1)) = Inb(scc->data); -} - - -/* Receive Special Condition interrupt handler */ -static inline void scc_spint(struct scc_channel *scc) -{ - unsigned char status; - struct sk_buff *skb; - scc->stat.spints++; - - status = InReg(scc->ctrl,R1); /* read receiver status */ - - Inb(scc->data); /* throw away Rx byte */ - skb = scc->rx_buff; - - if(status & Rx_OVR) /* receiver overrun */ + while(InReg(scc->ctrl, R0) & Rx_CH_AV) { - scc->stat.rx_over++; /* count them */ - or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ - - if (skb != NULL) - dev_kfree_skb_irq(skb); - scc->rx_buff = skb = NULL; - } + status = InReg(scc->ctrl, R1); - if(status & END_FR && skb != NULL) /* end of frame */ - { - /* CRC okay, frame ends on 8 bit boundary and received something ? */ - - if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + if (skb->len > scc->stat.bufsize) { - /* ignore last received byte (first of the CRC bytes) */ - skb_trim(skb, skb->len-1); - scc_net_rx(scc, skb); - scc->rx_buff = NULL; - scc->stat.rxframes++; - } else { /* a bad frame */ - dev_kfree_skb_irq(skb); +#ifdef notdef + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); +#endif + kfree_skb(skb); scc->rx_buff = NULL; - scc->stat.rxerrs++; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; } - } - Outb(scc->ctrl,ERR_RES); -} + *(skb_put(skb, 1)) = Inb(scc->data); + if (status & Rx_OVR) + { + scc->stat.rx_over++; + or(scc, R3, ENT_HM); + if (skb != NULL) kfree_skb(skb); + scc->rx_buff = NULL; + Outb(scc->ctrl,ERR_RES); + } -/* ----> interrupt service routine for the Z8530 <---- */ + if (status & END_FR && skb != NULL) + { + /* CRC okay, frame ends on 8 bit boundary and received something ? */ -static void scc_isr_dispatch(struct scc_channel *scc, int vector) -{ - switch (vector & VECTOR_MASK) - { - case TXINT: scc_txint(scc); break; - case EXINT: scc_exint(scc); break; - case RXINT: scc_rxint(scc); break; - case SPINT: scc_spint(scc); break; + if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + { + /* ignore last received byte (first of the CRC bytes) */ + skb_trim(skb, skb->len-1); + scc_net_rx(scc, skb); + scc->rx_buff = NULL; + scc->stat.rxframes++; + } else { /* a bad frame */ + kfree_skb(skb); + scc->rx_buff = NULL; + scc->stat.rxerrs++; + Outb(scc->ctrl,ERR_RES); + } + } } } -/* If the card has a latch for the interrupt vector (like the PA0HZP card) - use it to get the number of the chip that generated the int. - If not: poll all defined chips. - */ -#define SCC_IRQTIMEOUT 30000 +#define SCC_IRQTIMEOUT 5000 static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) { - unsigned char vector; + unsigned char istat; struct scc_channel *scc; - struct scc_ctrl *ctrl; + struct scc_ctrl **ctrl_p, *ctrl; int k; - if (Vector_Latch) - { - for(k=0; k < SCC_IRQTIMEOUT; k++) - { - Outb(Vector_Latch, 0); /* Generate INTACK */ - - /* Read the vector */ - if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; - if (vector & 0x01) break; - - scc=&SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; - - scc_isr_dispatch(scc, vector); - - OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ - } - - if (k == SCC_IRQTIMEOUT) - printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); - - return; - } - /* Find the SCC generating the interrupt by polling all attached SCCs * reading RR3A (the interrupt pending register) */ - ctrl = SCC_ctrl; - while (ctrl->chan_A) + ctrl = *scc_ctrl; + for (ctrl_p=scc_ctrl; *ctrl_p; ctrl_p++) { - if (ctrl->irq != irq) - { - ctrl++; - continue; - } + ctrl = *ctrl_p; + if (ctrl->channel_a.irq != irq) continue; - scc = NULL; - for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + for (k = 0; k < SCC_IRQTIMEOUT; k++) { - vector=InReg(ctrl->chan_B,R2); /* Read the vector */ - if (vector & 0x01) break; - - scc = &SCC_Info[vector >> 3 ^ 0x01]; - if (!scc->dev) break; + istat = InReg(ctrl->channel_a.ctrl, R3); + if (!(istat & (CHARxIP|CHATxIP|CHAEXT|CHBRxIP|CHBTxIP|CHBEXT))) + break; + + scc = &ctrl->channel_a; + if ((istat & (CHARxIP|CHATxIP|CHAEXT)) && scc->dev) + { + scc = &ctrl->channel_a; + if (istat & CHARxIP) + scc_rxint(scc); + if (istat & CHATxIP) + scc_txint(scc); + if (istat & CHAEXT) + scc_exint(scc); + } - scc_isr_dispatch(scc, vector); + scc = &ctrl->channel_b; + if ((istat & (CHBRxIP|CHBTxIP|CHBEXT)) && scc->dev) + { + scc = &ctrl->channel_a; + if (istat & CHBRxIP) + scc_rxint(scc); + if (istat & CHBTxIP) + scc_txint(scc); + if (istat & CHBEXT) + scc_exint(scc); + } } + if (k == SCC_IRQTIMEOUT) { printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); break; } - - /* This looks weird and it is. At least the BayCom USCC doesn't - * use the Interrupt Daisy Chain, thus we'll have to start - * all over again to be sure not to miss an interrupt from - * (any of) the other chip(s)... - * Honestly, the situation *is* braindamaged... - */ - - if (scc != NULL) - { - OutReg(scc->ctrl,R0,RES_H_IUS); - ctrl = SCC_ctrl; - } else - ctrl++; } } @@ -724,24 +648,131 @@ static inline void set_brg(struct scc_channel *scc, unsigned int tc) static inline void set_speed(struct scc_channel *scc) { - disable_irq(scc->irq); + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); - if (scc->modem.speed > 0) /* paranoia... */ - set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + if (scc->modem.rx_speed > 0) /* paranoia... */ + set_brg(scc, (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2); - enable_irq(scc->irq); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); } +/* + Construct value for clock mode register (R11) + + Here are some common settings: + ------------------------------ + + normal half duplex operation: + tx_clock_source = CLOCK_SOURCE_DPLL; + rx_clock_source = CLOCK_SOURCE_DPLL; + trxc_pin_mode = TRXCP_MODE_DPLL_OUT; + + note: this seems bogus, we used to switch to + tx_clock_source = CLOCK_SOURCE_BRG; + rx_clock_source = CLOCK_SOURCE_DPLL; + trxc_pin_mode = TRXCP_MODE_BRG_OUT; + on transmit which appears to be correct for Rx as well. + + normal operation for modems w/ own clock (re)generation on BayCom cards + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_TRxC + trxc_pin_mode = TRXCP_MODE_IN + + normal operation for modems w/ own clock (re)generation on elswhere + tx_clock_source = CLOCK_SOURCE_TRxC + rx_clock_source = CLOCK_SOURCE_RTxC + trxc_pin_mode = TRXCP_MODE_IN + + fullduplex mode for modems w/o own clock generator + (aka "divider mode") - BayCom style: + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_DPLL + trxc_pin_mode = TRXCP_MODE_DPLL_OUT; + (divider divides by 2) + + fullduplex mode for modems w/o own clock generator + (aka "divider mode") - usual style: + tx_clock_source = CLOCK_SOURCE_RTxC + rx_clock_source = CLOCK_SOURCE_DPLL + trxc_pin_mode = TRXCP_MODE_BRG_OUT + (BRG clock = 32 * DPLL clock, divider divides by 16) + + Note that only TRxC can be programmed to output, RTxC + is _always_ input +*/ -/* ----> initialize a SCC channel <---- */ +static int scc_calc_r11(int rx_clock_source, int tx_clock_source, int trxc_pin_mode) +{ + int clockmode = 0; -static inline void init_brg(struct scc_channel *scc) + switch(tx_clock_source) + { + case CLOCK_SOURCE_RTxC: + break; + case CLOCK_SOURCE_TRxC: + clockmode = TCTRxCP; + break; + case CLOCK_SOURCE_BRG: + clockmode = TCBR; + break; + case CLOCK_SOURCE_DPLL: + clockmode = TCDPLL; + } + + + switch(rx_clock_source) + { + case CLOCK_SOURCE_RTxC: + break; + case CLOCK_SOURCE_TRxC: + clockmode |= RCTRxCP; + break; + case CLOCK_SOURCE_BRG: + clockmode |= RCBR; + break; + case CLOCK_SOURCE_DPLL: + clockmode |= RCDPLL; + } + + + switch(trxc_pin_mode) + { + case TRXCP_MODE_IN: + break; + case TRXCP_MODE_TXC_OUT: + clockmode |= TRxCTC|TRxCOI; + break; + case TRXCP_MODE_BRG_OUT: + clockmode |= TRxCBR|TRxCOI; + break; + case TRXCP_MODE_DPLL_OUT: + clockmode |= TRxCDP|TRxCOI; + break; + } + + return clockmode; +} + + +static void scc_init_brg_and_dpll(struct scc_channel *scc) { wr(scc, R14, BRSRC); /* BRG source = PCLK */ - OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ - OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ + if (scc->modem.tx_clock_source == CLOCK_SOURCE_DPLL || + scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL) + { + OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ + OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ + } else { + OutReg(scc->ctrl, R14, DISDPLL|scc->wreg[R14]); + } } + +/* ----> initialize an SCC channel <---- */ + + /* * Initialization according to the Z8530 manual (SGS-Thomson's version): * @@ -789,10 +820,10 @@ static inline void init_brg(struct scc_channel *scc) static void init_channel(struct scc_channel *scc) { - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); + unsigned long flags; - disable_irq(scc->irq); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + del_timer(&scc->tx_timer); wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ wr(scc,R1,0); /* no W/REQ operation */ @@ -801,56 +832,11 @@ static void init_channel(struct scc_channel *scc) wr(scc,R6,0); /* SDLC address zero (not used) */ wr(scc,R7,FLAG); /* SDLC flag value */ wr(scc,R9,VIS); /* vector includes status */ - wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ + wr(scc,R10,(scc->modem.nrz_mode? NRZI : NRZ)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ wr(scc,R14, 0); - -/* set clock sources: - - CLK_DPLL: normal halfduplex operation - - RxClk: use DPLL - TxClk: use DPLL - TRxC mode DPLL output - - CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) - - BayCom: others: - - TxClk = pin RTxC TxClk = pin TRxC - RxClk = pin TRxC RxClk = pin RTxC - - - CLK_DIVIDER: - RxClk = use DPLL - TxClk = pin RTxC - - BayCom: others: - pin TRxC = DPLL pin TRxC = BRG - (RxClk * 1) (RxClk * 32) -*/ - - - switch(scc->modem.clocksrc) - { - case CLK_DPLL: - wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); - init_brg(scc); - break; - - case CLK_DIVIDER: - wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); - init_brg(scc); - break; - - case CLK_EXTERNAL: - wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); - OutReg(scc->ctrl, R14, DISDPLL); - break; - - } - - set_speed(scc); /* set baudrate */ + wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode)); + scc_init_brg_and_dpll(scc); if(scc->enhanced) { @@ -858,7 +844,7 @@ static void init_channel(struct scc_channel *scc) wr(scc,R7,AUTOEOM); } - if(scc->kiss.softdcd || (InReg(scc->ctrl,R0) & DCD)) + if(scc->modem.softdcd || (InReg(scc->ctrl,R0) & DCD)) /* DCD is now ON */ { start_hunt(scc); @@ -866,7 +852,7 @@ static void init_channel(struct scc_channel *scc) /* enable ABORT, DCD & SYNC/HUNT interrupts */ - wr(scc,R15, BRKIE|TxUIE|(scc->kiss.softdcd? SYNCIE:DCDIE)); + wr(scc,R15, BRKIE|TxUIE|(scc->modem.softdcd? SYNCIE:DCDIE)); Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ @@ -875,14 +861,13 @@ static void init_channel(struct scc_channel *scc) scc->status = InReg(scc->ctrl,R0); /* read initial status */ - or(scc,R9,MIE); /* master interrupt enable */ - - scc_init_timer(scc); - - enable_irq(scc->irq); -} + or(scc,R9,MIE); /* master interrupt enable */ + scc_init_timer(scc); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + set_speed(scc); /* set baudrate */ +} /* ******************************************************************** */ @@ -895,17 +880,20 @@ static void init_channel(struct scc_channel *scc) static void scc_key_trx(struct scc_channel *scc, char tx) { + unsigned long flags; unsigned int time_const; if (scc->brand & PRIMUS) - Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); + Outb(scc->ctrl + 4, scc->special_option | (tx? 0x80 : 0)); - if (scc->modem.speed < 300) - scc->modem.speed = 1200; - time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; + if (tx) + time_const = (unsigned) (scc->clock / (scc->modem.tx_speed * 2)) - 2; + else + time_const = (unsigned) (scc->clock / (scc->modem.rx_speed * 64)) - 2; - disable_irq(scc->irq); + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); if (tx) { @@ -913,7 +901,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx) or(scc, R15, TxUIE); } - if (scc->modem.clocksrc == CLK_DPLL) + if (scc->modem.rx_clock_source == CLOCK_SOURCE_DPLL) { /* force simplex operation */ if (tx) { @@ -927,7 +915,7 @@ static void scc_key_trx(struct scc_channel *scc, char tx) wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); /* By popular demand: tx_inhibit */ - if (scc->kiss.tx_inhibit) + if (scc->modem.tx_inhibit) { or(scc,R5, TxENAB); scc->wreg[R5] |= RTS; @@ -943,10 +931,10 @@ static void scc_key_trx(struct scc_channel *scc, char tx) wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); #ifndef CONFIG_SCC_TRXECHO - if (scc->kiss.softdcd) + if (scc->modem.softdcd) #endif { - or(scc,R15, scc->kiss.softdcd? SYNCIE:DCDIE); + or(scc,R15, scc->modem.softdcd? SYNCIE:DCDIE); start_hunt(scc); } } @@ -954,14 +942,14 @@ static void scc_key_trx(struct scc_channel *scc, char tx) if (tx) { #ifdef CONFIG_SCC_TRXECHO - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + if (scc->modem.fullduplex == 0) { cl(scc, R3, RxENABLE); cl(scc, R15, DCDIE|SYNCIE); } #endif - if (scc->kiss.tx_inhibit) + if (scc->modem.tx_inhibit) { or(scc,R5, TxENAB); scc->wreg[R5] |= RTS; @@ -971,87 +959,84 @@ static void scc_key_trx(struct scc_channel *scc, char tx) } else { cl(scc,R5,RTS|TxENAB); /* disable tx */ - if ((scc->kiss.fulldup == KISS_DUPLEX_HALF) && + if ((scc->modem.fullduplex == 0) && #ifndef CONFIG_SCC_TRXECHO - scc->kiss.softdcd) + scc->modem.softdcd) #else 1) #endif { - or(scc, R15, scc->kiss.softdcd? SYNCIE:DCDIE); + or(scc, R15, scc->modem.softdcd? SYNCIE:DCDIE); start_hunt(scc); } } } - enable_irq(scc->irq); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); } - -/* ----> SCC timer interrupt handler and friends. <---- */ - -static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(unsigned long), unsigned long when) +static void scc_kick_tx(struct scc_channel *scc) { + struct sk_buff *skb; unsigned long flags; - - - save_flags(flags); - cli(); - del_timer(&scc->tx_t); + spin_lock_irqsave(&scc->spinlocks.kick_tx, flags); - if (when == 0) - { - handler((unsigned long) scc); - } else - if (when != TIMER_OFF) - { - scc->tx_t.data = (unsigned long) scc; - scc->tx_t.function = handler; - scc->tx_t.expires = jiffies + (when*HZ)/100; - add_timer(&scc->tx_t); - } - - restore_flags(flags); -} + skb = scc->tx_new; + scc->tx_new = NULL; + netif_wake_queue(scc->dev); -static void scc_start_defer(struct scc_channel *scc) -{ - unsigned long flags; - - save_flags(flags); - cli(); + if (skb == NULL) goto nada; - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxdefer != 0 && scc->kiss.maxdefer != TIMER_OFF) + if (skb->len == 0) /* Paranoia... */ { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_busy; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxdefer; - add_timer(&scc->tx_wdog); + dev_kfree_skb(skb); + scc->tx_buff = NULL; + Outb(scc->ctrl, RES_Tx_P); + goto nada; } - restore_flags(flags); + + scc->tx_buff = skb; + scc->stat.tx_state = TXS_ACTIVE; + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ + or(scc,R10,ABUNDER); /* re-install underrun protection */ + Outb(scc->data,*skb->data); /* send byte */ + skb_pull(skb, 1); + + if (!scc->enhanced) /* reset EOM latch */ + Outb(scc->ctrl,RES_EOM_L); + + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; + +nada: + scc_tx_done(scc); + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; } -static void scc_start_maxkeyup(struct scc_channel *scc) +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(struct scc_channel *), unsigned long when) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.timer, flags); - del_timer(&scc->tx_wdog); - - if (scc->kiss.maxkeyup != 0 && scc->kiss.maxkeyup != TIMER_OFF) + del_timer(&scc->tx_timer); + + if (when != 0) { - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = t_maxkeyup; - scc->tx_wdog.expires = jiffies + HZ*scc->kiss.maxkeyup; - add_timer(&scc->tx_wdog); + scc->tx_timer.data = (unsigned long) scc; + scc->tx_timer.function = (void (*)(unsigned long)) handler; + scc->tx_timer.expires = jiffies + (when*HZ)/1000; + add_timer(&scc->tx_timer); + } else { + handler(scc); } - - restore_flags(flags); + + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); } /* @@ -1061,243 +1046,96 @@ static void scc_start_maxkeyup(struct scc_channel *scc) static void scc_tx_done(struct scc_channel *scc) { - /* - * trx remains keyed in fulldup mode 2 until t_idle expires. - */ - - switch (scc->kiss.fulldup) - { - case KISS_DUPLEX_LINK: - scc->stat.tx_state = TXS_IDLE2; - if (scc->kiss.idletime != TIMER_OFF) - scc_start_tx_timer(scc, t_idle, scc->kiss.idletime*100); - break; - case KISS_DUPLEX_OPTIMA: - scc_notify(scc, HWEV_ALL_SENT); - break; - default: - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } + scc->stat.tx_state = TXS_TAIL; - netif_wake_queue(scc->dev); + if (scc->modem.tx_tail != 0) + scc_start_tx_timer(scc, scc_tail, scc->modem.tx_tail); + else + scc_tail(scc); } - -static unsigned char Rand = 17; - -static inline int is_grouped(struct scc_channel *scc) +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc) { - int k; - struct scc_channel *scc2; - unsigned char grp1, grp2; - - grp1 = scc->kiss.group; - - for (k = 0; k < (Nchips * 2); k++) - { - scc2 = &SCC_Info[k]; - grp2 = scc2->kiss.group; - - if (scc2 == scc || !(scc2->dev && grp2)) - continue; - - if ((grp1 & 0x3f) == (grp2 & 0x3f)) - { - if ( (grp1 & TXGROUP) && (scc2->wreg[R5] & RTS) ) - return 1; - - if ( (grp1 & RXGROUP) && scc2->dcd ) - return 1; - } - } - return 0; + scc->stat.tx_state = TXS_TAIL; + + if (scc->tx_new) + scc_kick_tx(scc); + else + // remain key-up'ed for the time of tx_delay... + // what's the timeout in 6pack? + scc_start_tx_timer(scc, scc_tail, scc->modem.tx_delay); } +#endif -/* DWAIT and SLOTTIME expired - * - * fulldup == 0: DCD is active or Rand > P-persistence: start t_busy timer - * else key trx and start txdelay - * fulldup == 1: key trx and start txdelay - * fulldup == 2: mintime expired, reset status or key trx and start txdelay - */ - -static void t_dwait(unsigned long channel) +static void scc_tx_start(struct scc_channel *scc, struct sk_buff *skb) { - struct scc_channel *scc = (struct scc_channel *) channel; + scc->tx_new = skb; - if (scc->stat.tx_state == TXS_WAIT) /* maxkeyup or idle timeout */ - { - if (skb_queue_len(&scc->tx_queue) == 0) /* nothing to send */ - { - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); /* t_maxkeyup locked it. */ - return; - } - - scc->stat.tx_state = TXS_BUSY; - } + /* + * scc_set_rts may also start a tx delay wait time, if we + * get a frame to transmit within this time RTS would be set, + * shorten the tx delay time... + */ - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) + if (scc->stat.tx_state != TXS_TXDELAY) { - Rand = Rand * 17 + 31; - - if (scc->dcd || (scc->kiss.persist) < Rand || (scc->kiss.group && is_grouped(scc)) ) + scc->stat.tx_state = TXS_TXDELAY; + + if ( !(scc->wreg[R5] & RTS) ) { - scc_start_defer(scc); - scc_start_tx_timer(scc, t_dwait, scc->kiss.slottime); - return ; + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, scc_kick_tx, scc->modem.tx_delay); + } else { + scc_start_tx_timer(scc, scc_kick_tx, 0); } } +} + +#ifndef SCC_SIMPLE_MAC +static void scc_set_rts(struct scc_channel *scc) +{ + scc->stat.tx_state = TXS_TXDELAY; if ( !(scc->wreg[R5] & RTS) ) { scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); + scc_start_tx_timer(scc, scc_tx_forced, scc->modem.tx_delay); } else { - scc_start_tx_timer(scc, t_txdelay, 0); + scc_start_tx_timer(scc, scc_tx_forced, 0); } } +#endif - -/* TXDELAY expired - * - * kick transmission by a fake scc_txint(scc), start 'maxkeyup' watchdog. +/* + * TAILTIME expired */ -static void t_txdelay(unsigned long channel) +static void scc_tail(struct scc_channel *scc) { - struct scc_channel *scc = (struct scc_channel *) channel; - - scc_start_maxkeyup(scc); + if (scc->tx_buff != NULL) + return; - if (scc->tx_buff == NULL) + if (scc->tx_new != NULL) { - disable_irq(scc->irq); - scc_txint(scc); - enable_irq(scc->irq); + scc_kick_tx(scc); + return; } -} - -/* TAILTIME expired - * - * switch off transmitter. If we were stopped by Maxkeyup restart - * transmission after 'mintime' seconds - */ - -static void t_tail(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); scc_key_trx(scc, TX_OFF); - - restore_flags(flags); - - if (scc->stat.tx_state == TXS_TIMEOUT) /* we had a timeout? */ - { - scc->stat.tx_state = TXS_WAIT; - - if (scc->kiss.mintime != TIMER_OFF) /* try it again */ - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - else - scc_start_tx_timer(scc, t_dwait, 0); - return; - } - scc->stat.tx_state = TXS_IDLE; - netif_wake_queue(scc->dev); -} - - -/* BUSY timeout - * - * throw away send buffers if DCD remains active too long. - */ - -static void t_busy(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_t); - netif_stop_queue(scc->dev); /* don't pile on the wabbit! */ - - scc_discard_buffers(scc); - scc->stat.txerrs++; - scc->stat.tx_state = TXS_IDLE; - - netif_wake_queue(scc->dev); -} - -/* MAXKEYUP timeout - * - * this is our watchdog. - */ - -static void t_maxkeyup(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - /* - * let things settle down before we start to - * accept new data. - */ - - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); - - del_timer(&scc->tx_t); - - cl(scc, R1, TxINT_ENAB); /* force an ABORT, but don't */ - cl(scc, R15, TxUIE); /* count it. */ - OutReg(scc->ctrl, R0, RES_Tx_P); - - restore_flags(flags); - - scc->stat.txerrs++; - scc->stat.tx_state = TXS_TIMEOUT; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); -} - -/* IDLE timeout - * - * in fulldup mode 2 it keys down the transmitter after 'idle' seconds - * of inactivity. We will not restart transmission before 'mintime' - * expires. - */ - -static void t_idle(unsigned long channel) -{ - struct scc_channel *scc = (struct scc_channel *) channel; - - del_timer(&scc->tx_wdog); - - scc_key_trx(scc, TX_OFF); - - if (scc->kiss.mintime != TIMER_OFF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.mintime*100); - scc->stat.tx_state = TXS_WAIT; } static void scc_init_timer(struct scc_channel *scc) { unsigned long flags; - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.timer, flags); scc->stat.tx_state = TXS_IDLE; + // FIXME: this can't be all, can it...? - restore_flags(flags); + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); } @@ -1307,256 +1145,155 @@ static void scc_init_timer(struct scc_channel *scc) /* - * this will set the "hardware" parameters through KISS commands or ioctl() + * this will set the (some, anyway...) MODEM parameters */ -#define CAST(x) (unsigned long)(x) - -static unsigned int scc_set_param(struct scc_channel *scc, unsigned int cmd, unsigned int arg) +static unsigned int scc_set_param(struct scc_channel *scc, struct scc_modem *modem) { - switch (cmd) - { - case PARAM_TXDELAY: scc->kiss.txdelay=arg; break; - case PARAM_PERSIST: scc->kiss.persist=arg; break; - case PARAM_SLOTTIME: scc->kiss.slottime=arg; break; - case PARAM_TXTAIL: scc->kiss.tailtime=arg; break; - case PARAM_FULLDUP: scc->kiss.fulldup=arg; break; - case PARAM_DTR: break; /* does someone need this? */ - case PARAM_GROUP: scc->kiss.group=arg; break; - case PARAM_IDLE: scc->kiss.idletime=arg; break; - case PARAM_MIN: scc->kiss.mintime=arg; break; - case PARAM_MAXKEY: scc->kiss.maxkeyup=arg; break; - case PARAM_WAIT: scc->kiss.waittime=arg; break; - case PARAM_MAXDEFER: scc->kiss.maxdefer=arg; break; - case PARAM_TX: scc->kiss.tx_inhibit=arg; break; - - case PARAM_SOFTDCD: - scc->kiss.softdcd=arg; - if (arg) - { - or(scc, R15, SYNCIE); - cl(scc, R15, DCDIE); - start_hunt(scc); - } else { - or(scc, R15, DCDIE); - cl(scc, R15, SYNCIE); - } - break; - - case PARAM_SPEED: - if (arg < 256) - scc->modem.speed=arg*100; - else - scc->modem.speed=arg; + scc->modem.tx_delay = modem->tx_delay; + scc->modem.tx_tail = modem->tx_tail; + scc->modem.fullduplex = modem->fullduplex; + scc->modem.tx_inhibit = modem->tx_inhibit; + scc->modem.softdcd = modem->softdcd; - if (scc->stat.tx_state == 0) /* only switch baudrate on rx... ;-) */ - set_speed(scc); - break; - - case PARAM_RTS: - if ( !(scc->wreg[R5] & RTS) ) - { - if (arg != TX_OFF) - scc_key_trx(scc, TX_ON); - scc_start_tx_timer(scc, t_txdelay, scc->kiss.txdelay); - } else { - if (arg == TX_OFF) - { - scc->stat.tx_state = TXS_BUSY; - scc_start_tx_timer(scc, t_tail, scc->kiss.tailtime); - } - } - break; - - case PARAM_HWEVENT: - scc_notify(scc, scc->dcd? HWEV_DCD_ON:HWEV_DCD_OFF); - break; - default: return -EINVAL; + if (modem->softdcd) + { + or(scc, R15, SYNCIE); + cl(scc, R15, DCDIE); + start_hunt(scc); + } else { + or(scc, R15, DCDIE); + cl(scc, R15, SYNCIE); } return 0; } - - -static unsigned long scc_get_param(struct scc_channel *scc, unsigned int cmd) +static unsigned int scc_ddi_report_dcd(struct net_device *dev) { - switch (cmd) - { - case PARAM_TXDELAY: return CAST(scc->kiss.txdelay); - case PARAM_PERSIST: return CAST(scc->kiss.persist); - case PARAM_SLOTTIME: return CAST(scc->kiss.slottime); - case PARAM_TXTAIL: return CAST(scc->kiss.tailtime); - case PARAM_FULLDUP: return CAST(scc->kiss.fulldup); - case PARAM_SOFTDCD: return CAST(scc->kiss.softdcd); - case PARAM_DTR: return CAST((scc->wreg[R5] & DTR)? 1:0); - case PARAM_RTS: return CAST((scc->wreg[R5] & RTS)? 1:0); - case PARAM_SPEED: return CAST(scc->modem.speed); - case PARAM_GROUP: return CAST(scc->kiss.group); - case PARAM_IDLE: return CAST(scc->kiss.idletime); - case PARAM_MIN: return CAST(scc->kiss.mintime); - case PARAM_MAXKEY: return CAST(scc->kiss.maxkeyup); - case PARAM_WAIT: return CAST(scc->kiss.waittime); - case PARAM_MAXDEFER: return CAST(scc->kiss.maxdefer); - case PARAM_TX: return CAST(scc->kiss.tx_inhibit); - default: return NO_SUCH_PARAM; - } - + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "dcd=%d\n", scc->dcd); */ + return scc->dcd; } -#undef CAST - -/* ******************************************************************* */ -/* * Send calibration pattern * */ -/* ******************************************************************* */ - -static void scc_stop_calibrate(unsigned long channel) +static unsigned int scc_ddi_report_ptt(struct net_device *dev) { - struct scc_channel *scc = (struct scc_channel *) channel; - unsigned long flags; - - save_flags(flags); - cli(); - - del_timer(&scc->tx_wdog); - scc_key_trx(scc, TX_OFF); - wr(scc, R6, 0); - wr(scc, R7, FLAG); - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); - - netif_wake_queue(scc->dev); - restore_flags(flags); + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "rts=%d\n", (scc->wreg[R5] & RTS)? 1:0); */ + return (scc->wreg[R5] & RTS)? 1:0; } - -static void -scc_start_calibrate(struct scc_channel *scc, int duration, unsigned char pattern) +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *dev) { - unsigned long flags; - - save_flags(flags); - cli(); + struct scc_channel *scc = (struct scc_channel *) dev->priv; + int txs = scc->stat.tx_state; - netif_stop_queue(scc->dev); - scc_discard_buffers(scc); +/* printk(KERN_INFO "cts=%d\n", ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0); */ + return ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0; +} - del_timer(&scc->tx_wdog); +static void scc_ddi_set_rts(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "setrts\n"); */ + scc_set_rts(scc); +} +#endif - scc->tx_wdog.data = (unsigned long) scc; - scc->tx_wdog.function = scc_stop_calibrate; - scc->tx_wdog.expires = jiffies + HZ*duration; - add_timer(&scc->tx_wdog); +static void scc_ddi_set_bitrate(struct net_device *dev, unsigned int speed) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + scc->modem.rx_speed = speed; + scc->modem.tx_speed = speed; + if (scc->stat.tx_state != TXS_ACTIVE) + set_speed(scc); +} - /* This doesn't seem to work. Why not? */ - wr(scc, R6, 0); - wr(scc, R7, pattern); +/* Update general parameters so that they reflect our internal settings */ +static void scc_ddi_param_update(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; - /* - * Don't know if this works. - * Damn, where is my Z8530 programming manual...? - */ + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, scc->modem.fullduplex); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, scc->modem.rx_speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, scc->modem.tx_speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, scc->modem.tx_delay); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, scc->modem.tx_tail); + return; +} - Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ - Outb(scc->ctrl,RES_EXT_INT); +/* Called from upper layers when parameter was changed */ +static void scc_ddi_param_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; - scc_key_trx(scc, TX_ON); - restore_flags(flags); + switch (valueno) + { + case AX25_VALUES_MEDIA_DUPLEX: + if (!netif_running(dev)) scc->modem.fullduplex = new; + break; + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + scc_ddi_set_bitrate(dev, new); + break; + case AX25_VALUES_MEDIA_TXDELAY: + scc->modem.tx_delay = new; + break; + case AX25_VALUES_MEDIA_TXTAIL: + scc->modem.tx_tail = new; + break; + default: + break; + } + scc_ddi_param_update(dev); + return; } /* ******************************************************************* */ /* * Init channel structures, special HW, etc... * */ /* ******************************************************************* */ -/* - * Reset the Z8530s and setup special hardware - */ - -static void z8530_init(void) -{ - struct scc_channel *scc; - int chip, k; - unsigned long flags; - char *flag; - - - printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); - - flag=" "; - for (k = 0; k < 16; k++) - if (Ivec[k].used) - { - printk("%s%d", flag, k); - flag=","; - } - printk("\n"); - - - /* reset and pre-init all chips in the system */ - for (chip = 0; chip < Nchips; chip++) - { - scc=&SCC_Info[2*chip]; - if (!scc->ctrl) continue; - - /* Special SCC cards */ - if(scc->brand & EAGLE) /* this is an EAGLE card */ - Outb(scc->special,0x08); /* enable interrupt on the board */ - - if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ - Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ - - - /* Reset and pre-init Z8530 */ - - save_flags(flags); - cli(); - - Outb(scc->ctrl, 0); - OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ - udelay(100); /* give it 'a bit' more time than required */ - wr(scc, R2, chip*16); /* interrupt vector */ - wr(scc, R9, VIS); /* vector includes status */ - - restore_flags(flags); - } - - - Driver_Initialized = 1; -} /* * Allocate device structure, err, instance, and register driver */ -static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev) +static int scc_net_setup(struct scc_channel *scc) { + int k = 0; struct net_device *dev; - if (dev_get(name)) - { - printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); - return -EEXIST; - } - if ((scc->dev = (struct net_device *) kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) return -ENOMEM; dev = scc->dev; memset(dev, 0, sizeof(struct net_device)); - strcpy(dev->name, name); + do { + sprintf(dev->name, "scc%d", k); + if (++k > 256) goto bail_out; /* avoid endless loop */ + } while (dev_get_by_name(dev->name) == NULL); + dev->priv = (void *) scc; dev->init = scc_net_init; - if ((addev? register_netdevice(dev) : register_netdev(dev)) != 0) { - kfree(dev); - return -EIO; - } + if (register_netdev(dev) != 0) goto bail_out; + + scc->proc_dev_name = dev->name; + scc->tx_buff = NULL; + scc->tx_new = NULL; + scc->init = 1; + scc_ddi_param_update(dev); - SET_MODULE_OWNER(dev); return 0; + +bail_out: + kfree(dev); + scc->dev = NULL; + return -EINVAL; } @@ -1565,15 +1302,12 @@ static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev /* * Network driver methods * */ /* ******************************************************************** */ -static unsigned char ax25_bcast[AX25_ADDR_LEN] = -{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static unsigned char ax25_nocall[AX25_ADDR_LEN] = -{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; - /* ----> Initialize device <----- */ static int scc_net_init(struct net_device *dev) { + struct scc_channel *scc = (struct scc_channel *) dev->priv; + struct ax25_dev *ax25dev; dev_init_buffers(dev); dev->tx_queue_len = 16; /* should be enough... */ @@ -1582,23 +1316,28 @@ static int scc_net_init(struct net_device *dev) dev->stop = scc_net_close; dev->hard_start_xmit = scc_net_tx; - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; dev->set_mac_address = scc_net_set_mac_address; dev->get_stats = scc_net_get_stats; dev->do_ioctl = scc_net_ioctl; - dev->tx_timeout = NULL; - - memcpy(dev->broadcast, ax25_bcast, AX25_ADDR_LEN); - memcpy(dev->dev_addr, ax25_nocall, AX25_ADDR_LEN); - - dev->flags = 0; + dev->tx_timeout = scc_net_timeout; + dev->watchdog_timeo = SCC_WATCHDOG_TIMEOUT; dev->type = ARPHRD_AX25; dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; dev->mtu = AX25_DEF_PACLEN; dev->addr_len = AX25_ADDR_LEN; + AX25_PTR(dev) = ax25dev = &scc->ax25dev; + memset(ax25dev, 0, sizeof(struct ax25_dev)); + ax25dev->hw.ptt = scc_ddi_report_ptt; + ax25dev->hw.dcd = scc_ddi_report_dcd; +#ifndef SCC_SIMPLE_MAC + ax25dev->hw.cts = scc_ddi_report_cts; + ax25dev->hw.rts = scc_ddi_set_rts; +#endif + ax25dev->hw.parameter_change_notify = scc_ddi_param_notify; + ax25dev->hw.fast = 0; + return 0; } @@ -1608,15 +1347,17 @@ static int scc_net_open(struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - if (!scc->init) - return -EINVAL; + if (!scc->init) return -EINVAL; + + MOD_INC_USE_COUNT; scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); init_channel(scc); + scc_ddi_param_update(dev); netif_start_queue(dev); + return 0; } @@ -1628,21 +1369,21 @@ static int scc_net_close(struct net_device *dev) unsigned long flags; netif_stop_queue(dev); - - save_flags(flags); - cli(); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); - Outb(scc->ctrl,0); /* Make sure pointer is written */ - wr(scc,R1,0); /* disable interrupts */ + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ wr(scc,R3,0); - del_timer(&scc->tx_t); - del_timer(&scc->tx_wdog); + del_timer(&scc->tx_timer); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); - restore_flags(flags); - - scc_discard_buffers(scc); + if (scc->tx_buff) + kfree_skb(scc->tx_buff); + if (scc->tx_new) + kfree_skb(scc->tx_new); + MOD_DEC_USE_COUNT; return 0; } @@ -1650,8 +1391,9 @@ static int scc_net_close(struct net_device *dev) static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) { - if (skb->len == 0) { - dev_kfree_skb_irq(skb); + if (skb->len == 0) + { + kfree_skb(skb); return; } @@ -1671,299 +1413,186 @@ static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) static int scc_net_tx(struct sk_buff *skb, struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - unsigned long flags; - char kisscmd; - if (skb->len > scc->stat.bufsize || skb->len < 2) { + if (skb->len > scc->stat.bufsize || skb->len < 2) + { scc->dev_stat.tx_dropped++; /* bogus frame */ dev_kfree_skb(skb); return 0; } - + + netif_stop_queue(dev); + scc->dev_stat.tx_packets++; scc->stat.txframes++; - - kisscmd = *skb->data & 0x1f; - skb_pull(skb, 1); - if (kisscmd) { - scc_set_param(scc, kisscmd, *skb->data); - dev_kfree_skb(skb); +#ifdef notdef + /**** The following "fix" is bogus. Fix the protocol, dammit! ****/ + + /* avoid race condition when skb is a cloned broadcast */ + skb_cp = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb(skb); + if (skb_cp == NULL) { + scc->dev_stat.tx_dropped++; /* out of memory */ return 0; } +#endif - save_flags(flags); - cli(); + /* transmit frame */ - if (skb_queue_len(&scc->tx_queue) > scc->dev->tx_queue_len) { - struct sk_buff *skb_del; - skb_del = skb_dequeue(&scc->tx_queue); - dev_kfree_skb(skb_del); - } - skb_queue_tail(&scc->tx_queue, skb); dev->trans_start = jiffies; - + scc_tx_start(scc, skb); - /* - * Start transmission if the trx state is idle or - * t_idle hasn't expired yet. Use dwait/persistance/slottime - * algorithm for normal halfduplex operation. - */ + return 0; +} - if(scc->stat.tx_state == TXS_IDLE || scc->stat.tx_state == TXS_IDLE2) { - scc->stat.tx_state = TXS_BUSY; - if (scc->kiss.fulldup == KISS_DUPLEX_HALF) - scc_start_tx_timer(scc, t_dwait, scc->kiss.waittime); - else - scc_start_tx_timer(scc, t_dwait, 0); - } - restore_flags(flags); - return 0; +/* -----> Watchdog <----- */ + +static void scc_net_timeout(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + del_timer(&scc->tx_timer); + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + cl(scc, R1, TxINT_ENAB); + cl(scc, R15, TxUIE); + OutReg(scc->ctrl, R0, RES_Tx_P); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + + scc_start_tx_timer(scc, init_channel, 100); } -/* ----> ioctl functions <---- */ -/* - * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg - * SIOCSCCINI - initialize driver arg: --- - * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg - * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg - * SIOCSCCGKISS - get level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCSKISS - set level 1 parameter arg: (struct scc_kiss_cmd *) arg - * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg - * SIOCSCCCAL - send calib. pattern arg: (struct scc_calibrate *) arg - */ + +/* ----> ioctl functions <---- */ static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { - struct scc_kiss_cmd kiss_cmd; - struct scc_mem_config memcfg; - struct scc_hw_config hwcfg; - struct scc_calibrate cal; - int chan; - unsigned char device_name[10]; - void *arg; - struct scc_channel *scc; - - scc = (struct scc_channel *) dev->priv; - arg = (void *) ifr->ifr_data; - - if (!Driver_Initialized) - { - if (cmd == SIOCSCCCFG) - { - int found = 1; + return -ENOIOCTLCMD; +} - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg) return -EFAULT; +static int scc_check_and_init(struct scc_ctrl *ctrl) +{ + ctl_table *t_port[2]; + unsigned long flags; + int found_a = 1; + int found_b = 1; + int port, entry; - if (Nchips >= SCC_MAXCHIPS) - return -EINVAL; + /* are we still missing vital information? */ + if (ctrl->channel_a.ctrl <= 0 || ctrl->channel_a.data <= 0 || + ctrl->channel_b.ctrl <= 0 || ctrl->channel_b.ctrl <= 0 || + ctrl->channel_a.irq <= 0) + return 1; - if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) - return -EFAULT; + check_region(ctrl->channel_a.ctrl, 1); + check_region(ctrl->channel_b.ctrl, 1); - if (hwcfg.irq == 2) hwcfg.irq = 9; + spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags); - if (!Ivec[hwcfg.irq].used && hwcfg.irq) - { - if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) - printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); - else - Ivec[hwcfg.irq].used = 1; - } + /* reset the chip */ - if (hwcfg.vector_latch) { - if (!request_region(Vector_Latch, 1, "scc vector latch")) - printk(KERN_WARNING "z8530drv: warning, cannot reserve vector latch port 0x%lx\n, disabled.", hwcfg.vector_latch); - else - Vector_Latch = hwcfg.vector_latch; - } + Outb(ctrl->channel_a.ctrl, 0); + OutReg(ctrl->channel_a.ctrl, R9, FHWRES); + udelay(100); - if (hwcfg.clock == 0) - hwcfg.clock = SCC_DEFAULT_CLOCK; + /* check if it's really there... */ #ifndef SCC_DONT_CHECK - disable_irq(hwcfg.irq); + OutReg(ctrl->channel_a.ctrl, R13, 0x55); + udelay(5); + if (InReg(ctrl->channel_a.ctrl, R13) != 0x55) + found_a = 0; + + OutReg(ctrl->channel_b.ctrl, R13, 0xaa); + udelay(5); + if (InReg(ctrl->channel_b.ctrl, R13) != 0xaa) + found_b = 0; +#endif + spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags); - check_region(scc->ctrl, 1); - Outb(hwcfg.ctrl_a, 0); - OutReg(hwcfg.ctrl_a, R9, FHWRES); - udelay(100); - OutReg(hwcfg.ctrl_a,R13,0x55); /* is this chip really there? */ - udelay(5); + printk(KERN_INFO "z8530drv: chip %d, channel A (0x%3.3x/0x%3.3x) %sfound, " + "channel B (0x%3.3x/0x%3.3x) %sfound, irq %d\n", + Nchips, + (int) ctrl->channel_a.data, (int) ctrl->channel_a.ctrl, found_a? "":"not ", + (int) ctrl->channel_b.data, (int) ctrl->channel_b.ctrl, found_b? "":"not ", + ctrl->channel_a.irq); - if (InReg(hwcfg.ctrl_a,R13) != 0x55) - found = 0; + /* at least one channel not properly specified */ + if (!(found_a && found_b)) + return -EINVAL; - enable_irq(hwcfg.irq); -#endif + /* grab IRQ */ + if (!scc_irq_used[ctrl->channel_a.irq]) + { + if (request_irq(ctrl->channel_a.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) + { + printk(KERN_WARNING "z8530drv: cannot get IRQ %d\n", ctrl->channel_a.irq); + return -EINVAL; + } - if (found) - { - SCC_Info[2*Nchips ].ctrl = hwcfg.ctrl_a; - SCC_Info[2*Nchips ].data = hwcfg.data_a; - SCC_Info[2*Nchips ].irq = hwcfg.irq; - SCC_Info[2*Nchips+1].ctrl = hwcfg.ctrl_b; - SCC_Info[2*Nchips+1].data = hwcfg.data_b; - SCC_Info[2*Nchips+1].irq = hwcfg.irq; - - SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; - SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; - SCC_ctrl[Nchips].irq = hwcfg.irq; - } + scc_irq_used[ctrl->channel_a.irq]++; + } + /* register IO ports */ + request_region(ctrl->channel_a.ctrl, 1, "scc ctrl"); + request_region(ctrl->channel_b.ctrl, 1, "scc ctrl"); + request_region(ctrl->channel_a.data, 1, "scc data"); + request_region(ctrl->channel_b.data, 1, "scc data"); - for (chan = 0; chan < 2; chan++) - { - sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + /* register network devices */ + scc_net_setup(&ctrl->channel_a); + scc_net_setup(&ctrl->channel_b); - SCC_Info[2*Nchips+chan].special = hwcfg.special; - SCC_Info[2*Nchips+chan].clock = hwcfg.clock; - SCC_Info[2*Nchips+chan].brand = hwcfg.brand; - SCC_Info[2*Nchips+chan].option = hwcfg.option; - SCC_Info[2*Nchips+chan].enhanced = hwcfg.escc; -#ifdef SCC_DONT_CHECK - printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", - device_name, - SCC_Info[2*Nchips+chan].data, - SCC_Info[2*Nchips+chan].ctrl); + /* initialize special hardware -- note that port & option need to be set at this point */ + if (ctrl->channel_a.special_port > 0) + { + if (ctrl->channel_a.brand & EAGLE) /* enable interrupt on EAGLE card */ + Outb(ctrl->channel_a.special_port, 0x08); + if (ctrl->channel_a.brand & (PC100 | PRIMUS)) /* set MODEM mode on PC100 or PRIMUS cards */ + Outb(ctrl->channel_a.special_port, ctrl->channel_a.special_option); + } -#else - printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", - device_name, - chan? hwcfg.data_b : hwcfg.data_a, - chan? hwcfg.ctrl_b : hwcfg.ctrl_a, - found? "found" : "missing"); -#endif - if (found) - { - request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); - request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); - if (Nchips+chan != 0) - scc_net_setup(&SCC_Info[2*Nchips+chan], device_name, 1); - } - } - - if (found) Nchips++; - - return 0; - } - - if (cmd == SIOCSCCINI) - { - if (!capable(CAP_SYS_RAWIO)) - return -EPERM; - - if (Nchips == 0) - return -EINVAL; + /* inhibit change of vital parameters after init; this probably isn't entirely race free + * free, but we can live with this (at worst the new mode hasn't been propagated to the VFS + * yet and we get an EINVAL though the file seems to be writable. *shrugs* */ - z8530_init(); - return 0; + for (entry=0; ctrl->proc_tables.chip[entry].ctl_name; entry++) + { + switch(ctrl->proc_tables.chip[entry].ctl_name) + { + case NET_DEV_Z8530DRV_IRQ: + case NET_DEV_Z8530DRV_ESCC: + case NET_DEV_Z8530DRV_SPECIAL_PORT: + case NET_DEV_Z8530DRV_SPECIAL_OPT: + ctrl->proc_tables.chip[entry].mode = 0444; + break; } - - return -EINVAL; /* confuse the user */ } - - if (!scc->init) + + t_port[0] = ctrl->channel_a.proc_tables.channel; + t_port[1] = ctrl->channel_b.proc_tables.channel; + + for (port=0; port < 2; port++) { - if (cmd == SIOCSCCCHANINI) + for (entry=0; t_port[port][entry].ctl_name; entry++) { - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg) return -EINVAL; - - scc->stat.bufsize = SCC_BUFSIZE; - - if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) - return -EINVAL; - - /* default KISS Params */ - - if (scc->modem.speed < 4800) + switch(t_port[port][entry].ctl_name) { - scc->kiss.txdelay = 36; /* 360 ms */ - scc->kiss.persist = 42; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 16; /* 160 ms */ - scc->kiss.tailtime = 4; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 10; /* 10 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ - } else { - scc->kiss.txdelay = 10; /* 100 ms */ - scc->kiss.persist = 64; /* 25% persistence */ /* was 25 */ - scc->kiss.slottime = 8; /* 160 ms */ - scc->kiss.tailtime = 1; /* minimal reasonable value */ - scc->kiss.fulldup = 0; /* CSMA */ - scc->kiss.waittime = 50; /* 500 ms */ - scc->kiss.maxkeyup = 7; /* 7 s */ - scc->kiss.mintime = 3; /* 3 s */ - scc->kiss.idletime = 30; /* 30 s */ - scc->kiss.maxdefer = 120; /* 2 min */ - scc->kiss.softdcd = 0; /* hardware dcd */ + case NET_DEV_Z8530DRV_DATA_PORT: + case NET_DEV_Z8530DRV_CTRL_PORT: + t_port[port][entry].mode = 0444; + break; } - - scc->tx_buff = NULL; - skb_queue_head_init(&scc->tx_queue); - scc->init = 1; - - return 0; } - - return -EINVAL; } - - switch(cmd) - { - case SIOCSCCRESERVED: - return -ENOIOCTLCMD; - - case SIOCSCCSMEM: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) - return -EINVAL; - scc->stat.bufsize = memcfg.bufsize; - return 0; - - case SIOCSCCGSTAT: - if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) - return -EINVAL; - return 0; - - case SIOCSCCGKISS: - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - kiss_cmd.param = scc_get_param(scc, kiss_cmd.command); - if (copy_to_user(arg, &kiss_cmd, sizeof(kiss_cmd))) - return -EINVAL; - return 0; - - case SIOCSCCSKISS: - if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (!arg || copy_from_user(&kiss_cmd, arg, sizeof(kiss_cmd))) - return -EINVAL; - return scc_set_param(scc, kiss_cmd.command, kiss_cmd.param); - - case SIOCSCCCAL: - if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (!arg || copy_from_user(&cal, arg, sizeof(cal)) || cal.time == 0) - return -EINVAL; - scc_start_calibrate(scc, cal.time, cal.pattern); - return 0; - - default: - return -ENOIOCTLCMD; - - } - - return -EINVAL; + return 0; } /* ----> set interface callsign <---- */ @@ -1980,7 +1609,7 @@ static int scc_net_set_mac_address(struct net_device *dev, void *addr) static struct net_device_stats *scc_net_get_stats(struct net_device *dev) { struct scc_channel *scc = (struct scc_channel *) dev->priv; - + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; @@ -1990,193 +1619,635 @@ static struct net_device_stats *scc_net_get_stats(struct net_device *dev) } /* ******************************************************************** */ -/* * dump statistics to /proc/net/z8530drv * */ +/* * /proc/sys/ interface * */ /* ******************************************************************** */ +/* + * structure: + * --------- + * + * /proc/sys/net/dev/z8530drv/ + * nchips + * delay + * chip_0/ + * vendor + * clock + * irq + * delay + * enhanced + * special_port + * special_opt + * port_a/ + * data_port + * ctrl_port + * ifname + * modem/ + * rx_speed + * tx_speed + * rx_clock_source + * tx_clock_source + * trx_pin_mode + * fullduplex + * trx_does_feedback + * nrz_mode + * tx_inhibit + * softdcd + * tx_delay + * tx_tail + * stats/ + * rx_ints + * tx_ints + * ex_ints + * sp_ints + * tx_frames + * rx_frames + * tx_errors + * rx_errors + * tx_underruns + * rx_overruns + * no_space + * tx_state + * port_b/ + * ... + * chip_1/ + * ... + * + * The idea is to be able to configure the driver through SNMP, store + * the configuration in an LDAP directory or place it into an XML + * document... The structure may look overly complex for the + * purpose, but the old "initialize through a special ioctl()" was too + * complex to maintain. + */ + +/* parent node /proc/sys/net/dev */ + +static ctl_table scc_proc_root_table[] = { + {NET_DEV, "dev", NULL, 0, 0555, scc_proc_parent_table}, + {0} +}; + +/* our directory */ -static int scc_net_get_info(char *buffer, char **start, off_t offset, int length) +static ctl_table scc_proc_parent_table[] = { + {NET_DEV_Z8530DRV, "z8530drv", NULL, 0, 0555, scc_proc_nchips_table}, + {0} +}; + +/* number of chips, subdirectories get added or removed dynamically */ + +static ctl_table scc_proc_nchips_table[] = { + {NET_DEV_Z8530DRV_NCHIPS, "nchips", + &Nchips, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_nchips, NULL}, + {NET_DEV_Z8530DRV_DELAY, "io_delay", + &IO_Delay, sizeof(int), 0644, NULL, + &proc_dointvec, &sysctl_intvec, NULL}, + {0} +}; + + +static void scc_proc_setup_channel(struct scc_ctrl *ctrl, struct scc_channel *scc) { - struct scc_channel *scc; - struct scc_kiss *kiss; - struct scc_stat *stat; - int len = 0; - off_t pos = 0; - off_t begin = 0; - int k; + int entry; + ctl_table *t_port = scc->proc_tables.channel; + ctl_table *t_modem = scc->proc_tables.modem; + ctl_table *t_stats = scc->proc_tables.stats; + + /* Set /.../chip_<n>/channel_<p>/modem/ */ + + + /* Praise the Lord! We can do this with GCC! */ + entry=0; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_RX_SPEED, "rx_speed", + &scc->modem.rx_speed, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_SPEED, "tx_speed", + &scc->modem.tx_speed, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_RX_CLOCK_SOURCE, "rx_clock_source", + &scc->modem.rx_clock_source, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_CLOCK_SOURCE, "tx_clock_source", + &scc->modem.tx_clock_source, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TRXC_PIN_MODE, "trxc_pin_mode", + &scc->modem.trxc_pin_mode, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_FULLDUPLEX, "fullduplex", + &scc->modem.fullduplex, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TRX_FEEDBACK, "trx_feedback", + &scc->modem.trx_feedback, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_NRZ_MODE, "nrz_mode", + &scc->modem.nrz_mode, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_INHIBIT, "tx_inhibit", + &scc->modem.tx_inhibit, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SOFTDCD, "softdcd", + &scc->modem.softdcd, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_DELAY, "tx_delay", + &scc->modem.tx_delay, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_TX_TAIL, "tx_tail", + &scc->modem.tx_tail, sizeof(int), 0644, NULL, + proc_dointvec, scc_proc_intvec_modem, NULL, + (void *) scc + }; + t_modem[entry] = (ctl_table) {0}; + + /* Set /.../chip_<n>/channel_<p>/stats/ */ + entry = 0; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_INTS, "rx_ints", + &scc->stat.rxints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_INTS, "tx_ints", + &scc->stat.txints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_EX_INTS, "ex_ints", + &scc->stat.exints, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_FRAMES, "tx_frames", + &scc->stat.txframes, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_FRAMES, "rx_frames", + &scc->stat.rxframes, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_ERRORS, "tx_errors", + &scc->stat.txerrs, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_ERRORS, "rx_errors", + &scc->stat.rxerrs, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_UNDERRUNS, "tx_underruns", + &scc->stat.tx_under, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_RX_OVERRUNS, "rx_overruns", + &scc->stat.rx_over, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_NO_SPACE, "no_space", + &scc->stat.nospace, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT_TX_STATE, "tx_state", + &scc->stat.tx_state, sizeof(int), 0444, NULL, + &proc_dointvec, &scc_proc_intvec_modem, NULL + }; + t_stats[entry] = (ctl_table) {0}; + + /* Set /.../chip_<n>/channel_<p>/ */ + + entry = 0; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_DATA_PORT, "data_port", + &scc->data, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_port, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CTRL_PORT, "ctrl_port", + &scc->ctrl, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_IFNAME, "ifname", + &scc->proc_dev_name, IFNAMSIZ, 0444, NULL, + &proc_dostring, &sysctl_string, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_BUFSIZE, "bufsize", + &scc->stat.bufsize, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_port, NULL, + (void *) ctrl, (void *) scc + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_MODEM, "modem", + NULL, 0, 0555, t_modem + }; + t_port[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_STAT, "stats", + NULL, 0, 0555, t_stats + }; + t_port[entry] = (ctl_table) {0}; + + memset(scc, 0, sizeof(struct scc_channel)); + scc->clock = SCC_DEFAULT_CLOCK; + scc->ctrl = -1; + scc->data = -1; + scc->special_port = -1; + scc->irq = -1; + scc->modem.rx_speed = 9600; + scc->modem.tx_speed = 9600; + scc->modem.tx_clock_source = CLOCK_SOURCE_RTxC; + scc->modem.rx_clock_source = CLOCK_SOURCE_TRxC; + scc->modem.trxc_pin_mode = TRXCP_MODE_IN; + spin_lock_init(&scc->spinlocks.hwaccess); + spin_lock_init(&scc->spinlocks.timer); + spin_lock_init(&scc->spinlocks.kick_tx); +} - len += sprintf(buffer, "z8530drv-"VERSION"\n"); - if (!Driver_Initialized) - { - len += sprintf(buffer+len, "not initialized\n"); - goto done; - } +static void scc_proc_setup_chip(void) +{ + int entry; + ctl_table *t_chip = scc_ctrl[Nchips]->proc_tables.chip; + struct scc_channel *channel_a = &scc_ctrl[Nchips]->channel_a; + struct scc_channel *channel_b = &scc_ctrl[Nchips]->channel_b; + + /* Set /.../chip_<n>/ */ + + entry=0; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_VENDOR, "vendor", + &channel_a->brand, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CLOCK, "clock", + &channel_a->clock, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_IRQ, "irq", + &channel_a->irq, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_ESCC, "enhanced", + &channel_a->enhanced, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SPECIAL_PORT, "special_port", + &channel_a->special_port, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_SPECIAL_OPT, "special_opt", + &channel_a->special_option, sizeof(int), 0644, NULL, + &proc_dointvec, &scc_proc_intvec_chip, NULL, + (void *) &scc_ctrl[Nchips] + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CHANNEL_A, "channel_a", + NULL, 0, 0555, channel_a->proc_tables.channel + }; + t_chip[entry++] = (ctl_table) { + NET_DEV_Z8530DRV_CHANNEL_B, "channel_b", + NULL, 0, 0555, channel_b->proc_tables.channel + }; + t_chip[entry] = (ctl_table) {0}; +} + +static void scc_proc_remove_channel(struct scc_ctrl *ctrl, struct scc_channel *scc) +{ + long flags; + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + /* disable interrupts */ + cl(scc, R9, MIE); + udelay(50); - if (!Nchips) + /* free io ports */ + release_region(scc->ctrl, 1); + release_region(scc->data, 1); + if (scc->dev) { - len += sprintf(buffer+len, "chips missing\n"); - goto done; + unregister_netdev(scc->dev); + kfree(scc->dev); } - for (k = 0; k < Nchips*2; k++) - { - scc = &SCC_Info[k]; - stat = &scc->stat; - kiss = &scc->kiss; - - if (!scc->init) - continue; - - /* dev data ctrl irq clock brand enh vector special option - * baud nrz clocksrc softdcd bufsize - * rxints txints exints spints - * rcvd rxerrs over / xmit txerrs under / nospace bufsize - * txd pers slot tail ful wait min maxk idl defr txof grp - * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## - * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## - */ - - len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", - scc->dev->name, - scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, - scc->enhanced, Vector_Latch, scc->special, - scc->option); - len += sprintf(buffer+len, "\t%lu %d %d %d %d\n", - scc->modem.speed, scc->modem.nrz, - scc->modem.clocksrc, kiss->softdcd, - stat->bufsize); - len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", - stat->rxints, stat->txints, stat->exints, stat->spints); - len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n", - stat->rxframes, stat->rxerrs, stat->rx_over, - stat->txframes, stat->txerrs, stat->tx_under, - stat->nospace, stat->tx_state); - -#define K(x) kiss->x - len += sprintf(buffer+len, "\t%d %d %d %d %d %d %d %d %d %d %d %d\n", - K(txdelay), K(persist), K(slottime), K(tailtime), - K(fulldup), K(waittime), K(mintime), K(maxkeyup), - K(idletime), K(maxdefer), K(tx_inhibit), K(group)); -#undef K -#ifdef SCC_DEBUG - { - int reg; + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); +} - len += sprintf(buffer+len, "\tW "); - for (reg = 0; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); - len += sprintf(buffer+len, "\n"); - - len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); - for (reg = 3; reg < 8; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "XX "); - for (reg = 9; reg < 16; reg++) - len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); - len += sprintf(buffer+len, "\n"); - } -#endif - len += sprintf(buffer+len, "\n"); +static void scc_proc_remove_chip(struct scc_ctrl *ctrl) +{ + long flags; + int irq; - pos = begin + len; + spin_lock_irqsave(&ctrl->channel_a.spinlocks.hwaccess, flags); - if (pos < offset) { - len = 0; - begin = pos; - } + Outb(ctrl->channel_a.ctrl, 0); + OutReg(ctrl->channel_a.ctrl, R9, FHWRES); - if (pos > offset + length) - break; + irq = ctrl->channel_a.irq; + if (--scc_irq_used[irq] <= 0) + { + free_irq(irq, NULL); + scc_irq_used[irq] = 0; /* paranoia */ } -done: + spin_unlock_irqrestore(&ctrl->channel_a.spinlocks.hwaccess, flags); - *start = buffer + (offset - begin); - len -= (offset - begin); + unregister_sysctl_table(scc_ctrl[Nchips]->proc_table_head); + kfree(scc_ctrl[Nchips]->proc_tables.dir[0].procname); + kfree(scc_ctrl[Nchips]); +} - if (len > length) len = length; - return len; -} +static int scc_proc_intvec_modem(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) +{ + int retval = 0; + struct scc_channel *scc = (struct scc_channel *) table->extra1; + if (scc == NULL || !scc->init) return -EINVAL; - -/* ******************************************************************** */ -/* * Init SCC driver * */ -/* ******************************************************************** */ + switch(table->ctl_name) + { + case NET_DEV_Z8530DRV_RX_SPEED: + case NET_DEV_Z8530DRV_TX_SPEED: + if (scc->stat.tx_state != TXS_ACTIVE) + set_speed(scc); + break; + case NET_DEV_Z8530DRV_RX_CLOCK_SOURCE: + case NET_DEV_Z8530DRV_TX_CLOCK_SOURCE: + case NET_DEV_Z8530DRV_TRXC_PIN_MODE: + wr(scc, R11, scc_calc_r11(scc->modem.rx_clock_source, scc->modem.tx_clock_source, scc->modem.trxc_pin_mode)); + scc_init_brg_and_dpll(scc); + break; + case NET_DEV_Z8530DRV_FULLDUPLEX: + case NET_DEV_Z8530DRV_TRX_FEEDBACK: + case NET_DEV_Z8530DRV_TX_INHIBIT: + case NET_DEV_Z8530DRV_TX_DELAY: + case NET_DEV_Z8530DRV_TX_TAIL: + break; + case NET_DEV_Z8530DRV_NRZ_MODE: + case NET_DEV_Z8530DRV_SOFTDCD: + break; + default: + retval = -EINVAL; + } + + scc_ddi_param_update(scc->dev); + return retval; +} -static int __init scc_init_driver (void) +static int scc_proc_intvec_port(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) { - int result; - char devname[10]; - - printk(banner); - - sprintf(devname,"%s0", SCC_DriverName); + struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1; + struct scc_channel *scc = (struct scc_channel *) table->extra2; + int val; + + val = *((int *) newval); + + if (scc == NULL) return -EINVAL; - result = scc_net_setup(SCC_Info, devname, 0); - if (result) + switch(table->ctl_name) { - printk(KERN_ERR "z8530drv: cannot initialize module\n"); - return result; + case NET_DEV_Z8530DRV_DATA_PORT: + if (val > 0) + { + scc_check_and_init(ctrl); + return 1; + } + break; + case NET_DEV_Z8530DRV_CTRL_PORT: + if (val > 0) + { + scc_check_and_init(ctrl); + return 1; + } + break; + case NET_DEV_Z8530DRV_BUFSIZE: + break; } - proc_net_create("z8530drv", 0, scc_net_get_info); - return 0; } -static void __exit scc_cleanup_driver(void) +static int scc_proc_intvec_chip(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) { - long flags; - io_port ctrl; - int k; - struct scc_channel *scc; - - save_flags(flags); - cli(); + struct scc_ctrl *ctrl = (struct scc_ctrl *) table->extra1; + int val; + + if (ctrl == NULL) return -EINVAL; + if (ctrl->channel_a.init || ctrl->channel_b.init) + return -EINVAL; + + val = *((int *) newval); - if (Nchips == 0) + switch(table->ctl_name) { - unregister_netdev(SCC_Info[0].dev); - kfree(SCC_Info[0].dev); + case NET_DEV_Z8530DRV_VENDOR: + case NET_DEV_Z8530DRV_CLOCK: + case NET_DEV_Z8530DRV_ESCC: + case NET_DEV_Z8530DRV_SPECIAL_PORT: + case NET_DEV_Z8530DRV_SPECIAL_OPT: + break; + case NET_DEV_Z8530DRV_IRQ: + if (val > 0) + { + ctrl->channel_a.irq = val; + ctrl->channel_b.irq = val; + scc_check_and_init(ctrl); + return 1; + } + break; } - for (k = 0; k < Nchips; k++) - if ( (ctrl = SCC_ctrl[k].chan_A) ) - { - Outb(ctrl, 0); - OutReg(ctrl,R9,FHWRES); /* force hardware reset */ - udelay(50); - } - - for (k = 0; k < Nchips*2; k++) + return 0; +} + +static int scc_proc_intvec_nchips(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) +{ + struct scc_ctrl_proc_tables *tables = NULL; + struct scc_ctrl *ctrl = NULL; + int chip; + char *dirname; + int new_nchips, old_nchips; + + new_nchips = *((int *) newval); + old_nchips = *((int *) oldval); + + /* sanity checks...*/ + + if (new_nchips < 0 || new_nchips > maxchips) + return -EINVAL; + + /* adding chips */ + if (new_nchips > old_nchips) { - scc = &SCC_Info[k]; - if (scc->ctrl) + for (chip=0; chip < new_nchips - old_nchips; chip++) { - release_region(scc->ctrl, 1); - release_region(scc->data, 1); + ctrl = (struct scc_ctrl *) kmalloc(sizeof(struct scc_ctrl), GFP_KERNEL); + if (ctrl == NULL) return -ENOMEM; + scc_ctrl[Nchips] = ctrl; + memset(ctrl, 0, sizeof(struct scc_ctrl)); + + tables = &ctrl->proc_tables; + scc_proc_setup_channel(ctrl, &ctrl->channel_a); + scc_proc_setup_channel(ctrl, &ctrl->channel_b); + scc_proc_setup_chip(); + + /* !!FIXME!! free memory when removing a chip */ + dirname = kmalloc(sizeof(char)*IFNAMSIZ, GFP_KERNEL); + if (dirname == NULL) return -ENOMEM; + + sprintf(dirname, "chip_%d", Nchips); + tables->dir[0] = (ctl_table) { + NET_DEV_Z8530DRV_CHIP_BASE+Nchips, dirname, + NULL, 0, 0555, tables->chip + }; + tables->dir[1] = (ctl_table) {0}; + + tables->parent[0] = (ctl_table) { + NET_DEV_Z8530DRV, "z8530drv", + NULL, 0, 0555, tables->dir + }; + tables->parent[1] = (ctl_table) {0}; + + /* add return value to scc structure */ + scc_ctrl[Nchips]->proc_table_head = register_sysctl_table(tables->parent, 1); + Nchips++; } - if (scc->dev) + + *((int *) newval) = Nchips; + } + else if (new_nchips < old_nchips) + { + for(chip=0; chip < old_nchips - new_nchips; chip++) { - unregister_netdev(scc->dev); - kfree(scc->dev); + ctrl = scc_ctrl[Nchips-1]; + + /* !!FIXME!! possible race condition here */ + + if (netif_running(ctrl->channel_a.dev) || + netif_running(ctrl->channel_b.dev)) + break; + + scc_proc_remove_channel(ctrl, &ctrl->channel_a); + scc_proc_remove_channel(ctrl, &ctrl->channel_b); + scc_proc_remove_chip(ctrl); + + Nchips--; } + + *((int *) newval) = Nchips; } - for (k=0; k < 16 ; k++) - if (Ivec[k].used) free_irq(k, NULL); - - if (Vector_Latch) - release_region(Vector_Latch, 1); + return 0; +} + + + +/* ******************************************************************** */ +/* * Init SCC driver * */ +/* ******************************************************************** */ + + +int __init scc_init_driver(void) +{ + int k; + size_t size; + + printk(KERN_INFO BANNER); - restore_flags(flags); + size=sizeof(struct scc_ctrl)*(maxchips+1); + scc_ctrl = kmalloc(size, GFP_KERNEL); + if (scc_ctrl == NULL) return -ENOMEM; + memset(scc_ctrl, 0, size); + + for (k = 0; k < 16; k++) scc_irq_used[k] = 0; + +#ifdef CONFIG_PROC_FS + scc_proc_table_header = register_sysctl_table(scc_proc_root_table, 1); +#endif + return 0; +} + +/* ******************************************************************** */ +/* * Module support * */ +/* ******************************************************************** */ + + +void __exit scc_cleanup_driver(void) +{ + int k; + for (k = 0; k < Nchips; k++) + { + scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_a); + scc_proc_remove_channel(scc_ctrl[k], &scc_ctrl[k]->channel_b); + scc_proc_remove_chip(scc_ctrl[k]); + } + +#ifdef CONFIG_PROC_FS + unregister_sysctl_table(scc_proc_table_header); proc_net_remove("z8530drv"); +#endif + + kfree(scc_ctrl); } MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>"); -MODULE_DESCRIPTION("AX.25 Device Driver for Z8530 based HDLC cards"); -MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards for Amateur Radio"); +MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio"); +MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards without DMA support"); +MODULE_PARM(maxchips, "i"); module_init(scc_init_driver); module_exit(scc_cleanup_driver); diff --git a/drivers/net/hamradio/scc.c-noprocfs b/drivers/net/hamradio/scc.c-noprocfs new file mode 100644 index 000000000..96ba211a3 --- /dev/null +++ b/drivers/net/hamradio/scc.c-noprocfs @@ -0,0 +1,1926 @@ +#define RCS_ID "$Id: scc.c,v 1.75 1998/11/04 15:15:01 jreuter Exp jreuter $" + +#define VERSION "4.0" +#define BANNER "Z8530 SCC driver version "VERSION".dl1bke by DL1BKE\n" + +/* + * Please use z8530drv-utils-4.0 with this version. + * ------------------ + * + * You can find a subset of the documentation in + * linux/Documentation/networking/z8530drv.txt. + */ + +/* + ******************************************************************** + * SCC.C - Linux driver for Z8530 based HDLC cards for AX.25 * + ******************************************************************** + + + ******************************************************************** + + Copyright (c) 1993, 2000 Joerg Reuter DL1BKE + + portions (c) 1993 Guido ten Dolle PE1NNZ + + ******************************************************************** + + The driver and the programs in the archive are UNDER CONSTRUCTION. + The code is likely to fail, and so your kernel could --- even + a whole network. + + This driver is intended for Amateur Radio use. If you are running it + for commercial purposes, please drop me a note. I am nosy... + + ...BUT: + + ! You m u s t recognize the appropriate legislations of your country ! + ! before you connect a radio to the SCC board and start to transmit or ! + ! receive. The GPL allows you to use the d r i v e r, NOT the RADIO! ! + + For non-Amateur-Radio use please note that you might need a special + allowance/licence from the designer of the SCC Board and/or the + MODEM. + + This program is free software; you can redistribute it and/or modify + it under the terms of the (modified) GNU General Public License + delivered with the Linux kernel source. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should find a copy of the GNU General Public License in + /usr/src/linux/COPYING; + + ******************************************************************** + + + Incomplete history of z8530drv: + ------------------------------- + + 1994-09-13 started to write the driver, rescued most of my own + code (and Hans Alblas' memory buffer pool concept) from + an earlier project "sccdrv" which was initiated by + Guido ten Dolle. Not much of the old driver survived, + though. The first version I put my hands on was sccdrv1.3 + from August 1993. The memory buffer pool concept + appeared in an unauthorized sccdrv version (1.5) from + August 1994. + + 1995-01-31 changed copyright notice to GPL without limitations. + + . + . <SNIP> + . + + 1996-10-05 New semester, new driver... + + * KISS TNC emulator removed (TTY driver) + * Source moved to drivers/net/ + * Includes Z8530 defines from drivers/net/z8530.h + * Uses sk_buffer memory management + * Reduced overhead of /proc/net/z8530drv output + * Streamlined quite a lot things + * Invents brand new bugs... ;-) + + The move to version number 3.0 reflects theses changes. + You can use 'kissbridge' if you need a KISS TNC emulator. + + 1996-12-13 Fixed for Linux networking changes. (G4KLX) + 1997-01-08 Fixed the remaining problems. + 1997-04-02 Hopefully fixed the problems with the new *_timer() + routines, added calibration code. + 1997-10-12 Made SCC_DELAY a CONFIG option, added CONFIG_SCC_TRXECHO + 1998-01-29 Small fix to avoid lock-up on initialization + 1998-09-29 Fixed the "grouping" bugs, tx_inhibit works again, + using dev->tx_queue_len now instead of MAXQUEUE now. + 1998-10-21 Postponed the spinlock changes, would need a lot of + testing I currently don't have the time to. Softdcd doesn't + work. + 1998-11-04 Softdcd does not work correctly in DPLL mode, in fact it + never did. The DPLL locks on noise, the SYNC unit sees + flags that aren't... Restarting the DPLL does not help + either, it resynchronizes too slow and the first received + frame gets lost. + 1999-02-21 Started to implement the new AX.25 device interface + 2000-07-18 Ported to 2.4.x + + Thanks to all who contributed to this driver with ideas and bug + reports! + + NB -- if you find errors, change something, please let me know + first before you distribute it... And please don't touch + the version number. Just replace my callsign in + "v4.0.dl1bke" with your own. Just to avoid confusion... + + If you want to add your modification to the linux distribution + please (!) contact me first. + + New versions of the driver will be announced on the linux-hams + mailing list on vger.kernel.org. To subscribe send an e-mail + to majordomo@vger.kernel.org with the following line in + the body of the mail: + + subscribe linux-hams + + The content of the "Subject" field will be ignored. + + vy 73, + Joerg Reuter ampr-net: dl1bke@db0pra.ampr.org + AX-25 : DL1BKE @ DB0ABH.#BAY.DEU.EU + Internet: jreuter@yaina.de + www : http://yaina.de/jreuter/ +*/ + +/* ----------------------------------------------------------------------- */ + +#undef SCC_DONT_CHECK /* don't look if the SCCs you specified are available */ + +#define SCC_MAXCHIPS 4 /* number of max. supported chips */ +#define SCC_BUFSIZE 384 /* must not exceed 4096 */ +#undef SCC_DEBUG + +#define SCC_DEFAULT_CLOCK 4915200 + /* default pclock if nothing is specified */ + +#define SCC_SIMPLE_MAC /* no rts/cts control by DDI layer */ +#define SCC_WATCHDOG_TIMEOUT 10 + /* ten seconds */ + +/* ----------------------------------------------------------------------- */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/string.h> +#include <linux/in.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/delay.h> +#include <linux/spinlock.h> + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/if_ether.h> +#include <linux/if_arp.h> +#include <linux/socket.h> +#include <linux/init.h> + +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/proc_fs.h> + +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include <asm/irq.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> + +#include <linux/scc.h> +#include "z8530.h" + +int scc_init(void); + +static void scc_tx_done(struct scc_channel *); +static void scc_kick_tx(struct scc_channel *); +static void scc_start_tx_timer(struct scc_channel *, void (*)(struct scc_channel *), unsigned long); +static void scc_tail(struct scc_channel *scc); +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc); +static void scc_set_rts(struct scc_channel *scc); +#endif + +static void z8530_init(void); + +static void init_channel(struct scc_channel *scc); +static void scc_key_trx (struct scc_channel *scc, char tx); +static void scc_isr(int irq, void *dev_id, struct pt_regs *regs); +static void scc_init_timer(struct scc_channel *scc); + +static unsigned int scc_set_param(struct scc_channel *, struct scc_modem *); + +static unsigned int scc_ddi_report_dcd(struct net_device *); +static unsigned int scc_ddi_report_ptt(struct net_device *); +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *); +static void scc_ddi_set_rts(struct net_device *); +#endif +static void scc_ddi_set_bitrate(struct net_device *, unsigned int); +static void scc_ddi_param_update(struct net_device *); +static void scc_ddi_param_notify(struct net_device *, int, int, int); + +static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev); +static int scc_net_init(struct net_device *dev); +static int scc_net_open(struct net_device *dev); +static int scc_net_close(struct net_device *dev); +static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb); +static int scc_net_tx(struct sk_buff *skb, struct net_device *dev); +static void scc_net_timeout(struct net_device *dev); +static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); +static int scc_net_set_mac_address(struct net_device *dev, void *addr); +static struct net_device_stats * scc_net_get_stats(struct net_device *dev); + +static unsigned char *SCC_DriverName = "scc"; + +static struct irqflags { unsigned char used : 1; } Ivec[16]; + +static struct scc_channel SCC_Info[2 * SCC_MAXCHIPS]; /* information per channel */ + +static struct scc_ctrl { + io_port chan_A; + io_port chan_B; + int irq; +} SCC_ctrl[SCC_MAXCHIPS+1]; + +static unsigned char Driver_Initialized = 0; +static int Nchips = 0; +static io_port Vector_Latch = 0; +static long IO_Delay = 0; +static spinlock_t IO_Spinlock = SPIN_LOCK_UNLOCKED; + + +/* ******************************************************************** */ +/* * Port Access Functions * */ +/* ******************************************************************** */ + +static inline unsigned char +Inb(io_port port) +{ + int r; + + r = inb(port); + if (IO_Delay) udelay(IO_Delay); + return r; +} + +static inline void +Outb(io_port port, unsigned char val) +{ + outb(val, port); + if (IO_Delay) udelay(IO_Delay); +} + +/* These provide interrupt save 2-step access to the Z8530 registers */ + +static unsigned char +InReg(io_port port, unsigned char reg) +{ + unsigned long flags; + unsigned char r; + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + r=inb(port); + udelay(IO_Delay); + } else { + outb(reg, port); + r=inb(port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); + return r; +} + +static void +OutReg(io_port port, unsigned char reg, unsigned char val) +{ + unsigned long flags; + + spin_lock_irqsave(&IO_Spinlock, flags); + if (IO_Delay) + { + outb(reg, port); + udelay(IO_Delay); + outb(val, port); + udelay(IO_Delay); + } else { + outb(reg, port); + outb(val, port); + } + spin_unlock_irqrestore(&IO_Spinlock, flags); +} + +static inline +void wr(struct scc_channel *scc, unsigned char reg, unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] = val)); +} + +static inline void +or(struct scc_channel *scc, unsigned char reg, unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] |= val)); +} + +static inline void +cl(struct scc_channel *scc, unsigned char reg, unsigned char val) +{ + OutReg(scc->ctrl, reg, (scc->wreg[reg] &= ~val)); +} + +/* ******************************************************************** */ +/* * Interrupt Service Routines * */ +/* ******************************************************************** */ + + +static inline void flush_rx_FIFO(struct scc_channel *scc) +{ + int k; + + for (k=0; k<3; k++) + Inb(scc->data); + + if(scc->rx_buff != NULL) /* did we receive something? */ + { + scc->stat.rxerrs++; /* then count it as an error */ + kfree_skb(scc->rx_buff); + scc->rx_buff = NULL; + } +} + +static void start_hunt(struct scc_channel *scc) +{ + if ((scc->modem.clocksrc != CLK_EXTERNAL)) + OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ + or(scc,R3,ENT_HM|RxENABLE); /* enable the receiver, hunt mode */ +} + +/* ----> four different interrupt handlers for Tx, Rx, changing of */ +/* DCD/CTS and Rx/Tx errors */ + +/* Transmitter interrupt handler */ +static inline void scc_txint(struct scc_channel *scc) +{ + struct sk_buff *skb; + + scc->stat.txints++; + skb = scc->tx_buff; + + /* send first octet */ + + if (skb == NULL) + { + netif_wake_queue(scc->dev); + scc_tx_done(scc); + Outb(scc->ctrl, RES_Tx_P); + return; + } + + /* End Of Frame... */ + + if (skb->len == 0) + { + Outb(scc->ctrl, RES_Tx_P); /* reset pending int */ + cl(scc, R10, ABUNDER); /* send CRC */ + dev_kfree_skb(skb); + scc->tx_buff = NULL; + scc_kick_tx(scc); /* next frame */ + return; + } + + /* send octet */ + + Outb(scc->data,*skb->data); + skb_pull(skb, 1); +} + + +/* External/Status interrupt handler */ +static inline void scc_exint(struct scc_channel *scc) +{ + unsigned char status,changes,chg_and_stat; + + scc->stat.exints++; + + status = InReg(scc->ctrl,R0); + changes = status ^ scc->status; + chg_and_stat = changes & status; + + /* ABORT: generated whenever DCD drops while receiving */ + + if (chg_and_stat & BRK_ABRT) /* Received an ABORT */ + flush_rx_FIFO(scc); + + /* HUNT: software DCD; on = waiting for SYNC, off = receiving frame */ + + if ((changes & SYNC_HUNT) && scc->modem.softdcd) + { + if (status & SYNC_HUNT) + { + scc->dcd = 0; + flush_rx_FIFO(scc); + if ((scc->modem.clocksrc != CLK_EXTERNAL)) + OutReg(scc->ctrl,R14,SEARCH|scc->wreg[R14]); /* DPLL: enter search mode */ + } else { + scc->dcd = 1; + } + } + + /* DCD: on = start to receive packet, off = ABORT condition */ + /* (a successfully received packet generates a special condition int) */ + + if((changes & DCD) && !scc->modem.softdcd) /* DCD input changed state */ + { + if(status & DCD) /* DCD is now ON */ + { + start_hunt(scc); + scc->dcd = 1; + } else { /* DCD is now OFF */ + cl(scc,R3,ENT_HM|RxENABLE); /* disable the receiver */ + flush_rx_FIFO(scc); + scc->dcd = 0; + } + } + +#ifdef notdef + /* CTS: use external TxDelay (what's that good for?!) + * Anyway: If we _could_ use it (BayCom USCC uses CTS for + * own purposes) we _should_ use the "autoenable" feature + * of the Z8530 and not this interrupt... + */ + + if (chg_and_stat & CTS) /* CTS is now ON */ + { + if (scc->modem.txdelay == 0) /* zero TXDELAY = wait for CTS */ + scc_start_tx_timer(scc, t_txdelay, 0); + } +#endif + + if (scc->stat.tx_state == TXS_ACTIVE && (status & TxEOM)) + { + scc->stat.tx_under++; /* oops, an underrun! count 'em */ + Outb(scc->ctrl, RES_EXT_INT); /* reset ext/status interrupts */ + + if (scc->tx_buff != NULL) + { + dev_kfree_skb(scc->tx_buff); + scc->tx_buff = NULL; + } + + or(scc,R10,ABUNDER); + scc_tx_done(scc); + } + + scc->status = status; + Outb(scc->ctrl,RES_EXT_INT); +} + + +/* Receiver interrupt handler */ +static inline void scc_rxint(struct scc_channel *scc) +{ + struct sk_buff *skb; + + scc->stat.rxints++; + + if((scc->wreg[5] & RTS) && (scc->modem.fullduplex == 0)) + { + Inb(scc->data); /* discard char */ + or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ + return; + } + + skb = scc->rx_buff; + + if (skb == NULL) + { + skb = dev_alloc_skb(scc->stat.bufsize); + if (skb == NULL) + { + scc->dev_stat.rx_dropped++; + scc->stat.nospace++; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; + } + + scc->rx_buff = skb; + } + + if (skb->len > scc->stat.bufsize) + { +#ifdef notdef + printk(KERN_DEBUG "z8530drv: oops, scc_rxint() received huge frame...\n"); +#endif + kfree_skb(skb); + scc->rx_buff = NULL; + Inb(scc->data); + or(scc, R3, ENT_HM); + return; + } + + *(skb_put(skb, 1)) = Inb(scc->data); +} + + +/* Receive Special Condition interrupt handler */ +static inline void scc_spint(struct scc_channel *scc) +{ + unsigned char status; + struct sk_buff *skb; + + scc->stat.spints++; + + status = InReg(scc->ctrl,R1); /* read receiver status */ + + Inb(scc->data); /* throw away Rx byte */ + skb = scc->rx_buff; + + if(status & Rx_OVR) /* receiver overrun */ + { + scc->stat.rx_over++; /* count them */ + or(scc,R3,ENT_HM); /* enter hunt mode for next flag */ + + if (skb != NULL) + kfree_skb(skb); + scc->rx_buff = NULL; + } + + if(status & END_FR && skb != NULL) /* end of frame */ + { + /* CRC okay, frame ends on 8 bit boundary and received something ? */ + + if (!(status & CRC_ERR) && (status & 0xe) == RES8 && skb->len > 0) + { + /* ignore last received byte (first of the CRC bytes) */ + skb_trim(skb, skb->len-1); + scc_net_rx(scc, skb); + scc->rx_buff = NULL; + scc->stat.rxframes++; + } else { /* a bad frame */ + kfree_skb(skb); + scc->rx_buff = NULL; + scc->stat.rxerrs++; + } + } + + Outb(scc->ctrl,ERR_RES); +} + + +/* ----> interrupt service routine for the Z8530 <---- */ + +static void scc_isr_dispatch(struct scc_channel *scc, int vector) +{ + switch (vector & VECTOR_MASK) + { + case TXINT: scc_txint(scc); break; + case EXINT: scc_exint(scc); break; + case RXINT: scc_rxint(scc); break; + case SPINT: scc_spint(scc); break; + } +} + +/* If the card has a latch for the interrupt vector (like the PA0HZP card) + use it to get the number of the chip that generated the int. + If not: poll all defined chips. + */ + +#define SCC_IRQTIMEOUT 30000 + +static void scc_isr(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned char vector; + struct scc_channel *scc; + struct scc_ctrl *ctrl; + int k; + + if (Vector_Latch) + { + for(k=0; k < SCC_IRQTIMEOUT; k++) + { + Outb(Vector_Latch, 0); /* Generate INTACK */ + + /* Read the vector */ + if((vector=Inb(Vector_Latch)) >= 16 * Nchips) break; + if (vector & 0x01) break; + + scc=&SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->dev) break; + + scc_isr_dispatch(scc, vector); + + OutReg(scc->ctrl,R0,RES_H_IUS); /* Reset Highest IUS */ + } + + if (k == SCC_IRQTIMEOUT) + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?\n"); + + return; + } + + /* Find the SCC generating the interrupt by polling all attached SCCs + * reading RR3A (the interrupt pending register) + */ + + ctrl = SCC_ctrl; + while (ctrl->chan_A) + { + if (ctrl->irq != irq) + { + ctrl++; + continue; + } + + scc = NULL; + for (k = 0; InReg(ctrl->chan_A,R3) && k < SCC_IRQTIMEOUT; k++) + { + vector=InReg(ctrl->chan_B,R2); /* Read the vector */ + if (vector & 0x01) break; + + scc = &SCC_Info[vector >> 3 ^ 0x01]; + if (!scc->dev) break; + + scc_isr_dispatch(scc, vector); + } + + if (k == SCC_IRQTIMEOUT) + { + printk(KERN_WARNING "z8530drv: endless loop in scc_isr()?!\n"); + break; + } + + /* This looks weird and it is. At least the BayCom USCC doesn't + * use the Interrupt Daisy Chain, thus we'll have to start + * all over again to be sure not to miss an interrupt from + * (any of) the other chip(s)... + * Honestly, the situation *is* braindamaged... + */ + + if (scc != NULL) + { + OutReg(scc->ctrl,R0,RES_H_IUS); + ctrl = SCC_ctrl; + } else + ctrl++; + } +} + + + +/* ******************************************************************** */ +/* * Init Channel */ +/* ******************************************************************** */ + + +/* ----> set SCC channel speed <---- */ + +static inline void set_brg(struct scc_channel *scc, unsigned int tc) +{ + cl(scc,R14,BRENABL); /* disable baudrate generator */ + wr(scc,R12,tc & 255); /* brg rate LOW */ + wr(scc,R13,tc >> 8); /* brg rate HIGH */ + or(scc,R14,BRENABL); /* enable baudrate generator */ +} + +static inline void set_speed(struct scc_channel *scc) +{ + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + if (scc->modem.speed > 0) /* paranoia... */ + set_brg(scc, (unsigned) (scc->clock / (scc->modem.speed * 64)) - 2); + + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); +} + + +/* ----> initialize a SCC channel <---- */ + +static inline void init_brg(struct scc_channel *scc) +{ + wr(scc, R14, BRSRC); /* BRG source = PCLK */ + OutReg(scc->ctrl, R14, SSBR|scc->wreg[R14]); /* DPLL source = BRG */ + OutReg(scc->ctrl, R14, SNRZI|scc->wreg[R14]); /* DPLL NRZI mode */ +} + +/* + * Initialization according to the Z8530 manual (SGS-Thomson's version): + * + * 1. Modes and constants + * + * WR9 11000000 chip reset + * WR4 XXXXXXXX Tx/Rx control, async or sync mode + * WR1 0XX00X00 select W/REQ (optional) + * WR2 XXXXXXXX program interrupt vector + * WR3 XXXXXXX0 select Rx control + * WR5 XXXX0XXX select Tx control + * WR6 XXXXXXXX sync character + * WR7 XXXXXXXX sync character + * WR9 000X0XXX select interrupt control + * WR10 XXXXXXXX miscellaneous control (optional) + * WR11 XXXXXXXX clock control + * WR12 XXXXXXXX time constant lower byte (optional) + * WR13 XXXXXXXX time constant upper byte (optional) + * WR14 XXXXXXX0 miscellaneous control + * WR14 XXXSSSSS commands (optional) + * + * 2. Enables + * + * WR14 000SSSS1 baud rate enable + * WR3 SSSSSSS1 Rx enable + * WR5 SSSS1SSS Tx enable + * WR0 10000000 reset Tx CRG (optional) + * WR1 XSS00S00 DMA enable (optional) + * + * 3. Interrupt status + * + * WR15 XXXXXXXX enable external/status + * WR0 00010000 reset external status + * WR0 00010000 reset external status twice + * WR1 SSSXXSXX enable Rx, Tx and Ext/status + * WR9 000SXSSS enable master interrupt enable + * + * 1 = set to one, 0 = reset to zero + * X = user defined, S = same as previous init + * + * + * Note that the implementation differs in some points from above scheme. + * + */ + +static void init_channel(struct scc_channel *scc) +{ + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + del_timer(&scc->tx_timer); + + wr(scc,R4,X1CLK|SDLC); /* *1 clock, SDLC mode */ + wr(scc,R1,0); /* no W/REQ operation */ + wr(scc,R3,Rx8|RxCRC_ENAB); /* RX 8 bits/char, CRC, disabled */ + wr(scc,R5,Tx8|DTR|TxCRC_ENAB); /* TX 8 bits/char, disabled, DTR */ + wr(scc,R6,0); /* SDLC address zero (not used) */ + wr(scc,R7,FLAG); /* SDLC flag value */ + wr(scc,R9,VIS); /* vector includes status */ + wr(scc,R10,(scc->modem.nrz? NRZ : NRZI)|CRCPS|ABUNDER); /* abort on underrun, preset CRC generator, NRZ(I) */ + wr(scc,R14, 0); + + +/* set clock sources: + + CLK_DPLL: normal halfduplex operation + + RxClk: use DPLL + TxClk: use DPLL + TRxC mode DPLL output + + CLK_EXTERNAL: external clocking (G3RUH or DF9IC modem) + + BayCom: others: + + TxClk = pin RTxC TxClk = pin TRxC + RxClk = pin TRxC RxClk = pin RTxC + + + CLK_DIVIDER: + RxClk = use DPLL + TxClk = pin RTxC + + BayCom: others: + pin TRxC = DPLL pin TRxC = BRG + (RxClk * 1) (RxClk * 32) +*/ + + + switch(scc->modem.clocksrc) + { + case CLK_DPLL: + wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); + init_brg(scc); + break; + + case CLK_DIVIDER: + wr(scc, R11, ((scc->brand & BAYCOM)? TRxCDP : TRxCBR) | RCDPLL|TCRTxCP|TRxCOI); + init_brg(scc); + break; + + case CLK_EXTERNAL: + wr(scc, R11, (scc->brand & BAYCOM)? RCTRxCP|TCRTxCP : RCRTxCP|TCTRxCP); + OutReg(scc->ctrl, R14, DISDPLL); + break; + + } + + + if(scc->enhanced) + { + or(scc,R15,SHDLCE|FIFOE); /* enable FIFO, SDLC/HDLC Enhancements (From now R7 is R7') */ + wr(scc,R7,AUTOEOM); + } + + if(scc->modem.softdcd || (InReg(scc->ctrl,R0) & DCD)) + /* DCD is now ON */ + { + start_hunt(scc); + } + + /* enable ABORT, DCD & SYNC/HUNT interrupts */ + + wr(scc,R15, BRKIE|TxUIE|(scc->modem.softdcd? SYNCIE:DCDIE)); + + Outb(scc->ctrl,RES_EXT_INT); /* reset ext/status interrupts */ + Outb(scc->ctrl,RES_EXT_INT); /* must be done twice */ + + or(scc,R1,INT_ALL_Rx|TxINT_ENAB|EXT_INT_ENAB); /* enable interrupts */ + + scc->status = InReg(scc->ctrl,R0); /* read initial status */ + + or(scc,R9,MIE); /* master interrupt enable */ + + scc_init_timer(scc); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + + set_speed(scc); /* set baudrate */ +} + + +/* ******************************************************************** */ +/* * SCC timer functions * */ +/* ******************************************************************** */ + + +/* ----> scc_key_trx sets the time constant for the baudrate + generator and keys the transmitter <---- */ + +static void scc_key_trx(struct scc_channel *scc, char tx) +{ + unsigned long flags; + unsigned int time_const; + + if (scc->brand & PRIMUS) + Outb(scc->ctrl + 4, scc->option | (tx? 0x80 : 0)); + + if (scc->modem.speed < 300) + scc->modem.speed = 1200; + + time_const = (unsigned) (scc->clock / (scc->modem.speed * (tx? 2:64))) - 2; + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + if (tx) + { + or(scc, R1, TxINT_ENAB); /* t_maxkeyup may have reset these */ + or(scc, R15, TxUIE); + } + + if (scc->modem.clocksrc == CLK_DPLL) + { /* force simplex operation */ + if (tx) + { +#ifdef CONFIG_SCC_TRXECHO + cl(scc, R3, RxENABLE|ENT_HM); /* switch off receiver */ + cl(scc, R15, DCDIE|SYNCIE); /* No DCD changes, please */ +#endif + set_brg(scc, time_const); /* reprogram baudrate generator */ + + /* DPLL -> Rx clk, BRG -> Tx CLK, TRxC mode output, TRxC = BRG */ + wr(scc, R11, RCDPLL|TCBR|TRxCOI|TRxCBR); + + /* By popular demand: tx_inhibit */ + if (scc->modem.tx_inhibit) + { + or(scc,R5, TxENAB); + scc->wreg[R5] |= RTS; + } else { + or(scc,R5,RTS|TxENAB); /* set the RTS line and enable TX */ + } + } else { + cl(scc,R5,RTS|TxENAB); + + set_brg(scc, time_const); /* reprogram baudrate generator */ + + /* DPLL -> Rx clk, DPLL -> Tx CLK, TRxC mode output, TRxC = DPLL */ + wr(scc, R11, RCDPLL|TCDPLL|TRxCOI|TRxCDP); + +#ifndef CONFIG_SCC_TRXECHO + if (scc->modem.softdcd) +#endif + { + or(scc,R15, scc->modem.softdcd? SYNCIE:DCDIE); + start_hunt(scc); + } + } + } else { + if (tx) + { +#ifdef CONFIG_SCC_TRXECHO + if (scc->modem.fullduplex == 0) + { + cl(scc, R3, RxENABLE); + cl(scc, R15, DCDIE|SYNCIE); + } +#endif + + if (scc->modem.tx_inhibit) + { + or(scc,R5, TxENAB); + scc->wreg[R5] |= RTS; + } else { + or(scc,R5,RTS|TxENAB); /* enable tx */ + } + } else { + cl(scc,R5,RTS|TxENAB); /* disable tx */ + + if ((scc->modem.fullduplex == 0) && +#ifndef CONFIG_SCC_TRXECHO + scc->modem.softdcd) +#else + 1) +#endif + { + or(scc, R15, scc->modem.softdcd? SYNCIE:DCDIE); + start_hunt(scc); + } + } + } + + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); +} + +static void scc_kick_tx(struct scc_channel *scc) +{ + struct sk_buff *skb; + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.kick_tx, flags); + + skb = scc->tx_new; + scc->tx_new = NULL; + netif_wake_queue(scc->dev); + + if (skb == NULL) goto nada; + + if (skb->len == 0) /* Paranoia... */ + { + dev_kfree_skb(skb); + scc->tx_buff = NULL; + Outb(scc->ctrl, RES_Tx_P); + goto nada; + } + + scc->tx_buff = skb; + scc->stat.tx_state = TXS_ACTIVE; + OutReg(scc->ctrl, R0, RES_Tx_CRC); + /* reset CRC generator */ + or(scc,R10,ABUNDER); /* re-install underrun protection */ + Outb(scc->data,*skb->data); /* send byte */ + skb_pull(skb, 1); + + if (!scc->enhanced) /* reset EOM latch */ + Outb(scc->ctrl,RES_EOM_L); + + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; + +nada: + scc_tx_done(scc); + spin_unlock_irqrestore(&scc->spinlocks.kick_tx, flags); + return; +} + +/* ----> SCC timer interrupt handler and friends. <---- */ + +static void scc_start_tx_timer(struct scc_channel *scc, void (*handler)(struct scc_channel *), unsigned long when) +{ + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.timer, flags); + + del_timer(&scc->tx_timer); + + if (when != 0) + { + scc->tx_timer.data = (unsigned long) scc; + scc->tx_timer.function = (void (*)(unsigned long)) handler; + scc->tx_timer.expires = jiffies + (when*HZ)/1000; + add_timer(&scc->tx_timer); + } else { + handler(scc); + } + + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); +} + +/* + * This is called from scc_txint() when there are no more frames to send. + * Not exactly a timer function, but it is a close friend of the family... + */ + +static void scc_tx_done(struct scc_channel *scc) +{ + scc->stat.tx_state = TXS_TAIL; + + if (scc->modem.tailtime != 0) + scc_start_tx_timer(scc, scc_tail, scc->modem.tailtime); + else + scc_tail(scc); +} + +#ifndef SCC_SIMPLE_MAC +static void scc_tx_forced(struct scc_channel *scc) +{ + scc->stat.tx_state = TXS_TAIL; + + if (scc->tx_new) + scc_kick_tx(scc); + else + // remain key-up'ed for the time of txdelay... + // what's the timeout in 6pack? + scc_start_tx_timer(scc, scc_tail, scc->modem.txdelay); +} +#endif + +static void scc_tx_start(struct scc_channel *scc, struct sk_buff *skb) +{ + scc->tx_new = skb; + + /* + * scc_set_rts may also start a tx delay wait time, if we + * get a frame to transmit within this time RTS would be set, + * shorten the tx delay time... + */ + + if (scc->stat.tx_state != TXS_TXDELAY) + { + scc->stat.tx_state = TXS_TXDELAY; + + if ( !(scc->wreg[R5] & RTS) ) + { + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, scc_kick_tx, scc->modem.txdelay); + } else { + scc_start_tx_timer(scc, scc_kick_tx, 0); + } + } +} + +#ifndef SCC_SIMPLE_MAC +static void scc_set_rts(struct scc_channel *scc) +{ + scc->stat.tx_state = TXS_TXDELAY; + + if ( !(scc->wreg[R5] & RTS) ) + { + scc_key_trx(scc, TX_ON); + scc_start_tx_timer(scc, scc_tx_forced, scc->modem.txdelay); + } else { + scc_start_tx_timer(scc, scc_tx_forced, 0); + } +} +#endif + +/* + * TAILTIME expired + */ + +static void scc_tail(struct scc_channel *scc) +{ + if (scc->tx_buff != NULL) + return; + + if (scc->tx_new != NULL) + { + scc_kick_tx(scc); + return; + } + + scc_key_trx(scc, TX_OFF); + scc->stat.tx_state = TXS_IDLE; +} + +static void scc_init_timer(struct scc_channel *scc) +{ + unsigned long flags; + + spin_lock_irqsave(&scc->spinlocks.timer, flags); + + scc->stat.tx_state = TXS_IDLE; + // FIXME: this can't be all, can it...? + + spin_unlock_irqrestore(&scc->spinlocks.timer, flags); +} + + +/* ******************************************************************** */ +/* * Set/get L1 parameters * */ +/* ******************************************************************** */ + + +/* + * this will set the (some, anyway...) MODEM parameters + */ + +static unsigned int scc_set_param(struct scc_channel *scc, struct scc_modem *modem) +{ + scc->modem.txdelay = modem->txdelay; + scc->modem.tailtime = modem->tailtime; + scc->modem.fullduplex = modem->fullduplex; + scc->modem.tx_inhibit = modem->tx_inhibit; + scc->modem.softdcd = modem->softdcd; + + + if (modem->softdcd) + { + or(scc, R15, SYNCIE); + cl(scc, R15, DCDIE); + start_hunt(scc); + } else { + or(scc, R15, DCDIE); + cl(scc, R15, SYNCIE); + } + + return 0; +} + +static unsigned int scc_ddi_report_dcd(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "dcd=%d\n", scc->dcd); */ + return scc->dcd; +} + +static unsigned int scc_ddi_report_ptt(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "rts=%d\n", (scc->wreg[R5] & RTS)? 1:0); */ + return (scc->wreg[R5] & RTS)? 1:0; +} + +#ifndef SCC_SIMPLE_MAC +static unsigned int scc_ddi_report_cts(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + int txs = scc->stat.tx_state; + +/* printk(KERN_INFO "cts=%d\n", ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0); */ + return ((txs == TXS_ACTIVE) || (txs == TXS_TAIL))? 1:0; +} + +static void scc_ddi_set_rts(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; +/* printk(KERN_INFO "setrts\n"); */ + scc_set_rts(scc); +} +#endif + +static void scc_ddi_set_bitrate(struct net_device *dev, unsigned int speed) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + scc->modem.speed = speed; + if (scc->stat.tx_state != TXS_ACTIVE) + set_speed(scc); +} + +/* Update general parameters so that they reflect our internal settings */ +static void scc_ddi_param_update(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, scc->modem.fullduplex); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, scc->modem.speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, scc->modem.speed); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, scc->modem.txdelay); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, scc->modem.tailtime); + return; +} + +/* Called from upper layers when parameter was changed */ +static void scc_ddi_param_notify(struct net_device *dev, int valueno, int old, int new) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + switch (valueno) + { + case AX25_VALUES_MEDIA_DUPLEX: + if (!netif_running(dev)) scc->modem.fullduplex = new; + break; + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + scc_ddi_set_bitrate(dev, new); + break; + case AX25_VALUES_MEDIA_TXDELAY: + scc->modem.txdelay = new; + break; + case AX25_VALUES_MEDIA_TXTAIL: + scc->modem.tailtime = new; + break; + default: + break; + } + scc_ddi_param_update(dev); + return; +} + +/* ******************************************************************* */ +/* * Init channel structures, special HW, etc... * */ +/* ******************************************************************* */ + +/* + * Reset the Z8530s and setup special hardware + */ + +static void z8530_init(void) +{ + struct scc_channel *scc; + int chip, k; + unsigned long flags; + char *flag; + + + printk(KERN_INFO "Init Z8530 driver: %u channels, IRQ", Nchips*2); + + flag=" "; + for (k = 0; k < 16; k++) + if (Ivec[k].used) + { + printk("%s%d", flag, k); + flag=","; + } + printk("\n"); + + + /* reset and pre-init all chips in the system */ + for (chip = 0; chip < Nchips; chip++) + { + scc=&SCC_Info[2*chip]; + if (!scc->ctrl) continue; + + /* Special SCC cards */ + + if(scc->brand & EAGLE) /* this is an EAGLE card */ + Outb(scc->special,0x08); /* enable interrupt on the board */ + + if(scc->brand & (PC100 | PRIMUS)) /* this is a PC100/PRIMUS card */ + Outb(scc->special,scc->option); /* set the MODEM mode (0x22) */ + + + /* Reset and pre-init Z8530 */ + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + Outb(scc->ctrl,0); + OutReg(scc->ctrl,R9,FHWRES); /* force hardware reset */ + udelay(100); /* give it 'a bit' more time than required */ + wr(scc, R2, chip*16); /* interrupt vector */ + wr(scc, R9, VIS); /* vector includes status */ + + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + } + + + Driver_Initialized = 1; +} + +/* + * Allocate device structure, err, instance, and register driver + */ + +static int scc_net_setup(struct scc_channel *scc, unsigned char *name, int addev) +{ + struct net_device *dev; + + if (dev_get_by_name(name) != NULL) + { + printk(KERN_INFO "Z8530drv: device %s already exists.\n", name); + return -EEXIST; + } + + if ((scc->dev = (struct net_device *) kmalloc(sizeof(struct net_device), GFP_KERNEL)) == NULL) + return -ENOMEM; + + dev = scc->dev; + memset(dev, 0, sizeof(struct net_device)); + + dev->priv = (void *) scc; + strcpy(dev->name, name); + dev->init = scc_net_init; + + if ((addev? register_netdevice(dev) : register_netdev(dev)) != 0) + { + kfree(dev); + return -EIO; + } + + return 0; +} + + + +/* ******************************************************************** */ +/* * Network driver methods * */ +/* ******************************************************************** */ + +/* ----> Initialize device <----- */ + +static int scc_net_init(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + struct ax25_dev *ax25dev; + dev_init_buffers(dev); + + dev->tx_queue_len = 16; /* should be enough... */ + + dev->open = scc_net_open; + dev->stop = scc_net_close; + + dev->hard_start_xmit = scc_net_tx; + dev->set_mac_address = scc_net_set_mac_address; + dev->get_stats = scc_net_get_stats; + dev->do_ioctl = scc_net_ioctl; + dev->tx_timeout = scc_net_timeout; + dev->watchdog_timeo = SCC_WATCHDOG_TIMEOUT; + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN + AX25_BPQ_HEADER_LEN; + dev->mtu = AX25_DEF_PACLEN; + dev->addr_len = AX25_ADDR_LEN; + + AX25_PTR(dev) = ax25dev = &scc->ax25dev; + memset(ax25dev, 0, sizeof(struct ax25_dev)); + ax25dev->hw.ptt = scc_ddi_report_ptt; + ax25dev->hw.dcd = scc_ddi_report_dcd; +#ifndef SCC_SIMPLE_MAC + ax25dev->hw.cts = scc_ddi_report_cts; + ax25dev->hw.rts = scc_ddi_set_rts; +#endif + ax25dev->hw.parameter_change_notify = scc_ddi_param_notify; + ax25dev->hw.fast = 0; + + return 0; +} + +/* ----> open network device <---- */ + +static int scc_net_open(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (!scc->init) return -EINVAL; + + MOD_INC_USE_COUNT; + + scc->tx_buff = NULL; + + init_channel(scc); + + scc_ddi_param_update(dev); + netif_start_queue(dev); + + return 0; +} + +/* ----> close network device <---- */ + +static int scc_net_close(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + netif_stop_queue(dev); + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + Outb(scc->ctrl,0); /* Make sure pointer is written */ + wr(scc,R1,0); /* disable interrupts */ + wr(scc,R3,0); + + del_timer(&scc->tx_timer); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + + if (scc->tx_buff) + kfree_skb(scc->tx_buff); + if (scc->tx_new) + kfree_skb(scc->tx_new); + + MOD_DEC_USE_COUNT; + return 0; +} + +/* ----> receive frame, called from scc_rxint() <---- */ + +static void scc_net_rx(struct scc_channel *scc, struct sk_buff *skb) +{ + if (skb->len == 0) + { + kfree_skb(skb); + return; + } + + scc->dev_stat.rx_packets++; + + skb->dev = scc->dev; + skb->protocol = htons(ETH_P_AX25); + skb->mac.raw = skb->data; + skb->pkt_type = PACKET_HOST; + + netif_rx(skb); + return; +} + +/* ----> transmit frame <---- */ + +static int scc_net_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + if (skb->len > scc->stat.bufsize || skb->len < 2) + { + scc->dev_stat.tx_dropped++; /* bogus frame */ + dev_kfree_skb(skb); + return 0; + } + + netif_stop_queue(dev); + + scc->dev_stat.tx_packets++; + scc->stat.txframes++; + +#ifdef notdef + /**** The following "fix" is bogus. Fix the protocol, dammit! ****/ + + /* avoid race condition when skb is a cloned broadcast */ + skb_cp = skb_copy(skb, GFP_ATOMIC); + dev_kfree_skb(skb); + if (skb_cp == NULL) { + scc->dev_stat.tx_dropped++; /* out of memory */ + return 0; + } +#endif + + /* transmit frame */ + + dev->trans_start = jiffies; + scc_tx_start(scc, skb); + + return 0; +} + + +/* -----> Watchdog <----- */ + +static void scc_net_timeout(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + unsigned long flags; + + del_timer(&scc->tx_timer); + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + cl(scc, R1, TxINT_ENAB); + cl(scc, R15, TxUIE); + OutReg(scc->ctrl, R0, RES_Tx_P); + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + + scc_start_tx_timer(scc, init_channel, 100); +} + + + +/* ----> ioctl functions <---- */ + +/* + * SIOCSCCCFG - configure driver arg: (struct scc_hw_config *) arg + * SIOCSCCINI - initialize driver arg: --- + * SIOCSCCCHANINI - initialize channel arg: (struct scc_modem *) arg + * SIOCSCCSMEM - set memory arg: (struct scc_mem_config *) arg + * SIOCSCCGMODEM - get level 1 parameter arg: (struct scc_modem *) arg + * SIOCSCCSMODEM - set level 1 parameter arg: (struct scc_modem *) arg + * SIOCSCCGSTAT - get driver status arg: (struct scc_stat *) arg + */ + +static int scc_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct scc_modem modem; + struct scc_mem_config memcfg; + struct scc_hw_config hwcfg; + unsigned long flags; + int chan; + unsigned char device_name[10]; + void *arg; + struct scc_channel *scc; + unsigned int ret; + + scc = (struct scc_channel *) dev->priv; + arg = (void *) ifr->ifr_data; + + if (!Driver_Initialized) + { + if (cmd == SIOCSCCCFG) + { + int found = 1; + + if (!capable(CAP_SYS_RAWIO)) return -EPERM; + if (!arg) return -EFAULT; + + if (Nchips >= SCC_MAXCHIPS) + return -EINVAL; + + if (copy_from_user(&hwcfg, arg, sizeof(hwcfg))) + return -EFAULT; + + IO_Spinlock = SPIN_LOCK_UNLOCKED; + IO_Delay = hwcfg.delay; + + if (hwcfg.irq == 2) hwcfg.irq = 9; + if (!Ivec[hwcfg.irq].used && hwcfg.irq) + { + if (request_irq(hwcfg.irq, scc_isr, SA_INTERRUPT, "AX.25 SCC", NULL)) + printk(KERN_WARNING "z8530drv: warning, cannot get IRQ %d\n", hwcfg.irq); + else + Ivec[hwcfg.irq].used = 1; + } + + if (hwcfg.vector_latch) + Vector_Latch = hwcfg.vector_latch; + + if (hwcfg.clock == 0) + hwcfg.clock = SCC_DEFAULT_CLOCK; + + +#ifndef SCC_DONT_CHECK + save_flags(flags); + cli(); + + check_region(scc->ctrl, 1); + Outb(hwcfg.ctrl_a, 0); + OutReg(hwcfg.ctrl_a, R9, FHWRES); + udelay(100); + OutReg(hwcfg.ctrl_a, R13, 0x55); + udelay(5); + if (InReg(hwcfg.ctrl_a, R13) != 0x55) + found = 0; + + restore_flags(flags); +#endif + + if (found) + { + SCC_ctrl[Nchips].chan_A = hwcfg.ctrl_a; + SCC_ctrl[Nchips].chan_B = hwcfg.ctrl_b; + SCC_ctrl[Nchips].irq = hwcfg.irq; + } + + + for (chan = 0; chan < 2; chan++) + { + sprintf(device_name, "%s%i", SCC_DriverName, 2*Nchips+chan); + + SCC_Info[2*Nchips+chan].ctrl = chan? hwcfg.ctrl_b:hwcfg.ctrl_a; + SCC_Info[2*Nchips+chan].data = chan? hwcfg.data_b:hwcfg.data_a; + SCC_Info[2*Nchips+chan].irq = hwcfg.irq; + SCC_Info[2*Nchips+chan].special = hwcfg.special; + SCC_Info[2*Nchips+chan].clock = hwcfg.clock; + SCC_Info[2*Nchips+chan].brand = hwcfg.brand; + SCC_Info[2*Nchips+chan].option = hwcfg.option; + SCC_Info[2*Nchips+chan].enhanced= hwcfg.escc; + +#ifdef SCC_DONT_CHECK + printk(KERN_INFO "%s: data port = 0x%3.3x control port = 0x%3.3x\n", + device_name, + SCC_Info[2*Nchips+chan].data, + SCC_Info[2*Nchips+chan].ctrl); + +#else + printk(KERN_INFO "%s: data port = 0x%3.3lx control port = 0x%3.3lx -- %s\n", + device_name, + chan? hwcfg.data_b : hwcfg.data_a, + chan? hwcfg.ctrl_b : hwcfg.ctrl_a, + found? "found" : "missing"); +#endif + + if (found) + { + request_region(SCC_Info[2*Nchips+chan].ctrl, 1, "scc ctrl"); + request_region(SCC_Info[2*Nchips+chan].data, 1, "scc data"); + if (Nchips+chan != 0) + scc_net_setup(&SCC_Info[2*Nchips+chan], device_name, 1); + } + } + + if (found) Nchips++; + + return 0; + } + + if (cmd == SIOCSCCINI) + { + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + if (Nchips == 0) + return -EINVAL; + + z8530_init(); + scc_ddi_param_update(dev); + return 0; + } + + return -EINVAL; /* confuse the user */ + } + + if (!scc->init) + { + if (cmd == SIOCSCCCHANINI) + { + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (!arg) return -EINVAL; + + scc->stat.bufsize = SCC_BUFSIZE; + + if (copy_from_user(&scc->modem, arg, sizeof(struct scc_modem))) + return -EINVAL; + + scc->tx_buff = NULL; + scc->tx_new = NULL; + scc->init = 1; + + scc_ddi_param_update(dev); + return 0; + } + + return -EINVAL; + } + + switch(cmd) + { + case SIOCSCCRESERVED: + return -ENOIOCTLCMD; + + case SIOCSCCSMEM: + if (!capable(CAP_SYS_RAWIO)) return -EPERM; + if (!arg || copy_from_user(&memcfg, arg, sizeof(memcfg))) + return -EINVAL; + scc->stat.bufsize = memcfg.bufsize; + return 0; + + case SIOCSCCGSTAT: + if (!arg || copy_to_user(arg, &scc->stat, sizeof(scc->stat))) + return -EINVAL; + return 0; + + case SIOCSCCGMODEM: + if (!arg || copy_to_user(arg, &scc->modem, sizeof(struct scc_modem))) + return -EINVAL; + return 0; + + case SIOCSCCSMODEM: + if (!capable(CAP_NET_ADMIN)) return -EPERM; + if (!arg || copy_from_user(&modem, arg, sizeof(struct scc_modem))) + return -EINVAL; + ret = scc_set_param(scc, &modem); + scc_ddi_param_update(dev); + return ret; + + default: + return -ENOIOCTLCMD; + + } + + return -EINVAL; +} + +/* ----> set interface callsign <---- */ + +static int scc_net_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *) addr; + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + return 0; +} + +/* ----> get statistics <---- */ + +static struct net_device_stats *scc_net_get_stats(struct net_device *dev) +{ + struct scc_channel *scc = (struct scc_channel *) dev->priv; + + scc->dev_stat.rx_errors = scc->stat.rxerrs + scc->stat.rx_over; + scc->dev_stat.tx_errors = scc->stat.txerrs + scc->stat.tx_under; + scc->dev_stat.rx_fifo_errors = scc->stat.rx_over; + scc->dev_stat.tx_fifo_errors = scc->stat.tx_under; + + return &scc->dev_stat; +} + +/* ******************************************************************** */ +/* * dump statistics to /proc/net/z8530drv * */ +/* ******************************************************************** */ + + +static int scc_net_get_info(char *buffer, char **start, off_t offset, int length) +{ + struct scc_channel *scc; + struct scc_stat *stat; + int len = 0; + off_t pos = 0; + off_t begin = 0; + int k; + + len += sprintf(buffer, "z8530drv-"VERSION"\n"); + + if (!Driver_Initialized) + { + len += sprintf(buffer+len, "not initialized\n"); + goto done; + } + + if (!Nchips) + { + len += sprintf(buffer+len, "chips missing\n"); + goto done; + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + stat = &scc->stat; + + if (!scc->init) + continue; + + /* dev data ctrl irq clock brand enh vector special option + * baud nrz clksrc fulldplx txoff softdcd txd tail bufsize + * rxints txints exints spints + * rcvd rxerrs over / xmit txerrs under / nospace bufsize + * W ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## + * R ## ## XX ## ## ## ## ## XX ## ## ## ## ## ## ## + */ + + len += sprintf(buffer+len, "%s\t%3.3lx %3.3lx %d %lu %2.2x %d %3.3lx %3.3lx %d\n", + scc->dev->name, + scc->data, scc->ctrl, scc->irq, scc->clock, scc->brand, + scc->enhanced, Vector_Latch, scc->special, + scc->option); + len += sprintf(buffer+len, "\t%lu %d %d %d %d %d %d %d %d\n", + scc->modem.speed, scc->modem.nrz, + scc->modem.clocksrc, scc->modem.fullduplex, + scc->modem.tx_inhibit, scc->modem.softdcd, + scc->modem.txdelay, scc->modem.tailtime, + stat->bufsize); + len += sprintf(buffer+len, "\t%lu %lu %lu %lu\n", + stat->rxints, stat->txints, stat->exints, stat->spints); + len += sprintf(buffer+len, "\t%lu %lu %d / %lu %lu %d / %d %d\n", + stat->rxframes, stat->rxerrs, stat->rx_over, + stat->txframes, stat->txerrs, stat->tx_under, + stat->nospace, stat->tx_state); + +#ifdef SCC_DEBUG + { + int reg; + + len += sprintf(buffer+len, "\tW "); + for (reg = 0; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", scc->wreg[reg]); + len += sprintf(buffer+len, "\n"); + + len += sprintf(buffer+len, "\tR %2.2x %2.2x XX ", InReg(scc->ctrl,R0), InReg(scc->ctrl,R1)); + for (reg = 3; reg < 8; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "XX "); + for (reg = 9; reg < 16; reg++) + len += sprintf(buffer+len, "%2.2x ", InReg(scc->ctrl, reg)); + len += sprintf(buffer+len, "\n"); + } +#endif + len += sprintf(buffer+len, "\n"); + + pos = begin + len; + + if (pos < offset) { + len = 0; + begin = pos; + } + + if (pos > offset + length) + break; + } + +done: + + *start = buffer + (offset - begin); + len -= (offset - begin); + + if (len > length) len = length; + + return len; +} + + + +/* ******************************************************************** */ +/* * Init SCC driver * */ +/* ******************************************************************** */ + +int __init scc_init_driver(void) +{ + int chip, chan, k, result; + char devname[10]; + + printk(KERN_INFO BANNER); + + memset(&SCC_ctrl, 0, sizeof(SCC_ctrl)); + + /* pre-init channel information */ + + for (chip = 0; chip < SCC_MAXCHIPS; chip++) + { + for (chan = 0; chan < 2; chan++) + { + struct scc_channel *scc = &SCC_Info[2*chip+chan]; + memset((char *) scc, 0, sizeof(struct scc_channel)); + + scc->spinlocks.hwaccess = SPIN_LOCK_UNLOCKED; + scc->spinlocks.timer = SPIN_LOCK_UNLOCKED; + scc->spinlocks.kick_tx = SPIN_LOCK_UNLOCKED; + } + } + + for (k = 0; k < 16; k++) Ivec[k].used = 0; + + sprintf(devname,"%s0", SCC_DriverName); + + result = scc_net_setup(SCC_Info, devname, 0); + if (result) + { + printk(KERN_ERR "z8530drv: cannot initialize module\n"); + return result; + } + +#ifdef CONFIG_PROC_FS + proc_net_create("z8530drv", 0, scc_net_get_info); +#endif + + return 0; +} + +/* ******************************************************************** */ +/* * Module support * */ +/* ******************************************************************** */ + + +void __exit scc_cleanup_driver(void) +{ + long flags; + io_port ctrl; + int k; + struct scc_channel *scc = NULL; + + spin_lock_irqsave(&scc->spinlocks.hwaccess, flags); + + if (Nchips == 0) + unregister_netdev(SCC_Info[0].dev); + + for (k = 0; k < Nchips; k++) + if ( (ctrl = SCC_ctrl[k].chan_A) ) + { + Outb(ctrl,0); + OutReg(ctrl,R9,FHWRES); /* force hardware reset */ + udelay(50); + } + + for (k = 0; k < Nchips*2; k++) + { + scc = &SCC_Info[k]; + if (scc) + { + release_region(scc->ctrl, 1); + release_region(scc->data, 1); + if (scc->dev) + { + unregister_netdev(scc->dev); + kfree(scc->dev); + } + } + } + + for (k=0; k < 16 ; k++) + if (Ivec[k].used) free_irq(k, NULL); + + spin_unlock_irqrestore(&scc->spinlocks.hwaccess, flags); + +#ifdef CONFIG_PROC_FS + proc_net_remove("z8530drv"); +#endif +} + +MODULE_AUTHOR("Joerg Reuter <jreuter@yaina.de>"); +MODULE_DESCRIPTION("Network Device Driver for Z8530 based HDLC cards for Amateur Packet Radio"); +MODULE_SUPPORTED_DEVICE("Z8530 based SCC cards without DMA support"); +module_init(scc_init_driver); +module_exit(scc_cleanup_driver); diff --git a/drivers/net/hamradio/soundmodem/Makefile b/drivers/net/hamradio/soundmodem/Makefile deleted file mode 100644 index 959701ab1..000000000 --- a/drivers/net/hamradio/soundmodem/Makefile +++ /dev/null @@ -1,42 +0,0 @@ -# -# Makefile for the soundmodem device driver. -# -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# - -O_TARGET := soundmodem.o - -obj-y := sm.o -obj-$(CONFIG_SOUNDMODEM_SBC) += sm_sbc.o -obj-$(CONFIG_SOUNDMODEM_WSS) += sm_wss.o -obj-$(CONFIG_SOUNDMODEM_AFSK1200) += sm_afsk1200.o -obj-$(CONFIG_SOUNDMODEM_AFSK2400_7) += sm_afsk2400_7.o -obj-$(CONFIG_SOUNDMODEM_AFSK2400_8) += sm_afsk2400_8.o -obj-$(CONFIG_SOUNDMODEM_AFSK2666) += sm_afsk2666.o -obj-$(CONFIG_SOUNDMODEM_HAPN4800) += sm_hapn4800.o -obj-$(CONFIG_SOUNDMODEM_PSK4800) += sm_psk4800.o -obj-$(CONFIG_SOUNDMODEM_FSK9600) += sm_fsk9600.o - -obj-m := $(O_TARGET) - -all: all_targets -.PHONY: all - -gentbl: gentbl.c - $(HOSTCC) $(HOSTCFLAGS) $< -o $@ -lm - -TBLHDR := sm_tbl_afsk1200.h sm_tbl_afsk2400_8.h -TBLHDR += sm_tbl_afsk2666.h sm_tbl_psk4800.h -TBLHDR += sm_tbl_hapn4800.h sm_tbl_fsk9600.h - -$(TBLHDR): gentbl - ./gentbl - -fastdep: $(TBLHDR) - -include $(TOPDIR)/Rules.make diff --git a/drivers/net/hamradio/soundmodem/sm.c b/drivers/net/hamradio/soundmodem/sm.c deleted file mode 100644 index c5a52b8ab..000000000 --- a/drivers/net/hamradio/soundmodem/sm.c +++ /dev/null @@ -1,759 +0,0 @@ -/*****************************************************************************/ - -/* - * sm.c -- soundcard radio modem driver. - * - * Copyright (C) 1996-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * - * Command line options (insmod command line) - * - * mode mode string; eg. "wss:afsk1200" - * iobase base address of the soundcard; common values are 0x220 for sbc, - * 0x530 for wss - * irq interrupt number; common values are 7 or 5 for sbc, 11 for wss - * dma dma number; common values are 0 or 1 - * - * - * History: - * 0.1 21.09.1996 Started - * 18.10.1996 Changed to new user space access routines (copy_{to,from}_user) - * 0.4 21.01.1997 Separately compileable soundcard/modem modules - * 0.5 03.03.1997 fixed LPT probing (check_lpt result was interpreted the wrong way round) - * 0.6 16.04.1997 init code/data tagged - * 0.7 30.07.1997 fixed halfduplex interrupt handlers/hotfix for CS423X - * 0.8 14.04.1998 cleanups - * 0.9 03.08.1999 adapt to Linus' new __setup/__initcall - * use parport lowlevel drivers instead of directly writing to a parallel port - * removed some pre-2.2 kernel compatibility cruft - * 0.10 10.08.1999 Check if parport can do SPP and is safe to access during interrupt contexts - * 0.11 12.02.2000 adapted to softnet driver interface - * 0.12 03.07.2000 fix interface name handling - */ - -/*****************************************************************************/ - -#include <linux/config.h> -#include <linux/version.h> -#include <linux/module.h> -#include <linux/ioport.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/parport.h> -#include <asm/uaccess.h> -#include <asm/io.h> -#include "sm.h" - -/* --------------------------------------------------------------------- */ - -/*static*/ const char sm_drvname[] = "soundmodem"; -static const char sm_drvinfo[] = KERN_INFO "soundmodem: (C) 1996-2000 Thomas Sailer, HB9JNX/AE4WA\n" -KERN_INFO "soundmodem: version 0.12 compiled " __TIME__ " " __DATE__ "\n"; - -/* --------------------------------------------------------------------- */ - -/*static*/ const struct modem_tx_info *sm_modem_tx_table[] = { -#ifdef CONFIG_SOUNDMODEM_AFSK1200 - &sm_afsk1200_tx, -#endif /* CONFIG_SOUNDMODEM_AFSK1200 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 - &sm_afsk2400_7_tx, -#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 - &sm_afsk2400_8_tx, -#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2666 - &sm_afsk2666_tx, -#endif /* CONFIG_SOUNDMODEM_AFSK2666 */ -#ifdef CONFIG_SOUNDMODEM_PSK4800 - &sm_psk4800_tx, -#endif /* CONFIG_SOUNDMODEM_PSK4800 */ -#ifdef CONFIG_SOUNDMODEM_HAPN4800 - &sm_hapn4800_8_tx, - &sm_hapn4800_10_tx, - &sm_hapn4800_pm8_tx, - &sm_hapn4800_pm10_tx, -#endif /* CONFIG_SOUNDMODEM_HAPN4800 */ -#ifdef CONFIG_SOUNDMODEM_FSK9600 - &sm_fsk9600_4_tx, - &sm_fsk9600_5_tx, -#endif /* CONFIG_SOUNDMODEM_FSK9600 */ - NULL -}; - -/*static*/ const struct modem_rx_info *sm_modem_rx_table[] = { -#ifdef CONFIG_SOUNDMODEM_AFSK1200 - &sm_afsk1200_rx, -#endif /* CONFIG_SOUNDMODEM_AFSK1200 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2400_7 - &sm_afsk2400_7_rx, -#endif /* CONFIG_SOUNDMODEM_AFSK2400_7 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2400_8 - &sm_afsk2400_8_rx, -#endif /* CONFIG_SOUNDMODEM_AFSK2400_8 */ -#ifdef CONFIG_SOUNDMODEM_AFSK2666 - &sm_afsk2666_rx, -#endif /* CONFIG_SOUNDMODEM_AFSK2666 */ -#ifdef CONFIG_SOUNDMODEM_PSK4800 - &sm_psk4800_rx, -#endif /* CONFIG_SOUNDMODEM_PSK4800 */ -#ifdef CONFIG_SOUNDMODEM_HAPN4800 - &sm_hapn4800_8_rx, - &sm_hapn4800_10_rx, - &sm_hapn4800_pm8_rx, - &sm_hapn4800_pm10_rx, -#endif /* CONFIG_SOUNDMODEM_HAPN4800 */ -#ifdef CONFIG_SOUNDMODEM_FSK9600 - &sm_fsk9600_4_rx, - &sm_fsk9600_5_rx, -#endif /* CONFIG_SOUNDMODEM_FSK9600 */ - NULL -}; - -static const struct hardware_info *sm_hardware_table[] = { -#ifdef CONFIG_SOUNDMODEM_SBC - &sm_hw_sbc, - &sm_hw_sbcfdx, -#endif /* CONFIG_SOUNDMODEM_SBC */ -#ifdef CONFIG_SOUNDMODEM_WSS - &sm_hw_wss, - &sm_hw_wssfdx, -#endif /* CONFIG_SOUNDMODEM_WSS */ - NULL -}; - -/* --------------------------------------------------------------------- */ - -#define NR_PORTS 4 - -static struct net_device sm_device[NR_PORTS]; - -/* --------------------------------------------------------------------- */ - -#define UART_RBR(iobase) (iobase+0) -#define UART_THR(iobase) (iobase+0) -#define UART_IER(iobase) (iobase+1) -#define UART_IIR(iobase) (iobase+2) -#define UART_FCR(iobase) (iobase+2) -#define UART_LCR(iobase) (iobase+3) -#define UART_MCR(iobase) (iobase+4) -#define UART_LSR(iobase) (iobase+5) -#define UART_MSR(iobase) (iobase+6) -#define UART_SCR(iobase) (iobase+7) -#define UART_DLL(iobase) (iobase+0) -#define UART_DLM(iobase) (iobase+1) - -#define SER_EXTENT 8 - -#define MIDI_DATA(iobase) (iobase) -#define MIDI_STATUS(iobase) (iobase+1) -#define MIDI_READ_FULL 0x80 /* attention: negative logic!! */ -#define MIDI_WRITE_EMPTY 0x40 /* attention: negative logic!! */ - -#define MIDI_EXTENT 2 - -/* ---------------------------------------------------------------------- */ - -#define PARAM_TXDELAY 1 -#define PARAM_PERSIST 2 -#define PARAM_SLOTTIME 3 -#define PARAM_TXTAIL 4 -#define PARAM_FULLDUP 5 -#define PARAM_HARDWARE 6 -#define PARAM_RETURN 255 - -#define SP_SER 1 -#define SP_PAR 2 -#define SP_MIDI 4 - -/* - * ===================== port checking routines ======================== - */ - -enum uart { c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A}; -static const char *uart_str[] = - { "unknown", "8250", "16450", "16550", "16550A" }; - -static enum uart check_uart(unsigned int iobase) -{ - unsigned char b1,b2,b3; - enum uart u; - enum uart uart_tab[] = - { c_uart_16450, c_uart_unknown, c_uart_16550, c_uart_16550A }; - - if (iobase <= 0 || iobase > 0x1000-SER_EXTENT) - return c_uart_unknown; - if (check_region(iobase, SER_EXTENT)) - return c_uart_unknown; - b1 = inb(UART_MCR(iobase)); - outb(b1 | 0x10, UART_MCR(iobase)); /* loopback mode */ - b2 = inb(UART_MSR(iobase)); - outb(0x1a, UART_MCR(iobase)); - b3 = inb(UART_MSR(iobase)) & 0xf0; - outb(b1, UART_MCR(iobase)); /* restore old values */ - outb(b2, UART_MSR(iobase)); - if (b3 != 0x90) - return c_uart_unknown; - inb(UART_RBR(iobase)); - inb(UART_RBR(iobase)); - outb(0x01, UART_FCR(iobase)); /* enable FIFOs */ - u = uart_tab[(inb(UART_IIR(iobase)) >> 6) & 3]; - if (u == c_uart_16450) { - outb(0x5a, UART_SCR(iobase)); - b1 = inb(UART_SCR(iobase)); - outb(0xa5, UART_SCR(iobase)); - b2 = inb(UART_SCR(iobase)); - if ((b1 != 0x5a) || (b2 != 0xa5)) - u = c_uart_8250; - } - return u; -} - -/* --------------------------------------------------------------------- */ - -static int check_midi(unsigned int iobase) -{ - unsigned long timeout; - unsigned long flags; - unsigned char b; - - if (iobase <= 0 || iobase > 0x1000-MIDI_EXTENT) - return 0; - if (check_region(iobase, MIDI_EXTENT)) - return 0; - timeout = jiffies + (HZ / 100); - while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) - if ((signed)(jiffies - timeout) > 0) - return 0; - save_flags(flags); - cli(); - outb(0xff, MIDI_DATA(iobase)); - b = inb(MIDI_STATUS(iobase)); - restore_flags(flags); - if (!(b & MIDI_WRITE_EMPTY)) - return 0; - while (inb(MIDI_STATUS(iobase)) & MIDI_WRITE_EMPTY) - if ((signed)(jiffies - timeout) > 0) - return 0; - return 1; -} - -/* --------------------------------------------------------------------- */ - -void sm_output_status(struct sm_state *sm) -{ - int invert_dcd = 0; - int invert_ptt = 0; - - int ptt = /*hdlcdrv_ptt(&sm->hdrv)*/(sm->dma.ptt_cnt > 0) ^ invert_ptt; - int dcd = (!!sm->hdrv.hdlcrx.dcd) ^ invert_dcd; - - if (sm->hdrv.ptt_out.flags & SP_SER) { - outb(dcd | (ptt << 1), UART_MCR(sm->hdrv.ptt_out.seriobase)); - outb(0x40 & (-ptt), UART_LCR(sm->hdrv.ptt_out.seriobase)); - } - if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev && sm->pardev->port) - parport_write_data(sm->pardev->port, ptt | (dcd << 1)); - if (sm->hdrv.ptt_out.flags & SP_MIDI && hdlcdrv_ptt(&sm->hdrv)) - outb(0, MIDI_DATA(sm->hdrv.ptt_out.midiiobase)); -} - -/* --------------------------------------------------------------------- */ - -static void sm_output_open(struct sm_state *sm, const char *ifname) -{ - enum uart u = c_uart_unknown; - struct parport *pp = NULL; - - sm->hdrv.ptt_out.flags = 0; - if (sm->hdrv.ptt_out.seriobase > 0 && - sm->hdrv.ptt_out.seriobase <= 0x1000-SER_EXTENT && - ((u = check_uart(sm->hdrv.ptt_out.seriobase))) != c_uart_unknown) { - sm->hdrv.ptt_out.flags |= SP_SER; - request_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT, "sm ser ptt"); - outb(0, UART_IER(sm->hdrv.ptt_out.seriobase)); - /* 5 bits, 1 stop, no parity, no break, Div latch access */ - outb(0x80, UART_LCR(sm->hdrv.ptt_out.seriobase)); - outb(0, UART_DLM(sm->hdrv.ptt_out.seriobase)); - outb(1, UART_DLL(sm->hdrv.ptt_out.seriobase)); /* as fast as possible */ - /* LCR and MCR set by output_status */ - } - sm->pardev = NULL; - if (sm->hdrv.ptt_out.pariobase > 0) { - pp = parport_enumerate(); - while (pp && pp->base != sm->hdrv.ptt_out.pariobase) - pp = pp->next; - if (!pp) - printk(KERN_WARNING "%s: parport at address 0x%x not found\n", sm_drvname, sm->hdrv.ptt_out.pariobase); - else if ((~pp->modes) & (PARPORT_MODE_PCSPP | PARPORT_MODE_SAFEININT)) - printk(KERN_WARNING "%s: parport at address 0x%x cannot be used\n", sm_drvname, sm->hdrv.ptt_out.pariobase); - else { - sm->pardev = parport_register_device(pp, ifname, NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (!sm->pardev) { - pp = NULL; - printk(KERN_WARNING "%s: cannot register parport device (address 0x%x)\n", sm_drvname, sm->hdrv.ptt_out.pariobase); - } else { - if (parport_claim(sm->pardev)) { - parport_unregister_device(sm->pardev); - sm->pardev = NULL; - printk(KERN_WARNING "%s: cannot claim parport at address 0x%x\n", sm_drvname, sm->hdrv.ptt_out.pariobase); - } else - sm->hdrv.ptt_out.flags |= SP_PAR; - } - } - } - if (sm->hdrv.ptt_out.midiiobase > 0 && - sm->hdrv.ptt_out.midiiobase <= 0x1000-MIDI_EXTENT && - check_midi(sm->hdrv.ptt_out.midiiobase)) { - sm->hdrv.ptt_out.flags |= SP_MIDI; - request_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT, - "sm midi ptt"); - } - sm_output_status(sm); - - printk(KERN_INFO "%s: ptt output:", sm_drvname); - if (sm->hdrv.ptt_out.flags & SP_SER) - printk(" serial interface at 0x%x, uart %s", sm->hdrv.ptt_out.seriobase, - uart_str[u]); - if (sm->hdrv.ptt_out.flags & SP_PAR) - printk(" parallel interface at 0x%x", sm->hdrv.ptt_out.pariobase); - if (sm->hdrv.ptt_out.flags & SP_MIDI) - printk(" mpu401 (midi) interface at 0x%x", sm->hdrv.ptt_out.midiiobase); - if (!sm->hdrv.ptt_out.flags) - printk(" none"); - printk("\n"); -} - -/* --------------------------------------------------------------------- */ - -static void sm_output_close(struct sm_state *sm) -{ - /* release regions used for PTT output */ - sm->hdrv.hdlctx.ptt = sm->hdrv.hdlctx.calibrate = 0; - sm_output_status(sm); - if (sm->hdrv.ptt_out.flags & SP_SER) - release_region(sm->hdrv.ptt_out.seriobase, SER_EXTENT); - if (sm->hdrv.ptt_out.flags & SP_PAR && sm->pardev) { - parport_release(sm->pardev); - parport_unregister_device(sm->pardev); - } - if (sm->hdrv.ptt_out.flags & SP_MIDI) - release_region(sm->hdrv.ptt_out.midiiobase, MIDI_EXTENT); - sm->hdrv.ptt_out.flags = 0; -} - -/* --------------------------------------------------------------------- */ - -static int sm_open(struct net_device *dev); -static int sm_close(struct net_device *dev); -static int sm_ioctl(struct net_device *dev, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd); - -/* --------------------------------------------------------------------- */ - -static const struct hdlcdrv_ops sm_ops = { - sm_drvname, sm_drvinfo, sm_open, sm_close, sm_ioctl -}; - -/* --------------------------------------------------------------------- */ - -static int sm_open(struct net_device *dev) -{ - struct sm_state *sm; - int err; - - if (!dev || !dev->priv || - ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { - printk(KERN_ERR "sm_open: invalid device struct\n"); - return -EINVAL; - } - sm = (struct sm_state *)dev->priv; - - if (!sm->mode_tx || !sm->mode_rx || !sm->hwdrv || !sm->hwdrv->open) - return -ENODEV; - sm->hdrv.par.bitrate = sm->mode_rx->bitrate; - err = sm->hwdrv->open(dev, sm); - if (err) - return err; - sm_output_open(sm, dev->name); - MOD_INC_USE_COUNT; - printk(KERN_INFO "%s: %s mode %s.%s at iobase 0x%lx irq %u dma %u dma2 %u\n", - sm_drvname, sm->hwdrv->hw_name, sm->mode_tx->name, - sm->mode_rx->name, dev->base_addr, dev->irq, dev->dma, sm->hdrv.ptt_out.dma2); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sm_close(struct net_device *dev) -{ - struct sm_state *sm; - int err = -ENODEV; - - if (!dev || !dev->priv || - ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { - printk(KERN_ERR "sm_close: invalid device struct\n"); - return -EINVAL; - } - sm = (struct sm_state *)dev->priv; - - - if (sm->hwdrv && sm->hwdrv->close) - err = sm->hwdrv && sm->hwdrv->close(dev, sm); - sm_output_close(sm); - MOD_DEC_USE_COUNT; - printk(KERN_INFO "%s: close %s at iobase 0x%lx irq %u dma %u\n", - sm_drvname, sm->hwdrv->hw_name, dev->base_addr, dev->irq, dev->dma); - return err; -} - -/* --------------------------------------------------------------------- */ - -static int sethw(struct net_device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, ':'); - const struct hardware_info **hwp = sm_hardware_table; - - if (!cp) - cp = mode; - else { - *cp++ = '\0'; - while (hwp && (*hwp) && (*hwp)->hw_name && strcmp((*hwp)->hw_name, mode)) - hwp++; - if (!hwp || !*hwp || !(*hwp)->hw_name) - return -EINVAL; - if ((*hwp)->loc_storage > sizeof(sm->hw)) { - printk(KERN_ERR "%s: insufficient storage for hw driver %s (%d)\n", - sm_drvname, (*hwp)->hw_name, (*hwp)->loc_storage); - return -EINVAL; - } - sm->hwdrv = *hwp; - } - if (!*cp) - return 0; - if (sm->hwdrv && sm->hwdrv->sethw) - return sm->hwdrv->sethw(dev, sm, cp); - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int sm_ioctl(struct net_device *dev, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct sm_state *sm; - struct sm_ioctl bi; - unsigned long flags; - unsigned int newdiagmode; - unsigned int newdiagflags; - char *cp; - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp = sm_modem_rx_table; - const struct hardware_info **hwp = sm_hardware_table; - - if (!dev || !dev->priv || - ((struct sm_state *)dev->priv)->hdrv.magic != HDLCDRV_MAGIC) { - printk(KERN_ERR "sm_ioctl: invalid device struct\n"); - return -EINVAL; - } - sm = (struct sm_state *)dev->priv; - - if (cmd != SIOCDEVPRIVATE) { - if (!sm->hwdrv || !sm->hwdrv->ioctl) - return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd); - return -ENOIOCTLCMD; - } - switch (hi->cmd) { - default: - if (sm->hwdrv && sm->hwdrv->ioctl) - return sm->hwdrv->ioctl(dev, sm, ifr, hi, cmd); - return -ENOIOCTLCMD; - - case HDLCDRVCTL_GETMODE: - cp = hi->data.modename; - if (sm->hwdrv && sm->hwdrv->hw_name) - cp += sprintf(cp, "%s:", sm->hwdrv->hw_name); - else - cp += sprintf(cp, "<unspec>:"); - if (sm->mode_tx && sm->mode_tx->name) - cp += sprintf(cp, "%s", sm->mode_tx->name); - else - cp += sprintf(cp, "<unspec>"); - if (!sm->mode_rx || !sm->mode_rx || - strcmp(sm->mode_rx->name, sm->mode_tx->name)) { - if (sm->mode_rx && sm->mode_rx->name) - cp += sprintf(cp, ",%s", sm->mode_rx->name); - else - cp += sprintf(cp, ",<unspec>"); - } - if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi))) - return -EFAULT; - return 0; - - case HDLCDRVCTL_SETMODE: - if (netif_running(dev) || !capable(CAP_NET_ADMIN)) - return -EACCES; - hi->data.modename[sizeof(hi->data.modename)-1] = '\0'; - return sethw(dev, sm, hi->data.modename); - - case HDLCDRVCTL_MODELIST: - cp = hi->data.modename; - while (*hwp) { - if ((*hwp)->hw_name) - cp += sprintf(cp, "%s:,", (*hwp)->hw_name); - hwp++; - } - while (*mtp) { - if ((*mtp)->name) - cp += sprintf(cp, ">%s,", (*mtp)->name); - mtp++; - } - while (*mrp) { - if ((*mrp)->name) - cp += sprintf(cp, "<%s,", (*mrp)->name); - mrp++; - } - cp[-1] = '\0'; - if (copy_to_user(ifr->ifr_data, hi, sizeof(*hi))) - return -EFAULT; - return 0; - -#ifdef SM_DEBUG - case SMCTL_GETDEBUG: - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - bi.data.dbg.int_rate = sm->debug_vals.last_intcnt; - bi.data.dbg.mod_cycles = sm->debug_vals.mod_cyc; - bi.data.dbg.demod_cycles = sm->debug_vals.demod_cyc; - bi.data.dbg.dma_residue = sm->debug_vals.dma_residue; - sm->debug_vals.mod_cyc = sm->debug_vals.demod_cyc = - sm->debug_vals.dma_residue = 0; - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; -#endif /* SM_DEBUG */ - - case SMCTL_DIAGNOSE: - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - newdiagmode = bi.data.diag.mode; - newdiagflags = bi.data.diag.flags; - if (newdiagmode > SM_DIAGMODE_CONSTELLATION) - return -EINVAL; - bi.data.diag.mode = sm->diag.mode; - bi.data.diag.flags = sm->diag.flags; - bi.data.diag.samplesperbit = sm->mode_rx->sperbit; - if (sm->diag.mode != newdiagmode) { - save_flags(flags); - cli(); - sm->diag.ptr = -1; - sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; - sm->diag.mode = newdiagmode; - restore_flags(flags); - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - } - if (sm->diag.ptr < 0 || sm->diag.mode == SM_DIAGMODE_OFF) { - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - } - if (bi.data.diag.datalen > DIAGDATALEN) - bi.data.diag.datalen = DIAGDATALEN; - if (sm->diag.ptr < bi.data.diag.datalen) { - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - } - if (copy_to_user(bi.data.diag.data, sm->diag.data, - bi.data.diag.datalen * sizeof(short))) - return -EFAULT; - bi.data.diag.flags |= SM_DIAGFLAG_VALID; - save_flags(flags); - cli(); - sm->diag.ptr = -1; - sm->diag.flags = newdiagflags & ~SM_DIAGFLAG_VALID; - sm->diag.mode = newdiagmode; - restore_flags(flags); - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - } -} - -/* --------------------------------------------------------------------- */ - -/* - * command line settable parameters - */ -static char *mode[NR_PORTS] = { [0 ... NR_PORTS-1] = NULL }; -static int iobase[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 }; -static int irq[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 }; -static int dma[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 }; -static int dma2[NR_PORTS] = { [0 ... NR_PORTS-1] = -1 }; -static int serio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 }; -static int pario[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 }; -static int midiio[NR_PORTS] = { [0 ... NR_PORTS-1] = 0 }; - -MODULE_PARM(mode, "1-" __MODULE_STRING(NR_PORTS) "s"); -MODULE_PARM_DESC(mode, "soundmodem operating mode; eg. sbc:afsk1200 or wss:fsk9600"); -MODULE_PARM(iobase, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(iobase, "soundmodem base address"); -MODULE_PARM(irq, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(irq, "soundmodem interrupt"); -MODULE_PARM(dma, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(dma, "soundmodem dma channel"); -MODULE_PARM(dma2, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(dma2, "soundmodem 2nd dma channel; full duplex only"); -MODULE_PARM(serio, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(serio, "soundmodem PTT output on serial port"); -MODULE_PARM(pario, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(pario, "soundmodem PTT output on parallel port"); -MODULE_PARM(midiio, "1-" __MODULE_STRING(NR_PORTS) "i"); -MODULE_PARM_DESC(midiio, "soundmodem PTT output on midi port"); - -MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch, hb9jnx@hb9w.che.eu"); -MODULE_DESCRIPTION("Soundcard amateur radio modem driver"); - -/* --------------------------------------------------------------------- */ - -static int __init init_soundmodem(void) -{ - int i, j, found = 0; - char set_hw = 1; - struct sm_state *sm; - - printk(sm_drvinfo); - /* - * register net devices - */ - for (i = 0; i < NR_PORTS; i++) { - struct net_device *dev = sm_device+i; - char ifname[IFNAMSIZ]; - - sprintf(ifname, "sm%d", i); - if (!mode[i]) - set_hw = 0; - else { - if (!strncmp(mode[i], "sbc", 3)) { - if (iobase[i] == -1) - iobase[i] = 0x220; - if (irq[i] == -1) - irq[i] = 5; - if (dma[i] == -1) - dma[i] = 1; - } else { - if (iobase[i] == -1) - iobase[i] = 0x530; - if (irq[i] == -1) - irq[i] = 11; - if (dma[i] == -1) - dma[i] = 1; - } - } - if (!set_hw) - iobase[i] = irq[i] = 0; - j = hdlcdrv_register_hdlcdrv(dev, &sm_ops, sizeof(struct sm_state), ifname, iobase[i], irq[i], dma[i]); - if (!j) { - sm = (struct sm_state *)dev->priv; - sm->hdrv.ptt_out.dma2 = dma2[i]; - sm->hdrv.ptt_out.seriobase = serio[i]; - sm->hdrv.ptt_out.pariobase = pario[i]; - sm->hdrv.ptt_out.midiiobase = midiio[i]; - if (set_hw && sethw(dev, sm, mode[i])) - set_hw = 0; - found++; - } else { - printk(KERN_WARNING "%s: cannot register net device\n", sm_drvname); - } - } - if (!found) - return -ENXIO; - return 0; -} - -static void __exit cleanup_soundmodem(void) -{ - int i; - - printk(KERN_INFO "sm: cleanup_module called\n"); - - for(i = 0; i < NR_PORTS; i++) { - struct net_device *dev = sm_device+i; - struct sm_state *sm = (struct sm_state *)dev->priv; - - if (sm) { - if (sm->hdrv.magic != HDLCDRV_MAGIC) - printk(KERN_ERR "sm: invalid magic in " - "cleanup_module\n"); - else - hdlcdrv_unregister_hdlcdrv(dev); - } - } -} - -module_init(init_soundmodem); -module_exit(cleanup_soundmodem); - -/* --------------------------------------------------------------------- */ - -#ifndef MODULE - -/* - * format: soundmodem=io,irq,dma[,dma2[,serio[,pario]]],mode - * mode: hw:modem - * hw: sbc, wss, wssfdx - * modem: afsk1200, fsk9600 - */ - -static int __init sm_setup(char *str) -{ - static unsigned nr_dev = 0; - int ints[8]; - - if (nr_dev >= NR_PORTS) - return 0; - str = get_options(str, 8, ints); - mode[nr_dev] = str; - if (ints[0] >= 1) - iobase[nr_dev] = ints[1]; - if (ints[0] >= 2) - irq[nr_dev] = ints[2]; - if (ints[0] >= 3) - dma[nr_dev] = ints[3]; - if (ints[0] >= 4) - dma2[nr_dev] = ints[4]; - if (ints[0] >= 5) - serio[nr_dev] = ints[5]; - if (ints[0] >= 6) - pario[nr_dev] = ints[6]; - if (ints[0] >= 7) - midiio[nr_dev] = ints[7]; - nr_dev++; - return 1; -} - -__setup("soundmodem=", sm_setup); - -#endif /* MODULE */ -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm.h b/drivers/net/hamradio/soundmodem/sm.h deleted file mode 100644 index 24a586740..000000000 --- a/drivers/net/hamradio/soundmodem/sm.h +++ /dev/null @@ -1,386 +0,0 @@ -/*****************************************************************************/ - -/* - * sm.h -- soundcard radio modem driver internal header. - * - * Copyright (C) 1996-1999 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#ifndef _SM_H -#define _SM_H - -/* ---------------------------------------------------------------------- */ - -#include <linux/hdlcdrv.h> -#include <linux/soundmodem.h> -#include <asm/processor.h> -#include <linux/bitops.h> -#include <linux/parport.h> - -#define SM_DEBUG - -/* ---------------------------------------------------------------------- */ -/* - * Information that need to be kept for each board. - */ - -struct sm_state { - struct hdlcdrv_state hdrv; - - const struct modem_tx_info *mode_tx; - const struct modem_rx_info *mode_rx; - - const struct hardware_info *hwdrv; - - struct pardevice *pardev; - - /* - * Hardware (soundcard) access routines state - */ - struct { - void *ibuf; - unsigned int ifragsz; - unsigned int ifragptr; - unsigned int i16bit; - void *obuf; - unsigned int ofragsz; - unsigned int ofragptr; - unsigned int o16bit; - int ptt_cnt; - } dma; - - union { - long hw[32/sizeof(long)]; - } hw; - - /* - * state of the modem code - */ - union { - long m[48/sizeof(long)]; - } m; - union { - long d[256/sizeof(long)]; - } d; - -#define DIAGDATALEN 64 - struct diag_data { - unsigned int mode; - unsigned int flags; - volatile int ptr; - short data[DIAGDATALEN]; - } diag; - - -#ifdef SM_DEBUG - struct debug_vals { - unsigned long last_jiffies; - unsigned cur_intcnt; - unsigned last_intcnt; - unsigned mod_cyc; - unsigned demod_cyc; - unsigned dma_residue; - } debug_vals; -#endif /* SM_DEBUG */ -}; - -/* ---------------------------------------------------------------------- */ -/* - * Mode definition structure - */ - -struct modem_tx_info { - const char *name; - unsigned int loc_storage; - int srate; - int bitrate; - void (*modulator_u8)(struct sm_state *, unsigned char *, unsigned int); - void (*modulator_s16)(struct sm_state *, short *, unsigned int); - void (*init)(struct sm_state *); -}; - -struct modem_rx_info { - const char *name; - unsigned int loc_storage; - int srate; - int bitrate; - unsigned int overlap; - unsigned int sperbit; - void (*demodulator_u8)(struct sm_state *, const unsigned char *, unsigned int); - void (*demodulator_s16)(struct sm_state *, const short *, unsigned int); - void (*init)(struct sm_state *); -}; - -/* ---------------------------------------------------------------------- */ -/* - * Soundcard driver definition structure - */ - -struct hardware_info { - char *hw_name; /* used for request_{region,irq,dma} */ - unsigned int loc_storage; - /* - * mode specific open/close - */ - int (*open)(struct net_device *, struct sm_state *); - int (*close)(struct net_device *, struct sm_state *); - int (*ioctl)(struct net_device *, struct sm_state *, struct ifreq *, - struct hdlcdrv_ioctl *, int); - int (*sethw)(struct net_device *, struct sm_state *, char *); -}; - -/* --------------------------------------------------------------------- */ - -#define min(a, b) (((a) < (b)) ? (a) : (b)) -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -/* --------------------------------------------------------------------- */ - -extern const char sm_drvname[]; -extern const char sm_drvinfo[]; - -/* --------------------------------------------------------------------- */ -/* - * ===================== diagnostics stuff =============================== - */ - -extern inline void diag_trigger(struct sm_state *sm) -{ - if (sm->diag.ptr < 0) - if (!(sm->diag.flags & SM_DIAGFLAG_DCDGATE) || sm->hdrv.hdlcrx.dcd) - sm->diag.ptr = 0; -} - -/* --------------------------------------------------------------------- */ - -#define SHRT_MAX ((short)(((unsigned short)(~0U))>>1)) -#define SHRT_MIN (-SHRT_MAX-1) - -extern inline void diag_add(struct sm_state *sm, int valinp, int valdemod) -{ - int val; - - if ((sm->diag.mode != SM_DIAGMODE_INPUT && - sm->diag.mode != SM_DIAGMODE_DEMOD) || - sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) - return; - val = (sm->diag.mode == SM_DIAGMODE_DEMOD) ? valdemod : valinp; - /* clip */ - if (val > SHRT_MAX) - val = SHRT_MAX; - if (val < SHRT_MIN) - val = SHRT_MIN; - sm->diag.data[sm->diag.ptr++] = val; -} - -/* --------------------------------------------------------------------- */ - -extern inline void diag_add_one(struct sm_state *sm, int val) -{ - if ((sm->diag.mode != SM_DIAGMODE_INPUT && - sm->diag.mode != SM_DIAGMODE_DEMOD) || - sm->diag.ptr >= DIAGDATALEN || sm->diag.ptr < 0) - return; - /* clip */ - if (val > SHRT_MAX) - val = SHRT_MAX; - if (val < SHRT_MIN) - val = SHRT_MIN; - sm->diag.data[sm->diag.ptr++] = val; -} - -/* --------------------------------------------------------------------- */ - -static inline void diag_add_constellation(struct sm_state *sm, int vali, int valq) -{ - if ((sm->diag.mode != SM_DIAGMODE_CONSTELLATION) || - sm->diag.ptr >= DIAGDATALEN-1 || sm->diag.ptr < 0) - return; - /* clip */ - if (vali > SHRT_MAX) - vali = SHRT_MAX; - if (vali < SHRT_MIN) - vali = SHRT_MIN; - if (valq > SHRT_MAX) - valq = SHRT_MAX; - if (valq < SHRT_MIN) - valq = SHRT_MIN; - sm->diag.data[sm->diag.ptr++] = vali; - sm->diag.data[sm->diag.ptr++] = valq; -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== utility functions =============================== - */ - -#if 0 -extern inline unsigned int hweight32(unsigned int w) - __attribute__ ((unused)); -extern inline unsigned int hweight16(unsigned short w) - __attribute__ ((unused)); -extern inline unsigned int hweight8(unsigned char w) - __attribute__ ((unused)); - -extern inline unsigned int hweight32(unsigned int w) -{ - unsigned int res = (w & 0x55555555) + ((w >> 1) & 0x55555555); - res = (res & 0x33333333) + ((res >> 2) & 0x33333333); - res = (res & 0x0F0F0F0F) + ((res >> 4) & 0x0F0F0F0F); - res = (res & 0x00FF00FF) + ((res >> 8) & 0x00FF00FF); - return (res & 0x0000FFFF) + ((res >> 16) & 0x0000FFFF); -} - -extern inline unsigned int hweight16(unsigned short w) -{ - unsigned short res = (w & 0x5555) + ((w >> 1) & 0x5555); - res = (res & 0x3333) + ((res >> 2) & 0x3333); - res = (res & 0x0F0F) + ((res >> 4) & 0x0F0F); - return (res & 0x00FF) + ((res >> 8) & 0x00FF); -} - -extern inline unsigned int hweight8(unsigned char w) -{ - unsigned short res = (w & 0x55) + ((w >> 1) & 0x55); - res = (res & 0x33) + ((res >> 2) & 0x33); - return (res & 0x0F) + ((res >> 4) & 0x0F); -} - -#endif - -extern inline unsigned int gcd(unsigned int x, unsigned int y) - __attribute__ ((unused)); -extern inline unsigned int lcm(unsigned int x, unsigned int y) - __attribute__ ((unused)); - -extern inline unsigned int gcd(unsigned int x, unsigned int y) -{ - for (;;) { - if (!x) - return y; - if (!y) - return x; - if (x > y) - x %= y; - else - y %= x; - } -} - -extern inline unsigned int lcm(unsigned int x, unsigned int y) -{ - return x * y / gcd(x, y); -} - -/* --------------------------------------------------------------------- */ -/* - * ===================== profiling ======================================= - */ - - -#ifdef __i386__ - -/* - * only do 32bit cycle counter arithmetic; we hope we won't overflow. - * in fact, overflowing modems would require over 2THz CPU clock speeds :-) - */ - -#define time_exec(var,cmd) \ -({ \ - if (cpu_has_tsc) { \ - unsigned int cnt1, cnt2, cnt3; \ - __asm__(".byte 0x0f,0x31" : "=a" (cnt1), "=d" (cnt3)); \ - cmd; \ - __asm__(".byte 0x0f,0x31" : "=a" (cnt2), "=d" (cnt3)); \ - var = cnt2-cnt1; \ - } else { \ - cmd; \ - } \ -}) - -#else /* __i386__ */ - -#define time_exec(var,cmd) cmd - -#endif /* __i386__ */ - -/* --------------------------------------------------------------------- */ - -extern const struct modem_tx_info sm_afsk1200_tx; -extern const struct modem_tx_info sm_afsk2400_7_tx; -extern const struct modem_tx_info sm_afsk2400_8_tx; -extern const struct modem_tx_info sm_afsk2666_tx; -extern const struct modem_tx_info sm_psk4800_tx; -extern const struct modem_tx_info sm_hapn4800_8_tx; -extern const struct modem_tx_info sm_hapn4800_10_tx; -extern const struct modem_tx_info sm_hapn4800_pm8_tx; -extern const struct modem_tx_info sm_hapn4800_pm10_tx; -extern const struct modem_tx_info sm_fsk9600_4_tx; -extern const struct modem_tx_info sm_fsk9600_5_tx; - -extern const struct modem_rx_info sm_afsk1200_rx; -extern const struct modem_rx_info sm_afsk2400_7_rx; -extern const struct modem_rx_info sm_afsk2400_8_rx; -extern const struct modem_rx_info sm_afsk2666_rx; -extern const struct modem_rx_info sm_psk4800_rx; -extern const struct modem_rx_info sm_hapn4800_8_rx; -extern const struct modem_rx_info sm_hapn4800_10_rx; -extern const struct modem_rx_info sm_hapn4800_pm8_rx; -extern const struct modem_rx_info sm_hapn4800_pm10_rx; -extern const struct modem_rx_info sm_fsk9600_4_rx; -extern const struct modem_rx_info sm_fsk9600_5_rx; - -extern const struct hardware_info sm_hw_sbc; -extern const struct hardware_info sm_hw_sbcfdx; -extern const struct hardware_info sm_hw_wss; -extern const struct hardware_info sm_hw_wssfdx; - -extern const struct modem_tx_info *sm_modem_tx_table[]; -extern const struct modem_rx_info *sm_modem_rx_table[]; -extern const struct hardware_info *sm_hardware_table[]; - -/* --------------------------------------------------------------------- */ - -void sm_output_status(struct sm_state *sm); -/*void sm_output_open(struct sm_state *sm);*/ -/*void sm_output_close(struct sm_state *sm);*/ - -/* --------------------------------------------------------------------- */ - -extern void inline sm_int_freq(struct sm_state *sm) -{ -#ifdef SM_DEBUG - unsigned long cur_jiffies = jiffies; - /* - * measure the interrupt frequency - */ - sm->debug_vals.cur_intcnt++; - if ((cur_jiffies - sm->debug_vals.last_jiffies) >= HZ) { - sm->debug_vals.last_jiffies = cur_jiffies; - sm->debug_vals.last_intcnt = sm->debug_vals.cur_intcnt; - sm->debug_vals.cur_intcnt = 0; - } -#endif /* SM_DEBUG */ -} - -/* --------------------------------------------------------------------- */ -#endif /* _SM_H */ diff --git a/drivers/net/hamradio/soundmodem/sm_afsk1200.c b/drivers/net/hamradio/soundmodem/sm_afsk1200.c deleted file mode 100644 index 64b20a57c..000000000 --- a/drivers/net/hamradio/soundmodem/sm_afsk1200.c +++ /dev/null @@ -1,272 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_afsk1200.c -- soundcard radio modem driver, 1200 baud AFSK modem - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include "sm.h" -#include "sm_tbl_afsk1200.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_afsk12 { - unsigned int shreg; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - unsigned char last_rxbit; -}; - -struct mod_state_afsk12 { - unsigned int shreg; - unsigned char tx_bit; - unsigned int bit_pll; - unsigned int dds_inc; - unsigned int txphase; -}; - -/* --------------------------------------------------------------------- */ - -static const int dds_inc[2] = { - AFSK12_TX_FREQ_LO*0x10000/AFSK12_SAMPLE_RATE, - AFSK12_TX_FREQ_HI*0x10000/AFSK12_SAMPLE_RATE -}; - -static void modulator_1200_u8(struct sm_state *sm, unsigned char *buf, - unsigned int buflen) -{ - struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!((st->txphase++) & 7)) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - } - st->dds_inc = dds_inc[st->tx_bit & 1]; - *buf++ = OFFSCOS(st->bit_pll); - st->bit_pll += st->dds_inc; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_1200_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_afsk12 *st = (struct mod_state_afsk12 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!((st->txphase++) & 7)) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - } - st->dds_inc = dds_inc[st->tx_bit & 1]; - *buf++ = COS(st->bit_pll); - st->bit_pll += st->dds_inc; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int convolution8_u8(const unsigned char *st, const int *coeff, int csum) -{ - int sum = -0x80 * csum; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - - sum >>= 7; - return sum * sum; -} - -extern __inline__ int convolution8_s16(const short *st, const int *coeff, int csum) -{ - int sum = 0; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - - sum >>= 15; - return sum * sum; -} - -extern __inline__ int do_filter_1200_u8(const unsigned char *buf) -{ - int sum = convolution8_u8(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); - sum += convolution8_u8(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); - sum -= convolution8_u8(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); - sum -= convolution8_u8(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); - return sum; -} - -extern __inline__ int do_filter_1200_s16(const short *buf) -{ - int sum = convolution8_s16(buf, afsk12_tx_lo_i, SUM_AFSK12_TX_LO_I); - sum += convolution8_s16(buf, afsk12_tx_lo_q, SUM_AFSK12_TX_LO_Q); - sum -= convolution8_s16(buf, afsk12_tx_hi_i, SUM_AFSK12_TX_HI_I); - sum -= convolution8_s16(buf, afsk12_tx_hi_q, SUM_AFSK12_TX_HI_Q); - return sum; -} - -/* --------------------------------------------------------------------- */ - -static const int pll_corr[2] = { -0x1000, 0x1000 }; - -static void demodulator_1200_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_1200_u8(buf); - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - st->bit_pll += pll_corr - [st->bit_pll < 0x9000]; - j = 4 * hweight8(st->dcd_shreg & 0x38) - - hweight16(st->dcd_shreg & 0x7c0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, (((int)*buf)-0x80) << 8, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_1200_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_1200_s16(buf); - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - st->bit_pll += pll_corr - [st->bit_pll < 0x9000]; - j = 4 * hweight8(st->dcd_shreg & 0x38) - - hweight16(st->dcd_shreg & 0x7c0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, *buf, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_1200(struct sm_state *sm) -{ - struct demod_state_afsk12 *st = (struct demod_state_afsk12 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_afsk1200_tx = { - "afsk1200", sizeof(struct mod_state_afsk12), - AFSK12_SAMPLE_RATE, 1200, modulator_1200_u8, modulator_1200_s16, NULL -}; - -const struct modem_rx_info sm_afsk1200_rx = { - "afsk1200", sizeof(struct demod_state_afsk12), - AFSK12_SAMPLE_RATE, 1200, 8, AFSK12_SAMPLE_RATE/1200, - demodulator_1200_u8, demodulator_1200_s16, demod_init_1200 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c deleted file mode 100644 index d217936ab..000000000 --- a/drivers/net/hamradio/soundmodem/sm_afsk2400_7.c +++ /dev/null @@ -1,296 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_afsk2400_7.c -- soundcard radio modem driver, 2400 baud AFSK modem - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -/* - * This driver is intended to be compatible with TCM3105 modems - * overclocked to 7.3728MHz. The mark and space frequencies therefore - * lie at 3658 and 1996 Hz. - * Note that I do _not_ recommend the building of such links, I provide - * this only for the users who live in the coverage area of such - * a "legacy" link. - */ - -#include "sm.h" -#include "sm_tbl_afsk2400_7.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_afsk24 { - unsigned int shreg; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - unsigned char last_rxbit; -}; - -struct mod_state_afsk24 { - unsigned int shreg; - unsigned char tx_bit; - unsigned int bit_pll; - unsigned int tx_seq; - unsigned int phinc; -}; - -/* --------------------------------------------------------------------- */ - -static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, - AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; - -static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (st->tx_seq < 0x5555) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - st->phinc = dds_inc[st->tx_bit & 1]; - } - st->tx_seq += 0x5555; - st->tx_seq &= 0xffff; - *buf = OFFSCOS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (st->tx_seq < 0x5555) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - st->phinc = dds_inc[st->tx_bit & 1]; - } - st->tx_seq += 0x5555; - st->tx_seq &= 0xffff; - *buf = COS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) -{ - int sum = -0x80 * csum; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - sum += (st[-12] * coeff[12]); - sum += (st[-13] * coeff[13]); - - sum >>= 7; - return sum * sum; -} - -extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) -{ - int sum = 0; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - sum += (st[-12] * coeff[12]); - sum += (st[-13] * coeff[13]); - - sum >>= 15; - return sum * sum; -} - -extern __inline__ int do_filter_2400_u8(const unsigned char *buf) -{ - int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); - sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); - sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); - sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); - return sum; -} - -extern __inline__ int do_filter_2400_s16(const short *buf) -{ - int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); - sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); - sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); - sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); - return sum; -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_2400_u8(buf); - st->dcd_shreg <<= 1; - st->bit_pll += AFSK24_BITPLL_INC; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) - st->bit_pll += AFSK24_BITPLL_INC/2; - else - st->bit_pll -= AFSK24_BITPLL_INC/2; - j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) - - hweight16(st->dcd_shreg & 0x1e0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, (((int)*buf)-0x80) << 8, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_2400_s16(buf); - st->dcd_shreg <<= 1; - st->bit_pll += AFSK24_BITPLL_INC; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) - st->bit_pll += AFSK24_BITPLL_INC/2; - else - st->bit_pll -= AFSK24_BITPLL_INC/2; - j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) - - hweight16(st->dcd_shreg & 0x1e0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, *buf, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_2400(struct sm_state *sm) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_afsk2400_7_tx = { - "afsk2400_7", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, - modulator_2400_u8, modulator_2400_s16, NULL -}; - -const struct modem_rx_info sm_afsk2400_7_rx = { - "afsk2400_7", sizeof(struct demod_state_afsk24), - AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, - demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c b/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c deleted file mode 100644 index 23d233746..000000000 --- a/drivers/net/hamradio/soundmodem/sm_afsk2400_8.c +++ /dev/null @@ -1,296 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_afsk2400_8.c -- soundcard radio modem driver, 2400 baud AFSK modem - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -/* - * This driver is intended to be compatible with TCM3105 modems - * overclocked to 8MHz. The mark and space frequencies therefore - * lie at 3970 and 2165 Hz. - * Note that I do _not_ recommend the building of such links, I provide - * this only for the users who live in the coverage area of such - * a "legacy" link. - */ - -#include "sm.h" -#include "sm_tbl_afsk2400_8.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_afsk24 { - unsigned int shreg; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - unsigned char last_rxbit; -}; - -struct mod_state_afsk24 { - unsigned int shreg; - unsigned char tx_bit; - unsigned int bit_pll; - unsigned int tx_seq; - unsigned int phinc; -}; - -/* --------------------------------------------------------------------- */ - -static const int dds_inc[2] = { AFSK24_TX_FREQ_LO*0x10000/AFSK24_SAMPLERATE, - AFSK24_TX_FREQ_HI*0x10000/AFSK24_SAMPLERATE }; - -static void modulator_2400_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (st->tx_seq < 0x5555) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - st->phinc = dds_inc[st->tx_bit & 1]; - } - st->tx_seq += 0x5555; - st->tx_seq &= 0xffff; - *buf = OFFSCOS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_2400_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_afsk24 *st = (struct mod_state_afsk24 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (st->tx_seq < 0x5555) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit ^ (!(st->shreg & 1))) & 1; - st->shreg >>= 1; - st->phinc = dds_inc[st->tx_bit & 1]; - } - st->tx_seq += 0x5555; - st->tx_seq &= 0xffff; - *buf = COS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int convolution14_u8(const unsigned char *st, const int *coeff, int csum) -{ - int sum = -0x80 * csum; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - sum += (st[-12] * coeff[12]); - sum += (st[-13] * coeff[13]); - - sum >>= 7; - return sum * sum; -} - -extern __inline__ int convolution14_s16(const short *st, const int *coeff, int csum) -{ - int sum = 0; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - sum += (st[-12] * coeff[12]); - sum += (st[-13] * coeff[13]); - - sum >>= 15; - return sum * sum; -} - -extern __inline__ int do_filter_2400_u8(const unsigned char *buf) -{ - int sum = convolution14_u8(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); - sum += convolution14_u8(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); - sum -= convolution14_u8(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); - sum -= convolution14_u8(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); - return sum; -} - -extern __inline__ int do_filter_2400_s16(const short *buf) -{ - int sum = convolution14_s16(buf, afsk24_tx_lo_i, SUM_AFSK24_TX_LO_I); - sum += convolution14_s16(buf, afsk24_tx_lo_q, SUM_AFSK24_TX_LO_Q); - sum -= convolution14_s16(buf, afsk24_tx_hi_i, SUM_AFSK24_TX_HI_I); - sum -= convolution14_s16(buf, afsk24_tx_hi_q, SUM_AFSK24_TX_HI_Q); - return sum; -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2400_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_2400_u8(buf); - st->dcd_shreg <<= 1; - st->bit_pll += AFSK24_BITPLL_INC; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) - st->bit_pll += AFSK24_BITPLL_INC/2; - else - st->bit_pll -= AFSK24_BITPLL_INC/2; - j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) - - hweight16(st->dcd_shreg & 0x1e0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, (((int)*buf)-0x80) << 8, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2400_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - int j; - int sum; - unsigned char newsample; - - for (; buflen > 0; buflen--, buf++) { - sum = do_filter_2400_s16(buf); - st->dcd_shreg <<= 1; - st->bit_pll += AFSK24_BITPLL_INC; - newsample = (sum > 0); - if (st->last_sample ^ newsample) { - st->last_sample = newsample; - st->dcd_shreg |= 1; - if (st->bit_pll < (0x8000+AFSK24_BITPLL_INC/2)) - st->bit_pll += AFSK24_BITPLL_INC/2; - else - st->bit_pll -= AFSK24_BITPLL_INC/2; - j = /* 2 * */ hweight8(st->dcd_shreg & 0x1c) - - hweight16(st->dcd_shreg & 0x1e0); - st->dcd_sum0 += j; - } - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 120; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->shreg >>= 1; - st->shreg |= (!(st->last_rxbit ^ - st->last_sample)) << 16; - st->last_rxbit = st->last_sample; - diag_trigger(sm); - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - diag_add(sm, *buf, sum); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_2400(struct sm_state *sm) -{ - struct demod_state_afsk24 *st = (struct demod_state_afsk24 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_afsk2400_8_tx = { - "afsk2400_8", sizeof(struct mod_state_afsk24), AFSK24_SAMPLERATE, 2400, - modulator_2400_u8, modulator_2400_s16, NULL -}; - -const struct modem_rx_info sm_afsk2400_8_rx = { - "afsk2400_8", sizeof(struct demod_state_afsk24), - AFSK24_SAMPLERATE, 2400, 14, AFSK24_SAMPLERATE/2400, - demodulator_2400_u8, demodulator_2400_s16, demod_init_2400 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_afsk2666.c b/drivers/net/hamradio/soundmodem/sm_afsk2666.c deleted file mode 100644 index 2aa2972e4..000000000 --- a/drivers/net/hamradio/soundmodem/sm_afsk2666.c +++ /dev/null @@ -1,356 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_afsk2666.c -- soundcard radio modem driver, 2666 baud AFSK modem - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include "sm.h" -#include "sm_tbl_afsk2666.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_afsk26 { - unsigned int shreg; - unsigned long descram; - int dem_sum[8]; - int dem_sum_mean; - int dem_cnt; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; -}; - -struct mod_state_afsk26 { - unsigned int shreg; - unsigned long scram; - unsigned int bit_pll; - unsigned int phinc; - unsigned int tx_seq; -}; - -/* --------------------------------------------------------------------- */ - -#define DESCRAM_TAP1 0x20000 -#define DESCRAM_TAP2 0x01000 -#define DESCRAM_TAP3 0x00001 - -#define DESCRAM_TAPSH1 17 -#define DESCRAM_TAPSH2 12 -#define DESCRAM_TAPSH3 0 - -#define SCRAM_TAP1 0x20000 /* X^17 */ -#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static void modulator_2666_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = ((st->scram << 1) | (st->scram & 1)); - st->scram ^= (!(st->shreg & 1)); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))]; - } - if (st->tx_seq >= 6) - st->tx_seq = 0; - *buf = OFFSCOS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_2666_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_afsk26 *st = (struct mod_state_afsk26 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = ((st->scram << 1) | (st->scram & 1)); - st->scram ^= (!(st->shreg & 1)); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->phinc = afsk26_carfreq[!(st->scram & (SCRAM_TAP1 << 2))]; - } - if (st->tx_seq >= 6) - st->tx_seq = 0; - *buf = COS(st->bit_pll); - st->bit_pll += st->phinc; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int convolution12_u8(const unsigned char *st, const int *coeff, int csum) -{ - int sum = -0x80 * csum; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - - return sum; -} - -extern __inline__ int convolution12_s16(const short *st, const int *coeff, int csum) -{ - int sum = 0; - - sum += (st[0] * coeff[0]); - sum += (st[-1] * coeff[1]); - sum += (st[-2] * coeff[2]); - sum += (st[-3] * coeff[3]); - sum += (st[-4] * coeff[4]); - sum += (st[-5] * coeff[5]); - sum += (st[-6] * coeff[6]); - sum += (st[-7] * coeff[7]); - sum += (st[-8] * coeff[8]); - sum += (st[-9] * coeff[9]); - sum += (st[-10] * coeff[10]); - sum += (st[-11] * coeff[11]); - - sum >>= 8; - return sum; -} - -/* ---------------------------------------------------------------------- */ - -#if 0 -static int binexp(unsigned int i) -{ - int ret = 31; - - if (!i) - return 0; - if (i < 0x10000LU) { - i <<= 16; - ret -= 16; - } - if (i < 0x1000000LU) { - i <<= 8; - ret -= 8; - } - if (i < 0x10000000LU) { - i <<= 4; - ret -= 4; - } - if (i < 0x40000000LU) { - i <<= 2; - ret -= 2; - } - if (i < 0x80000000LU) - ret -= 1; - return ret; -} - -static const sqrt_tab[16] = { - 00000, 16384, 23170, 28378, 32768, 36636, 40132, 43348, - 46341, 49152, 51811, 54340, 56756, 59073, 61303, 63455 -}; - - -static unsigned int int_sqrt_approx(unsigned int i) -{ - unsigned int j; - - if (i < 16) - return sqrt_tab[i] >> 14; - j = binexp(i) >> 1; - i >>= (j * 2 - 2); - return (sqrt_tab[i & 0xf] << j) >> 15; -} -#endif - -/* --------------------------------------------------------------------- */ - -extern unsigned int est_pwr(int i, int q) -{ - unsigned int ui = abs(i); - unsigned int uq = abs(q); - - if (uq > ui) { - unsigned int tmp; - tmp = ui; - ui = uq; - uq = tmp; - } - if (uq > (ui >> 1)) - return 7*(ui>>3) + 9*(uq>>4); - else - return ui + (uq>>2); -} - -/* --------------------------------------------------------------------- */ - -static void demod_one_sample(struct sm_state *sm, struct demod_state_afsk26 *st, int curval, - int loi, int loq, int hii, int hiq) -{ - static const int pll_corr[2] = { -0xa00, 0xa00 }; - unsigned char curbit; - unsigned int descx; - int val; - - /* - * estimate power - */ - val = est_pwr(hii, hiq) - est_pwr(loi, loq); - /* - * estimate center value - */ - st->dem_sum[0] += val >> 8; - if ((++st->dem_cnt) >= 256) { - st->dem_cnt = 0; - st->dem_sum_mean = (st->dem_sum[0]+st->dem_sum[1]+ - st->dem_sum[2]+st->dem_sum[3]+ - st->dem_sum[4]+st->dem_sum[5]+ - st->dem_sum[6]+st->dem_sum[7]) >> 3; - memmove(st->dem_sum+1, st->dem_sum, - sizeof(st->dem_sum)-sizeof(st->dem_sum[0])); - st->dem_sum[0] = 0; - } - /* - * decision and bit clock regen - */ - val -= st->dem_sum_mean; - diag_add(sm, curval, val); - - st->dcd_shreg <<= 1; - st->bit_pll += 0x1555; - curbit = (val > 0); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < (0x8000+0x1555)]; - st->dcd_sum0 += 4*hweight8(st->dcd_shreg & 0x1e) - - hweight16(st->dcd_shreg & 0xfe00); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, curbit); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 400; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffffu; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2666_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); - - for (; buflen > 0; buflen--, buf++) { - demod_one_sample(sm, st, (*buf-0x80)<<8, - convolution12_u8(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0), - convolution12_u8(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0), - convolution12_u8(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1), - convolution12_u8(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1)); - demod_one_sample(sm, st, (*buf-0x80)<<8, - convolution12_u8(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0), - convolution12_u8(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0), - convolution12_u8(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1), - convolution12_u8(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1)); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_2666_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); - - for (; buflen > 0; buflen--, buf++) { - demod_one_sample(sm, st, *buf, - convolution12_s16(buf, afsk26_dem_tables[0][0].i, AFSK26_DEM_SUM_I_0_0), - convolution12_s16(buf, afsk26_dem_tables[0][0].q, AFSK26_DEM_SUM_Q_0_0), - convolution12_s16(buf, afsk26_dem_tables[0][1].i, AFSK26_DEM_SUM_I_0_1), - convolution12_s16(buf, afsk26_dem_tables[0][1].q, AFSK26_DEM_SUM_Q_0_1)); - demod_one_sample(sm, st, *buf, - convolution12_s16(buf, afsk26_dem_tables[1][0].i, AFSK26_DEM_SUM_I_1_0), - convolution12_s16(buf, afsk26_dem_tables[1][0].q, AFSK26_DEM_SUM_Q_1_0), - convolution12_s16(buf, afsk26_dem_tables[1][1].i, AFSK26_DEM_SUM_I_1_1), - convolution12_s16(buf, afsk26_dem_tables[1][1].q, AFSK26_DEM_SUM_Q_1_1)); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_2666(struct sm_state *sm) -{ - struct demod_state_afsk26 *st = (struct demod_state_afsk26 *)(&sm->d); - - st->dcd_time = 400; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_afsk2666_tx = { - "afsk2666", sizeof(struct mod_state_afsk26), AFSK26_SAMPLERATE, 2666, - modulator_2666_u8, modulator_2666_s16, NULL -}; - -const struct modem_rx_info sm_afsk2666_rx = { - "afsk2666", sizeof(struct demod_state_afsk26), AFSK26_SAMPLERATE, 2666, 12, 6, - demodulator_2666_u8, demodulator_2666_s16, demod_init_2666 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_fsk9600.c b/drivers/net/hamradio/soundmodem/sm_fsk9600.c deleted file mode 100644 index bc2fb53b1..000000000 --- a/drivers/net/hamradio/soundmodem/sm_fsk9600.c +++ /dev/null @@ -1,391 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_fsk9600.c -- soundcard radio modem driver, - * 9600 baud G3RUH compatible FSK modem - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include "sm.h" -#include "sm_tbl_fsk9600.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_fsk96 { - unsigned int shreg; - unsigned long descram; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; -}; - -struct mod_state_fsk96 { - unsigned int shreg; - unsigned long scram; - unsigned char tx_bit; - unsigned char *txtbl; - unsigned int txphase; -}; - -/* --------------------------------------------------------------------- */ - -#define DESCRAM_TAP1 0x20000 -#define DESCRAM_TAP2 0x01000 -#define DESCRAM_TAP3 0x00001 - -#define DESCRAM_TAPSH1 17 -#define DESCRAM_TAPSH2 12 -#define DESCRAM_TAPSH3 0 - -#define SCRAM_TAP1 0x20000 /* X^17 */ -#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_4_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); - } - if (st->txphase >= 4) - st->txphase = 0; - *buf++ = *st->txtbl; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_4_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_4 + (st->tx_bit & 0xff); - } - if (st->txphase >= 4) - st->txphase = 0; - *buf++ = ((*st->txtbl)-0x80) << 8; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_4_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x4000; - curbit = (*buf >= 0x80); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0xa000]; - st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - - !!(st->dcd_shreg & 0x10); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, ((short)(*buf - 0x80)) << 8); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_4_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x4000; - curbit = (*buf >= 0); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0xa000]; - st->dcd_sum0 += 8 * hweight8(st->dcd_shreg & 0x0c) - - !!(st->dcd_shreg & 0x10); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, *buf); - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_5_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); - } - if (st->txphase >= 5) - st->txphase = 0; - *buf++ = *st->txtbl; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_9600_5_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_fsk96 *st = (struct mod_state_fsk96 *)(&sm->m); - - for (; buflen > 0; buflen--) { - if (!st->txphase++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | (st->scram & 1); - st->scram ^= !(st->shreg & 1); - st->shreg >>= 1; - if (st->scram & (SCRAM_TAP1 << 1)) - st->scram ^= SCRAM_TAPN << 1; - st->tx_bit = (st->tx_bit << 1) | (!!(st->scram & (SCRAM_TAP1 << 2))); - st->txtbl = fsk96_txfilt_5 + (st->tx_bit & 0xff); - } - if (st->txphase >= 5) - st->txphase = 0; - *buf++ = ((*st->txtbl)-0x80)<<8; - st->txtbl += 0x100; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_5_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x3333; - curbit = (*buf >= 0x80); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0x9999]; - st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - - hweight8(st->dcd_shreg & 0x70); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, ((short)(*buf - 0x80)) << 8); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_9600_5_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - static const int pll_corr[2] = { -0x1000, 0x1000 }; - unsigned char curbit; - unsigned int descx; - - for (; buflen > 0; buflen--, buf++) { - st->dcd_shreg <<= 1; - st->bit_pll += 0x3333; - curbit = (*buf >= 0); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr[st->bit_pll < 0x9999]; - st->dcd_sum0 += 16 * hweight8(st->dcd_shreg & 0x0c) - - hweight8(st->dcd_shreg & 0x70); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, *buf); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_9600(struct sm_state *sm) -{ - struct demod_state_fsk96 *st = (struct demod_state_fsk96 *)(&sm->d); - - st->dcd_time = 240; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_fsk9600_4_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 38400, 9600, - modulator_9600_4_u8, modulator_9600_4_s16, NULL -}; - -const struct modem_rx_info sm_fsk9600_4_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 38400, 9600, 1, 4, - demodulator_9600_4_u8, demodulator_9600_4_s16, demod_init_9600 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_fsk9600_5_tx = { - "fsk9600", sizeof(struct mod_state_fsk96), 48000, 9600, - modulator_9600_5_u8, modulator_9600_5_s16, NULL -}; - -const struct modem_rx_info sm_fsk9600_5_rx = { - "fsk9600", sizeof(struct demod_state_fsk96), 48000, 9600, 1, 5, - demodulator_9600_5_u8, demodulator_9600_5_s16, demod_init_9600 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_hapn4800.c b/drivers/net/hamradio/soundmodem/sm_hapn4800.c deleted file mode 100644 index f6babcd9d..000000000 --- a/drivers/net/hamradio/soundmodem/sm_hapn4800.c +++ /dev/null @@ -1,560 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_hapn4800.c -- soundcard radio modem driver, 4800 baud HAPN modem - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - * - * This module implements a (hopefully) HAPN (Hamilton Area Packet - * Network) compatible 4800 baud modem. - * The HAPN modem uses kind of "duobinary signalling" (not really, - * duobinary signalling gives ... 0 0 -1 0 1 0 0 ... at the sampling - * instants, whereas HAPN signalling gives ... 0 0 -1 1 0 0 ..., see - * Proakis, Digital Communications). - * The code is untested. It is compatible with itself (i.e. it can decode - * the packets it sent), but I could not test if it is compatible with - * any "real" HAPN modem, since noone uses it in my region of the world. - * Feedback therefore welcome. - */ - -#include "sm.h" -#include "sm_tbl_hapn4800.h" - -/* --------------------------------------------------------------------- */ - -struct demod_state_hapn48 { - unsigned int shreg; - unsigned int bit_pll; - unsigned char last_bit; - unsigned char last_bit2; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; - int lvlhi, lvllo; -}; - -struct mod_state_hapn48 { - unsigned int shreg; - unsigned char tx_bit; - unsigned int tx_seq; - const unsigned char *tbl; -}; - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_10_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_8_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm10_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm10_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = ((st->tx_bit << 1) | - (st->tx_bit & 1)); - st->tx_bit ^= (!(st->shreg & 1)); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm10 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 10) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm8_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = *st->tbl; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_hapn4800_pm8_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_hapn48 *st = (struct mod_state_hapn48 *)(&sm->m); - - for (; buflen > 0; buflen--, buf++) { - if (!st->tx_seq++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->tx_bit = (st->tx_bit << 1) | (st->tx_bit & 1); - st->tx_bit ^= !(st->shreg & 1); - st->shreg >>= 1; - st->tbl = hapn48_txfilt_pm8 + (st->tx_bit & 0xf); - } - if (st->tx_seq >= 8) - st->tx_seq = 0; - *buf = ((*st->tbl)-0x80)<<8; - st->tbl += 0x10; - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_10_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = ((int)(buf[-2])-0x80) << 8; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x199a; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - - hweight32(st->dcd_shreg & 0xe739ce70); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_10_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = buf[-2]; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x199a; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x8ccdu]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x18c6318c) - - hweight32(st->dcd_shreg & 0xe739ce70); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_8_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = ((int)(buf[-2])-0x80) << 8; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - - hweight32(st->dcd_shreg & 0xbbbbbbbb); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_hapn4800_8_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - static const int pll_corr[2] = { -0x800, 0x800 }; - int curst, cursync; - int inv; - - for (; buflen > 0; buflen--, buf++) { - inv = buf[-2]; - st->lvlhi = (st->lvlhi * 65309) >> 16; /* decay */ - st->lvllo = (st->lvllo * 65309) >> 16; /* decay */ - if (inv > st->lvlhi) - st->lvlhi = inv; - if (inv < st->lvllo) - st->lvllo = inv; - if (buflen & 1) - st->dcd_shreg <<= 1; - st->bit_pll += 0x2000; - curst = cursync = 0; - if (inv > st->lvlhi >> 1) { - curst = 1; - cursync = (buf[-2] > buf[-1] && buf[-2] > buf[-3] && - buf[-2] > buf[-0] && buf[-2] > buf[-4]); - } else if (inv < st->lvllo >> 1) { - curst = -1; - cursync = (buf[-2] < buf[-1] && buf[-2] < buf[-3] && - buf[-2] < buf[-0] && buf[-2] < buf[-4]); - } - if (cursync) { - st->dcd_shreg |= cursync; - st->bit_pll += pll_corr[((st->bit_pll - 0x8000u) & 0xffffu) < 0x9000u]; - st->dcd_sum0 += 16 * hweight32(st->dcd_shreg & 0x44444444) - - hweight32(st->dcd_shreg & 0xbbbbbbbb); - } - hdlcdrv_channelbit(&sm->hdrv, cursync); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffff; - st->last_bit2 = st->last_bit; - if (curst < 0) - st->last_bit = 0; - else if (curst > 0) - st->last_bit = 1; - st->shreg >>= 1; - st->shreg |= ((st->last_bit ^ st->last_bit2 ^ 1) & 1) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, inv); - } -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_hapn4800(struct sm_state *sm) -{ - struct demod_state_hapn48 *st = (struct demod_state_hapn48 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_8_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), 38400, 4800, - modulator_hapn4800_8_u8, modulator_hapn4800_8_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_8_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, - demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_10_tx = { - "hapn4800", sizeof(struct mod_state_hapn48), 48000, 4800, - modulator_hapn4800_10_u8, modulator_hapn4800_10_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_10_rx = { - "hapn4800", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, - demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_pm8_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), 38400, 4800, - modulator_hapn4800_pm8_u8, modulator_hapn4800_pm8_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_pm8_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), 38400, 4800, 5, 8, - demodulator_hapn4800_8_u8, demodulator_hapn4800_8_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_hapn4800_pm10_tx = { - "hapn4800pm", sizeof(struct mod_state_hapn48), 48000, 4800, - modulator_hapn4800_pm10_u8, modulator_hapn4800_pm10_s16, NULL -}; - -const struct modem_rx_info sm_hapn4800_pm10_rx = { - "hapn4800pm", sizeof(struct demod_state_hapn48), 48000, 4800, 5, 10, - demodulator_hapn4800_10_u8, demodulator_hapn4800_10_s16, demod_init_hapn4800 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_psk4800.c b/drivers/net/hamradio/soundmodem/sm_psk4800.c deleted file mode 100644 index cbb49042b..000000000 --- a/drivers/net/hamradio/soundmodem/sm_psk4800.c +++ /dev/null @@ -1,418 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_psk4800.c -- soundcard radio modem driver, 4800 baud 8PSK modem - * - * Copyright (C) 1997 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include "sm.h" -#include "sm_tbl_psk4800.h" - -/* --------------------------------------------------------------------- */ - -#define DESCRAM_TAP1 0x20000 -#define DESCRAM_TAP2 0x01000 -#define DESCRAM_TAP3 0x00001 - -#define DESCRAM_TAPSH1 17 -#define DESCRAM_TAPSH2 12 -#define DESCRAM_TAPSH3 0 - -#define SCRAM_TAP1 0x20000 /* X^17 */ -#define SCRAM_TAPN 0x00021 /* X^0+X^5 */ - -#define SCRAM_SHIFT 17 - -/* --------------------------------------------------------------------- */ - -struct demod_state_psk48 { - /* - * input mixer and lowpass - */ - short infi[PSK48_RXF_LEN/2], infq[PSK48_RXF_LEN/2]; - unsigned int downmixer; - int ovrphase; - short magi, magq; - /* - * sampling instant recovery - */ - int pwrhist[5]; - unsigned int s_phase; - int cur_sync; - /* - * phase recovery - */ - short cur_phase_dev; - short last_ph_err; - unsigned short pskph; - unsigned int phase; - unsigned short last_pskph; - unsigned char cur_raw, last_raw, rawbits; - /* - * decoding - */ - unsigned int shreg; - unsigned long descram; - unsigned int bit_pll; - unsigned char last_sample; - unsigned int dcd_shreg; - int dcd_sum0, dcd_sum1, dcd_sum2; - unsigned int dcd_time; -}; - -struct mod_state_psk48 { - unsigned char txbits[PSK48_TXF_NUMSAMPLES]; - unsigned short txphase; - unsigned int shreg; - unsigned long scram; - const short *tbl; - unsigned int txseq; -}; - -/* --------------------------------------------------------------------- */ - -static void modulator_4800_u8(struct sm_state *sm, unsigned char *buf, unsigned int buflen) -{ - struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); - int i, j; - int si, sq; - - for (; buflen > 0; buflen--, buf++) { - if (!st->txseq++) { - memmove(st->txbits+1, st->txbits, - sizeof(st->txbits)-sizeof(st->txbits[0])); - for (i = 0; i < 3; i++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | - (st->shreg & 1); - st->shreg >>= 1; - if (st->scram & SCRAM_TAP1) - st->scram ^= SCRAM_TAPN; - } - j = (st->scram >> (SCRAM_SHIFT+3)) & 7; - st->txbits[0] -= (j ^ (j >> 1)); - st->txbits[0] &= 7; - st->tbl = psk48_tx_table; - } - if (st->txseq >= PSK48_TXF_OVERSAMPLING) - st->txseq = 0; - for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) { - si += st->tbl[st->txbits[j]]; - sq += st->tbl[st->txbits[j]+8]; - } - *buf = ((si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 23) + 0x80; - st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu; - } -} - -/* --------------------------------------------------------------------- */ - -static void modulator_4800_s16(struct sm_state *sm, short *buf, unsigned int buflen) -{ - struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); - int i, j; - int si, sq; - - for (; buflen > 0; buflen--, buf++) { - if (!st->txseq++) { - memmove(st->txbits+1, st->txbits, - sizeof(st->txbits)-sizeof(st->txbits[0])); - for (i = 0; i < 3; i++) { - if (st->shreg <= 1) - st->shreg = hdlcdrv_getbits(&sm->hdrv) | 0x10000; - st->scram = (st->scram << 1) | - (st->shreg & 1); - st->shreg >>= 1; - if (st->scram & SCRAM_TAP1) - st->scram ^= SCRAM_TAPN; - } - j = (st->scram >> (SCRAM_SHIFT+3)) & 7; - st->txbits[0] -= (j ^ (j >> 1)); - st->txbits[0] &= 7; - st->tbl = psk48_tx_table; - } - if (st->txseq >= PSK48_TXF_OVERSAMPLING) - st->txseq = 0; - for (j = si = sq = 0; j < PSK48_TXF_NUMSAMPLES; j++, st->tbl += 16) { - si += st->tbl[st->txbits[j]]; - sq += st->tbl[st->txbits[j]+8]; - } - *buf = (si*COS(st->txphase)+ sq*SIN(st->txphase)) >> 15; - st->txphase = (st->txphase + PSK48_PHASEINC) & 0xffffu; - } -} - -/* --------------------------------------------------------------------- */ - -static __inline__ unsigned short tbl_atan(short q, short i) -{ - short tmp; - unsigned short argoffs = 0; - - if (i == 0 && q == 0) - return 0; - switch (((q < 0) << 1) | (i < 0)) { - case 0: - break; - case 1: - tmp = q; - q = -i; - i = tmp; - argoffs = 0x4000; - break; - case 3: - q = -q; - i = -i; - argoffs = 0x8000; - break; - case 2: - tmp = -q; - q = i; - i = tmp; - argoffs = 0xc000; - break; - } - if (q > i) { - tmp = i / q * ATAN_TABLEN; - return (argoffs+0x4000-atan_tab[((i<<15)/q*ATAN_TABLEN>>15)]) - &0xffffu; - } - return (argoffs+atan_tab[((q<<15)/i*ATAN_TABLEN)>>15])&0xffffu; -} - -#define ATAN(q,i) tbl_atan(q, i) - -/* --------------------------------------------------------------------- */ - -static void demod_psk48_baseband(struct sm_state *sm, struct demod_state_psk48 *st, - short vali, short valq) -{ - int i, j; - - st->magi = vali; - st->magq = valq; - memmove(st->pwrhist+1, st->pwrhist, - sizeof(st->pwrhist)-sizeof(st->pwrhist[0])); - st->pwrhist[0] = st->magi * st->magi + - st->magq * st->magq; - st->cur_sync = ((st->pwrhist[4] >> 2) > st->pwrhist[2] && - (st->pwrhist[0] >> 2) > st->pwrhist[2] && - st-> pwrhist[3] > st->pwrhist[2] && - st->pwrhist[1] > st->pwrhist[2]); - st->s_phase &= 0xffff; - st->s_phase += PSK48_SPHASEINC; - st->dcd_shreg <<= 1; - if (st->cur_sync) { - if (st->s_phase >= (0x8000 + 5*PSK48_SPHASEINC/2)) - st->s_phase -= PSK48_SPHASEINC/6; - else - st->s_phase += PSK48_SPHASEINC/6; - st->dcd_sum0 = 4*hweight8(st->dcd_shreg & 0xf8)- - hweight16(st->dcd_shreg & 0x1f00); - } - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->s_phase < 0x10000) - return; - /* - * sample one constellation - */ - st->last_pskph = st->pskph; - st->pskph = (ATAN(st->magq, st->magi)- - st->phase) & 0xffffu; - st->last_ph_err = (st->pskph & 0x1fffu) - 0x1000; - st->phase += st->last_ph_err/16; - st->last_raw = st->cur_raw; - st->cur_raw = ((st->pskph >> 13) & 7); - i = (st->cur_raw - st->last_raw) & 7; - st->rawbits = i ^ (i >> 1) ^ (i >> 2); - st->descram = (st->descram << 3) | (st->rawbits); - hdlcdrv_channelbit(&sm->hdrv, st->descram & 4); - hdlcdrv_channelbit(&sm->hdrv, st->descram & 2); - hdlcdrv_channelbit(&sm->hdrv, st->descram & 1); - i = (((st->descram >> DESCRAM_TAPSH1) & 7) ^ - ((st->descram >> DESCRAM_TAPSH2) & 7) ^ - ((st->descram >> DESCRAM_TAPSH3) & 7)); - for (j = 4; j; j >>= 1) { - st->shreg >>= 1; - st->shreg |= (!!(i & j)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - } - -#if 0 - st->dcd_shreg <<= 1; - st->bit_pll += 0x4000; - curbit = (*buf >= 0x80); - if (st->last_sample ^ curbit) { - st->dcd_shreg |= 1; - st->bit_pll += pll_corr - [st->bit_pll < 0xa000]; - st->dcd_sum0 += 8 * - hweight8(st->dcd_shreg & 0x0c) - - !!(st->dcd_shreg & 0x10); - } - st->last_sample = curbit; - hdlcdrv_channelbit(&sm->hdrv, st->last_sample); - if ((--st->dcd_time) <= 0) { - hdlcdrv_setdcd(&sm->hdrv, (st->dcd_sum0 + - st->dcd_sum1 + - st->dcd_sum2) < 0); - st->dcd_sum2 = st->dcd_sum1; - st->dcd_sum1 = st->dcd_sum0; - st->dcd_sum0 = 2; /* slight bias */ - st->dcd_time = 240; - } - if (st->bit_pll >= 0x10000) { - st->bit_pll &= 0xffffu; - st->descram = (st->descram << 1) | curbit; - descx = st->descram ^ (st->descram >> 1); - descx ^= ((descx >> DESCRAM_TAPSH1) ^ - (descx >> DESCRAM_TAPSH2)); - st->shreg >>= 1; - st->shreg |= (!(descx & 1)) << 16; - if (st->shreg & 1) { - hdlcdrv_putbits(&sm->hdrv, st->shreg >> 1); - st->shreg = 0x10000; - } - diag_trigger(sm); - } - diag_add_one(sm, ((short)(*buf - 0x80)) << 8); -#endif - - diag_trigger(sm); - diag_add_constellation(sm, (vali*COS(st->phase)+ valq*SIN(st->phase)) >> 13, - (valq*COS(st->phase) - vali*SIN(st->phase)) >> 13); -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_4800_u8(struct sm_state *sm, const unsigned char *buf, unsigned int buflen) -{ - struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); - int i, si, sq; - const short *coeff; - - for (; buflen > 0; buflen--, buf++) { - memmove(st->infi+1, st->infi, - sizeof(st->infi)-sizeof(st->infi[0])); - memmove(st->infq+1, st->infq, - sizeof(st->infq)-sizeof(st->infq[0])); - si = *buf; - si &= 0xff; - si -= 128; - diag_add_one(sm, si << 8); - st->infi[0] = (si * COS(st->downmixer))>>7; - st->infq[0] = (si * SIN(st->downmixer))>>7; - st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu; - for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); - i++, coeff += 2) { - si += st->infi[i] * (*coeff); - sq += st->infq[i] * (*coeff); - } - demod_psk48_baseband(sm, st, si >> 15, sq >> 15); - for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); - i++, coeff += 2) { - si += st->infi[i] * (*coeff); - sq += st->infq[i] * (*coeff); - } - demod_psk48_baseband(sm, st, si >> 15, sq >> 15); - } -} - -/* --------------------------------------------------------------------- */ - -static void demodulator_4800_s16(struct sm_state *sm, const short *buf, unsigned int buflen) -{ - struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); - int i, si, sq; - const short *coeff; - - for (; buflen > 0; buflen--, buf++) { - memmove(st->infi+1, st->infi, - sizeof(st->infi)-sizeof(st->infi[0])); - memmove(st->infq+1, st->infq, - sizeof(st->infq)-sizeof(st->infq[0])); - si = *buf; - diag_add_one(sm, si); - st->infi[0] = (si * COS(st->downmixer))>>15; - st->infq[0] = (si * SIN(st->downmixer))>>15; - st->downmixer = (st->downmixer-PSK48_PHASEINC)&0xffffu; - for (i = si = sq = 0, coeff = psk48_rx_coeff; i < (PSK48_RXF_LEN/2); - i++, coeff += 2) { - si += st->infi[i] * (*coeff); - sq += st->infq[i] * (*coeff); - } - demod_psk48_baseband(sm, st, si >> 15, sq >> 15); - for (i = si = sq = 0, coeff = psk48_rx_coeff + 1; i < (PSK48_RXF_LEN/2); - i++, coeff += 2) { - si += st->infi[i] * (*coeff); - sq += st->infq[i] * (*coeff); - } - demod_psk48_baseband(sm, st, si >> 15, sq >> 15); - } -} - -/* --------------------------------------------------------------------- */ - -static void mod_init_4800(struct sm_state *sm) -{ - struct mod_state_psk48 *st = (struct mod_state_psk48 *)(&sm->m); - - st->scram = 1; -} - -/* --------------------------------------------------------------------- */ - -static void demod_init_4800(struct sm_state *sm) -{ - struct demod_state_psk48 *st = (struct demod_state_psk48 *)(&sm->d); - - st->dcd_time = 120; - st->dcd_sum0 = 2; -} - -/* --------------------------------------------------------------------- */ - -const struct modem_tx_info sm_psk4800_tx = { - "psk4800", sizeof(struct mod_state_psk48), - PSK48_SAMPLERATE, 4800, - modulator_4800_u8, modulator_4800_s16, mod_init_4800 -}; - -const struct modem_rx_info sm_psk4800_rx = { - "psk4800", sizeof(struct demod_state_psk48), - PSK48_SAMPLERATE, 4800, 1, PSK48_TXF_OVERSAMPLING, - demodulator_4800_u8, demodulator_4800_s16, demod_init_4800 -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_sbc.c b/drivers/net/hamradio/soundmodem/sm_sbc.c deleted file mode 100644 index 85ae47588..000000000 --- a/drivers/net/hamradio/soundmodem/sm_sbc.c +++ /dev/null @@ -1,941 +0,0 @@ - - -/* - * sm_sbc.c -- soundcard radio modem driver soundblaster hardware driver - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include <linux/ptrace.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <asm/io.h> -#include <asm/dma.h> -#include <linux/ioport.h> -#include <linux/soundmodem.h> -#include <linux/delay.h> -#include "sm.h" -#include "smdma.h" - -/* --------------------------------------------------------------------- */ - -/* - * currently this module is supposed to support both module styles, i.e. - * the old one present up to about 2.1.9, and the new one functioning - * starting with 2.1.21. The reason is I have a kit allowing to compile - * this module also under 2.0.x which was requested by several people. - * This will go in 2.2 - */ -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= 0x20100 -#include <asm/uaccess.h> -#else -#include <asm/segment.h> -#include <linux/mm.h> - -#undef put_user -#undef get_user - -#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) -#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) - -extern inline int copy_from_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_READ, from, n); - if (i) - return i; - memcpy_fromfs(to, from, n); - return 0; -} - -extern inline int copy_to_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_WRITE, to, n); - if (i) - return i; - memcpy_tofs(to, from, n); - return 0; -} -#endif - -/* --------------------------------------------------------------------- */ - -struct sc_state_sbc { - unsigned char revhi, revlo; - unsigned char fmt[2]; - unsigned int sr[2]; -}; - -#define SCSTATE ((struct sc_state_sbc *)(&sm->hw)) - -/* --------------------------------------------------------------------- */ -/* - * the sbc converter's registers - */ -#define DSP_RESET(iobase) (iobase+0x6) -#define DSP_READ_DATA(iobase) (iobase+0xa) -#define DSP_WRITE_DATA(iobase) (iobase+0xc) -#define DSP_WRITE_STATUS(iobase) (iobase+0xc) -#define DSP_DATA_AVAIL(iobase) (iobase+0xe) -#define DSP_MIXER_ADDR(iobase) (iobase+0x4) -#define DSP_MIXER_DATA(iobase) (iobase+0x5) -#define DSP_INTACK_16BIT(iobase) (iobase+0xf) -#define SBC_EXTENT 16 - -/* --------------------------------------------------------------------- */ -/* - * SBC commands - */ -#define SBC_OUTPUT 0x14 -#define SBC_INPUT 0x24 -#define SBC_BLOCKSIZE 0x48 -#define SBC_HI_OUTPUT 0x91 -#define SBC_HI_INPUT 0x99 -#define SBC_LO_OUTPUT_AUTOINIT 0x1c -#define SBC_LO_INPUT_AUTOINIT 0x2c -#define SBC_HI_OUTPUT_AUTOINIT 0x90 -#define SBC_HI_INPUT_AUTOINIT 0x98 -#define SBC_IMMED_INT 0xf2 -#define SBC_GET_REVISION 0xe1 -#define ESS_GET_REVISION 0xe7 -#define SBC_SPEAKER_ON 0xd1 -#define SBC_SPEAKER_OFF 0xd3 -#define SBC_DMA_ON 0xd0 -#define SBC_DMA_OFF 0xd4 -#define SBC_SAMPLE_RATE 0x40 -#define SBC_SAMPLE_RATE_OUT 0x41 -#define SBC_SAMPLE_RATE_IN 0x42 -#define SBC_MONO_8BIT 0xa0 -#define SBC_MONO_16BIT 0xa4 -#define SBC_STEREO_8BIT 0xa8 -#define SBC_STEREO_16BIT 0xac - -#define SBC4_OUT8_AI 0xc6 -#define SBC4_IN8_AI 0xce -#define SBC4_MODE_UNS_MONO 0x00 -#define SBC4_MODE_SIGN_MONO 0x10 - -#define SBC4_OUT16_AI 0xb6 -#define SBC4_IN16_AI 0xbe - -/* --------------------------------------------------------------------- */ - -static int inline reset_dsp(struct net_device *dev) -{ - int i; - - outb(1, DSP_RESET(dev->base_addr)); - udelay(300); - outb(0, DSP_RESET(dev->base_addr)); - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) - if (inb(DSP_READ_DATA(dev->base_addr)) == 0xaa) - return 1; - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void inline write_dsp(struct net_device *dev, unsigned char data) -{ - int i; - - for (i = 0; i < 0xffff; i++) - if (!(inb(DSP_WRITE_STATUS(dev->base_addr)) & 0x80)) { - outb(data, DSP_WRITE_DATA(dev->base_addr)); - return; - } -} - -/* --------------------------------------------------------------------- */ - -static int inline read_dsp(struct net_device *dev, unsigned char *data) -{ - int i; - - if (!data) - return 0; - for (i = 0; i < 0xffff; i++) - if (inb(DSP_DATA_AVAIL(dev->base_addr)) & 0x80) { - *data = inb(DSP_READ_DATA(dev->base_addr)); - return 1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int config_resources(struct net_device *dev, struct sm_state *sm, int fdx) -{ - unsigned char irqreg = 0, dmareg = 0, realirq, realdma; - unsigned long flags; - - switch (dev->irq) { - case 2: - case 9: - irqreg |= 0x01; - break; - - case 5: - irqreg |= 0x02; - break; - - case 7: - irqreg |= 0x04; - break; - - case 10: - irqreg |= 0x08; - break; - - default: - return -ENODEV; - } - - switch (dev->dma) { - case 0: - dmareg |= 0x01; - break; - - case 1: - dmareg |= 0x02; - break; - - case 3: - dmareg |= 0x08; - break; - - default: - return -ENODEV; - } - - if (fdx) { - switch (sm->hdrv.ptt_out.dma2) { - case 5: - dmareg |= 0x20; - break; - - case 6: - dmareg |= 0x40; - break; - - case 7: - dmareg |= 0x80; - break; - - default: - return -ENODEV; - } - } - save_flags(flags); - cli(); - outb(0x80, DSP_MIXER_ADDR(dev->base_addr)); - outb(irqreg, DSP_MIXER_DATA(dev->base_addr)); - realirq = inb(DSP_MIXER_DATA(dev->base_addr)); - outb(0x81, DSP_MIXER_ADDR(dev->base_addr)); - outb(dmareg, DSP_MIXER_DATA(dev->base_addr)); - realdma = inb(DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - if ((~realirq) & irqreg || (~realdma) & dmareg) { - printk(KERN_ERR "%s: sbc resource registers cannot be set; PnP device " - "and IRQ/DMA specified wrongly?\n", sm_drvname); - return -EINVAL; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static void inline sbc_int_ack_8bit(struct net_device *dev) -{ - inb(DSP_DATA_AVAIL(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static void inline sbc_int_ack_16bit(struct net_device *dev) -{ - inb(DSP_INTACK_16BIT(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static void setup_dma_dsp(struct net_device *dev, struct sm_state *sm, int send) -{ - unsigned long flags; - static const unsigned char sbcmode[2][2] = { - { SBC_LO_INPUT_AUTOINIT, SBC_LO_OUTPUT_AUTOINIT }, - { SBC_HI_INPUT_AUTOINIT, SBC_HI_OUTPUT_AUTOINIT } - }; - static const unsigned char sbc4mode[2] = { SBC4_IN8_AI, SBC4_OUT8_AI }; - static const unsigned char sbcskr[2] = { SBC_SPEAKER_OFF, SBC_SPEAKER_ON }; - unsigned int nsamps; - - send = !!send; - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); - return; - } - save_flags(flags); - cli(); - sbc_int_ack_8bit(dev); - write_dsp(dev, SBC_SAMPLE_RATE); /* set sampling rate */ - write_dsp(dev, SCSTATE->fmt[send]); - write_dsp(dev, sbcskr[send]); - nsamps = dma_setup(sm, send, dev->dma) - 1; - sbc_int_ack_8bit(dev); - if (SCSTATE->revhi >= 4) { - write_dsp(dev, sbc4mode[send]); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, nsamps & 0xff); - write_dsp(dev, nsamps >> 8); - } else { - write_dsp(dev, SBC_BLOCKSIZE); - write_dsp(dev, nsamps & 0xff); - write_dsp(dev, nsamps >> 8); - write_dsp(dev, sbcmode[SCSTATE->fmt[send] >= 180][send]); - /* hispeed mode if sample rate > 13kHz */ - } - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbc_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned int curfrag; - - if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) - return; - cli(); - sbc_int_ack_8bit(dev); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag); - enable_dma(dev->dma); - sm_int_freq(sm); - sti(); - if (sm->dma.ptt_cnt <= 0) { - dma_receive(sm, curfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - if (hdlcdrv_ptt(&sm->hdrv)) { - /* starting to transmit */ - disable_dma(dev->dma); - hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ - dma_start_transmit(sm); - setup_dma_dsp(dev, sm, 1); - dma_transmit(sm); - } - } else if (dma_end_transmit(sm, curfrag)) { - /* stopping transmission */ - disable_dma(dev->dma); - sti(); - dma_init_receive(sm); - setup_dma_dsp(dev, sm, 0); - } else - dma_transmit(sm); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); - -} - -/* --------------------------------------------------------------------- */ - -static int sbc_open(struct net_device *dev, struct sm_state *sm) -{ - int err; - unsigned int dmasz, u; - - if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { - printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", - sizeof(struct sc_state_sbc), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, SBC_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", - sm_drvname, dev->base_addr); - return -ENODEV; - } - write_dsp(dev, SBC_GET_REVISION); - if (!read_dsp(dev, &SCSTATE->revhi) || - !read_dsp(dev, &SCSTATE->revlo)) - return -ENODEV; - printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, - SCSTATE->revhi, SCSTATE->revlo); - if (SCSTATE->revhi < 2) { - printk(KERN_ERR "%s: your card is an antiquity, at least DSP " - "rev 2.00 required\n", sm_drvname); - return -ENODEV; - } - if (SCSTATE->revhi < 3 && - (SCSTATE->fmt[0] >= 180 || SCSTATE->fmt[1] >= 180)) { - printk(KERN_ERR "%s: sbc io 0x%lx: DSP rev %d.%02d too " - "old, at least 3.00 required\n", sm_drvname, - dev->base_addr, SCSTATE->revhi, SCSTATE->revlo); - return -ENODEV; - } - if (SCSTATE->revhi >= 4 && - (err = config_resources(dev, sm, 0))) { - printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); - return err; - } - /* - * initialize some variables - */ - dma_init_receive(sm); - dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (u > dmasz) - dmasz = u; - if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_irq(dev->irq, sbc_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - free_dma(dev->dma); - kfree(sm->dma.obuf); - return -EBUSY; - } - request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); - setup_dma_dsp(dev, sm, 0); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_close(struct net_device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - reset_dsp(dev); - free_irq(dev->irq, dev); - free_dma(dev->dma); - release_region(dev->base_addr, SBC_EXTENT); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_sethw(struct net_device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) - continue; - if (!(*mtp)->modulator_u8) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if (!(*mrp)->demodulator_u8) - continue; - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mrp)->srate >= 5000 && (*mrp)->srate <= 44100) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = 256-((1000000L+sm->mode_rx->srate/2)/ - sm->mode_rx->srate); - SCSTATE->fmt[1] = 256-((1000000L+sm->mode_tx->srate/2)/ - sm->mode_tx->srate); - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - sm->dma.i16bit = sm->dma.o16bit = 0; - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int sbc_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct sm_ioctl bi; - unsigned long flags; - int i; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - - case SMCTL_GETMIXER: - i = 0; - bi.data.mix.sample_rate = sm->mode_rx->srate; - bi.data.mix.bit_rate = sm->hdrv.par.bitrate; - bi.data.mix.mixer_type = SM_MIXER_INVALID; - switch (SCSTATE->revhi) { - case 2: - bi.data.mix.mixer_type = SM_MIXER_CT1335; - break; - case 3: - bi.data.mix.mixer_type = SM_MIXER_CT1345; - break; - case 4: - bi.data.mix.mixer_type = SM_MIXER_CT1745; - break; - } - if (bi.data.mix.mixer_type != SM_MIXER_INVALID && - bi.data.mix.reg < 0x80) { - save_flags(flags); - cli(); - outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); - bi.data.mix.data = inb(DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - i = 1; - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return i; - - case SMCTL_SETMIXER: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - switch (SCSTATE->revhi) { - case 2: - if (bi.data.mix.mixer_type != SM_MIXER_CT1335) - return -EINVAL; - break; - case 3: - if (bi.data.mix.mixer_type != SM_MIXER_CT1345) - return -EINVAL; - break; - case 4: - if (bi.data.mix.mixer_type != SM_MIXER_CT1745) - return -EINVAL; - break; - default: - return -ENODEV; - } - if (bi.data.mix.reg >= 0x80) - return -EACCES; - save_flags(flags); - cli(); - outb(bi.data.mix.reg, DSP_MIXER_ADDR(dev->base_addr)); - outb(bi.data.mix.data, DSP_MIXER_DATA(dev->base_addr)); - restore_flags(flags); - return 0; - - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_sbc = { - "sbc", sizeof(struct sc_state_sbc), - sbc_open, sbc_close, sbc_ioctl, sbc_sethw -}; - -/* --------------------------------------------------------------------- */ - -static void setup_dma_fdx_dsp(struct net_device *dev, struct sm_state *sm) -{ - unsigned long flags; - unsigned int isamps, osamps; - - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: cannot reset sb dsp\n", sm_drvname); - return; - } - save_flags(flags); - cli(); - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - /* should eventually change to set rates individually by SBC_SAMPLE_RATE_{IN/OUT} */ - write_dsp(dev, SBC_SAMPLE_RATE_IN); - write_dsp(dev, SCSTATE->sr[0] >> 8); - write_dsp(dev, SCSTATE->sr[0] & 0xff); - write_dsp(dev, SBC_SAMPLE_RATE_OUT); - write_dsp(dev, SCSTATE->sr[1] >> 8); - write_dsp(dev, SCSTATE->sr[1] & 0xff); - write_dsp(dev, SBC_SPEAKER_ON); - if (sm->dma.o16bit) { - /* - * DMA channel 1 (8bit) does input (capture), - * DMA channel 2 (16bit) does output (playback) - */ - isamps = dma_setup(sm, 0, dev->dma) - 1; - osamps = dma_setup(sm, 1, sm->hdrv.ptt_out.dma2) - 1; - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC4_IN8_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, isamps & 0xff); - write_dsp(dev, isamps >> 8); - write_dsp(dev, SBC4_OUT16_AI); - write_dsp(dev, SBC4_MODE_SIGN_MONO); - write_dsp(dev, osamps & 0xff); - write_dsp(dev, osamps >> 8); - } else { - /* - * DMA channel 1 (8bit) does output (playback), - * DMA channel 2 (16bit) does input (capture) - */ - isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; - osamps = dma_setup(sm, 1, dev->dma) - 1; - sbc_int_ack_8bit(dev); - sbc_int_ack_16bit(dev); - write_dsp(dev, SBC4_OUT8_AI); - write_dsp(dev, SBC4_MODE_UNS_MONO); - write_dsp(dev, osamps & 0xff); - write_dsp(dev, osamps >> 8); - write_dsp(dev, SBC4_IN16_AI); - write_dsp(dev, SBC4_MODE_SIGN_MONO); - write_dsp(dev, isamps & 0xff); - write_dsp(dev, isamps >> 8); - } - dma_init_receive(sm); - dma_init_transmit(sm); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void sbcfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned char intsrc, pbint = 0, captint = 0; - unsigned int ocfrag, icfrag; - unsigned long flags; - - if (!dev || !sm || sm->hdrv.magic != HDLCDRV_MAGIC) - return; - save_flags(flags); - cli(); - outb(0x82, DSP_MIXER_ADDR(dev->base_addr)); - intsrc = inb(DSP_MIXER_DATA(dev->base_addr)); - if (intsrc & 0x01) { - sbc_int_ack_8bit(dev); - if (sm->dma.o16bit) { - captint = 1; - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, 0, dev->dma, &icfrag); - enable_dma(dev->dma); - } else { - pbint = 1; - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - dma_ptr(sm, 1, dev->dma, &ocfrag); - enable_dma(dev->dma); - } - } - if (intsrc & 0x02) { - sbc_int_ack_16bit(dev); - if (sm->dma.o16bit) { - pbint = 1; - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - dma_ptr(sm, 1, sm->hdrv.ptt_out.dma2, &ocfrag); - enable_dma(sm->hdrv.ptt_out.dma2); - } else { - captint = 1; - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag); - enable_dma(sm->hdrv.ptt_out.dma2); - } - } - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (pbint) { - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - } - if (captint) { - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_open(struct net_device *dev, struct sm_state *sm) -{ - int err; - - if (sizeof(sm->m) < sizeof(struct sc_state_sbc)) { - printk(KERN_ERR "sm sbc: sbc state too big: %d > %d\n", - sizeof(struct sc_state_sbc), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-SBC_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, SBC_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (!reset_dsp(dev)) { - printk(KERN_ERR "%s: sbc: no card at io address 0x%lx\n", - sm_drvname, dev->base_addr); - return -ENODEV; - } - write_dsp(dev, SBC_GET_REVISION); - if (!read_dsp(dev, &SCSTATE->revhi) || - !read_dsp(dev, &SCSTATE->revlo)) - return -ENODEV; - printk(KERN_INFO "%s: SoundBlaster DSP revision %d.%d\n", sm_drvname, - SCSTATE->revhi, SCSTATE->revlo); - if (SCSTATE->revhi < 4) { - printk(KERN_ERR "%s: at least DSP rev 4.00 required\n", sm_drvname); - return -ENODEV; - } - if ((err = config_resources(dev, sm, 1))) { - printk(KERN_ERR "%s: invalid IRQ and/or DMA specified\n", sm_drvname); - return err; - } - /* - * initialize some variables - */ - if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { - kfree(sm->dma.ibuf); - return -ENOMEM; - } - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - return -EBUSY; - } - if (request_irq(dev->irq, sbcfdx_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - return -EBUSY; - } - request_region(dev->base_addr, SBC_EXTENT, sm->hwdrv->hw_name); - setup_dma_fdx_dsp(dev, sm); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_close(struct net_device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - reset_dsp(dev); - free_irq(dev->irq, dev); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - release_region(dev->base_addr, SBC_EXTENT); - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((*mtp)->srate < 5000 || (*mtp)->srate > 44100) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mtp)->srate >= 5000 && (*mtp)->srate <= 44100 && - (*mrp)->srate == (*mtp)->srate) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->sr[0] = sm->mode_rx->srate; - SCSTATE->sr[1] = sm->mode_tx->srate; - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_u8) { - sm->dma.i16bit = 1; - sm->dma.o16bit = 0; - sm->dma.ifragsz <<= 1; - } else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_s16) { - sm->dma.i16bit = 0; - sm->dma.o16bit = 1; - sm->dma.ofragsz <<= 1; - } else { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int sbcfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - return sbc_ioctl(dev, sm, ifr, hi, cmd); -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_sbcfdx = { - "sbcfdx", sizeof(struct sc_state_sbc), - sbcfdx_open, sbcfdx_close, sbcfdx_ioctl, sbcfdx_sethw -}; - -/* --------------------------------------------------------------------- */ diff --git a/drivers/net/hamradio/soundmodem/sm_wss.c b/drivers/net/hamradio/soundmodem/sm_wss.c deleted file mode 100644 index 19840c491..000000000 --- a/drivers/net/hamradio/soundmodem/sm_wss.c +++ /dev/null @@ -1,965 +0,0 @@ -/*****************************************************************************/ - -/* - * sm_wss.c -- soundcard radio modem driver, WSS (half duplex) driver - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#include <linux/ptrace.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <asm/io.h> -#include <asm/dma.h> -#include <linux/ioport.h> -#include <linux/soundmodem.h> -#include "sm.h" -#include "smdma.h" - -/* --------------------------------------------------------------------- */ - -/* - * currently this module is supposed to support both module styles, i.e. - * the old one present up to about 2.1.9, and the new one functioning - * starting with 2.1.21. The reason is I have a kit allowing to compile - * this module also under 2.0.x which was requested by several people. - * This will go in 2.2 - */ -#include <linux/version.h> - -#if LINUX_VERSION_CODE >= 0x20100 -#include <asm/uaccess.h> -#else -#include <asm/segment.h> -#include <linux/mm.h> - -#undef put_user -#undef get_user - -#define put_user(x,ptr) ({ __put_user((unsigned long)(x),(ptr),sizeof(*(ptr))); 0; }) -#define get_user(x,ptr) ({ x = ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr)))); 0; }) - -extern inline int copy_from_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_READ, from, n); - if (i) - return i; - memcpy_fromfs(to, from, n); - return 0; -} - -extern inline int copy_to_user(void *to, const void *from, unsigned long n) -{ - int i = verify_area(VERIFY_WRITE, to, n); - if (i) - return i; - memcpy_tofs(to, from, n); - return 0; -} -#endif - -/* --------------------------------------------------------------------- */ - -struct sc_state_wss { - unsigned char revwss, revid, revv, revcid; - unsigned char fmt[2]; - unsigned char crystal; -}; - -#define SCSTATE ((struct sc_state_wss *)(&sm->hw)) - -/* --------------------------------------------------------------------- */ - -#define WSS_CONFIG(iobase) (iobase+0) -#define WSS_STATUS(iobase) (iobase+3) -#define WSS_CODEC_IA(iobase) (iobase+4) -#define WSS_CODEC_ID(iobase) (iobase+5) -#define WSS_CODEC_STATUS(iobase) (iobase+6) -#define WSS_CODEC_DATA(iobase) (iobase+7) - -#define WSS_EXTENT 8 - -#define CS423X_HOTFIX - -/* --------------------------------------------------------------------- */ - -static void write_codec(struct net_device *dev, unsigned char idx, - unsigned char data) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) - timeout--; - outb(idx, WSS_CODEC_IA(dev->base_addr)); - outb(data, WSS_CODEC_ID(dev->base_addr)); -} - - -/* --------------------------------------------------------------------- */ - -static unsigned char read_codec(struct net_device *dev, unsigned char idx) -{ - int timeout = 900000; - - /* wait until codec ready */ - while (timeout > 0 && inb(WSS_CODEC_IA(dev->base_addr)) & 0x80) - timeout--; - outb(idx & 0x1f, WSS_CODEC_IA(dev->base_addr)); - return inb(WSS_CODEC_ID(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -extern void inline wss_ack_int(struct net_device *dev) -{ - outb(0, WSS_CODEC_STATUS(dev->base_addr)); -} - -/* --------------------------------------------------------------------- */ - -static int wss_srate_tab[16] = { - 8000, 5510, 16000, 11025, 27420, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6620 -}; - -static int wss_srate_index(int srate) -{ - int i; - - for (i = 0; i < (sizeof(wss_srate_tab)/sizeof(wss_srate_tab[0])); i++) - if (srate == wss_srate_tab[i] && wss_srate_tab[i] > 0) - return i; - return -1; -} - -/* --------------------------------------------------------------------- */ - -static int wss_set_codec_fmt(struct net_device *dev, struct sm_state *sm, unsigned char fmt, - unsigned char fmt2, char fdx, char fullcalib) -{ - unsigned long time; - unsigned long flags; - - save_flags(flags); - cli(); - /* Clock and data format register */ - write_codec(dev, 0x48, fmt); - if (SCSTATE->crystal) { - write_codec(dev, 0x5c, fmt2 & 0xf0); - /* MCE and interface config reg */ - write_codec(dev, 0x49, (fdx ? 0 : 0x4) | (fullcalib ? 0x18 : 0)); - } else - /* MCE and interface config reg */ - write_codec(dev, 0x49, fdx ? 0x8 : 0xc); - outb(0xb, WSS_CODEC_IA(dev->base_addr)); /* leave MCE */ - if (SCSTATE->crystal && !fullcalib) - return 0; - /* - * wait for ACI start - */ - time = 1000; - while (!(read_codec(dev, 0x0b) & 0x20)) - if (!(--time)) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (1)\n", - sm_drvname); - restore_flags(flags); - return -1; - } - /* - * wait for ACI end - */ - sti(); - time = jiffies + HZ/4; - while ((read_codec(dev, 0x0b) & 0x20) && ((signed)(jiffies - time) < 0)); - restore_flags(flags); - if ((signed)(jiffies - time) >= 0) { - printk(KERN_WARNING "%s: ad1848 auto calibration timed out (2)\n", - sm_drvname); - return -1; - } - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_init_codec(struct net_device *dev, struct sm_state *sm, char fdx, - unsigned char src_l, unsigned char src_r, - int igain_l, int igain_r, - int ogain_l, int ogain_r) -{ - unsigned char tmp, reg0, reg1, reg6, reg7; - static const signed char irqtab[16] = - { -1, -1, 0x10, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20, -1, -1, - -1, -1 }; - static const signed char dmatab[4] = { 1, 2, -1, 3 }; - - tmp = inb(WSS_STATUS(dev->base_addr)); - if ((tmp & 0x3f) != 0x04 && (tmp & 0x3f) != 0x00 && - (tmp & 0x3f) != 0x0f) { - printk(KERN_WARNING "sm: WSS card id register not found, " - "address 0x%lx, ID register 0x%02x\n", - dev->base_addr, (int)tmp); - /* return -1; */ - SCSTATE->revwss = 0; - } else { - if ((tmp & 0x80) && ((dev->dma == 0) || - ((dev->irq >= 8) && (dev->irq != 9)))) { - printk(KERN_ERR "%s: WSS: DMA0 and/or IRQ8..IRQ15 " - "(except IRQ9) cannot be used on an 8bit " - "card\n", sm_drvname); - return -1; - } - if (dev->irq > 15 || irqtab[dev->irq] == -1) { - printk(KERN_ERR "%s: WSS: invalid interrupt %d\n", - sm_drvname, (int)dev->irq); - return -1; - } - if (dev->dma > 3 || dmatab[dev->dma] == -1) { - printk(KERN_ERR "%s: WSS: invalid dma channel %d\n", - sm_drvname, (int)dev->dma); - return -1; - } - tmp = irqtab[dev->irq] | dmatab[dev->dma]; - /* irq probe */ - outb((tmp & 0x38) | 0x40, WSS_CONFIG(dev->base_addr)); - if (!(inb(WSS_STATUS(dev->base_addr)) & 0x40)) { - outb(0, WSS_CONFIG(dev->base_addr)); - printk(KERN_ERR "%s: WSS: IRQ%d is not free!\n", - sm_drvname, dev->irq); - } - outb(tmp, WSS_CONFIG(dev->base_addr)); - SCSTATE->revwss = inb(WSS_STATUS(dev->base_addr)) & 0x3f; - } - /* - * initialize the codec - */ - if (igain_l < 0) - igain_l = 0; - if (igain_r < 0) - igain_r = 0; - if (ogain_l > 0) - ogain_l = 0; - if (ogain_r > 0) - ogain_r = 0; - reg0 = (src_l << 6) & 0xc0; - reg1 = (src_r << 6) & 0xc0; - if (reg0 == 0x80 && igain_l >= 20) { - reg0 |= 0x20; - igain_l -= 20; - } - if (reg1 == 0x80 && igain_r >= 20) { - reg1 |= 0x20; - igain_r -= 20; - } - if (igain_l > 23) - igain_l = 23; - if (igain_r > 23) - igain_r = 23; - reg0 |= igain_l * 2 / 3; - reg1 |= igain_r * 2 / 3; - reg6 = (ogain_l < -95) ? 0x80 : (ogain_l * (-2) / 3); - reg7 = (ogain_r < -95) ? 0x80 : (ogain_r * (-2) / 3); - write_codec(dev, 9, 0); - write_codec(dev, 0, 0x45); - if (read_codec(dev, 0) != 0x45) - goto codec_err; - write_codec(dev, 0, 0xaa); - if (read_codec(dev, 0) != 0xaa) - goto codec_err; - write_codec(dev, 12, 0x40); /* enable MODE2 */ - write_codec(dev, 16, 0); - write_codec(dev, 0, 0x45); - SCSTATE->crystal = (read_codec(dev, 16) != 0x45); - write_codec(dev, 0, 0xaa); - SCSTATE->crystal &= (read_codec(dev, 16) != 0xaa); - if (SCSTATE->crystal) { - SCSTATE->revcid = read_codec(dev, 0x19); - SCSTATE->revv = (SCSTATE->revcid >> 5) & 7; - SCSTATE->revcid &= 7; - write_codec(dev, 0x10, 0x80); /* maximum output level */ - write_codec(dev, 0x11, 0x02); /* xtal enable and no HPF */ - write_codec(dev, 0x12, 0x80); /* left line input control */ - write_codec(dev, 0x13, 0x80); /* right line input control */ - write_codec(dev, 0x16, 0); /* disable alternative freq sel */ - write_codec(dev, 0x1a, 0xe0); /* mono IO disable */ - write_codec(dev, 0x1b, 0x00); /* left out no att */ - write_codec(dev, 0x1d, 0x00); /* right out no att */ - } - - if (wss_set_codec_fmt(dev, sm, SCSTATE->fmt[0], SCSTATE->fmt[0], fdx, 1)) - goto codec_err; - - write_codec(dev, 0, reg0); /* left input control */ - write_codec(dev, 1, reg1); /* right input control */ - write_codec(dev, 2, 0x80); /* left aux#1 input control */ - write_codec(dev, 3, 0x80); /* right aux#1 input control */ - write_codec(dev, 4, 0x80); /* left aux#2 input control */ - write_codec(dev, 5, 0x80); /* right aux#2 input control */ - write_codec(dev, 6, reg6); /* left dac control */ - write_codec(dev, 7, reg7); /* right dac control */ - write_codec(dev, 0xa, 0x2); /* pin control register */ - write_codec(dev, 0xd, 0x0); /* digital mix control */ - SCSTATE->revid = read_codec(dev, 0xc) & 0xf; - /* - * print revisions - */ - if (SCSTATE->crystal) - printk(KERN_INFO "%s: Crystal CODEC ID %d, Chip revision %d, " - " Chip ID %d\n", sm_drvname, (int)SCSTATE->revid, - (int)SCSTATE->revv, (int)SCSTATE->revcid); - else - printk(KERN_INFO "%s: WSS revision %d, CODEC revision %d\n", - sm_drvname, (int)SCSTATE->revwss, - (int)SCSTATE->revid); - return 0; - codec_err: - outb(0, WSS_CONFIG(dev->base_addr)); - printk(KERN_ERR "%s: no WSS soundcard found at address 0x%lx\n", - sm_drvname, dev->base_addr); - return -1; -} - -/* --------------------------------------------------------------------- */ - -static void setup_dma_wss(struct net_device *dev, struct sm_state *sm, int send) -{ - unsigned long flags; - static const unsigned char codecmode[2] = { 0x0e, 0x0d }; - unsigned char oldcodecmode; - long abrt; - unsigned char fmt; - unsigned int numsamps; - - send = !!send; - fmt = SCSTATE->fmt[send]; - save_flags(flags); - cli(); - /* - * perform the final DMA sequence to disable the codec request - */ - oldcodecmode = read_codec(dev, 9); - write_codec(dev, 9, 0xc); /* disable codec */ - wss_ack_int(dev); - if (read_codec(dev, 11) & 0x10) { - dma_setup(sm, oldcodecmode & 1, dev->dma); - abrt = 0; - while ((read_codec(dev, 11) & 0x10) || ((++abrt) >= 0x10000)); - } -#ifdef CS423X_HOTFIX - if (read_codec(dev, 0x8) != fmt || SCSTATE->crystal) - wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); -#else /* CS423X_HOTFIX */ - if (read_codec(dev, 0x8) != fmt) - wss_set_codec_fmt(dev, sm, fmt, fmt, 0, 0); -#endif /* CS423X_HOTFIX */ - numsamps = dma_setup(sm, send, dev->dma) - 1; - write_codec(dev, 15, numsamps & 0xff); - write_codec(dev, 14, numsamps >> 8); - write_codec(dev, 9, codecmode[send]); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wss_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned int curfrag; - unsigned int nums; - - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || - sm->hdrv.magic != HDLCDRV_MAGIC) - return; - cli(); - wss_ack_int(dev); - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - nums = dma_ptr(sm, sm->dma.ptt_cnt > 0, dev->dma, &curfrag) - 1; - write_codec(dev, 15, nums & 0xff); - write_codec(dev, 14, nums >> 8); - enable_dma(dev->dma); - sm_int_freq(sm); - sti(); - if (sm->dma.ptt_cnt <= 0) { - dma_receive(sm, curfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - if (hdlcdrv_ptt(&sm->hdrv)) { - /* starting to transmit */ - disable_dma(dev->dma); - hdlcdrv_transmitter(dev, &sm->hdrv); /* prefill HDLC buffer */ - dma_start_transmit(sm); - setup_dma_wss(dev, sm, 1); - dma_transmit(sm); - } - } else if (dma_end_transmit(sm, curfrag)) { - /* stopping transmission */ - disable_dma(dev->dma); - dma_init_receive(sm); - setup_dma_wss(dev, sm, 0); - } else - dma_transmit(sm); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int wss_open(struct net_device *dev, struct sm_state *sm) -{ - unsigned int dmasz, u; - - if (sizeof(sm->m) < sizeof(struct sc_state_wss)) { - printk(KERN_ERR "sm wss: wss state too big: %d > %d\n", - sizeof(struct sc_state_wss), sizeof(sm->m)); - return -ENODEV; - } - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, WSS_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (wss_init_codec(dev, sm, 0, 1, 1, 0, 0, -45, -45)) - return -ENODEV; - /* - * initialize some variables - */ - dma_init_receive(sm); - dmasz = (NUM_FRAGMENTS + 1) * sm->dma.ifragsz; - u = NUM_FRAGMENTS * sm->dma.ofragsz; - if (u > dmasz) - dmasz = u; - if (!(sm->dma.ibuf = sm->dma.obuf = kmalloc(dmasz, GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_irq(dev->irq, wss_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - free_dma(dev->dma); - kfree(sm->dma.obuf); - return -EBUSY; - } - request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); - setup_dma_wss(dev, sm, 0); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_close(struct net_device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - write_codec(dev, 9, 0xc); /* disable codec */ - free_irq(dev->irq, dev); - free_dma(dev->dma); - release_region(dev->base_addr, WSS_EXTENT); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wss_sethw(struct net_device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - int i, j; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((i = wss_srate_index((*mtp)->srate)) < 0) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - ((j = wss_srate_index((*mrp)->srate)) >= 0)) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = j; - SCSTATE->fmt[1] = i; - sm->dma.ifragsz = (sm->mode_rx->srate + 50)/100; - sm->dma.ofragsz = (sm->mode_tx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - /* prefer same data format if possible to minimize switching times */ - sm->dma.i16bit = sm->dma.o16bit = 2; - if (sm->mode_rx->srate == sm->mode_tx->srate) { - if (sm->mode_rx->demodulator_s16 && sm->mode_tx->modulator_s16) - sm->dma.i16bit = sm->dma.o16bit = 1; - else if (sm->mode_rx->demodulator_u8 && sm->mode_tx->modulator_u8) - sm->dma.i16bit = sm->dma.o16bit = 0; - } - if (sm->dma.i16bit == 2) { - if (sm->mode_rx->demodulator_s16) - sm->dma.i16bit = 1; - else if (sm->mode_rx->demodulator_u8) - sm->dma.i16bit = 0; - } - if (sm->dma.o16bit == 2) { - if (sm->mode_tx->modulator_s16) - sm->dma.o16bit = 1; - else if (sm->mode_tx->modulator_u8) - sm->dma.o16bit = 0; - } - if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } -#ifdef __BIG_ENDIAN - /* big endian 16bit only works on crystal cards... */ - if (sm->dma.i16bit) { - SCSTATE->fmt[0] |= 0xc0; - sm->dma.ifragsz <<= 1; - } - if (sm->dma.o16bit) { - SCSTATE->fmt[1] |= 0xc0; - sm->dma.ofragsz <<= 1; - } -#else /* __BIG_ENDIAN */ - if (sm->dma.i16bit) { - SCSTATE->fmt[0] |= 0x40; - sm->dma.ifragsz <<= 1; - } - if (sm->dma.o16bit) { - SCSTATE->fmt[1] |= 0x40; - sm->dma.ofragsz <<= 1; - } -#endif /* __BIG_ENDIAN */ - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int wss_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - struct sm_ioctl bi; - int i; - - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_SERIOBASE | - HDLCDRV_PARMASK_PARIOBASE | HDLCDRV_PARMASK_MIDIIOBASE; - - if (copy_from_user(&bi, ifr->ifr_data, sizeof(bi))) - return -EFAULT; - - switch (bi.cmd) { - default: - return -ENOIOCTLCMD; - - case SMCTL_GETMIXER: - i = 0; - bi.data.mix.sample_rate = sm->mode_rx->srate; - bi.data.mix.bit_rate = sm->hdrv.par.bitrate; - bi.data.mix.mixer_type = SCSTATE->crystal ? - SM_MIXER_CRYSTAL : SM_MIXER_AD1848; - if (((SCSTATE->crystal ? 0x2c0c20fflu: 0x20fflu) - >> bi.data.mix.reg) & 1) { - bi.data.mix.data = read_codec(dev, bi.data.mix.reg); - i = 1; - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return i; - - case SMCTL_SETMIXER: - if (!capable(CAP_SYS_RAWIO)) - return -EACCES; - if ((bi.data.mix.mixer_type != SM_MIXER_CRYSTAL || - !SCSTATE->crystal) && - (bi.data.mix.mixer_type != SM_MIXER_AD1848 || - bi.data.mix.reg >= 0x10)) - return -EINVAL; - if (!((0x2c0c20fflu >> bi.data.mix.reg) & 1)) - return -EACCES; - write_codec(dev, bi.data.mix.reg, bi.data.mix.data); - return 0; - - } - if (copy_to_user(ifr->ifr_data, &bi, sizeof(bi))) - return -EFAULT; - return 0; - -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_wss = { - "wss", sizeof(struct sc_state_wss), - wss_open, wss_close, wss_ioctl, wss_sethw -}; - -/* --------------------------------------------------------------------- */ - -static void setup_fdx_dma_wss(struct net_device *dev, struct sm_state *sm) -{ - unsigned long flags; - unsigned char oldcodecmode, codecdma; - long abrt; - unsigned int osamps, isamps; - - save_flags(flags); - cli(); - /* - * perform the final DMA sequence to disable the codec request - */ - oldcodecmode = read_codec(dev, 9); - write_codec(dev, 9, 0); /* disable codec DMA */ - wss_ack_int(dev); - if ((codecdma = read_codec(dev, 11)) & 0x10) { - dma_setup(sm, 1, dev->dma); - dma_setup(sm, 0, sm->hdrv.ptt_out.dma2); - abrt = 0; - while (((codecdma = read_codec(dev, 11)) & 0x10) || ((++abrt) >= 0x10000)); - } - wss_set_codec_fmt(dev, sm, SCSTATE->fmt[1], SCSTATE->fmt[0], 1, 1); - osamps = dma_setup(sm, 1, dev->dma) - 1; - isamps = dma_setup(sm, 0, sm->hdrv.ptt_out.dma2) - 1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - } - write_codec(dev, 9, 3); - restore_flags(flags); -} - -/* --------------------------------------------------------------------- */ - -static void wssfdx_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct sm_state *sm = (struct sm_state *)dev->priv; - unsigned long flags; - unsigned char cry_int_src; - unsigned icfrag, ocfrag, isamps, osamps; - - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx || - sm->hdrv.magic != HDLCDRV_MAGIC) - return; - save_flags(flags); - cli(); - if (SCSTATE->crystal) { - /* Crystal has an essentially different interrupt handler! */ - cry_int_src = read_codec(dev, 0x18); - wss_ack_int(dev); - if (cry_int_src & 0x10) { /* playback interrupt */ - disable_dma(dev->dma); - clear_dma_ff(dev->dma); - osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - enable_dma(dev->dma); - } - if (cry_int_src & 0x20) { /* capture interrupt */ - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - enable_dma(sm->hdrv.ptt_out.dma2); - } - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (cry_int_src & 0x10) { - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - } - if (cry_int_src & 0x20) { - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - } - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); - return; - } - wss_ack_int(dev); - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - clear_dma_ff(dev->dma); - clear_dma_ff(sm->hdrv.ptt_out.dma2); - osamps = dma_ptr(sm, 1, dev->dma, &ocfrag)-1; - isamps = dma_ptr(sm, 0, sm->hdrv.ptt_out.dma2, &icfrag)-1; - write_codec(dev, 15, osamps & 0xff); - write_codec(dev, 14, osamps >> 8); - if (SCSTATE->crystal) { - write_codec(dev, 31, isamps & 0xff); - write_codec(dev, 30, isamps >> 8); - } - enable_dma(dev->dma); - enable_dma(sm->hdrv.ptt_out.dma2); - restore_flags(flags); - sm_int_freq(sm); - sti(); - if (dma_end_transmit(sm, ocfrag)) - dma_clear_transmit(sm); - dma_transmit(sm); - dma_receive(sm, icfrag); - hdlcdrv_arbitrate(dev, &sm->hdrv); - sm_output_status(sm); - hdlcdrv_transmitter(dev, &sm->hdrv); - hdlcdrv_receiver(dev, &sm->hdrv); -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_open(struct net_device *dev, struct sm_state *sm) -{ - if (!dev || !sm || !sm->mode_rx || !sm->mode_tx) - return -ENXIO; - if (dev->base_addr <= 0 || dev->base_addr > 0x1000-WSS_EXTENT || - dev->irq < 2 || dev->irq > 15 || dev->dma > 3) - return -ENXIO; - if (check_region(dev->base_addr, WSS_EXTENT)) - return -EACCES; - /* - * check if a card is available - */ - if (wss_init_codec(dev, sm, 1, 1, 1, 0, 0, -45, -45)) - return -ENODEV; - /* - * initialize some variables - */ - if (!(sm->dma.ibuf = kmalloc(sm->dma.ifragsz * (NUM_FRAGMENTS+1), GFP_KERNEL | GFP_DMA))) - return -ENOMEM; - if (!(sm->dma.obuf = kmalloc(sm->dma.ofragsz * NUM_FRAGMENTS, GFP_KERNEL | GFP_DMA))) { - kfree(sm->dma.ibuf); - return -ENOMEM; - } - dma_init_transmit(sm); - dma_init_receive(sm); - - memset(&sm->m, 0, sizeof(sm->m)); - memset(&sm->d, 0, sizeof(sm->d)); - if (sm->mode_tx->init) - sm->mode_tx->init(sm); - if (sm->mode_rx->init) - sm->mode_rx->init(sm); - - if (request_dma(dev->dma, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return -EBUSY; - } - if (request_dma(sm->hdrv.ptt_out.dma2, sm->hwdrv->hw_name)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - return -EBUSY; - } - if (request_irq(dev->irq, wssfdx_interrupt, SA_INTERRUPT, - sm->hwdrv->hw_name, dev)) { - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - return -EBUSY; - } - request_region(dev->base_addr, WSS_EXTENT, sm->hwdrv->hw_name); - setup_fdx_dma_wss(dev, sm); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_close(struct net_device *dev, struct sm_state *sm) -{ - if (!dev || !sm) - return -EINVAL; - /* - * disable interrupts - */ - disable_dma(dev->dma); - disable_dma(sm->hdrv.ptt_out.dma2); - write_codec(dev, 9, 0xc); /* disable codec */ - free_irq(dev->irq, dev); - free_dma(dev->dma); - free_dma(sm->hdrv.ptt_out.dma2); - release_region(dev->base_addr, WSS_EXTENT); - kfree(sm->dma.ibuf); - kfree(sm->dma.obuf); - return 0; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_sethw(struct net_device *dev, struct sm_state *sm, char *mode) -{ - char *cp = strchr(mode, '.'); - const struct modem_tx_info **mtp = sm_modem_tx_table; - const struct modem_rx_info **mrp; - int i; - - if (!strcmp(mode, "off")) { - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return 0; - } - if (cp) - *cp++ = '\0'; - else - cp = mode; - for (; *mtp; mtp++) { - if ((*mtp)->loc_storage > sizeof(sm->m)) { - printk(KERN_ERR "%s: insufficient storage for modulator %s (%d)\n", - sm_drvname, (*mtp)->name, (*mtp)->loc_storage); - continue; - } - if (!(*mtp)->name || strcmp((*mtp)->name, mode)) - continue; - if ((i = wss_srate_index((*mtp)->srate)) < 0) - continue; - for (mrp = sm_modem_rx_table; *mrp; mrp++) { - if ((*mrp)->loc_storage > sizeof(sm->d)) { - printk(KERN_ERR "%s: insufficient storage for demodulator %s (%d)\n", - sm_drvname, (*mrp)->name, (*mrp)->loc_storage); - continue; - } - if ((*mrp)->name && !strcmp((*mrp)->name, cp) && - (*mtp)->srate == (*mrp)->srate) { - sm->mode_tx = *mtp; - sm->mode_rx = *mrp; - SCSTATE->fmt[0] = SCSTATE->fmt[1] = i; - sm->dma.ifragsz = sm->dma.ofragsz = (sm->mode_rx->srate + 50)/100; - if (sm->dma.ifragsz < sm->mode_rx->overlap) - sm->dma.ifragsz = sm->mode_rx->overlap; - sm->dma.i16bit = sm->dma.o16bit = 2; - if (sm->mode_rx->demodulator_s16) { - sm->dma.i16bit = 1; - sm->dma.ifragsz <<= 1; -#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ - SCSTATE->fmt[0] |= 0xc0; -#else /* __BIG_ENDIAN */ - SCSTATE->fmt[0] |= 0x40; -#endif /* __BIG_ENDIAN */ - } else if (sm->mode_rx->demodulator_u8) - sm->dma.i16bit = 0; - if (sm->mode_tx->modulator_s16) { - sm->dma.o16bit = 1; - sm->dma.ofragsz <<= 1; -#ifdef __BIG_ENDIAN /* big endian 16bit only works on crystal cards... */ - SCSTATE->fmt[1] |= 0xc0; -#else /* __BIG_ENDIAN */ - SCSTATE->fmt[1] |= 0x40; -#endif /* __BIG_ENDIAN */ - } else if (sm->mode_tx->modulator_u8) - sm->dma.o16bit = 0; - if (sm->dma.i16bit == 2 || sm->dma.o16bit == 2) { - printk(KERN_INFO "%s: mode %s or %s unusable\n", sm_drvname, - sm->mode_rx->name, sm->mode_tx->name); - sm->mode_tx = NULL; - sm->mode_rx = NULL; - return -EINVAL; - } - return 0; - } - } - } - return -EINVAL; -} - -/* --------------------------------------------------------------------- */ - -static int wssfdx_ioctl(struct net_device *dev, struct sm_state *sm, struct ifreq *ifr, - struct hdlcdrv_ioctl *hi, int cmd) -{ - if (cmd != SIOCDEVPRIVATE) - return -ENOIOCTLCMD; - - if (hi->cmd == HDLCDRVCTL_MODEMPARMASK) - return HDLCDRV_PARMASK_IOBASE | HDLCDRV_PARMASK_IRQ | - HDLCDRV_PARMASK_DMA | HDLCDRV_PARMASK_DMA2 | - HDLCDRV_PARMASK_SERIOBASE | HDLCDRV_PARMASK_PARIOBASE | - HDLCDRV_PARMASK_MIDIIOBASE; - - return wss_ioctl(dev, sm, ifr, hi, cmd); -} - -/* --------------------------------------------------------------------- */ - -const struct hardware_info sm_hw_wssfdx = { - "wssfdx", sizeof(struct sc_state_wss), - wssfdx_open, wssfdx_close, wssfdx_ioctl, wssfdx_sethw -}; - -/* --------------------------------------------------------------------- */ - -#undef SCSTATE diff --git a/drivers/net/hamradio/soundmodem/smdma.h b/drivers/net/hamradio/soundmodem/smdma.h deleted file mode 100644 index 44e457a7a..000000000 --- a/drivers/net/hamradio/soundmodem/smdma.h +++ /dev/null @@ -1,217 +0,0 @@ -/*****************************************************************************/ - -/* - * smdma.h -- soundcard radio modem driver dma buffer routines. - * - * Copyright (C) 1996 Thomas Sailer (sailer@ife.ee.ethz.ch) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Please note that the GPL allows you to use the driver, NOT the radio. - * In order to use the radio, you need a license from the communications - * authority of your country. - * - */ - -#ifndef _SMDMA_H -#define _SMDMA_H - -/* ---------------------------------------------------------------------- */ - -#include "sm.h" - -/* ---------------------------------------------------------------------- */ - -#define DMA_MODE_AUTOINIT 0x10 -#define NUM_FRAGMENTS 4 - -/* - * NOTE: make sure that hdlcdrv_hdlcbuffer contains enough space - * for the modulator to fill the whole DMA buffer without underrun - * at the highest possible baud rate, otherwise the TX state machine will - * not work correctly. That is (9k6 FSK): HDLCDRV_HDLCBUFFER > 6*NUM_FRAGMENTS - */ - -/* --------------------------------------------------------------------- */ -/* - * ===================== DMA buffer management =========================== - */ - -/* - * returns the number of samples per fragment - */ -extern __inline__ unsigned int dma_setup(struct sm_state *sm, int send, unsigned int dmanr) -{ - if (send) { - disable_dma(dmanr); - clear_dma_ff(dmanr); - set_dma_mode(dmanr, DMA_MODE_WRITE | DMA_MODE_AUTOINIT); - set_dma_addr(dmanr, virt_to_bus(sm->dma.obuf)); - set_dma_count(dmanr, sm->dma.ofragsz * NUM_FRAGMENTS); - enable_dma(dmanr); - if (sm->dma.o16bit) - return sm->dma.ofragsz/2; - return sm->dma.ofragsz; - } else { - disable_dma(dmanr); - clear_dma_ff(dmanr); - set_dma_mode(dmanr, DMA_MODE_READ | DMA_MODE_AUTOINIT); - set_dma_addr(dmanr, virt_to_bus(sm->dma.ibuf)); - set_dma_count(dmanr, sm->dma.ifragsz * NUM_FRAGMENTS); - enable_dma(dmanr); - if (sm->dma.i16bit) - return sm->dma.ifragsz/2; - return sm->dma.ifragsz; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ unsigned int dma_ptr(struct sm_state *sm, int send, unsigned int dmanr, - unsigned int *curfrag) -{ - unsigned int dmaptr, sz, frg, offs; - - dmaptr = get_dma_residue(dmanr); - if (send) { - sz = sm->dma.ofragsz * NUM_FRAGMENTS; - if (dmaptr == 0 || dmaptr > sz) - dmaptr = sz; - dmaptr--; - frg = dmaptr / sm->dma.ofragsz; - offs = (dmaptr % sm->dma.ofragsz) + 1; - *curfrag = NUM_FRAGMENTS - 1 - frg; -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = offs; -#endif /* SM_DEBUG */ - if (sm->dma.o16bit) - return offs/2; - return offs; - } else { - sz = sm->dma.ifragsz * NUM_FRAGMENTS; - if (dmaptr == 0 || dmaptr > sz) - dmaptr = sz; - dmaptr--; - frg = dmaptr / sm->dma.ifragsz; - offs = (dmaptr % sm->dma.ifragsz) + 1; - *curfrag = NUM_FRAGMENTS - 1 - frg; -#ifdef SM_DEBUG - if (!sm->debug_vals.dma_residue || offs < sm->debug_vals.dma_residue) - sm->debug_vals.dma_residue = offs; -#endif /* SM_DEBUG */ - if (sm->dma.i16bit) - return offs/2; - return offs; - } -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ int dma_end_transmit(struct sm_state *sm, unsigned int curfrag) -{ - unsigned int diff = (NUM_FRAGMENTS + curfrag - sm->dma.ofragptr) % NUM_FRAGMENTS; - - sm->dma.ofragptr = curfrag; - if (sm->dma.ptt_cnt <= 0) { - sm->dma.ptt_cnt = 0; - return 0; - } - sm->dma.ptt_cnt -= diff; - if (sm->dma.ptt_cnt <= 0) { - sm->dma.ptt_cnt = 0; - return -1; - } - return 0; -} - -extern __inline__ void dma_transmit(struct sm_state *sm) -{ - void *p; - - while (sm->dma.ptt_cnt < NUM_FRAGMENTS && hdlcdrv_ptt(&sm->hdrv)) { - p = (unsigned char *)sm->dma.obuf + sm->dma.ofragsz * - ((sm->dma.ofragptr + sm->dma.ptt_cnt) % NUM_FRAGMENTS); - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_s16(sm, p, sm->dma.ofragsz/2)); - } else { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_u8(sm, p, sm->dma.ofragsz)); - } - sm->dma.ptt_cnt++; - } -} - -extern __inline__ void dma_init_transmit(struct sm_state *sm) -{ - sm->dma.ofragptr = 0; - sm->dma.ptt_cnt = 0; -} - -extern __inline__ void dma_start_transmit(struct sm_state *sm) -{ - sm->dma.ofragptr = 0; - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_s16(sm, sm->dma.obuf, sm->dma.ofragsz/2)); - } else { - time_exec(sm->debug_vals.mod_cyc, - sm->mode_tx->modulator_u8(sm, sm->dma.obuf, sm->dma.ofragsz)); - } - sm->dma.ptt_cnt = 1; -} - -extern __inline__ void dma_clear_transmit(struct sm_state *sm) -{ - sm->dma.ptt_cnt = 0; - memset(sm->dma.obuf, (sm->dma.o16bit) ? 0 : 0x80, sm->dma.ofragsz * NUM_FRAGMENTS); -} - -/* --------------------------------------------------------------------- */ - -extern __inline__ void dma_receive(struct sm_state *sm, unsigned int curfrag) -{ - void *p; - - while (sm->dma.ifragptr != curfrag) { - if (sm->dma.ifragptr) - p = (unsigned char *)sm->dma.ibuf + - sm->dma.ifragsz * sm->dma.ifragptr; - else { - p = (unsigned char *)sm->dma.ibuf + NUM_FRAGMENTS * sm->dma.ifragsz; - memcpy(p, sm->dma.ibuf, sm->dma.ifragsz); - } - if (sm->dma.o16bit) { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator_s16(sm, p, sm->dma.ifragsz/2)); - } else { - time_exec(sm->debug_vals.demod_cyc, - sm->mode_rx->demodulator_u8(sm, p, sm->dma.ifragsz)); - } - sm->dma.ifragptr = (sm->dma.ifragptr + 1) % NUM_FRAGMENTS; - } -} - -extern __inline__ void dma_init_receive(struct sm_state *sm) -{ - sm->dma.ifragptr = 0; -} - -/* --------------------------------------------------------------------- */ -#endif /* _SMDMA_H */ - - - diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 836414a53..97f12491d 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -1,6 +1,5 @@ -/*****************************************************************************/ - -/* +/***************************************************************************** + * * yam.c -- YAM radio modem driver. * * Copyright (C) 1998 Frederic Rible F1OAT (frible@teaser.fr) @@ -26,22 +25,25 @@ * * * History: - * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3 - * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration - * 0.2 F6FBB 08.06.98 Added delay after FPGA programming - * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2 - * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistance - * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics - * 0.6 F6FBB 25.08.98 Added 1200Bds format - * 0.7 F6FBB 12.09.98 Added to the kernel configuration - * 0.8 F6FBB 14.10.98 Fixed slottime/persistance timing bug - */ - -/*****************************************************************************/ + * 0.0 F1OAT 06.06.98 Begin of work with baycom.c source code V 0.3 + * 0.1 F1OAT 07.06.98 Add timer polling routine for channel arbitration + * 0.2 F6FBB 08.06.98 Added delay after FPGA programming + * 0.3 F6FBB 29.07.98 Delayed PTT implementation for dupmode=2 + * 0.4 F6FBB 30.07.98 Added TxTail, Slottime and Persistance + * 0.5 F6FBB 01.08.98 Shared IRQs, /proc/net and network statistics + * 0.6 F6FBB 25.08.98 Added 1200Bds format + * 0.7 F6FBB 12.09.98 Added to the kernel configuration + * 0.8 F6FBB 14.10.98 Fixed slottime/persistance timing bug + * 0.9 DG1KJD 10.03.00 Fixed media access and converted to new DDI + * + *****************************************************************************/ #include <linux/config.h> -#include <linux/module.h> +#include <linux/version.h> #include <linux/types.h> +#include <linux/module.h> +#include <linux/init.h> +#include <asm/uaccess.h> #include <linux/net.h> #include <linux/in.h> #include <linux/if.h> @@ -52,28 +54,17 @@ #include <asm/system.h> #include <linux/interrupt.h> #include <linux/ioport.h> - #include <linux/netdevice.h> #include <linux/if_arp.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) -/* prototypes for ax25_encapsulate and ax25_rebuild_header */ #include <net/ax25.h> -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - -/* make genksyms happy */ +#include <net/ax25dev.h> #include <linux/ip.h> #include <linux/udp.h> #include <linux/tcp.h> - #include <linux/kernel.h> #include <linux/proc_fs.h> - -#include <linux/version.h> -#include <asm/uaccess.h> -#include <linux/init.h> - #include <linux/yam.h> #include "yam9600.h" #include "yam1200.h" @@ -81,71 +72,53 @@ /* --------------------------------------------------------------------- */ static const char yam_drvname[] = "yam"; -static const char yam_drvinfo[] __initdata = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n"; +static const char yam_drvinfo[] = KERN_INFO "YAM driver version 0.8 by F1OAT/F6FBB\n"; /* --------------------------------------------------------------------- */ #define YAM_9600 1 #define YAM_1200 2 - -#define NR_PORTS 4 +#define NR_PORTS 4 #define YAM_MAGIC 0xF10A7654 /* Transmitter states */ - -#define TX_OFF 0 +#define TX_OFF 0 #define TX_HEAD 1 -#define TX_DATA 2 -#define TX_CRC1 3 -#define TX_CRC2 4 -#define TX_TAIL 5 +#define TX_DATA 2 +#define TX_CRC1 3 +#define TX_CRC2 4 +#define TX_TAIL 5 #define YAM_MAX_FRAME 1024 -#define DEFAULT_BITRATE 9600 /* bps */ -#define DEFAULT_HOLDD 10 /* sec */ -#define DEFAULT_TXD 300 /* ms */ -#define DEFAULT_TXTAIL 10 /* ms */ -#define DEFAULT_SLOT 100 /* ms */ -#define DEFAULT_PERS 64 /* 0->255 */ +#define DEFAULT_BITRATE 9600 /* bps */ +#define DEFAULT_HOLDD 10 /* sec */ +#define DEFAULT_TXD 300 /* ms */ +#define DEFAULT_TXTAIL 10 /* ms */ struct yam_port { int magic; - int bitrate; - int baudrate; int iobase; int irq; - int dupmode; + /* device stuff */ struct net_device dev; + struct ax25_dev ax25dev; /* Stats section */ - struct net_device_stats stats; - int nb_rxint; int nb_mdint; - /* Parameters section */ - - int txd; /* tx delay */ - int holdd; /* duplex ptt delay */ - int txtail; /* txtail delay */ - int slot; /* slottime */ - int pers; /* persistence */ - /* Tx section */ - int tx_state; int tx_count; - int slotcnt; unsigned char tx_buf[YAM_MAX_FRAME]; int tx_len; int tx_crcl, tx_crch; struct sk_buff_head send_queue; /* Packets awaiting transmission */ /* Rx section */ - int dcd; unsigned char rx_buf[YAM_MAX_FRAME]; int rx_len; @@ -158,49 +131,65 @@ struct yam_mcs { struct yam_mcs *next; }; +enum uart { + c_uart_unknown, c_uart_8250, + c_uart_16450, c_uart_16550, c_uart_16550A +}; + +static const char *uart_str[] = +{"unknown", "8250", "16450", "16550", "16550A"}; + static struct yam_port yam_ports[NR_PORTS]; +static struct yam_mcs *yam_data = NULL; +static unsigned irqs[16]; +static struct timer_list yam_timer; -static struct yam_mcs *yam_data; +#ifdef CONFIG_PROC_FS +static int yam_net_get_info(char *buffer, char **start, off_t offset, int length); -static char ax25_bcast[7] = -{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; -static char ax25_test[7] = -{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; +#define yam_net_procfs_init() proc_net_create("yam", 0, yam_net_get_info); +#define yam_net_procfs_remove() proc_net_remove("yam"); +#else +#define yam_net_procfs_init() +#define yam_net_procfs_remove() +#endif -static struct timer_list yam_timer; +static unsigned int yam_report_dcd(struct net_device *dev); +static unsigned int yam_report_ptt(struct net_device *dev); +static void yam_param_notify(struct net_device *dev, int valueno, int old, int new); /* --------------------------------------------------------------------- */ -#define RBR(iobase) (iobase+0) -#define THR(iobase) (iobase+0) -#define IER(iobase) (iobase+1) -#define IIR(iobase) (iobase+2) -#define FCR(iobase) (iobase+2) -#define LCR(iobase) (iobase+3) -#define MCR(iobase) (iobase+4) -#define LSR(iobase) (iobase+5) -#define MSR(iobase) (iobase+6) -#define SCR(iobase) (iobase+7) -#define DLL(iobase) (iobase+0) -#define DLM(iobase) (iobase+1) - -#define YAM_EXTENT 8 +#define RBR(iobase) (iobase+0) +#define THR(iobase) (iobase+0) +#define IER(iobase) (iobase+1) +#define IIR(iobase) (iobase+2) +#define FCR(iobase) (iobase+2) +#define LCR(iobase) (iobase+3) +#define MCR(iobase) (iobase+4) +#define LSR(iobase) (iobase+5) +#define MSR(iobase) (iobase+6) +#define SCR(iobase) (iobase+7) +#define DLL(iobase) (iobase+0) +#define DLM(iobase) (iobase+1) + +#define YAM_EXTENT 8 /* Interrupt Identification Register Bit Masks */ -#define IIR_NOPEND 1 -#define IIR_MSR 0 -#define IIR_TX 2 -#define IIR_RX 4 -#define IIR_LSR 6 +#define IIR_NOPEND 1 +#define IIR_MSR 0 +#define IIR_TX 2 +#define IIR_RX 4 +#define IIR_LSR 6 #define IIR_TIMEOUT 12 /* Fifo mode only */ #define IIR_MASK 0x0F /* Interrupt Enable Register Bit Masks */ -#define IER_RX 1 /* enable rx interrupt */ -#define IER_TX 2 /* enable tx interrupt */ -#define IER_LSR 4 /* enable line status interrupts */ -#define IER_MSR 8 /* enable modem status interrupts */ +#define IER_RX 1 /* enable rx interrupt */ +#define IER_TX 2 /* enable tx interrupt */ +#define IER_LSR 4 /* enable line status interrupts */ +#define IER_MSR 8 /* enable modem status interrupts */ /* Modem Control Register Bit Masks */ #define MCR_DTR 0x01 /* DTR output */ @@ -254,6 +243,8 @@ static struct timer_list yam_timer; #define ENABLE_TXINT IER_MSR /* enable uart ms interrupt during tx */ #define ENABLE_RTXINT (IER_RX|IER_MSR) /* full duplex operations */ +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) /************************************************************************* * CRC Tables @@ -298,6 +289,8 @@ static const unsigned char chktabh[256] = 0xf7, 0xe6, 0xd4, 0xc5, 0xb1, 0xa0, 0x92, 0x83, 0x7b, 0x6a, 0x58, 0x49, 0x3d, 0x2c, 0x1e, 0x0f}; +/* --------------------------------------------------------------------- */ + /************************************************************************* * FPGA functions ************************************************************************/ @@ -308,10 +301,11 @@ static void delay(int ms) while (jiffies < timeout); } +/* --------------------------------------------------------------------- */ + /* * reset FPGA */ - static void fpga_reset(int iobase) { outb(0, IER(iobase)); @@ -330,10 +324,11 @@ static void fpga_reset(int iobase) delay(100); } +/* --------------------------------------------------------------------- */ + /* * send one byte to FPGA */ - static int fpga_write(int iobase, unsigned char wrd) { unsigned char bit; @@ -349,10 +344,26 @@ static int fpga_write(int iobase, unsigned char wrd) if (jiffies > timeout) return -1; } - return 0; } +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +static void free_mcs(void) +{ + struct yam_mcs *p; + + while (yam_data) { + p = yam_data; + yam_data = yam_data->next; + kfree(p); + } +} +#endif + +/* --------------------------------------------------------------------- */ + static unsigned char *add_mcs(unsigned char *bits, int bitrate) { struct yam_mcs *p; @@ -368,7 +379,8 @@ static unsigned char *add_mcs(unsigned char *bits, int bitrate) } /* Allocate a new mcs */ - if ((p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL)) == NULL) { + p = kmalloc(sizeof(struct yam_mcs), GFP_KERNEL); + if (p == NULL) { printk(KERN_WARNING "YAM: no memory to allocate mcs\n"); return NULL; } @@ -380,6 +392,8 @@ static unsigned char *add_mcs(unsigned char *bits, int bitrate) return p->bits; } +/* --------------------------------------------------------------------- */ + static unsigned char *get_mcs(int bitrate) { struct yam_mcs *p; @@ -400,11 +414,12 @@ static unsigned char *get_mcs(int bitrate) } } +/* --------------------------------------------------------------------- */ + /* * download bitstream to FPGA * data is contained in bits[] array in yam1200.h resp. yam9600.h */ - static int fpga_download(int iobase, int bitrate) { int i, rc; @@ -417,7 +432,7 @@ static int fpga_download(int iobase, int bitrate) fpga_reset(iobase); for (i = 0; i < YAM_FPGA_SIZE; i++) { if (fpga_write(iobase, pbits[i])) { - printk(KERN_ERR "yam: error in write cycle\n"); + printk("yam: error in write cycle\n"); return -1; /* write... */ } } @@ -431,6 +446,7 @@ static int fpga_download(int iobase, int bitrate) return (rc & MSR_DSR) ? 0 : -1; } +/* --------------------------------------------------------------------- */ /************************************************************************ * Serial port init @@ -438,8 +454,7 @@ static int fpga_download(int iobase, int bitrate) static void yam_set_uart(struct net_device *dev) { - struct yam_port *yp = (struct yam_port *) dev->priv; - int divisor = 115200 / yp->baudrate; + int divisor = 115200 / (2 * ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE)); outb(0, IER(dev->base_addr)); outb(LCR_DLAB | LCR_BIT8, LCR(dev->base_addr)); @@ -459,17 +474,8 @@ static void yam_set_uart(struct net_device *dev) outb(ENABLE_RTXINT, IER(dev->base_addr)); } - /* --------------------------------------------------------------------- */ -enum uart { - c_uart_unknown, c_uart_8250, - c_uart_16450, c_uart_16550, c_uart_16550A -}; - -static const char *uart_str[] = -{"unknown", "8250", "16450", "16550", "16550A"}; - static enum uart yam_check_uart(unsigned int iobase) { unsigned char b1, b2, b3; @@ -501,27 +507,29 @@ static enum uart yam_check_uart(unsigned int iobase) return u; } +/* --------------------------------------------------------------------- */ + /****************************************************************************** * Rx Section ******************************************************************************/ -static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp) + +static void inline yam_rx_flag(struct net_device *dev, struct yam_port *yp) { if (yp->dcd && yp->rx_len >= 3 && yp->rx_len < YAM_MAX_FRAME) { - int pkt_len = yp->rx_len - 2 + 1; /* -CRC + kiss */ + int pkt_len = yp->rx_len - 2; /* -CRC */ struct sk_buff *skb; if ((yp->rx_crch & yp->rx_crcl) != 0xFF) { /* Bad crc */ } else { if (!(skb = dev_alloc_skb(pkt_len))) { - printk(KERN_WARNING "%s: memory squeeze, dropping packet\n", dev->name); + printk("%s: memory squeeze, dropping packet\n", dev->name); ++yp->stats.rx_dropped; } else { unsigned char *cp; skb->dev = dev; cp = skb_put(skb, pkt_len); - *cp++ = 0; /* KISS kludge */ - memcpy(cp, yp->rx_buf, pkt_len - 1); + memcpy(cp, yp->rx_buf, pkt_len); skb->protocol = htons(ETH_P_AX25); skb->mac.raw = skb->data; netif_rx(skb); @@ -534,7 +542,9 @@ static inline void yam_rx_flag(struct net_device *dev, struct yam_port *yp) yp->rx_crch = 0xf3; } -static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb) +/* --------------------------------------------------------------------- */ + +static void inline yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsigned char rxb) { if (yp->rx_len < YAM_MAX_FRAME) { unsigned char c = yp->rx_crcl; @@ -544,6 +554,8 @@ static inline void yam_rx_byte(struct net_device *dev, struct yam_port *yp, unsi } } +/* --------------------------------------------------------------------- */ + /******************************************************************************** * TX Section ********************************************************************************/ @@ -553,38 +565,48 @@ static void ptt_on(struct net_device *dev) outb(PTT_ON, MCR(dev->base_addr)); } +/* --------------------------------------------------------------------- */ + static void ptt_off(struct net_device *dev) { outb(PTT_OFF, MCR(dev->base_addr)); } +/* --------------------------------------------------------------------- */ + static int yam_send_packet(struct sk_buff *skb, struct net_device *dev) { struct yam_port *yp = dev->priv; + if (skb == NULL) { + return 0; + } skb_queue_tail(&yp->send_queue, skb); dev->trans_start = jiffies; return 0; } +/* --------------------------------------------------------------------- */ + static void yam_start_tx(struct net_device *dev, struct yam_port *yp) { - if ((yp->tx_state == TX_TAIL) || (yp->txd == 0)) + int bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + int txd = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY); + + if ((yp->tx_state == TX_TAIL) || (txd == 0)) yp->tx_count = 1; else - yp->tx_count = (yp->bitrate * yp->txd) / 8000; + yp->tx_count = (bitrate * txd) / 8000; yp->tx_state = TX_HEAD; ptt_on(dev); } -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} +/* --------------------------------------------------------------------- */ +/* + * note: this used to be a real channel arbiter, but this is now done in + * the DDI layer of AX.25 protocol engine. + */ static void yam_arbitrate(struct net_device *dev) { struct yam_port *yp = dev->priv; @@ -593,31 +615,13 @@ static void yam_arbitrate(struct net_device *dev) || yp->tx_state != TX_OFF || skb_queue_empty(&yp->send_queue)) { return; } - /* tx_state is TX_OFF and there is data to send */ - - if (yp->dupmode) { - /* Full duplex mode, don't wait */ - yam_start_tx(dev, yp); - return; - } - if (yp->dcd) { - /* DCD on, wait slotime ... */ - yp->slotcnt = yp->slot / 10; - return; - } - /* Is slottime passed ? */ - if ((--yp->slotcnt) > 0) - return; - - yp->slotcnt = yp->slot / 10; - - /* is random > persist ? */ - if ((random_num() % 256) > yp->pers) - return; + /* tx_state is TX_OFF and there is data to send */ yam_start_tx(dev, yp); } +/* --------------------------------------------------------------------- */ + static void yam_dotimer(unsigned long dummy) { int i; @@ -631,10 +635,14 @@ static void yam_dotimer(unsigned long dummy) add_timer(&yam_timer); } +/* --------------------------------------------------------------------- */ + static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) { struct sk_buff *skb; unsigned char b, temp; + int bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + int txtail = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL); switch (yp->tx_state) { case TX_OFF: @@ -647,17 +655,12 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) break; } yp->tx_state = TX_DATA; - if (skb->data[0] != 0) { -/* do_kiss_params(s, skb->data, skb->len); */ - dev_kfree_skb(skb); - break; - } - yp->tx_len = skb->len - 1; /* strip KISS byte */ + yp->tx_len = skb->len; if (yp->tx_len >= YAM_MAX_FRAME || yp->tx_len < 2) { dev_kfree_skb(skb); break; } - memcpy(yp->tx_buf, skb->data + 1, yp->tx_len); + memcpy(yp->tx_buf, skb->data, yp->tx_len); dev_kfree_skb(skb); yp->tx_count = 0; yp->tx_crcl = 0x21; @@ -684,9 +687,7 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) case TX_CRC2: outb(chktabh[yp->tx_crch] ^ 0xFF, THR(dev->base_addr)); if (skb_queue_empty(&yp->send_queue)) { - yp->tx_count = (yp->bitrate * yp->txtail) / 8000; - if (yp->dupmode == 2) - yp->tx_count += (yp->bitrate * yp->holdd) / 8; + yp->tx_count = (bitrate * txtail) / 8000; if (yp->tx_count == 0) yp->tx_count = 1; yp->tx_state = TX_TAIL; @@ -705,6 +706,8 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) } } +/* --------------------------------------------------------------------- */ + /*********************************************************************************** * ISR routine ************************************************************************************/ @@ -714,7 +717,7 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs) struct net_device *dev; struct yam_port *yp; unsigned char iir; - int counter = 100; + int irq_work = 100; int i; sti(); @@ -736,8 +739,8 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs) yp->dcd = (msr & RX_DCD) ? 1 : 0; - if (--counter <= 0) { - printk(KERN_ERR "%s: too many irq iir=%d\n", dev->name, iir); + if (--irq_work <= 0) { + printk("%s: too many irq iir=%d\n", dev->name, iir); return; } if (msr & TX_RDY) { @@ -756,6 +759,8 @@ static void yam_interrupt(int irq, void *dev_id, struct pt_regs *regs) } } +/* --------------------------------------------------------------------- */ + static int yam_net_get_info(char *buffer, char **start, off_t offset, int length) { int len = 0; @@ -766,21 +771,31 @@ static int yam_net_get_info(char *buffer, char **start, off_t offset, int length cli(); for (i = 0; i < NR_PORTS; i++) { + struct net_device *dev; + if (yam_ports[i].iobase == 0 || yam_ports[i].irq == 0) continue; - len += sprintf(buffer + len, "Device yam%d\n", i); + dev = &yam_ports[i].dev; + + len += sprintf(buffer + len, "Device %d\n", i); len += sprintf(buffer + len, " Up %d\n", netif_running(&yam_ports[i].dev)); - len += sprintf(buffer + len, " Speed %u\n", yam_ports[i].bitrate); + len += sprintf(buffer + len, " Speed %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE)); len += sprintf(buffer + len, " IoBase 0x%x\n", yam_ports[i].iobase); - len += sprintf(buffer + len, " BaudRate %u\n", yam_ports[i].baudrate); + len += sprintf(buffer + len, " BaudRate %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE)*2); len += sprintf(buffer + len, " IRQ %u\n", yam_ports[i].irq); len += sprintf(buffer + len, " TxState %u\n", yam_ports[i].tx_state); - len += sprintf(buffer + len, " Duplex %u\n", yam_ports[i].dupmode); - len += sprintf(buffer + len, " HoldDly %u\n", yam_ports[i].holdd); - len += sprintf(buffer + len, " TxDelay %u\n", yam_ports[i].txd); - len += sprintf(buffer + len, " TxTail %u\n", yam_ports[i].txtail); - len += sprintf(buffer + len, " SlotTime %u\n", yam_ports[i].slot); - len += sprintf(buffer + len, " Persist %u\n", yam_ports[i].pers); + len += sprintf(buffer + len, " Duplex %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX)); + len += sprintf(buffer + len, " TxDelay %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY)); + len += sprintf(buffer + len, " TxTail %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)); + len += sprintf(buffer + len, " SlotTime %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME)); + len += sprintf(buffer + len, " Persist %u\n", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE)); len += sprintf(buffer + len, " TxFrames %lu\n", yam_ports[i].stats.tx_packets); len += sprintf(buffer + len, " RxFrames %lu\n", yam_ports[i].stats.rx_packets); len += sprintf(buffer + len, " TxInt %u\n", yam_ports[i].nb_mdint); @@ -805,13 +820,14 @@ static int yam_net_get_info(char *buffer, char **start, off_t offset, int length if (len > length) len = length; - return len; } + + /* --------------------------------------------------------------------- */ -static struct net_device_stats *yam_get_stats(struct net_device *dev) +static struct net_device_stats * yam_get_stats(struct net_device *dev) { struct yam_port *yp; @@ -836,39 +852,38 @@ static int yam_open(struct net_device *dev) struct yam_port *yp = (struct yam_port *) dev->priv; enum uart u; int i; + int bitrate; printk(KERN_INFO "Trying %s at iobase 0x%lx irq %u\n", dev->name, dev->base_addr, dev->irq); - if (!dev || !yp || !yp->bitrate) - return -ENXIO; + if (!dev) return -ENXIO; + bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + if (!yp || !bitrate) return -ENXIO; if (!dev->base_addr || dev->base_addr > 0x1000 - YAM_EXTENT || dev->irq < 2 || dev->irq > 15) { return -ENXIO; } if (check_region(dev->base_addr, YAM_EXTENT)) { - printk(KERN_ERR "%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); + printk("%s: cannot 0x%lx busy\n", dev->name, dev->base_addr); return -EACCES; } if ((u = yam_check_uart(dev->base_addr)) == c_uart_unknown) { - printk(KERN_ERR "%s: cannot find uart type\n", dev->name); + printk("%s: cannot find uart type\n", dev->name); return -EIO; } - if (fpga_download(dev->base_addr, yp->bitrate)) { - printk(KERN_ERR "%s: cannot init FPGA\n", dev->name); + if (fpga_download(dev->base_addr, bitrate)) { + printk("%s: cannot init FPGA\n", dev->name); return -EIO; } outb(0, IER(dev->base_addr)); if (request_irq(dev->irq, yam_interrupt, SA_INTERRUPT | SA_SHIRQ, dev->name, NULL)) { - printk(KERN_ERR "%s: irq %d busy\n", dev->name, dev->irq); + printk("%s: irq %d busy\n", dev->name, dev->irq); return -EBUSY; } request_region(dev->base_addr, YAM_EXTENT, dev->name); yam_set_uart(dev); - netif_start_queue(dev); - - yp->slotcnt = yp->slot / 10; /* Reset overruns for all ports - FPGA programming makes overruns */ for (i = 0; i < NR_PORTS; i++) { @@ -898,7 +913,9 @@ static int yam_close(struct net_device *dev) /* Remove IRQ handler if last */ free_irq(dev->irq, NULL); release_region(dev->base_addr, YAM_EXTENT); + netif_stop_queue(dev); + while ((skb = skb_dequeue(&yp->send_queue))) dev_kfree_skb(skb); @@ -963,9 +980,6 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EINVAL; /* Cannot change this parameter when up */ if ((yi.cfg.mask & YAM_BITRATE) && netif_running(dev)) return -EINVAL; /* Cannot change this parameter when up */ - if ((yi.cfg.mask & YAM_BAUDRATE) && netif_running(dev)) - return -EINVAL; /* Cannot change this parameter when up */ - if (yi.cfg.mask & YAM_IOBASE) { yp->iobase = yi.cfg.iobase; dev->base_addr = yi.cfg.iobase; @@ -979,43 +993,23 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (yi.cfg.mask & YAM_BITRATE) { if (yi.cfg.bitrate > YAM_MAXBITRATE) return -EINVAL; - yp->bitrate = yi.cfg.bitrate; - } - if (yi.cfg.mask & YAM_BAUDRATE) { - if (yi.cfg.baudrate > YAM_MAXBAUDRATE) - return -EINVAL; - yp->baudrate = yi.cfg.baudrate; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, yi.cfg.bitrate); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, yi.cfg.bitrate); } if (yi.cfg.mask & YAM_MODE) { if (yi.cfg.mode > YAM_MAXMODE) return -EINVAL; - yp->dupmode = yi.cfg.mode; - } - if (yi.cfg.mask & YAM_HOLDDLY) { - if (yi.cfg.holddly > YAM_MAXHOLDDLY) - return -EINVAL; - yp->holdd = yi.cfg.holddly; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, yi.cfg.mode); } if (yi.cfg.mask & YAM_TXDELAY) { if (yi.cfg.txdelay > YAM_MAXTXDELAY) return -EINVAL; - yp->txd = yi.cfg.txdelay; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, yi.cfg.txdelay); } if (yi.cfg.mask & YAM_TXTAIL) { if (yi.cfg.txtail > YAM_MAXTXTAIL) return -EINVAL; - yp->txtail = yi.cfg.txtail; - } - if (yi.cfg.mask & YAM_PERSIST) { - if (yi.cfg.persist > YAM_MAXPERSIST) - return -EINVAL; - yp->pers = yi.cfg.persist; - } - if (yi.cfg.mask & YAM_SLOTTIME) { - if (yi.cfg.slottime > YAM_MAXSLOTTIME) - return -EINVAL; - yp->slot = yi.cfg.slottime; - yp->slotcnt = yp->slot / 10; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, yi.cfg.txtail); } break; @@ -1023,14 +1017,10 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) yi.cfg.mask = 0xffffffff; yi.cfg.iobase = yp->iobase; yi.cfg.irq = yp->irq; - yi.cfg.bitrate = yp->bitrate; - yi.cfg.baudrate = yp->baudrate; - yi.cfg.mode = yp->dupmode; - yi.cfg.txdelay = yp->txd; - yi.cfg.holddly = yp->holdd; - yi.cfg.txtail = yp->txtail; - yi.cfg.persist = yp->pers; - yi.cfg.slottime = yp->slot; + yi.cfg.bitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + yi.cfg.mode = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX); + yi.cfg.txdelay = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY); + yi.cfg.txtail = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL); if (copy_to_user(ifr->ifr_data, &yi, sizeof(struct yamdrv_ioctl_cfg))) return -EFAULT; break; @@ -1045,6 +1035,50 @@ static int yam_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* --------------------------------------------------------------------- */ +static unsigned int yam_report_dcd(struct net_device *dev) +{ + struct yam_port *yp = (struct yam_port *) dev->priv; + + if (!yp) return 0; /* paranoia */ + return yp->dcd; +} + +/* --------------------------------------------------------------------- */ + +static unsigned int yam_report_ptt(struct net_device *dev) +{ + struct yam_port *yp = (struct yam_port *) dev->priv; + + return (yp->tx_state != TX_OFF); +} + +/* --------------------------------------------------------------------- */ + +static void yam_param_notify(struct net_device *dev, int valueno, int old, int new) +{ + switch (valueno) { + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_TXBITRATE: + if (netif_running(dev)) break; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, new); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, new); + return; + case AX25_VALUES_MEDIA_DUPLEX: + if (netif_running(dev)) break; + return; + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + default: + /* just let them do it. */ + return; + } + /* reject */ + ax25_dev_set_value(dev, valueno, old); + return; +} + +/* --------------------------------------------------------------------- */ + static int yam_set_mac_address(struct net_device *dev, void *addr) { struct sockaddr *sa = (struct sockaddr *) addr; @@ -1065,62 +1099,58 @@ static int yam_probe(struct net_device *dev) yp = (struct yam_port *) dev->priv; - dev->open = yam_open; - dev->stop = yam_close; - dev->do_ioctl = yam_ioctl; - dev->hard_start_xmit = yam_send_packet; - dev->get_stats = yam_get_stats; - + dev->open = yam_open; + dev->stop = yam_close; + dev->do_ioctl = yam_ioctl; + dev->hard_start_xmit = yam_send_packet; + dev->get_stats = yam_get_stats; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->set_mac_address = yam_set_mac_address; + dev->type = ARPHRD_AX25; /* AF_AX25 device */ + dev->hard_header_len = AX25_MAX_HEADER_LEN; /* We do digipeaters now */ + dev->mtu = 256; /* AX25 is the default */ + dev->addr_len = 7; /* sizeof an ax.25 address */ dev_init_buffers(dev); skb_queue_head_init(&yp->send_queue); -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - dev->hard_header = ax25_encapsulate; - dev->rebuild_header = ax25_rebuild_header; -#else /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - dev->hard_header = NULL; - dev->rebuild_header = NULL; -#endif /* CONFIG_AX25 || CONFIG_AX25_MODULE */ - - dev->set_mac_address = yam_set_mac_address; - - dev->type = ARPHRD_AX25; /* AF_AX25 device */ - dev->hard_header_len = 73; /* We do digipeaters now */ - dev->mtu = 256; /* AX25 is the default */ - dev->addr_len = 7; /* sizeof an ax.25 address */ - memcpy(dev->broadcast, ax25_bcast, 7); - memcpy(dev->dev_addr, ax25_test, 7); - /* New style flags */ - dev->flags = 0; + dev->flags = IFF_BROADCAST; + /* AX.25 downcalls */ + AX25_PTR(dev)->hw.dcd = yam_report_dcd; + AX25_PTR(dev)->hw.ptt = yam_report_ptt; + AX25_PTR(dev)->hw.parameter_change_notify = yam_param_notify; return 0; } /* --------------------------------------------------------------------- */ -static int __init yam_init_driver(void) +int __init yam_init(void) { struct net_device *dev; int i; printk(yam_drvinfo); + /* Clears the IRQ table */ + + memset(irqs, 0, sizeof(irqs)); + memset(yam_ports, 0, sizeof(yam_ports)); + for (i = 0; i < NR_PORTS; i++) { sprintf(yam_ports[i].dev.name, "yam%d", i); yam_ports[i].magic = YAM_MAGIC; - yam_ports[i].bitrate = DEFAULT_BITRATE; - yam_ports[i].baudrate = DEFAULT_BITRATE * 2; + dev = &yam_ports[i].dev; + AX25_PTR(dev)=&yam_ports[i].ax25dev; + memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev)); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, DEFAULT_BITRATE); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, DEFAULT_BITRATE); yam_ports[i].iobase = 0; yam_ports[i].irq = 0; - yam_ports[i].dupmode = 0; - yam_ports[i].holdd = DEFAULT_HOLDD; - yam_ports[i].txd = DEFAULT_TXD; - yam_ports[i].txtail = DEFAULT_TXTAIL; - yam_ports[i].slot = DEFAULT_SLOT; - yam_ports[i].pers = DEFAULT_PERS; - - dev = &yam_ports[i].dev; + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, DEFAULT_TXD); + ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, DEFAULT_TXTAIL); dev->priv = &yam_ports[i]; dev->base_addr = yam_ports[i].iobase; @@ -1141,15 +1171,14 @@ static int __init yam_init_driver(void) yam_timer.expires = jiffies + HZ / 100; add_timer(&yam_timer); - proc_net_create("yam", 0, yam_net_get_info); + yam_net_procfs_init(); return 0; } /* --------------------------------------------------------------------- */ -static void __exit yam_cleanup_driver(void) +void __exit yam_exit(void) { - struct yam_mcs *p; int i; del_timer(&yam_timer); @@ -1161,23 +1190,13 @@ static void __exit yam_cleanup_driver(void) yam_close(dev); unregister_netdev(dev); } - - while (yam_data) { - p = yam_data; - yam_data = yam_data->next; - kfree(p); - } - - proc_net_remove("yam"); + free_mcs(); + yam_net_procfs_remove(); } /* --------------------------------------------------------------------- */ MODULE_AUTHOR("Frederic Rible F1OAT frible@teaser.fr"); -MODULE_DESCRIPTION("Yam amateur radio modem driver"); - -module_init(yam_init_driver); -module_exit(yam_cleanup_driver); - -/* --------------------------------------------------------------------- */ - +MODULE_DESCRIPTION("YAM Amateur Radio modem driver"); +module_init(yam_init); +module_exit(yam_exit); diff --git a/include/asm-alpha/termios.h b/include/asm-alpha/termios.h index 374793646..b1bf59210 100644 --- a/include/asm-alpha/termios.h +++ b/include/asm-alpha/termios.h @@ -72,7 +72,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-arm/termios.h b/include/asm-arm/termios.h index 3da727e71..95a242b9a 100644 --- a/include/asm-arm/termios.h +++ b/include/asm-arm/termios.h @@ -55,7 +55,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-i386/termios.h b/include/asm-i386/termios.h index 70c1ecefd..d8c6f6a93 100644 --- a/include/asm-i386/termios.h +++ b/include/asm-i386/termios.h @@ -45,7 +45,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-m68k/termios.h b/include/asm-m68k/termios.h index 411018fdb..75b0d9432 100644 --- a/include/asm-m68k/termios.h +++ b/include/asm-m68k/termios.h @@ -55,7 +55,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-mips/termios.h b/include/asm-mips/termios.h index abc5deb3c..a37f05970 100644 --- a/include/asm-mips/termios.h +++ b/include/asm-mips/termios.h @@ -90,7 +90,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved fo Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-ppc/termios.h b/include/asm-ppc/termios.h index 942c6e4b6..c481f19e4 100644 --- a/include/asm-ppc/termios.h +++ b/include/asm-ppc/termios.h @@ -180,7 +180,7 @@ struct termio { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 /* X.25 async */ #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-sparc/termios.h b/include/asm-sparc/termios.h index a05f65856..2ff48aaa6 100644 --- a/include/asm-sparc/termios.h +++ b/include/asm-sparc/termios.h @@ -60,7 +60,7 @@ struct winsize { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/asm-sparc64/termios.h b/include/asm-sparc64/termios.h index 082b57ac7..55d542645 100644 --- a/include/asm-sparc64/termios.h +++ b/include/asm-sparc64/termios.h @@ -60,7 +60,7 @@ struct winsize { #define N_MOUSE 2 #define N_PPP 3 #define N_STRIP 4 -#define N_AX25 5 +#define N_KISS 5 #define N_X25 6 #define N_6PACK 7 #define N_MASC 8 /* Reserved for Mobitex module <kaz@cafe.net> */ diff --git a/include/linux/ax25.h b/include/linux/ax25.h index 9191445bb..f316c9960 100644 --- a/include/linux/ax25.h +++ b/include/linux/ax25.h @@ -6,8 +6,10 @@ #ifndef AX25_KERNEL_H #define AX25_KERNEL_H +#define AX25_NEW_DEVIF 1 + #define AX25_MTU 256 -#define AX25_MAX_DIGIS 8 +#define AX25_MAX_DIGIS 8 #define AX25_WINDOW 1 #define AX25_T1 2 @@ -35,6 +37,9 @@ #define SIOCAX25DEVCTL (SIOCPROTOPRIVATE+12) #define SIOCAX25GETINFO (SIOCPROTOPRIVATE+13) +#define AX25_SET_DEV_BITRATE 0 +#define AX25_SET_DEV_MODE 1 + #define AX25_SET_RT_IPMODE 2 #define AX25_NOUID_DEFAULT 0 @@ -58,6 +63,59 @@ struct full_sockaddr_ax25 { ax25_address fsa_digipeater[AX25_MAX_DIGIS]; }; +#define fsax25_family fsa_ax25.sax25_family +#define fsax25_call fsa_ax25.sax25_call +#define fsax25_ndigis fsa_ax25.sax25_ndigis + +typedef struct { + ax25_address addr; + ax25_address digipeater[AX25_MAX_DIGIS];; + unsigned char dcount; +} ax25_path_t; + +typedef struct { + ax25_address dest; + ax25_address src; + ax25_address digipeater[AX25_MAX_DIGIS]; + unsigned char dcount; + char lastrepeat; +} ax25_addr_t; + +struct ax25_rtmsg { + char port_name[32]; + unsigned int mode; + ax25_addr_t addr; +}; + +struct ax25_armsg { + char port_name[32]; + unsigned int ip_addr; + ax25_address ax_addr; +}; + +struct ax25_pathmsg { + char port_name[32]; + unsigned int mode; + ax25_path_t path; +}; + +struct ax25_nlmsg { + +#define AX25_MSG_RTINFO 0 +#define AX25_MSG_ARINFO 1 +#define AX25_MSG_SETRT 2 +#define AX25_MSG_DELRT 3 +#define AX25_MSG_OPTRT 4 + unsigned int msg_type; + + union { + unsigned char raw[128-sizeof(unsigned int)]; + struct ax25_rtmsg rtmsg; + struct ax25_armsg armsg; + struct ax25_pathmsg pathmsg; + } msg; +}; + struct ax25_routes_struct { ax25_address port_addr; ax25_address dest_addr; @@ -73,13 +131,11 @@ struct ax25_route_opt_struct { }; struct ax25_ctl_struct { - ax25_address port_addr; - ax25_address source_addr; - ax25_address dest_addr; - unsigned int cmd; - unsigned long arg; - unsigned char digi_count; - ax25_address digi_addr[AX25_MAX_DIGIS]; + ax25_address port_addr; + ax25_address source_addr; + ax25_address dest_addr; + unsigned int cmd; + unsigned long arg; }; /* this will go away. Please do not export to user land */ diff --git a/include/linux/ax25_userdev.h b/include/linux/ax25_userdev.h new file mode 100644 index 000000000..cc58a3e5e --- /dev/null +++ b/include/linux/ax25_userdev.h @@ -0,0 +1,120 @@ +/* + * AX.25 usermode device driver. + * Copyright (C) 1999-2000 Maxim Krasnyansky <max_mk@yahoo.com> + * Copyright 2001, by Joerg Reuter <jreuter@yaina.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __IF_AX25_UDEV_H +#define __IF_AX25_UDEV_H + +#define AX25_UDEV_VER "0.1" +#define AX25_UDEV_DEBUG + +/* Uncomment to enable debugging */ +/* #define AX25_UDEV_DEBUG 1 */ + +#ifdef __KERNEL__ + +#ifdef AX25_UDEV_DEBUG +#define DBG if(ax25_udev->debug)printk +#define DBG1 if(debug==2)printk +#else +#define DBG( a... ) +#define DBG1( a... ) +#endif + +struct ax25_udev_struct { + char name[IFNAMSIZ]; + unsigned long flags; + + struct fasync_struct *fasync; + wait_queue_head_t read_wait; + + struct net_device dev; + struct sk_buff_head txq; + struct net_device_stats stats; + struct ax25_dev ax25_dev; + + struct { + int duplex; + int arbitration; + } capabilities; + + struct { + int dcd:1; + int cts:1; + int rts:1; + int ptt:1; + } status; + +#ifdef AX25_UDEV_DEBUG + int debug; +#endif +}; + +#endif /* __KERNEL__ */ + +/* Number of devices */ +#define AX25_UDEV_MAX_DEV 255 + +/* TX queue size */ +#define AX25_UDEV_TXQ_SIZE 10 + +/* Max frame size */ +#define AX25_UDEV_MAX_FRAME 4096 + +/* AX25_UDEV device flags */ +#define AX25_UDEV_TYPE_MASK 0x000f +#define AX25_UDEV_FASYNC 0x0010 +#define AX25_UDEV_IFF_SET 0x1000 + +/* AX25_UDEV IFF ioctl */ +#define AX25_UDEV_SETIFF _IOW(0x25, 0, void *) +#define AX25_UDEV_SETDEBUG _IOW(0x25, 1, int) + +/* Packet layout: + * + * (int) packet_type + * (int) data + * ... + * + * Exception for AX25_UDEV_DATA: + * (int) 0 + * (char) octet + * ... + * + */ + +typedef enum ax25_user_dev_commands_e { + AX25_UDEV_DATA, + AX25_UDEV_CAPABILITIES, + AX25_UDEV_DCD_STATUS, + AX25_UDEV_CTS_STATUS, + AX25_UDEV_PTT_STATUS, + AX25_UDEV_SET_MAC_VALUE, + AX25_UDEV_REQUEST_RTS +} ax25_user_dev_commands_t; + +typedef enum ax25_user_dev_capabilities_e { + AX25_UDEV_CAP_HALF_DUPLEX, + AX25_UDEV_CAP_FULL_DUPLEX, + AX25_UDEV_CAP_ADVANCED_ARBITRATION, + AX25_UDEV_CAP_OWN_ARBITRATION, + AX25_UDEV_CAP_SIMPLE_ARBITRATION +} ax25_user_dev_capabilities_t; + +typedef int ax25_udev_pt_t; +typedef int ax25_udev_val_t; + +#endif /* __IF_AX25_UDEV_H */ diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 34c8dcb04..1fd9db550 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -33,6 +33,7 @@ #define SGI_USEMACLONE 151 #define TUN_MINOR 200 +#define AX25_UDEV_MINOR 210 extern int misc_init(void); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 011caf23d..686ebda28 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -317,6 +317,7 @@ struct net_device void *dn_ptr; /* DECnet specific data */ void *ip6_ptr; /* IPv6 specific data */ void *ec_ptr; /* Econet specific data */ + void *ax25_ptr; /* AX.25 specific data */ struct Qdisc *qdisc; struct Qdisc *qdisc_sleeping; diff --git a/include/linux/netlink.h b/include/linux/netlink.h index 2afb52e98..6eaaa86bf 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -5,6 +5,7 @@ #define NETLINK_SKIP 1 /* Reserved for ENskip */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_AX25 4 /* AX25 routing daemon hook */ #define NETLINK_ARPD 8 #define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */ #define NETLINK_IP6_FW 13 diff --git a/include/linux/scc.h b/include/linux/scc.h index a896704e6..1ec441746 100644 --- a/include/linux/scc.h +++ b/include/linux/scc.h @@ -8,7 +8,7 @@ /* selection of hardware types */ #define PA0HZP 0x00 /* hardware type for PA0HZP SCC card and compatible */ -#define EAGLE 0x01 /* hardware type for EAGLE card */ +#define EAGLE 0x01 /* hardware type for EAGLE card */ #define PC100 0x02 /* hardware type for PC100 card */ #define PRIMUS 0x04 /* hardware type for PRIMUS-PC (DG9BL) card */ #define DRSI 0x08 /* hardware type for DRSI PC*Packet card */ @@ -22,87 +22,45 @@ enum SCC_ioctl_cmds { SIOCSCCINI, SIOCSCCCHANINI, SIOCSCCSMEM, - SIOCSCCGKISS, - SIOCSCCSKISS, + SIOCSCCGMODEM, + SIOCSCCSMODEM, SIOCSCCGSTAT, SIOCSCCCAL }; -/* Device parameter control (from WAMPES) */ - -enum L1_params { - PARAM_DATA, - PARAM_TXDELAY, - PARAM_PERSIST, - PARAM_SLOTTIME, - PARAM_TXTAIL, - PARAM_FULLDUP, - PARAM_SOFTDCD, /* was: PARAM_HW */ - PARAM_MUTE, /* ??? */ - PARAM_DTR, - PARAM_RTS, - PARAM_SPEED, - PARAM_ENDDELAY, /* ??? */ - PARAM_GROUP, - PARAM_IDLE, - PARAM_MIN, - PARAM_MAXKEY, - PARAM_WAIT, - PARAM_MAXDEFER, - PARAM_TX, - PARAM_HWEVENT = 31, - PARAM_RETURN = 255 /* reset kiss mode */ -}; - -/* fulldup parameter */ +/* magic number */ -enum FULLDUP_modes { - KISS_DUPLEX_HALF, /* normal CSMA operation */ - KISS_DUPLEX_FULL, /* fullduplex, key down trx after transmission */ - KISS_DUPLEX_LINK, /* fullduplex, key down trx after 'idletime' sec */ - KISS_DUPLEX_OPTIMA /* fullduplex, let the protocol layer control the hw */ -}; +#define SCC_MAGIC 0x8530 /* ;-) */ /* misc. parameters */ #define TIMER_OFF 65535U /* to switch off timers */ -#define NO_SUCH_PARAM 65534U /* param not implemented */ - -/* HWEVENT parameter */ -enum HWEVENT_opts { - HWEV_DCD_ON, - HWEV_DCD_OFF, - HWEV_ALL_SENT -}; - -/* channel grouping */ +/* Tx/Rx clock sources */ -#define RXGROUP 0100 /* if set, only tx when all channels clear */ -#define TXGROUP 0200 /* if set, don't transmit simultaneously */ +typedef enum { + CLOCK_SOURCE_RTxC, /* use RTxC pin */ + CLOCK_SOURCE_TRxC, /* use TRxC pin */ + CLOCK_SOURCE_BRG, /* use baud rate generator */ + CLOCK_SOURCE_DPLL /* use DPLL */ +} Scc_clock_sources; -/* Tx/Rx clock sources */ +typedef enum { + TRXCP_MODE_IN, /* TRxC pin is input */ + TRXCP_MODE_TXC_OUT, /* TRxC bears Tx clock */ + TRXCP_MODE_BRG_OUT, /* TRxC bears BRG output */ + TRXCP_MODE_DPLL_OUT /* TRxC bears DPLL */ -enum CLOCK_sources { - CLK_DPLL, /* normal halfduplex operation */ - CLK_EXTERNAL, /* external clocking (G3RUH/DF9IC modems) */ - CLK_DIVIDER, /* Rx = DPLL, Tx = divider (fullduplex with */ - /* modems without clock regeneration */ - CLK_BRG /* experimental fullduplex mode with DPLL/BRG for */ - /* MODEMs without clock recovery */ -}; +} Scc_trxc_pin_modes; /* Tx state */ -enum TX_state { +typedef enum { TXS_IDLE, /* Transmitter off, no data pending */ - TXS_BUSY, /* waiting for permission to send / tailtime */ + TXS_TXDELAY, /* waiting for transmitter keyed up */ TXS_ACTIVE, /* Transmitter on, sending data */ - TXS_NEWFRAME, /* reset CRC and send (next) frame */ - TXS_IDLE2, /* Transmitter on, no data pending */ - TXS_WAIT, /* Waiting for Mintime to expire */ - TXS_TIMEOUT /* We had a transmission timeout */ -}; + TXS_TAIL, /* Transmition tail time */ +} Scc_tx_states; typedef unsigned long io_port; /* type definition for an 'io port address' */ @@ -124,50 +82,22 @@ struct scc_stat { unsigned int tx_under; /* Transmitter Underruns */ unsigned int tx_state; /* Transmitter state */ - int tx_queued; /* tx frames enqueued */ - - unsigned int maxqueue; /* allocated tx_buffers */ unsigned int bufsize; /* used buffersize */ }; struct scc_modem { - long speed; /* Line speed, bps */ - char clocksrc; /* 0 = DPLL, 1 = external, 2 = divider */ - char nrz; /* NRZ instead of NRZI */ -}; - -struct scc_kiss_cmd { - int command; /* one of the KISS-Commands defined above */ - unsigned param; /* KISS-Param */ -}; - -struct scc_hw_config { - io_port data_a; /* data port channel A */ - io_port ctrl_a; /* control port channel A */ - io_port data_b; /* data port channel B */ - io_port ctrl_b; /* control port channel B */ - io_port vector_latch; /* INTACK-Latch (#) */ - io_port special; /* special function port */ - - int irq; /* irq */ - long clock; /* clock */ - char option; /* command for function port */ - - char brand; /* hardware type */ - char escc; /* use ext. features of a 8580/85180/85280 */ -}; - -/* (#) only one INTACK latch allowed. */ - - -struct scc_mem_config { - unsigned int dummy; - unsigned int bufsize; -}; - -struct scc_calibrate { - unsigned int time; - unsigned char pattern; + int rx_speed; /* Line speed, bps */ + int tx_speed; + int rx_clock_source; /* see CLOCK_sources above */ + int tx_clock_source; /* dito */ + int trxc_pin_mode; /* see TRXCP_modes above */ + int nrz_mode; /* 0 = NRZ, 1 = NRZI */ + int tx_delay; /* tx delay in msec */ + int tx_tail; /* tx tail time in msec */ + int fullduplex; /* Duplex mode: 0 = "halfduplex", 1 = "fullduplex" */ + int trx_feedback; /* TRX feeds transmit signal back to RxD */ + int tx_inhibit; /* Transmit is not allowed when set */ + int softdcd; /* Use DPLL instead of DCD pin for carrier detect */ }; #ifdef __KERNEL__ @@ -182,71 +112,53 @@ enum {TX_OFF, TX_ON}; /* command for scc_key_trx() */ #define RXINT 0x04 #define SPINT 0x06 -#ifdef CONFIG_SCC_DELAY -#define Inb(port) inb_p(port) -#define Outb(port, val) outb_p(val, port) -#else -#define Inb(port) inb(port) -#define Outb(port, val) outb(val, port) -#endif - -/* SCC channel control structure for KISS */ - -struct scc_kiss { - unsigned char txdelay; /* Transmit Delay 10 ms/cnt */ - unsigned char persist; /* Persistence (0-255) as a % */ - unsigned char slottime; /* Delay to wait on persistence hit */ - unsigned char tailtime; /* Delay after last byte written */ - unsigned char fulldup; /* Full Duplex mode 0=CSMA 1=DUP 2=ALWAYS KEYED */ - unsigned char waittime; /* Waittime before any transmit attempt */ - unsigned int maxkeyup; /* Maximum time to transmit (seconds) */ - unsigned char mintime; /* Minimal offtime after MAXKEYUP timeout (seconds) */ - unsigned int idletime; /* Maximum idle time in ALWAYS KEYED mode (seconds) */ - unsigned int maxdefer; /* Timer for CSMA channel busy limit */ - unsigned char tx_inhibit; /* Transmit is not allowed when set */ - unsigned char group; /* Group ID for AX.25 TX interlocking */ - unsigned char mode; /* 'normal' or 'hwctrl' mode (unused) */ - unsigned char softdcd; /* Use DPLL instead of DCD pin for carrier detect */ -}; - - /* SCC channel structure */ struct scc_channel { - int magic; /* magic word */ - int init; /* channel exists? */ struct net_device *dev; /* link to device control structure */ struct net_device_stats dev_stat;/* device statistics */ + struct ax25_dev ax25dev; + char * proc_dev_name; /* points to dev->name or NULL */ char brand; /* manufacturer of the board */ long clock; /* used clock */ io_port ctrl; /* I/O address of CONTROL register */ io_port data; /* I/O address of DATA register */ - io_port special; /* I/O address of special function port */ + io_port special_port; /* I/O address of special function port */ int irq; /* Number of Interrupt */ - char option; + char special_option; char enhanced; /* Enhanced SCC support */ unsigned char wreg[16]; /* Copy of last written value in WRx */ unsigned char status; /* Copy of R0 at last external interrupt */ unsigned char dcd; /* DCD status */ - struct scc_kiss kiss; /* control structure for KISS params */ - struct scc_stat stat; /* statistical information */ struct scc_modem modem; /* modem information */ + struct scc_stat stat; /* statistical information */ + + struct { + spinlock_t hwaccess; /* hardware access */ + spinlock_t timer; /* timer functions */ + spinlock_t kick_tx; /* start of transmission */ + } spinlocks; + + struct { + ctl_table channel[6]; /* /.../chip_<n>/channel_<k>/ */ + ctl_table modem[13]; /* /.../channel_<k>/modem/ */ + ctl_table stats[1]; /* /.../channel_<k>/stats/ */ + } proc_tables; + - struct sk_buff_head tx_queue; /* next tx buffer */ struct sk_buff *rx_buff; /* pointer to frame currently received */ struct sk_buff *tx_buff; /* pointer to frame currently transmitted */ - - /* Timer */ - struct timer_list tx_t; /* tx timer for this channel */ - struct timer_list tx_wdog; /* tx watchdogs */ + struct sk_buff *tx_new; /* next transmit buffer */ + struct timer_list tx_timer; /* Tx delay & tail timer */ }; +int scc_init(void); #endif /* defined(__KERNEL__) */ #endif /* defined(_SCC_H) */ diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index cbfaafbfe..24ca11425 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -155,7 +155,8 @@ enum NET_TR=14, NET_DECNET=15, NET_ECONET=16, - NET_KHTTPD=17 + NET_KHTTPD=17, + NET_DEV=18 }; /* /proc/sys/kernel/random */ @@ -423,7 +424,16 @@ enum { NET_AX25_N2=11, NET_AX25_PACLEN=12, NET_AX25_PROTOCOL=13, - NET_AX25_DAMA_SLAVE_TIMEOUT=14 + NET_AX25_DAMA_SLAVE_TIMEOUT=14, + NET_AX25_MEDIA_DUPLEX=15, + NET_AX25_MEDIA_TXDELAY=16, + NET_AX25_MEDIA_TXTAIL=17, + NET_AX25_MEDIA_TXBITRATE=18, + NET_AX25_MEDIA_RXBITRATE=19, + NET_AX25_MEDIA_SLOTTIME=20, + NET_AX25_MEDIA_PPERSISTENCE=21, + NET_AX25_MEDIA_AUTO_ADJUST=22, + NET_AX25_DEVICE_NAME=23 }; /* /proc/sys/net/rose */ @@ -510,6 +520,80 @@ enum { NET_DECNET_CONF_DEV_STATE = 7 }; +/* /proc/sys/net/dev/ (hardware configuration for net devices) */ + +enum { + NET_DEV_Z8530DRV=1 +}; + +/* /proc/sys/net/dev/z8530drv/ */ + +enum { + NET_DEV_Z8530DRV_NCHIPS=1, + NET_DEV_Z8530DRV_DELAY=2, + NET_DEV_Z8530DRV_CHIP_BASE=32 +}; + +/* /proc/sys/net/dev/z8530drv/chip<n>/ */ + +enum { + NET_DEV_Z8530DRV_VENDOR=1, + NET_DEV_Z8530DRV_CLOCK=2, + NET_DEV_Z8530DRV_IRQ=3, + NET_DEV_Z8530DRV_ESCC=4, + NET_DEV_Z8530DRV_SPECIAL_PORT=5, + NET_DEV_Z8530DRV_SPECIAL_OPT=6, + NET_DEV_Z8530DRV_CHANNEL_A=32, + NET_DEV_Z8530DRV_CHANNEL_B=33 +}; + +/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/ */ + +enum { + NET_DEV_Z8530DRV_DATA_PORT=1, + NET_DEV_Z8530DRV_CTRL_PORT=2, + NET_DEV_Z8530DRV_IFNAME=3, + NET_DEV_Z8530DRV_BUFSIZE=4, +/* + NET_DEV_Z8530DRV_SPECIAL_PORT=5, + NET_DEV_Z8530DRV_SPECIAL_OPT=6, +*/ + NET_DEV_Z8530DRV_MODEM=32, + NET_DEV_Z8530DRV_STAT=33 +}; + +/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/modem/ */ + +enum { + NET_DEV_Z8530DRV_RX_SPEED=1, + NET_DEV_Z8530DRV_TX_SPEED=2, + NET_DEV_Z8530DRV_RX_CLOCK_SOURCE=3, + NET_DEV_Z8530DRV_TX_CLOCK_SOURCE=4, + NET_DEV_Z8530DRV_TRXC_PIN_MODE=5, + NET_DEV_Z8530DRV_FULLDUPLEX=6, + NET_DEV_Z8530DRV_TRX_FEEDBACK=7, + NET_DEV_Z8530DRV_NRZ_MODE=8, + NET_DEV_Z8530DRV_TX_INHIBIT=9, + NET_DEV_Z8530DRV_SOFTDCD=10, + NET_DEV_Z8530DRV_TX_DELAY=11, + NET_DEV_Z8530DRV_TX_TAIL=12 +}; + +/* /proc/sys/net/dev/z8530drv/chip<n>/port<c>/stats/ */ + +enum { + NET_DEV_Z8530DRV_STAT_RX_INTS=1, + NET_DEV_Z8530DRV_STAT_TX_INTS=2, + NET_DEV_Z8530DRV_STAT_EX_INTS=3, + NET_DEV_Z8530DRV_STAT_TX_FRAMES=4, + NET_DEV_Z8530DRV_STAT_RX_FRAMES=5, + NET_DEV_Z8530DRV_STAT_TX_ERRORS=6, + NET_DEV_Z8530DRV_STAT_RX_ERRORS=7, + NET_DEV_Z8530DRV_STAT_TX_UNDERRUNS=8, + NET_DEV_Z8530DRV_STAT_RX_OVERRUNS=9, + NET_DEV_Z8530DRV_STAT_NO_SPACE=10, + NET_DEV_Z8530DRV_STAT_TX_STATE=11 +}; /* CTL_PROC names: */ /* CTL_FS names: */ diff --git a/include/net/ax25.h b/include/net/ax25.h index 0957d1aae..fc0509a3f 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -6,19 +6,27 @@ #ifndef _AX25_H #define _AX25_H -#include <linux/config.h> #include <linux/ax25.h> -#define AX25_T1CLAMPLO 1 -#define AX25_T1CLAMPHI (30 * HZ) +#define AX25_TICS (HZ/10) /* AX25 timertic is 1/10 sec (100 ms) */ +#define AX25_SLOWHZ 10 /* 10 timertics, 1 second */ + +#define AX25_T1CLAMPLO AX25_SLOWHZ /* 1 sec */ +#define AX25_T1CLAMPHI (30 * AX25_SLOWHZ) /* 30 secs */ #define AX25_BPQ_HEADER_LEN 16 -#define AX25_KISS_HEADER_LEN 1 -#define AX25_HEADER_LEN 17 #define AX25_ADDR_LEN 7 -#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) -#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN) +#define AX25_MIN_HEADER_LEN (AX25_ADDR_LEN*2+1) /* Source, Destination, Control */ +#define AX25_MAX_HEADER_LEN (AX25_MIN_HEADER_LEN+1+AX25_ADDR_LEN*AX25_MAX_DIGIS) /* ... including Digipeaters */ + +/* + * these are obsolete + * + * #define AX25_KISS_HEADER_LEN 1 + * #define AX25_HEADER_LEN 17 + * #define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN) + */ /* AX.25 Protocol IDs */ #define AX25_P_ROSE 0x01 @@ -27,6 +35,8 @@ #define AX25_P_TEXT 0xF0 #define AX25_P_NETROM 0xCF #define AX25_P_SEGMENT 0x08 +#define AX25_P_VJCOMP 0x06 /* Matthias Welwarsky (DG2FEF) */ +#define AX25_P_VJUNCOMP 0x07 /* Matthias Welwarsky (DG2FEF) */ /* AX.25 Segment control values */ #define AX25_SEG_REM 0x7F @@ -38,18 +48,45 @@ #define AX25_SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */ #define AX25_ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */ -#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121 */ +#define AX25_DAMA_FLAG 0x20 /* Well, it is *NOT* unused! (dl1bke 951121) */ #define AX25_COND_ACK_PENDING 0x01 #define AX25_COND_REJECT 0x02 #define AX25_COND_PEER_RX_BUSY 0x04 #define AX25_COND_OWN_RX_BUSY 0x08 -#define AX25_COND_DAMA_MODE 0x10 +#define AX25_COND_STATE_CHANGE 0x10 +#define AX25_COND_RELEASE 0x20 +#define AX25_COND_SETUP 0x40 +#define AX25_COND_START_T1 0x80 + +#define AX25_SCHED_IDLE 0 +#define AX25_SCHED_READY 1 #ifndef _LINUX_NETDEVICE_H #include <linux/netdevice.h> #endif +/* + * These headers are taken from the KA9Q package by Phil Karn. These specific + * files have been placed under the GPL (not the whole package) by Phil. + * + * + * Copyright 1991 Phil Karn, KA9Q + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 dated June, 1991. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA. + */ + /* Upper sub-layer (LAPB) definitions */ /* Control field templates */ @@ -81,270 +118,122 @@ /* Define Link State constants. */ -enum { - AX25_STATE_0, +enum { + AX25_STATE_0 = 0, AX25_STATE_1, AX25_STATE_2, AX25_STATE_3, - AX25_STATE_4 -}; - -#define AX25_MODULUS 8 /* Standard AX.25 modulus */ -#define AX25_EMODULUS 128 /* Extended AX.25 modulus */ - -enum { - AX25_PROTO_STD_SIMPLEX, - AX25_PROTO_STD_DUPLEX, - AX25_PROTO_DAMA_SLAVE, - AX25_PROTO_DAMA_MASTER + AX25_STATE_4, + AX25_LISTEN }; -enum { - AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ - AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ - AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ - AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ - AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ - AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ - AX25_VALUES_T1, /* Default T1 timeout value */ - AX25_VALUES_T2, /* Default T2 timeout value */ - AX25_VALUES_T3, /* Default T3 timeout value */ - AX25_VALUES_IDLE, /* Connected mode idle timer */ - AX25_VALUES_N2, /* Default N2 value */ - AX25_VALUES_PACLEN, /* AX.25 MTU */ - AX25_VALUES_PROTOCOL, /* Std AX.25, DAMA Slave, DAMA Master */ - AX25_VALUES_DS_TIMEOUT, /* DAMA Slave timeout */ - AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ -}; +typedef enum { + AX25_SEQMASK = 7, + AX25_ESEQMASK = 127 +} ax25_seqmask_t; -#define AX25_DEF_IPDEFMODE 0 /* Datagram */ -#define AX25_DEF_AXDEFMODE 0 /* Normal */ -#define AX25_DEF_BACKOFF 1 /* Linear backoff */ -#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ -#define AX25_DEF_WINDOW 2 /* Window=2 */ -#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ -#define AX25_DEF_T1 (10 * HZ) /* T1=10s */ -#define AX25_DEF_T2 (3 * HZ) /* T2=3s */ -#define AX25_DEF_T3 (300 * HZ) /* T3=300s */ -#define AX25_DEF_N2 10 /* N2=10 */ -#define AX25_DEF_IDLE (0 * 60 * HZ) /* Idle=None */ -#define AX25_DEF_PACLEN 256 /* Paclen=256 */ -#define AX25_DEF_PROTOCOL AX25_PROTO_STD_SIMPLEX /* Standard AX.25 */ -#define AX25_DEF_DS_TIMEOUT (3 * 60 * HZ) /* DAMA timeout 3 minutes */ - -typedef struct ax25_uid_assoc { - struct ax25_uid_assoc *next; - uid_t uid; - ax25_address call; -} ax25_uid_assoc; +#ifndef _AX25_VJ_H +struct axvj_slcomp; +#endif typedef struct { - ax25_address calls[AX25_MAX_DIGIS]; - unsigned char repeated[AX25_MAX_DIGIS]; - unsigned char ndigi; - char lastrepeat; -} ax25_digi; - -typedef struct ax25_route { - struct ax25_route *next; - ax25_address callsign; - struct net_device *dev; - ax25_digi *digipeat; - char ip_mode; -} ax25_route; - -typedef struct { - char slave; /* slave_mode? */ - struct timer_list slave_timer; /* timeout timer */ - unsigned short slave_timeout; /* when? */ -} ax25_dama_info; - -struct ctl_table; - -typedef struct ax25_dev { - struct ax25_dev *next; - struct net_device *dev; - struct net_device *forward; - struct ctl_table *systable; - int values[AX25_MAX_VALUES]; -#if defined(CONFIG_AX25_DAMA_SLAVE) || defined(CONFIG_AX25_DAMA_MASTER) - ax25_dama_info dama; -#endif -} ax25_dev; + unsigned int csum; + struct sk_buff *skb; +} ax25_reseq_t; typedef struct ax25_cb { + struct ax25_cb *prev; /* doubly linked list now */ struct ax25_cb *next; - ax25_address source_addr, dest_addr; - ax25_digi *digipeat; - ax25_dev *ax25_dev; - unsigned char iamdigi; - unsigned char state, modulus, pidincl; - unsigned short vs, vr, va; - unsigned char condition, backoff; + struct ax25_cb *peer; /* for FlexNet style digipeating */ + struct net_device *device; /* backlink to device structure */ + int inserted; /* Socket is on a list */ + struct sock *sk; /* Backlink to socket */ + + /* used by the DDI layer */ + struct { + int state; + struct ax25_cb *prev; + struct ax25_cb *next; + } ready; + + unsigned short tx_cmd, tx_rsp; + + /* layer II values + * + * vs = sequence number assigned to next to be transmitted I frame + * va = sequence number of first I frame our peer has not acked yet + * vr = sequence number of next frame we expect to receive from peer + * vl = last frame we ack'ed yet + */ + ax25_addr_t addr; + unsigned char state, condition; + unsigned short vs, vr, va, vs_max, vl, window; + ax25_seqmask_t seqmask; + unsigned long rtt_timestamp; + short rtt, vs_rtt; unsigned char n2, n2count; - struct timer_list t1timer, t2timer, t3timer, idletimer; - unsigned long t1, t2, t3, idle, rtt; + unsigned short t1, t2, t3, idle; + unsigned short wrt_timer, ack_timer, idletimer, killtimer; unsigned short paclen, fragno, fraglen; + unsigned char iamdigi, pidincl, backoff; struct sk_buff_head write_queue; - struct sk_buff_head reseq_queue; + struct sk_buff_head rcv_queue; /* MW: for flow control handling */ struct sk_buff_head ack_queue; struct sk_buff_head frag_queue; - unsigned char window; - struct timer_list timer; - struct sock *sk; /* Backlink to socket */ + struct axvj_slcomp *slcomp; /* MW: for VJ Compression */ + unsigned char slcomp_enable; /* MW: dito. */ + ax25_reseq_t reseq[AX25_SEQMASK+1]; /* MW: resequencer, not for EMODULUS */ + rwlock_t timer_lock; } ax25_cb; -/* af_ax25.c */ -extern ax25_cb *volatile ax25_list; -extern void ax25_free_cb(ax25_cb *); -extern void ax25_insert_socket(ax25_cb *); -struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int); -struct sock *ax25_find_socket(ax25_address *, ax25_address *, int); -extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *); -extern struct sock *ax25_addr_match(ax25_address *); -extern void ax25_send_to_raw(struct sock *, struct sk_buff *, int); -extern void ax25_destroy_socket(ax25_cb *); -extern ax25_cb *ax25_create_cb(void); -extern void ax25_fillin_cb(ax25_cb *, ax25_dev *); -extern int ax25_create(struct socket *, int); -extern struct sock *ax25_make_new(struct sock *, struct ax25_dev *); - -/* ax25_addr.c */ +typedef struct { + ax25_addr_t addr; + unsigned short frametype; + unsigned short nr; + unsigned short ns; + unsigned char pf; + unsigned char cmdrsp; + unsigned char dama; +} ax25_pktinfo; + +#define DAMA_STATE(ax25_cb) (AX25_PTR(ax25_cb->device)->dama_mode) + +/* needed by netrom/rose */ +extern __inline__ unsigned long ax25_display_timer(struct timer_list *timer) +{ + if (!timer_pending(timer)) + return 0; + return timer->expires - jiffies; +} + +#include <net/ax25call.h> + +extern ax25_address* asc2ax(char *); extern ax25_address null_ax25_address; -extern char *ax2asc(ax25_address *); -extern ax25_address *asc2ax(char *); -extern int ax25cmp(ax25_address *, ax25_address *); -extern int ax25digicmp(ax25_digi *, ax25_digi *); -extern unsigned char *ax25_addr_parse(unsigned char *, int, ax25_address *, ax25_address *, ax25_digi *, int *, int *); -extern int ax25_addr_build(unsigned char *, ax25_address *, ax25_address *, ax25_digi *, int, int); -extern int ax25_addr_size(ax25_digi *); -extern void ax25_digi_invert(ax25_digi *, ax25_digi *); - -/* ax25_dev.c */ -extern ax25_dev *ax25_dev_list; -extern ax25_dev *ax25_dev_ax25dev(struct net_device *); -extern ax25_dev *ax25_addr_ax25dev(ax25_address *); -extern void ax25_dev_device_up(struct net_device *); -extern void ax25_dev_device_down(struct net_device *); -extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); -extern struct net_device *ax25_fwd_dev(struct net_device *); -extern void ax25_dev_free(void); - -/* ax25_ds_in.c */ -extern int ax25_ds_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_ds_subr.c */ -extern void ax25_ds_nr_error_recovery(ax25_cb *); -extern void ax25_ds_enquiry_response(ax25_cb *); -extern void ax25_ds_establish_data_link(ax25_cb *); -extern void ax25_dev_dama_on(ax25_dev *); -extern void ax25_dev_dama_off(ax25_dev *); -extern void ax25_dama_on(ax25_cb *); -extern void ax25_dama_off(ax25_cb *); - -/* ax25_ds_timer.c */ -extern void ax25_ds_set_timer(ax25_dev *); -extern void ax25_ds_del_timer(ax25_dev *); -extern void ax25_ds_timer(ax25_cb *); -extern void ax25_ds_t1_timeout(ax25_cb *); -extern void ax25_ds_heartbeat_expiry(ax25_cb *); -extern void ax25_ds_t3timer_expiry(ax25_cb *); -extern void ax25_ds_idletimer_expiry(ax25_cb *); - -/* ax25_iface.c */ -extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); -extern void ax25_protocol_release(unsigned int); -extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); -extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); -extern int ax25_listen_register(ax25_address *, struct net_device *); -extern void ax25_listen_release(ax25_address *, struct net_device *); -extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); -extern int ax25_listen_mine(ax25_address *, struct net_device *); -extern void ax25_link_failed(ax25_cb *, int); -extern int ax25_protocol_is_registered(unsigned int); - -/* ax25_in.c */ -extern int ax25_rx_iframe(ax25_cb *, struct sk_buff *); -extern int ax25_kiss_rcv(struct sk_buff *, struct net_device *, struct packet_type *); - -/* ax25_ip.c */ -extern int ax25_encapsulate(struct sk_buff *, struct net_device *, unsigned short, void *, void *, unsigned int); -extern int ax25_rebuild_header(struct sk_buff *); +extern char* ax2asc(ax25_address *); +extern int ax25cmp(ax25_address *, ax25_address *); +extern ax25_cb *ax25_send_frame(struct sk_buff*, int, ax25_addr_t*, struct net_device*); +extern ax25_cb *ax25_find_cb(ax25_addr_t*, struct net_device*); /* ax25_out.c */ -extern ax25_cb *ax25_send_frame(struct sk_buff *, int, ax25_address *, ax25_address *, ax25_digi *, struct net_device *); extern void ax25_output(ax25_cb *, int, struct sk_buff *); -extern void ax25_kick(ax25_cb *); -extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int); -extern void ax25_queue_xmit(struct sk_buff *); -extern int ax25_check_iframes_acked(ax25_cb *, unsigned short); - -/* ax25_route.c */ -extern void ax25_rt_device_down(struct net_device *); -extern int ax25_rt_ioctl(unsigned int, void *); -extern int ax25_rt_get_info(char *, char **, off_t, int); -extern int ax25_rt_autobind(ax25_cb *, ax25_address *); -extern ax25_route *ax25_rt_find_route(ax25_address *, struct net_device *); -extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_rt_free(void); - -/* ax25_std_in.c */ -extern int ax25_std_frame_in(ax25_cb *, struct sk_buff *, int); - -/* ax25_std_subr.c */ -extern void ax25_std_nr_error_recovery(ax25_cb *); -extern void ax25_std_establish_data_link(ax25_cb *); -extern void ax25_std_transmit_enquiry(ax25_cb *); -extern void ax25_std_enquiry_response(ax25_cb *); -extern void ax25_std_timeout_response(ax25_cb *); - -/* ax25_std_timer.c */ -extern void ax25_std_heartbeat_expiry(ax25_cb *); -extern void ax25_std_t1timer_expiry(ax25_cb *); -extern void ax25_std_t2timer_expiry(ax25_cb *); -extern void ax25_std_t3timer_expiry(ax25_cb *); -extern void ax25_std_idletimer_expiry(ax25_cb *); - -/* ax25_subr.c */ -extern void ax25_clear_queues(ax25_cb *); -extern void ax25_frames_acked(ax25_cb *, unsigned short); -extern void ax25_requeue_frames(ax25_cb *); -extern int ax25_validate_nr(ax25_cb *, unsigned short); -extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *); -extern void ax25_send_control(ax25_cb *, int, int, int); -extern void ax25_return_dm(struct net_device *, ax25_address *, ax25_address *, ax25_digi *); -extern void ax25_calculate_t1(ax25_cb *); -extern void ax25_calculate_rtt(ax25_cb *); -extern void ax25_disconnect(ax25_cb *, int); /* ax25_timer.c */ -extern void ax25_start_heartbeat(ax25_cb *); -extern void ax25_start_t1timer(ax25_cb *); -extern void ax25_start_t2timer(ax25_cb *); -extern void ax25_start_t3timer(ax25_cb *); -extern void ax25_start_idletimer(ax25_cb *); -extern void ax25_stop_heartbeat(ax25_cb *); -extern void ax25_stop_t1timer(ax25_cb *); -extern void ax25_stop_t2timer(ax25_cb *); -extern void ax25_stop_t3timer(ax25_cb *); -extern void ax25_stop_idletimer(ax25_cb *); -extern int ax25_t1timer_running(ax25_cb *); -extern unsigned long ax25_display_timer(struct timer_list *); - -/* ax25_uid.c */ -extern int ax25_uid_policy; -extern ax25_address *ax25_findbyuid(uid_t); -extern int ax25_uid_ioctl(int, struct sockaddr_ax25 *); -extern int ax25_uid_get_info(char *, char **, off_t, int); -extern void ax25_uid_free(void); +extern void ax25_timer(ax25_cb *); +extern void ax25_link_failed(ax25_cb *, int); +extern int (*ax25_protocol_function(unsigned int))(struct sk_buff *, ax25_cb *); /* sysctl_net_ax25.c */ -#ifdef CONFIG_SYSCTL extern void ax25_register_sysctl(void); extern void ax25_unregister_sysctl(void); -#else -extern inline void ax25_register_sysctl(void) {}; -extern inline void ax25_unregister_sysctl(void) {}; -#endif /* CONFIG_SYSCTL */ + +/* support routines for modules that use AX.25, in ax25_timer.c */ +extern int ax25_protocol_register(unsigned int, int (*)(struct sk_buff *, ax25_cb *)); +extern void ax25_protocol_release(unsigned int); +extern int ax25_linkfail_register(void (*)(ax25_cb *, int)); +extern void ax25_linkfail_release(void (*)(ax25_cb *, int)); +extern int ax25_listen_register(ax25_address *, struct net_device *); +extern void ax25_listen_release(ax25_address *, struct net_device *); +extern int ax25_protocol_is_registered(unsigned int); #endif diff --git a/include/net/ax25_uid.h b/include/net/ax25_uid.h new file mode 100644 index 000000000..ae40ff0fd --- /dev/null +++ b/include/net/ax25_uid.h @@ -0,0 +1,33 @@ +/* + * This code REQUIRES 1.2.1 or higher/ NET3.029 + * + * This module: + * This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _AX25_UID_H +#define _AX25_UID_H + +typedef struct ax25_uid_assoc { + struct ax25_uid_assoc *next; + uid_t uid; + ax25_address call; + char device[IFNAMSIZ]; +} ax25_uid_assoc; + +typedef enum { + AX25_UID_POLICY_ARBITRARY_CALLSIGN, + AX25_UID_POLICY_GIVEN_CALLSIGN, + AX25_UID_POLICY_ANY_SSID +} t_ax25_uid_policy; + +extern t_ax25_uid_policy ax25_uid_policy; +extern int ax25_cs_get_info(char *, char **, off_t, int); +extern int ax25_uid_ioctl(int, struct sockaddr_ax25*); +extern ax25_address* ax25_find_by_uid(uid_t uid); +extern ax25_address* ax25_find_match_for_uid(uid_t uid, ax25_address *providec, char *device); + +#endif diff --git a/include/net/ax25call.h b/include/net/ax25call.h new file mode 100644 index 000000000..68b8a70c0 --- /dev/null +++ b/include/net/ax25call.h @@ -0,0 +1,2 @@ +/* Separate to keep compilation of protocols.c simpler */ +extern void ax25_proto_init(struct net_proto *pro); diff --git a/include/net/ax25dev.h b/include/net/ax25dev.h new file mode 100644 index 000000000..f2e125e6b --- /dev/null +++ b/include/net/ax25dev.h @@ -0,0 +1,164 @@ +/* + * include/net/ax25dev.h + * + * intra-kernel interface declaration, anything needed by device drivers for + * AX.25 interfaces is defined here. + * + */ + +#ifndef _NET_AX25DEV_H +#define _NET_AX25DEV_H + +#include <linux/netdevice.h> +#include <linux/sysctl.h> +#include <linux/tqueue.h> +#include <linux/timer.h> + +#ifndef AX25_TICS +#define AX25_TICS (HZ/10) +#endif + +#define AX25_MAX_DEVICES 20 /* Max No of AX.25 devices */ +#define AX25_DEV_MAGIC 0x88babe73 + +enum { + AX25_VALUES_IPDEFMODE, /* 0=DG 1=VC */ + AX25_VALUES_AXDEFMODE, /* 0=Normal 1=Extended Seq Nos */ + AX25_VALUES_BACKOFF, /* 0=None 1=Linear 2=Exponential */ + AX25_VALUES_CONMODE, /* Allow connected modes - 0=No 1=no "PID text" 2=all PIDs */ + AX25_VALUES_WINDOW, /* Default window size for standard AX.25 */ + AX25_VALUES_EWINDOW, /* Default window size for extended AX.25 */ + AX25_VALUES_T1, /* Default T1 timeout value */ + AX25_VALUES_T2, /* Default T2 timeout value */ + AX25_VALUES_T3, /* Default T3 timeout value */ + AX25_VALUES_IDLE, /* Connected mode idle timer */ + AX25_VALUES_N2, /* Default N2 value */ + AX25_VALUES_PACLEN, /* AX.25 MTU */ + AX25_VALUES_PROTOCOL, /* What is this? */ + AX25_VALUES_DAMA_SLAVE_TIMEOUT, /* DAMA */ + AX25_VALUES_MEDIA_DUPLEX, /* Duplex type */ + AX25_VALUES_MEDIA_TXDELAY, /* TX-Delay */ + AX25_VALUES_MEDIA_TXTAIL, /* TX-Tail */ + AX25_VALUES_MEDIA_TXBITRATE, /* TX-Bitrate */ + AX25_VALUES_MEDIA_RXBITRATE, /* RX-Bitrate */ + AX25_VALUES_MEDIA_SLOTTIME, /* Slottime */ + AX25_VALUES_MEDIA_PPERSISTENCE, /* P-Persistence */ + AX25_VALUES_MEDIA_AUTO_ADJUST, /* Enable/disable auto adjustment */ + AX25_VALUES_DUMMY_0, /* For future use */ + AX25_VALUES_DUMMY_1, /* For future use */ + AX25_VALUES_DUMMY_2, /* For future use */ + AX25_VALUES_DUMMY_3, /* For future use */ + AX25_VALUES_DUMMY_4, /* For future use */ + AX25_VALUES_DUMMY_5, /* For future use */ + AX25_VALUES_DUMMY_6, /* For future use */ + AX25_VALUES_DUMMY_7, /* For future use */ + AX25_MAX_VALUES /* THIS MUST REMAIN THE LAST ENTRY OF THIS LIST */ +}; + +#define AX25_DEF_IPDEFMODE 1 /* VC */ +#define AX25_DEF_AXDEFMODE 0 /* Normal */ +#define AX25_DEF_BACKOFF 1 /* Linear backoff */ +#define AX25_DEF_CONMODE 2 /* Connected mode allowed */ +#define AX25_DEF_WINDOW 7 /* Window=7 */ +#define AX25_DEF_EWINDOW 32 /* Module-128 Window=32 */ +#define AX25_DEF_T1 (10 * AX25_SLOWHZ) /* T1=10s */ +#define AX25_DEF_T2 (3 * AX25_SLOWHZ) /* T2=3s */ +#define AX25_DEF_T3 (90 * AX25_SLOWHZ) /* T3=90s */ +#define AX25_DEF_N2 10 /* N2=10 */ +#define AX25_DEF_IDLE (120 * AX25_SLOWHZ) /* Idle=120s */ +#define AX25_DEF_PACLEN 256 /* Paclen=256 */ +#define AX25_DEF_PROTOCOL 1 /* 0=CSMA; 1=CSMA/DAMA slave auto; 2=DAMA master */ +#define AX25_DEF_DAMA_SLAVE_TIMEOUT 0 /* ? */ +#define AX25_DEF_MEDIA_DUPLEX 0 /* Half duplex */ +#define AX25_DEF_MEDIA_TXDELAY 60 /* 60 ms */ +#define AX25_DEF_MEDIA_TXTAIL 10 /* 10 ms */ +#define AX25_DEF_MEDIA_TXBITRATE 9600 /* 9600 bps */ +#define AX25_DEF_MEDIA_RXBITRATE 9600 /* 9600 bps */ +#define AX25_DEF_MEDIA_SLOTTIME 10 /* 10 ms */ +#define AX25_DEF_MEDIA_PPERSISTENCE 32 /* 32/256 */ +#define AX25_DEF_MEDIA_AUTO_ADJUST 1 /* Auto adjust on */ + +struct ax25_dev { + unsigned long magic; + struct net_device *netdev; /* backlink to the network device */ + struct net_device *forward; + struct timer_list timer; + struct timer_list tics; + struct tq_struct task_queue; + struct sk_buff_head unproto_queue; + int needs_transmit; + unsigned int dama_mode; + struct { + struct ax25_cb *ready; + struct ax25_cb *all; + } list; + + struct { + unsigned char fast; /* + * set for "infinitely" + * fast channels to skip + * arbitration + */ + + /* downcalls */ + unsigned int (*dcd)(struct net_device *dev); /* required: report dcd state */ + unsigned int (*ptt)(struct net_device *dev); /* required: report ptt state */ + unsigned int (*cts)(struct net_device *dev); /* optional: report "clear to send" */ + void (*rts)(struct net_device *dev); /* optional: forced "key transmitter" */ + void (*parameter_change_notify)( /* configure media parameter */ + struct net_device *dev, + int id, /* parameter type */ + int old, /* old value */ + int new); /* new value */ + } hw; + + struct ctl_table systable[AX25_MAX_VALUES+1]; + int values[AX25_MAX_VALUES]; + + /* handled by DDI layer */ + unsigned int bit_per_jiffie; /* internal use */ + unsigned int jiffies_per_slot; /* internal use */ + unsigned char dcd_memory; /* internal use */ + unsigned char dcd_dropped; /* internal use */ + unsigned char dama_polled; /* internal use */ + unsigned int bytes_sent; + unsigned int max_bytes; + + /* statistics */ + unsigned long tx_iframes; + unsigned long rx_iframes; + unsigned long tx_bytes; + unsigned long rx_bytes; + unsigned long rx_rejects; + + rwlock_t ready_lock; +}; + +#define DAMA_SLAVE (1<<0) +#define DAMA_MASTER (1<<1) + +#define AX25_PTR(dev) ((struct ax25_dev *)dev->ax25_ptr) + +extern void register_ax25device(struct net_device *dev); +extern void unregister_ax25device(struct net_device *dev); + + +/* + * Call this function from device driver code for reading a parameter + */ +extern inline int ax25_dev_get_value(struct net_device *dev, int valueno) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + return ax25_device->values[valueno]; +} + +/* + * Call this function from device driver code for writing a parameter + */ +extern inline void ax25_dev_set_value(struct net_device *dev, int valueno, int newvalue) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + ax25_device->values[valueno] = newvalue; +} + +#endif /* _NET_AX25DEV_H */ diff --git a/include/net/netrom.h b/include/net/netrom.h index 21c9f8b86..b6ccf33a9 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -76,8 +76,7 @@ typedef struct { struct nr_neigh { struct nr_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; + ax25_addr_t addr; ax25_cb *ax25; struct net_device *dev; unsigned char quality; diff --git a/include/net/rose.h b/include/net/rose.h index f1f893e27..167e629b9 100644 --- a/include/net/rose.h +++ b/include/net/rose.h @@ -81,8 +81,7 @@ enum { struct rose_neigh { struct rose_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; + ax25_addr_t addr; ax25_cb *ax25; struct net_device *dev; unsigned short count; diff --git a/net/ax25/Config.in b/net/ax25/Config.in index b8e5d7333..bba299843 100644 --- a/net/ax25/Config.in +++ b/net/ax25/Config.in @@ -3,7 +3,7 @@ # # 19971130 Now in an own category to make correct compilation of the # AX.25 stuff easier... -# Joerg Reuter DL1BKE <jreuter@yaina.de> +# Joerg Reuter DL1BKE <jreuter@poboxes.com> # 19980129 Moved to net/ax25/Config.in, sourcing device drivers. mainmenu_option next_comment @@ -11,26 +11,23 @@ comment 'Amateur Radio support' bool 'Amateur Radio support' CONFIG_HAMRADIO if [ "$CONFIG_HAMRADIO" != "n" ]; then - if [ "$CONFIG_NET" != "n" ]; then - comment 'Packet Radio protocols' - tristate ' Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 - if [ "$CONFIG_AX25" != "n" ]; then - bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE -# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER - dep_tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 - dep_tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 - fi - - if [ "$CONFIG_AX25" != "n" ]; then - mainmenu_option next_comment - comment 'AX.25 network device drivers' - - source drivers/net/hamradio/Config.in - - endmenu - fi - fi + if [ "$CONFIG_NET" != "n" ]; then + comment 'Packet Radio protocols' + tristate 'Amateur Radio AX.25 Level 2 protocol' CONFIG_AX25 $CONFIG_NETLINK + if [ "$CONFIG_AX25" != "n" ]; then +# bool ' AX.25 DAMA Slave support' CONFIG_AX25_DAMA_SLAVE +# bool ' AX.25 DAMA Master support' CONFIG_AX25_DAMA_MASTER + tristate ' Amateur Radio NET/ROM protocol' CONFIG_NETROM $CONFIG_AX25 + tristate ' Amateur Radio X.25 PLP (Rose)' CONFIG_ROSE $CONFIG_AX25 + fi + if [ "$CONFIG_AX25" != "n" ]; then + mainmenu_option next_comment + comment 'AX.25 network device drivers' + source drivers/net/hamradio/Config.in + endmenu + fi + fi fi endmenu diff --git a/net/ax25/Makefile b/net/ax25/Makefile index 5974031f4..c35f59105 100644 --- a/net/ax25/Makefile +++ b/net/ax25/Makefile @@ -6,20 +6,19 @@ # unless it's something special (ie not a .c file). # # Note 2! The CFLAGS definition is now in the main makefile... - +# O_TARGET := ax25.o export-objs := af_ax25.o -obj-y := ax25_addr.o ax25_dev.o ax25_iface.o ax25_in.o ax25_ip.o ax25_out.o \ - ax25_route.o ax25_std_in.o ax25_std_subr.o ax25_std_timer.o \ - ax25_subr.o ax25_timer.o ax25_uid.o af_ax25.o - -obj-m := $(O_TARGET) +obj-y := af_ax25.o ax25_in.o ax25_out.o ax25_route.o ax25_subr.o ax25_timer.o \ + ax25_vj.o ax25_ddi.o ax25_uid.o ax25_ctl.o ax25_core.o \ + ax25_ipax.o ax25_lapb.o ax25_netlink.o sysctl_net_ax25.o -obj-$(CONFIG_AX25_DAMA_SLAVE) += ax25_ds_in.o ax25_ds_subr.o ax25_ds_timer.o -obj-$(CONFIG_SYSCTL) += sysctl_net_ax25.o +obj-m := $(O_TARGET) include $(TOPDIR)/Rules.make +tar: + tar -cvf /dev/f1 . diff --git a/net/ax25/af_ax25.c b/net/ax25/af_ax25.c index 4a2684ebe..6fa817d65 100644 --- a/net/ax25/af_ax25.c +++ b/net/ax25/af_ax25.c @@ -1,107 +1,22 @@ /* - * AX.25 release 038 + * af_ax25.c: Network subsystem interface and NEW-AX.25 main functions * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), + * Jonathan (G4KLX), Alan Cox (GW4PTS) * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: SOCK_DGRAM support is missing and should be implemented ASAP. + * There is currently no clean way for unproto operation. Most + * application use AF_PACKET, SOCK_RAW which is seriously broken + * because it skips the DDI arbiter etc. etc. * - * History - * AX.25 006 Alan(GW4PTS) Nearly died of shock - it's working 8-) - * AX.25 007 Alan(GW4PTS) Removed the silliest bugs - * AX.25 008 Alan(GW4PTS) Cleaned up, fixed a few state machine problems, added callbacks - * AX.25 009 Alan(GW4PTS) Emergency patch kit to fix memory corruption - * AX.25 010 Alan(GW4PTS) Added RAW sockets/Digipeat. - * AX.25 011 Alan(GW4PTS) RAW socket and datagram fixes (thanks) - Raw sendto now gets PID right - * datagram sendto uses correct target address. - * AX.25 012 Alan(GW4PTS) Correct incoming connection handling, send DM to failed connects. - * Use skb->data not skb+1. Support sk->priority correctly. - * Correct receive on SOCK_DGRAM. - * AX.25 013 Alan(GW4PTS) Send DM to all unknown frames, missing initialiser fixed - * Leave spare SSID bits set (DAMA etc) - thanks for bug report, - * removed device registration (it's not used or needed). Clean up for - * gcc 2.5.8. PID to AX25_P_ - * AX.25 014 Alan(GW4PTS) Cleanup and NET3 merge - * AX.25 015 Alan(GW4PTS) Internal test version. - * AX.25 016 Alan(GW4PTS) Semi Internal version for PI card - * work. - * AX.25 017 Alan(GW4PTS) Fixed some small bugs reported by - * G4KLX - * AX.25 018 Alan(GW4PTS) Fixed a small error in SOCK_DGRAM - * AX.25 019 Alan(GW4PTS) Clean ups for the non INET kernel and device ioctls in AX.25 - * AX.25 020 Jonathan(G4KLX) /proc support and other changes. - * AX.25 021 Alan(GW4PTS) Added AX25_T1, AX25_N2, AX25_T3 as requested. - * AX.25 022 Jonathan(G4KLX) More work on the ax25 auto router and /proc improved (again)! - * Alan(GW4PTS) Added TIOCINQ/OUTQ - * AX.25 023 Alan(GW4PTS) Fixed shutdown bug - * AX.25 023 Alan(GW4PTS) Linus changed timers - * AX.25 024 Alan(GW4PTS) Small bug fixes - * AX.25 025 Alan(GW4PTS) More fixes, Linux 1.1.51 compatibility stuff, timers again! - * AX.25 026 Alan(GW4PTS) Small state fix. - * AX.25 027 Alan(GW4PTS) Socket close crash fixes. - * AX.25 028 Alan(GW4PTS) Callsign control including settings per uid. - * Small bug fixes. - * Protocol set by sockets only. - * Small changes to allow for start of NET/ROM layer. - * AX.25 028a Jonathan(G4KLX) Changes to state machine. - * AX.25 028b Jonathan(G4KLX) Extracted ax25 control block - * from sock structure. - * AX.25 029 Alan(GW4PTS) Combined 028b and some KA9Q code - * Jonathan(G4KLX) and removed all the old Berkeley, added IP mode registration. - * Darryl(G7LED) stuff. Cross-port digipeating. Minor fixes and enhancements. - * Alan(GW4PTS) Missed suser() on axassociate checks - * AX.25 030 Alan(GW4PTS) Added variable length headers. - * Jonathan(G4KLX) Added BPQ Ethernet interface. - * Steven(GW7RRM) Added digi-peating control ioctl. - * Added extended AX.25 support. - * Added AX.25 frame segmentation. - * Darryl(G7LED) Changed connect(), recvfrom(), sendto() sockaddr/addrlen to - * fall inline with bind() and new policy. - * Moved digipeating ctl to new ax25_dev structs. - * Fixed ax25_release(), set TCP_CLOSE, wakeup app - * context, THEN make the sock dead. - * Alan(GW4PTS) Cleaned up for single recvmsg methods. - * Alan(GW4PTS) Fixed not clearing error on connect failure. - * AX.25 031 Jonathan(G4KLX) Added binding to any device. - * Joerg(DL1BKE) Added DAMA support, fixed (?) digipeating, fixed buffer locking - * for "virtual connect" mode... Result: Probably the - * "Most Buggiest Code You've Ever Seen" (TM) - * HaJo(DD8NE) Implementation of a T5 (idle) timer - * Joerg(DL1BKE) Renamed T5 to IDLE and changed behaviour: - * the timer gets reloaded on every received or transmitted - * I frame for IP or NETROM. The idle timer is not active - * on "vanilla AX.25" connections. Furthermore added PACLEN - * to provide AX.25-layer based fragmentation (like WAMPES) - * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout error. - * ax25_send_frame() limits the number of enqueued - * datagrams per socket. - * AX.25 033 Jonathan(G4KLX) Removed auto-router. - * Hans(PE1AYX) Converted to Module. - * Joerg(DL1BKE) Moved BPQ Ethernet to separate driver. - * AX.25 034 Jonathan(G4KLX) 2.1 changes - * Alan(GW4PTS) Small POSIXisations - * AX.25 035 Alan(GW4PTS) Started fixing to the new - * format. - * Hans(PE1AYX) Fixed interface to IP layer. - * Alan(GW4PTS) Added asynchronous support. - * Frederic(F1OAT) Support for pseudo-digipeating. - * Jonathan(G4KLX) Support for packet forwarding. - * AX.25 036 Jonathan(G4KLX) Major restructuring. - * Joerg(DL1BKE) Fixed DAMA Slave. - * Jonathan(G4KLX) Fix wildcard listen parameter setting. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * AX.25 038 Matthias(DG2FEF) Small fixes to the syscall interface to make kernel - * independent of AX25_MAX_DIGIS used by applications. - * Tomi(OH2BNS) Fixed ax25_getname(). - * Joerg(DL1BKE) Starting to phase out the support for full_sockaddr_ax25 - * with only 6 digipeaters and sockaddr_ax25 in ax25_bind(), - * ax25_connect() and ax25_sendmsg() - * Joerg(DL1BKE) Added support for SO_BINDTODEVICE - * Arnaldo C. Melo s/suser/capable(CAP_NET_ADMIN)/, some more cleanups - * Michal Ostrowski Module initialization cleanup. + * Changelog: + * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de> + * port to kernel 2.4.1 + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ #include <linux/config.h> @@ -111,518 +26,327 @@ #include <linux/socket.h> #include <linux/in.h> #include <linux/kernel.h> -#include <linux/sched.h> #include <linux/timer.h> #include <linux/string.h> #include <linux/sockios.h> #include <linux/net.h> -#include <net/ax25.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> +#include <linux/if.h> #include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> +#include <linux/spinlock.h> #include <linux/fcntl.h> #include <linux/termios.h> /* For TIOCINQ/OUTQ */ -#include <linux/mm.h> #include <linux/interrupt.h> #include <linux/notifier.h> #include <linux/proc_fs.h> #include <linux/stat.h> -#include <linux/netfilter.h> -#include <linux/sysctl.h> #include <linux/init.h> -#include <net/ip.h> -#include <net/arp.h> - - - -ax25_cb *volatile ax25_list; +#include <linux/smp.h> +#include <linux/smp_lock.h> -static struct proto_ops ax25_proto_ops; +#include <net/sock.h> +#include <net/ax25.h> +#include <net/ax25_uid.h> +#include <net/ax25dev.h> + +#include "af_ax25.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_route.h" +#include "ax25_core.h" +#include "ax25_ctl.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_netlink.h" +#include "ax25_timer.h" /* - * Free an allocated ax25 control block. This is done to centralise - * the MOD count code. + * ------------------------------------------------------------------------ + * declaration of static functions + * ------------------------------------------------------------------------ */ -void ax25_free_cb(ax25_cb *ax25) -{ - if (ax25->digipeat != NULL) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; - } - - kfree(ax25); - - MOD_DEC_USE_COUNT; -} - -static void ax25_free_sock(struct sock *sk) -{ - ax25_free_cb(sk->protinfo.ax25); -} +static int ax25_device_event(struct notifier_block* ,unsigned long, void*); +static int ax25_setsockopt(struct socket*, int, int, char*, int); +static int ax25_getsockopt(struct socket*, int, int, char*, int*); +static int ax25_listen(struct socket*, int); +static int ax25_shutdown(struct socket*, int); +static int ax25_create(struct socket*, int); +static int ax25_release(struct socket *); +static int ax25_bind(struct socket*, struct sockaddr*, int); +static int ax25_connect(struct socket*, struct sockaddr*, int, int); +static int ax25_accept(struct socket*, struct socket*, int); +static int ax25_getname(struct socket*, struct sockaddr*, int*, int); +static int ax25_sendmsg(struct socket*, struct msghdr*, int, struct scm_cookie*); +static int ax25_recvmsg(struct socket*, struct msghdr*, int, int, struct scm_cookie*); +static int ax25_ioctl(struct socket*, unsigned int, unsigned long); +static int ax25_print_list(char*, off_t*, off_t, int, off_t*, ax25_cb*, char*); +static int ax25_get_info(char*, char **, off_t, int); +static int ax25_gifconf(struct net_device *dev, char *buf, int len); + +/* in ax25_ipax.c */ +int ipax_init(void); +int ipax_cleanup(void); /* - * Socket removal during an interrupt is now safe. + * ------------------------------------------------------------------------ + * static variables and structures + * ------------------------------------------------------------------------ */ -static void ax25_remove_socket(ax25_cb *ax25) -{ - ax25_cb *s; - unsigned long flags; - - save_flags(flags); cli(); - - if ((s = ax25_list) == ax25) { - ax25_list = s->next; - restore_flags(flags); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == ax25) { - s->next = ax25->next; - restore_flags(flags); - return; - } - - s = s->next; - } - - restore_flags(flags); -} /* - * Kill all bound sockets on a dropped device. + * table of exportes symbols */ -static void ax25_kill_by_device(struct net_device *dev) -{ - ax25_dev *ax25_dev; - ax25_cb *s; - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; +EXPORT_SYMBOL(ax25_find_match_for_uid); +EXPORT_SYMBOL(ax25_find_cb); +EXPORT_SYMBOL(ax25_linkfail_register); +EXPORT_SYMBOL(ax25_linkfail_release); +EXPORT_SYMBOL(ax25_listen_register); +EXPORT_SYMBOL(ax25_listen_release); +EXPORT_SYMBOL(ax25_protocol_register); +EXPORT_SYMBOL(ax25_protocol_release); +EXPORT_SYMBOL(ax25_send_frame); +EXPORT_SYMBOL(ax25_uid_policy); +EXPORT_SYMBOL(ax25cmp); +EXPORT_SYMBOL(ax2asc); +EXPORT_SYMBOL(asc2ax); +EXPORT_SYMBOL(null_ax25_address); - for (s = ax25_list; s != NULL; s = s->next) { - if (s->ax25_dev == ax25_dev) { - s->ax25_dev = NULL; - ax25_disconnect(s, ENETUNREACH); - } - } -} +/* for debugging */ +EXPORT_SYMBOL(ax25_kill_by_device); /* - * Handle device status changes. + * This list contains all sockets that are not bound to + * a specific device. */ -static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) -{ - struct net_device *dev = (struct net_device *)ptr; - - /* Reject non AX.25 devices */ - if (dev->type != ARPHRD_AX25) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_UP: - ax25_dev_device_up(dev); - break; - case NETDEV_DOWN: - ax25_kill_by_device(dev); - ax25_rt_device_down(dev); - ax25_dev_device_down(dev); - break; - default: - break; - } - - return NOTIFY_DONE; -} +ax25_cb *ax25_list = NULL; /* - * Add a socket to the bound sockets list. + * Protocol family registration data */ -void ax25_insert_socket(ax25_cb *ax25) +static struct net_proto_family ax25_family_ops = { - unsigned long flags; - - save_flags(flags); - cli(); - - ax25->next = ax25_list; - ax25_list = ax25; - - restore_flags(flags); -} + family: PF_AX25, + create: ax25_create, +}; /* - * Find a socket that wants to accept the SABM we have just - * received. + * Protocol operations for AF_AX25 */ -struct sock *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev, int type) -{ - unsigned long flags; - ax25_cb *s; - - save_flags(flags); - cli(); +static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { + family: PF_AX25, - for (s = ax25_list; s != NULL; s = s->next) { - if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) - continue; - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == type && s->sk->state == TCP_LISTEN) { - /* If device is null we match any device */ - if (s->ax25_dev == NULL || s->ax25_dev->dev == dev) { - restore_flags(flags); - return s->sk; - } - } - } + release: ax25_release, + bind: ax25_bind, + connect: ax25_connect, + socketpair: sock_no_socketpair, + accept: ax25_accept, + getname: ax25_getname, + poll: datagram_poll, + ioctl: ax25_ioctl, + listen: ax25_listen, + shutdown: ax25_shutdown, + setsockopt: ax25_setsockopt, + getsockopt: ax25_getsockopt, + sendmsg: ax25_sendmsg, + recvmsg: ax25_recvmsg, + mmap: sock_no_mmap +}; - restore_flags(flags); - return NULL; -} +SOCKOPS_WRAP(ax25_proto, PF_AX25); /* - * Find an AX.25 socket given both ends. + * Device up/down notifier block */ -struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, int type) -{ - ax25_cb *s; - unsigned long flags; - - save_flags(flags); - cli(); - - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && ax25cmp(&s->source_addr, my_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->sk->type == type) { - restore_flags(flags); - return s->sk; - } - } - - restore_flags(flags); +static struct notifier_block ax25_dev_notifier = { + ax25_device_event, + 0 +}; - return NULL; -} +/* + * ------------------------------------------------------------------------ + * Interface implementation + * All public functions of this module are defined here + * ------------------------------------------------------------------------ + */ /* - * Find an AX.25 control block given both ends. It will only pick up - * floating AX.25 control blocks or non Raw socket bound control blocks. + * ------------------------------------------------------------------------ + * Init functions. called by the kernel on startup + * ------------------------------------------------------------------------ */ -ax25_cb *ax25_find_cb(ax25_address *src_addr, ax25_address *dest_addr, ax25_digi *digi, struct net_device *dev) +void __init ax25_proto_init(struct net_proto *pro) { - ax25_cb *s; - unsigned long flags; + struct net_device *dev; - save_flags(flags); - cli(); + sock_register(&ax25_family_ops); + ax25_packet_type.type = htons(ETH_P_AX25); + dev_add_pack(&ax25_packet_type); + register_gifconf(PF_AX25, ax25_gifconf); + register_netdevice_notifier(&ax25_dev_notifier); + ax25_register_sysctl(); + ax25_ddi_init(); + ax25_netlink_init(); - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) - continue; - if (s->ax25_dev == NULL) - continue; - if (ax25cmp(&s->source_addr, src_addr) == 0 && ax25cmp(&s->dest_addr, dest_addr) == 0 && s->ax25_dev->dev == dev) { - if (digi != NULL && digi->ndigi != 0) { - if (s->digipeat == NULL) - continue; - if (ax25digicmp(s->digipeat, digi) != 0) - continue; - } else { - if (s->digipeat != NULL && s->digipeat->ndigi != 0) - continue; - } - restore_flags(flags); - return s; + proc_net_create("ax25_route", 0, ax25_rt_get_info); + proc_net_create("ax25", 0, ax25_get_info); + proc_net_create("ax25_calls", 0, ax25_cs_get_info); + proc_net_create("ax25_ports", 0, ax25_dev_get_info); + printk(KERN_INFO "NET4: AX.25 for Linux 2.4-NET4 by DG2FEF\n"); + +#ifdef CONFIG_INET + ipax_init(); +#endif + for (dev=dev_base; dev!=NULL; dev=dev->next) { + if (dev->type == ARPHRD_AX25 && AX25_PTR(dev)) { + register_ax25device(dev); + if (netif_running(dev)) ax25_dev_device_up(dev); } } - - restore_flags(flags); - - return NULL; } -/* - * Look for any matching address - RAW sockets can bind to arbitrary names - */ -struct sock *ax25_addr_match(ax25_address *addr) +void __exit ax25_proto_remove(void) { - unsigned long flags; - ax25_cb *s; + int i; + struct net_device *dev; - save_flags(flags); - cli(); + proc_net_remove("ax25_route"); + proc_net_remove("ax25"); + proc_net_remove("ax25_calls"); + proc_net_remove("ax25_ports"); - for (s = ax25_list; s != NULL; s = s->next) { - if (s->sk != NULL && ax25cmp(&s->source_addr, addr) == 0 && s->sk->type == SOCK_RAW) { - restore_flags(flags); - return s->sk; + for (i=0; i<AX25_MAX_DEVICES; i++) { + if ((dev = ax25_devices[i])) { + if (netif_running(dev)) ax25_dev_device_down(dev); + unregister_ax25device(dev); } } - restore_flags(flags); - - return NULL; -} - -void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) -{ - struct sk_buff *copy; - - while (sk != NULL) { - if (sk->type == SOCK_RAW && - sk->protocol == proto && - atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) { - if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) - return; + ax25_rt_free(); +#ifdef CONFIG_INET + ipax_cleanup(); +#endif + ax25_netlink_cleanup(); + ax25_unregister_sysctl(); - if (sock_queue_rcv_skb(sk, copy) != 0) - kfree_skb(copy); - } + unregister_netdevice_notifier(&ax25_dev_notifier); + register_gifconf(PF_AX25, NULL); + dev_remove_pack(&ax25_packet_type); + sock_unregister(AF_AX25); - sk = sk->next; - } } /* - * Deferred destroy. + * ------------------------------------------------------------------------ + * module registration/unregistration + * ------------------------------------------------------------------------ */ -void ax25_destroy_socket(ax25_cb *); -/* - * Handler for deferred kills. - */ -static void ax25_destroy_timer(unsigned long data) -{ - ax25_destroy_socket((ax25_cb *)data); -} -/* - * This is called from user mode and the timers. Thus it protects itself against - * interrupt users but doesn't worry about being called during work. - * Once it is removed from the queue no interrupt or bottom half will - * touch it and we are (fairly 8-) ) safe. - */ -void ax25_destroy_socket(ax25_cb *ax25) /* Not static as it's used by the timer */ +int __init ax25_init_module(void) { - struct sk_buff *skb; - unsigned long flags; - - save_flags(flags); cli(); + ax25_proto_init(NULL); + return 0; +} - ax25_stop_heartbeat(ax25); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); +MODULE_AUTHOR("Matthias Welwarsky, dg2fef@afthd.tu-darmstadt.de, dg2fef@db0ais.ampr.org"); +MODULE_DESCRIPTION("Packet Radio AX.25 Protocol stack"); +module_init(ax25_init_module); +module_exit(ax25_proto_remove); - ax25_remove_socket(ax25); - ax25_clear_queues(ax25); /* Flush the queues */ - if (ax25->sk != NULL) { - while ((skb = skb_dequeue(&ax25->sk->receive_queue)) != NULL) { - if (skb->sk != ax25->sk) { /* A pending connection */ - skb->sk->dead = 1; /* Queue the unaccepted socket for death */ - ax25_start_heartbeat(skb->sk->protinfo.ax25); - skb->sk->protinfo.ax25->state = AX25_STATE_0; - } +/* ---------------------------------------------------------------------*/ +/* + * Find the AX.25 device that matches the hardware address supplied. + */ - kfree_skb(skb); - } - } +struct net_device *ax25rtr_get_dev(ax25_address *addr) +{ + struct net_device *dev = NULL; + int i; - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->wmem_alloc) != 0 || - atomic_read(&ax25->sk->rmem_alloc) != 0) { - /* Defer: outstanding buffers */ - init_timer(&ax25->timer); - ax25->timer.expires = jiffies + 10 * HZ; - ax25->timer.function = ax25_destroy_timer; - ax25->timer.data = (unsigned long)ax25; - add_timer(&ax25->timer); - } else { - sk_free(ax25->sk); - } - } else { - ax25_free_cb(ax25); + read_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + dev = ax25_devices[i]; + if (dev != NULL && !ax25cmp(addr, (ax25_address *)dev->dev_addr)) + break; } + read_unlock(&ax25_dev_lock); - restore_flags(flags); + return dev; } +/* ---------------------------------------------------------------------*/ /* - * dl1bke 960311: set parameters for existing AX.25 connections, - * includes a KILL command to abort any connection. - * VERY useful for debugging ;-) + * Kill all bound sockets on a dropped device. */ -static int ax25_ctl_ioctl(const unsigned int cmd, void *arg) +void ax25_kill_by_device(struct net_device *dev) { - struct ax25_ctl_struct ax25_ctl; - ax25_digi digi; - ax25_dev *ax25_dev; - ax25_cb *ax25; - unsigned int k; - - if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) - return -EFAULT; - - if ((ax25_dev = ax25_addr_ax25dev(&ax25_ctl.port_addr)) == NULL) - return -ENODEV; - - if (ax25_ctl.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - - digi.ndigi = ax25_ctl.digi_count; - for (k = 0; k < digi.ndigi; k++) - digi.calls[k] = ax25_ctl.digi_addr[k]; - - if ((ax25 = ax25_find_cb(&ax25_ctl.source_addr, &ax25_ctl.dest_addr, &digi, ax25_dev->dev)) == NULL) - return -ENOTCONN; - - switch (ax25_ctl.cmd) { - case AX25_KILL: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); -#ifdef CONFIG_AX25_DAMA_SLAVE - if (ax25_dev->dama.slave && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_off(ax25); -#endif - ax25_disconnect(ax25, ENETRESET); - break; + ax25_cb *s; + unsigned long flags; - case AX25_WINDOW: - if (ax25->modulus == AX25_MODULUS) { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) - return -EINVAL; - } else { - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) - return -EINVAL; - } - ax25->window = ax25_ctl.arg; - break; - - case AX25_T1: - if (ax25_ctl.arg < 1) - return -EINVAL; - ax25->rtt = (ax25_ctl.arg * HZ) / 2; - ax25->t1 = ax25_ctl.arg * HZ; - break; - - case AX25_T2: - if (ax25_ctl.arg < 1) - return -EINVAL; - ax25->t2 = ax25_ctl.arg * HZ; - break; - - case AX25_N2: - if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) - return -EINVAL; - ax25->n2count = 0; - ax25->n2 = ax25_ctl.arg; - break; - - case AX25_T3: - if (ax25_ctl.arg < 0) - return -EINVAL; - ax25->t3 = ax25_ctl.arg * HZ; - break; - - case AX25_IDLE: - if (ax25_ctl.arg < 0) - return -EINVAL; - ax25->idle = ax25_ctl.arg * 60 * HZ; - break; - - case AX25_PACLEN: - if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) - return -EINVAL; - ax25->paclen = ax25_ctl.arg; - break; - - default: - return -EINVAL; - } - - return 0; + printk(KERN_WARNING "ax25_kill_by_device(%s)\n", dev->name); + save_flags(flags); + cli(); + for (s = ax25_dev_list(dev); s != NULL; s = ax25_dev_list(dev)) { + /* + * list structure is being modified by ax25_remove_cb, + * so we can not walk along ax25->next path + */ + if (s->peer && s->peer->device != s->device) + ax25_destroy_cb(s->peer); + if (s->sk) { + ax25_remove_cb(s); + ax25_disconnect(s, ENETUNREACH); + ax25_close_socket(s->sk, ENETUNREACH); + } else + ax25_destroy_cb(s); + } + restore_flags(flags); } /* - * Fill in a created AX.25 created control block with the default - * values for a particular device. + * ------------------------------------------------------------------------ + * End of public area, all private functions of this module are defined + * here. + * ------------------------------------------------------------------------ */ -void ax25_fillin_cb(ax25_cb *ax25, ax25_dev *ax25_dev) -{ - ax25->ax25_dev = ax25_dev; - - if (ax25->ax25_dev != NULL) { - ax25->rtt = ax25_dev->values[AX25_VALUES_T1] / 2; - ax25->t1 = ax25_dev->values[AX25_VALUES_T1]; - ax25->t2 = ax25_dev->values[AX25_VALUES_T2]; - ax25->t3 = ax25_dev->values[AX25_VALUES_T3]; - ax25->n2 = ax25_dev->values[AX25_VALUES_N2]; - ax25->paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - ax25->idle = ax25_dev->values[AX25_VALUES_IDLE]; - ax25->backoff = ax25_dev->values[AX25_VALUES_BACKOFF]; - - if (ax25_dev->values[AX25_VALUES_AXDEFMODE]) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } - } else { - ax25->rtt = AX25_DEF_T1 / 2; - ax25->t1 = AX25_DEF_T1; - ax25->t2 = AX25_DEF_T2; - ax25->t3 = AX25_DEF_T3; - ax25->n2 = AX25_DEF_N2; - ax25->paclen = AX25_DEF_PACLEN; - ax25->idle = AX25_DEF_IDLE; - ax25->backoff = AX25_DEF_BACKOFF; - - if (AX25_DEF_AXDEFMODE) { - ax25->modulus = AX25_EMODULUS; - ax25->window = AX25_DEF_EWINDOW; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = AX25_DEF_WINDOW; - } - } -} +/* ---------------------------------------------------------------------*/ /* - * Create an empty AX.25 control block. + * Handle device status changes. */ -ax25_cb *ax25_create_cb(void) +static int ax25_device_event(struct notifier_block *this,unsigned long event, void *ptr) { - ax25_cb *ax25; - - if ((ax25 = kmalloc(sizeof(*ax25), GFP_ATOMIC)) == NULL) - return NULL; - - MOD_INC_USE_COUNT; - - memset(ax25, 0x00, sizeof(*ax25)); - - skb_queue_head_init(&ax25->write_queue); - skb_queue_head_init(&ax25->frag_queue); - skb_queue_head_init(&ax25->ack_queue); - skb_queue_head_init(&ax25->reseq_queue); - - init_timer(&ax25->timer); - init_timer(&ax25->t1timer); - init_timer(&ax25->t2timer); - init_timer(&ax25->t3timer); - init_timer(&ax25->idletimer); - - ax25_fillin_cb(ax25, NULL); + struct net_device *dev = (struct net_device *)ptr; - ax25->state = AX25_STATE_0; + /* Reject non AX.25 devices */ + if (dev->type != ARPHRD_AX25 || !AX25_PTR(dev)) + return NOTIFY_DONE; - return ax25; + switch (event) { + case NETDEV_UP: + ax25_dev_device_up(dev); + break; + case NETDEV_DOWN: + ax25_dev_device_down(dev); + break; + case NETDEV_REGISTER: + register_ax25device(dev); + break; + case NETDEV_UNREGISTER: + unregister_ax25device(dev); + break; + default: + break; + } + return NOTIFY_DONE; } + +/* ---------------------------------------------------------------------*/ /* * Handling for system calls applied via the various interfaces to an * AX25 socket object @@ -646,7 +370,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op switch (optname) { case AX25_WINDOW: - if (sk->protinfo.ax25->modulus == AX25_MODULUS) { + if (sk->protinfo.ax25->seqmask == AX25_SEQMASK) { if (opt < 1 || opt > 7) return -EINVAL; } else { @@ -659,14 +383,14 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T1: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->rtt = (opt * HZ) / 2; - sk->protinfo.ax25->t1 = opt * HZ; + sk->protinfo.ax25->t1 = opt; + sk->protinfo.ax25->rtt = (opt * AX25_TICS) / 4; return 0; case AX25_T2: - if (opt < 1) + if (opt < 0) return -EINVAL; - sk->protinfo.ax25->t2 = opt * HZ; + sk->protinfo.ax25->t2 = opt; return 0; case AX25_N2: @@ -678,13 +402,13 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op case AX25_T3: if (opt < 1) return -EINVAL; - sk->protinfo.ax25->t3 = opt * HZ; + sk->protinfo.ax25->t3 = opt * AX25_SLOWHZ; return 0; case AX25_IDLE: if (opt < 0) return -EINVAL; - sk->protinfo.ax25->idle = opt * 60 * HZ; + sk->protinfo.ax25->idle = opt * AX25_SLOWHZ; return 0; case AX25_BACKOFF: @@ -694,7 +418,7 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op return 0; case AX25_EXTSEQ: - sk->protinfo.ax25->modulus = opt ? AX25_EMODULUS : AX25_MODULUS; + sk->protinfo.ax25->seqmask = opt ? AX25_ESEQMASK : AX25_SEQMASK; return 0; case AX25_PIDINCL: @@ -712,19 +436,18 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op return 0; case SO_BINDTODEVICE: - if (optlen > IFNAMSIZ) optlen=IFNAMSIZ; + if (optlen > IFNAMSIZ) optlen = IFNAMSIZ; if (copy_from_user(devname, optval, optlen)) return -EFAULT; dev = dev_get_by_name(devname); if (dev == NULL) return -ENODEV; - if (sk->type == SOCK_SEQPACKET && + if (sk->type == SOCK_SEQPACKET && (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) return -EADDRNOTAVAIL; - - sk->protinfo.ax25->ax25_dev = ax25_dev_ax25dev(dev); - ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); + + ax25_fillin_cb(sk->protinfo.ax25, dev); return 0; default: @@ -732,10 +455,12 @@ static int ax25_setsockopt(struct socket *sock, int level, int optname, char *op } } +/* ---------------------------------------------------------------------*/ + static int ax25_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; - struct ax25_dev *ax25_dev; + struct net_device *dev; char devname[IFNAMSIZ]; void *valptr; int val = 0; @@ -759,11 +484,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T1: - val = sk->protinfo.ax25->t1 / HZ; + val = sk->protinfo.ax25->t1; break; case AX25_T2: - val = sk->protinfo.ax25->t2 / HZ; + val = sk->protinfo.ax25->t2; break; case AX25_N2: @@ -771,11 +496,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_T3: - val = sk->protinfo.ax25->t3 / HZ; + val = sk->protinfo.ax25->t3 / AX25_SLOWHZ; break; case AX25_IDLE: - val = sk->protinfo.ax25->idle / (60 * HZ); + val = sk->protinfo.ax25->idle / AX25_SLOWHZ; break; case AX25_BACKOFF: @@ -783,7 +508,7 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case AX25_EXTSEQ: - val = (sk->protinfo.ax25->modulus == AX25_EMODULUS); + val = (sk->protinfo.ax25->seqmask == AX25_ESEQMASK); break; case AX25_PIDINCL: @@ -799,11 +524,11 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op break; case SO_BINDTODEVICE: - ax25_dev = sk->protinfo.ax25->ax25_dev; + dev = sk->protinfo.ax25->device; - if (ax25_dev != NULL && ax25_dev->dev != NULL) { - strncpy(devname, ax25_dev->dev->name, IFNAMSIZ); - length = min(strlen(ax25_dev->dev->name)+1, maxlen); + if (dev != NULL) { + strncpy(devname, dev->name, IFNAMSIZ); + length = min(strlen(dev->name)+1, maxlen); devname[length-1] = '\0'; } else { *devname = '\0'; @@ -820,68 +545,84 @@ static int ax25_getsockopt(struct socket *sock, int level, int optname, char *op if (put_user(length, optlen)) return -EFAULT; - return copy_to_user(optval, valptr, length) ? -EFAULT : 0; + if (copy_to_user(optval, valptr, length)) + return -EFAULT; + + return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_listen(struct socket *sock, int backlog) { struct sock *sk = sock->sk; - if (sk->type == SOCK_SEQPACKET && sk->state != TCP_LISTEN) { + if (sk->type != SOCK_SEQPACKET) + return -EOPNOTSUPP; + + if (sk->state != TCP_LISTEN) { + /* + * POSIX VIOLATION: according to listen(2) the call can + * never return EADDRINUSE. bind(2) should have done this. + * However, things are different with AX.25. You are + * _required_ to bind() before connect() to set the + * source callsign of the outgoing connection. But as you + * may open multiple connections at one time with the + * same source callsign, you cannot perform this check + * within bind(). And as I like to have descriptive errors, + * EADDRINUSE is perfect to be returned here. + */ + if (ax25_find_listener(&sk->protinfo.ax25->addr.src, sk->protinfo.ax25->iamdigi, sk->protinfo.ax25->device)) + return -EADDRINUSE; + + ax25_insert_cb(sk->protinfo.ax25); sk->max_ack_backlog = backlog; sk->state = TCP_LISTEN; return 0; } - return -EOPNOTSUPP; + return -EINVAL; } -int ax25_create(struct socket *sock, int protocol) +/* ---------------------------------------------------------------------*/ + +static int ax25_create(struct socket *sock, int protocol) { struct sock *sk; ax25_cb *ax25; switch (sock->type) { - case SOCK_DGRAM: - if (protocol == 0 || protocol == PF_AX25) - protocol = AX25_P_TEXT; + case SOCK_DGRAM: + if (protocol == 0 || protocol == PF_AX25) + protocol = AX25_P_TEXT; + break; + case SOCK_SEQPACKET: + switch (protocol) { + case 0: + case PF_AX25: /* For CLX */ + protocol = AX25_P_TEXT; break; - case SOCK_SEQPACKET: - switch (protocol) { - case 0: - case PF_AX25: /* For CLX */ - protocol = AX25_P_TEXT; - break; - case AX25_P_SEGMENT: + case AX25_P_SEGMENT: #ifdef CONFIG_INET - case AX25_P_ARP: - case AX25_P_IP: + case AX25_P_ARP: #endif #ifdef CONFIG_NETROM - case AX25_P_NETROM: + case AX25_P_NETROM: #endif #ifdef CONFIG_ROSE - case AX25_P_ROSE: -#endif - return -ESOCKTNOSUPPORT; -#ifdef CONFIG_NETROM_MODULE - case AX25_P_NETROM: - if (ax25_protocol_is_registered(AX25_P_NETROM)) - return -ESOCKTNOSUPPORT; + case AX25_P_ROSE: #endif -#ifdef CONFIG_ROSE_MODULE - case AX25_P_ROSE: - if (ax25_protocol_is_registered(AX25_P_ROSE)) - return -ESOCKTNOSUPPORT; -#endif - default: - break; - } - break; - case SOCK_RAW: - break; - default: return -ESOCKTNOSUPPORT; + + default: + if (ax25_protocol_is_registered(protocol)) + return -ESOCKTNOSUPPORT; + } + break; + case SOCK_RAW: + break; + default: + return -ESOCKTNOSUPPORT; } if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) @@ -904,226 +645,162 @@ int ax25_create(struct socket *sock, int protocol) return 0; } -struct sock *ax25_make_new(struct sock *osk, struct ax25_dev *ax25_dev) +static int ax25_release(struct socket *sock) { - struct sock *sk; + struct sock *sk = sock->sk; ax25_cb *ax25; - if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) - return NULL; + if (sk == NULL) + return 0; - if ((ax25 = ax25_create_cb()) == NULL) { - sk_free(sk); - return NULL; - } + ax25 = sk->protinfo.ax25; - switch (osk->type) { - case SOCK_DGRAM: - break; - case SOCK_SEQPACKET: - break; - default: - sk_free(sk); - ax25_free_cb(ax25); - return NULL; - } + sk->state = TCP_CLOSE; + sk->shutdown = SHUTDOWN_MASK; + /* + * don't wake me, I'm dying: this one has cost me nerves a bit. + * Seems that we should not attempt to wake an application + * that is currently exiting, which is exactly what state_change() + * does. It results in calling __wake_up() with invalid arguments + */ + if (!(current->flags & PF_EXITING)) + sk->state_change(sk); + sk->dead = 1; - sock_init_data(NULL, sk); + if (sk->type == SOCK_STREAM || sk->type == SOCK_SEQPACKET) { + switch (ax25->state) { + default: + ax25_remove_cb(ax25); + break; - sk->destruct = ax25_free_sock; - sk->type = osk->type; - sk->socket = osk->socket; - sk->priority = osk->priority; - sk->protocol = osk->protocol; - sk->rcvbuf = osk->rcvbuf; - sk->sndbuf = osk->sndbuf; - sk->debug = osk->debug; - sk->state = TCP_ESTABLISHED; - sk->sleep = osk->sleep; - sk->zapped = osk->zapped; - - ax25->modulus = osk->protinfo.ax25->modulus; - ax25->backoff = osk->protinfo.ax25->backoff; - ax25->pidincl = osk->protinfo.ax25->pidincl; - ax25->iamdigi = osk->protinfo.ax25->iamdigi; - ax25->rtt = osk->protinfo.ax25->rtt; - ax25->t1 = osk->protinfo.ax25->t1; - ax25->t2 = osk->protinfo.ax25->t2; - ax25->t3 = osk->protinfo.ax25->t3; - ax25->n2 = osk->protinfo.ax25->n2; - ax25->idle = osk->protinfo.ax25->idle; - ax25->paclen = osk->protinfo.ax25->paclen; - ax25->window = osk->protinfo.ax25->window; - - ax25->ax25_dev = ax25_dev; - ax25->source_addr = osk->protinfo.ax25->source_addr; - - if (osk->protinfo.ax25->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - sk_free(sk); - return NULL; + case AX25_STATE_3: /* connected */ + case AX25_STATE_4: /* timer recovery */ + ax25_set_cond(ax25, AX25_COND_RELEASE); + break; } - - memcpy(ax25->digipeat, osk->protinfo.ax25->digipeat, sizeof(ax25_digi)); } - sk->protinfo.ax25 = ax25; - ax25->sk = sk; - - return sk; -} - -static int ax25_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk == NULL) return 0; - - if (sk->type == SOCK_SEQPACKET) { - switch (sk->protinfo.ax25->state) { - case AX25_STATE_0: - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); - break; - - case AX25_STATE_1: - case AX25_STATE_2: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(sk->protinfo.ax25, 0); - ax25_destroy_socket(sk->protinfo.ax25); - break; - - case AX25_STATE_3: - case AX25_STATE_4: - ax25_clear_queues(sk->protinfo.ax25); - sk->protinfo.ax25->n2count = 0; - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_control(sk->protinfo.ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_stop_t2timer(sk->protinfo.ax25); - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); - break; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_stop_t3timer(sk->protinfo.ax25); - ax25_stop_idletimer(sk->protinfo.ax25); - break; -#endif - } - ax25_calculate_t1(sk->protinfo.ax25); - ax25_start_t1timer(sk->protinfo.ax25); - sk->protinfo.ax25->state = AX25_STATE_2; - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - sk->destroy = 1; - break; - - default: - break; - } + sk->protinfo.ax25 = NULL; + if (ax25->inserted && ax25->device != NULL) { + ax25->killtimer = 0; + ax25->sk = NULL; } else { - sk->state = TCP_CLOSE; - sk->shutdown |= SEND_SHUTDOWN; - sk->state_change(sk); - sk->dead = 1; - ax25_destroy_socket(sk->protinfo.ax25); + ax25_destroy_cb(ax25); } - - sock->sk = NULL; - sk->socket = NULL; /* Not used, but we should do this */ - + ax25_destroy_socket(sk); return 0; } +/* ---------------------------------------------------------------------*/ /* - * We support a funny extension here so you can (as root) give any callsign - * digipeated via a local address as source. This hack is obsolete now - * that we've implemented support for SO_BINDTODEVICE. It is however small - * and trivially backward compatible. + * Former semantics: + * + * - struct sockaddr_ax25 contains the interface callsign, outgoing + * user connects either get the interface callsign or the one + * provided by the uid/callsign translation table for the source + * address + * + * - struct full_sockaddr_ax25 provides the interface callsign as + * the first digipeater, fsa.fsa_ax25call provides the source + * address for the connection. + * + * New semantics: + * + * We now have SO_BINDTODEVICE, ax25_bind (should) only set the + * source address. Thus we'll allow the "bind to device callsign + * provided with the digipeater field" hack only for backward + * compatibility. + * + * NB: I don't follow Matthias' and Jens' patch here as I do + * plan to allow multiple callsigns for one uid (including + * multiple SSIDs) and assigning user callsigns per interface. */ + static int ax25_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; - ax25_address *call; - ax25_dev *ax25_dev = NULL; + struct net_device *dev; + ax25_address *call = NULL; + /* already bound */ if (sk->zapped == 0) return -EINVAL; - if (addr_len != sizeof(struct sockaddr_ax25) && - addr_len != sizeof(struct full_sockaddr_ax25)) { - /* support for old structure may go away some time */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - printk(KERN_WARNING "ax25_bind(): %s uses old (6 digipeater) socket structure.\n", - current->comm); - } - - if (addr->fsa_ax25.sax25_family != AF_AX25) + if (addr_len != sizeof(struct sockaddr_ax25) && + addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; - call = ax25_findbyuid(current->euid); - if (call == NULL && ax25_uid_policy && !capable(CAP_NET_ADMIN)) - return -EACCES; - - if (call == NULL) - sk->protinfo.ax25->source_addr = addr->fsa_ax25.sax25_call; - else - sk->protinfo.ax25->source_addr = *call; + /* wrong family */ + if (addr->fsax25_family != AF_AX25) + return -EINVAL; /* - * User already set interface with SO_BINDTODEVICE + * User did not set interface with SO_BINDTODEVICE + * thus we'll use the compatibility code */ - if (sk->protinfo.ax25->ax25_dev != NULL) - goto done; + dev = sk->protinfo.ax25->device; + if (dev == NULL) { + /* Try to find the interface... */ + if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) { + /* device callsign provided... */ + if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && + (dev = ax25rtr_get_dev(&addr->fsa_digipeater[0])) == NULL) + return -EADDRNOTAVAIL; + } else { + /* addr->fsax25_call is device callsign */ + if ((dev = ax25rtr_get_dev(&addr->fsax25_call)) == NULL) + return -EADDRNOTAVAIL; + } + + ax25_fillin_cb(sk->protinfo.ax25, dev); + } - if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsa_ax25.sax25_ndigis == 1) { - if (ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) != 0 && - (ax25_dev = ax25_addr_ax25dev(&addr->fsa_digipeater[0])) == NULL) - return -EADDRNOTAVAIL; - } else { - if ((ax25_dev = ax25_addr_ax25dev(&addr->fsa_ax25.sax25_call)) == NULL) - return -EADDRNOTAVAIL; + /* root can do whatever (s)he likes, but anyone else... */ + if (!capable(CAP_NET_BIND_SERVICE)) + { + call = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name); + if (call == NULL && ax25_uid_policy) + return -EACCES; } - if (ax25_dev != NULL) - ax25_fillin_cb(sk->protinfo.ax25, ax25_dev); + if (call == NULL) call = &addr->fsax25_call; -done: - ax25_insert_socket(sk->protinfo.ax25); + sk->protinfo.ax25->addr.src = *call; + sk->protinfo.ax25->addr.dcount = 0; + sk->protinfo.ax25->addr.lastrepeat = -1; + +// ax25_insert_socket(sk->protinfo.ax25); /* FIXME: gone with Matthias' patch, intentionally? */ sk->zapped = 0; return 0; } -/* - * FIXME: nonblock behaviour looks like it may have a bug. - */ -static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) +/* ---------------------------------------------------------------------*/ + +static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) { struct sock *sk = sock->sk; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - ax25_digi *digi = NULL; + ax25_addr_t *addr; + ax25_address *dest; int ct = 0, err; /* deal with restarts */ if (sock->state == SS_CONNECTING) { switch (sk->state) { case TCP_SYN_SENT: /* still trying */ - return -EINPROGRESS; + if (!(flags & O_NONBLOCK)) + goto wait_for_con; + return -EALREADY; case TCP_ESTABLISHED: /* connection established */ sock->state = SS_CONNECTED; return 0; case TCP_CLOSE: /* connection refused */ + case TCP_CLOSE_WAIT: sock->state = SS_UNCONNECTED; return -ECONNREFUSED; } @@ -1139,118 +816,77 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le * some sanity checks. code further down depends on this */ - if (addr_len == sizeof(struct sockaddr_ax25)) { - /* support for this will go away in early 2.5.x */ + if (addr_len == sizeof(struct sockaddr_ax25)) printk(KERN_WARNING "ax25_connect(): %s uses obsolete socket structure\n", current->comm); - } - else if (addr_len != sizeof(struct full_sockaddr_ax25)) { - /* support for old structure may go away some time */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - printk(KERN_WARNING "ax25_connect(): %s uses old (6 digipeater) socket structure.\n", - current->comm); - } - - if (fsa->fsa_ax25.sax25_family != AF_AX25) + else if (addr_len != sizeof(struct full_sockaddr_ax25)) return -EINVAL; - if (sk->protinfo.ax25->digipeat != NULL) { - kfree(sk->protinfo.ax25->digipeat); - sk->protinfo.ax25->digipeat = NULL; - } - /* * Handle digi-peaters to be used. */ - if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsa_ax25.sax25_ndigis != 0) { + SOCK_DEBUG(sk, "ax25_connect: ndigi=%d\n", fsa->fsax25_ndigis); + + addr = &sk->protinfo.ax25->addr; + addr->dcount = 0; + addr->lastrepeat = -1; + if (addr_len > sizeof(struct sockaddr_ax25) && fsa->fsax25_ndigis != 0) { /* Valid number of digipeaters ? */ - if (fsa->fsa_ax25.sax25_ndigis < 1 || fsa->fsa_ax25.sax25_ndigis > AX25_MAX_DIGIS) + if (fsa->fsax25_ndigis < 0 || fsa->fsax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - if ((digi = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) - return -ENOBUFS; + addr->dcount = fsa->fsax25_ndigis; - digi->ndigi = fsa->fsa_ax25.sax25_ndigis; - digi->lastrepeat = -1; - - while (ct < fsa->fsa_ax25.sax25_ndigis) { - if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) { - digi->repeated[ct] = 1; - digi->lastrepeat = ct; - } else { - digi->repeated[ct] = 0; - } - digi->calls[ct] = fsa->fsa_digipeater[ct]; + while (ct < fsa->fsax25_ndigis) { + if ((fsa->fsa_digipeater[ct].ax25_call[6] & AX25_HBIT) && sk->protinfo.ax25->iamdigi) + addr->lastrepeat = ct; + addr->digipeater[ct] = fsa->fsa_digipeater[ct]; ct++; } + + /* where to go next? either to next digipeater in path ...*/ + dest = &addr->digipeater[addr->lastrepeat+1]; + } else { + /* ... or directly to the destination */ + dest = &fsa->fsax25_call; } - /* - * Must bind first - autobinding in this may or may not work. If - * the socket is already bound, check to see if the device has - * been filled in, error if it hasn't. - */ - if (sk->zapped) { - /* check if we can remove this feature. It is broken. */ - printk(KERN_WARNING "ax25_connect(): %s uses autobind, please contact jreuter@yaina.de\n", - current->comm); - if ((err = ax25_rt_autobind(sk->protinfo.ax25, &fsa->fsa_ax25.sax25_call)) < 0) + if (sk->protinfo.ax25->device == NULL) { + if ((err = ax25_rt_fillin_dev(sk->protinfo.ax25, dest)) < 0) return err; - ax25_fillin_cb(sk->protinfo.ax25, sk->protinfo.ax25->ax25_dev); - ax25_insert_socket(sk->protinfo.ax25); - } else { - if (sk->protinfo.ax25->ax25_dev == NULL) - return -EHOSTUNREACH; + SOCK_DEBUG(sk, "ax25_connect: device filled in\n"); } + addr->dest = fsa->fsax25_call; - if (sk->type == SOCK_SEQPACKET && ax25_find_cb(&sk->protinfo.ax25->source_addr, &fsa->fsa_ax25.sax25_call, digi, sk->protinfo.ax25->ax25_dev->dev) != NULL) { - if (digi != NULL) kfree(digi); - return -EADDRINUSE; /* Already such a connection */ + if (sk->type == SOCK_SEQPACKET) { + ax25_cb* ax25 = ax25_find_cb(addr, sk->protinfo.ax25->device); + if (ax25) { + if (ax25->state != AX25_STATE_0) + return -EADDRINUSE; /* Already such a connection */ + ax25_destroy_cb(ax25); + } } - sk->protinfo.ax25->dest_addr = fsa->fsa_ax25.sax25_call; - sk->protinfo.ax25->digipeat = digi; + ax25_insert_cb(sk->protinfo.ax25); /* First the easy one */ - if (sk->type != SOCK_SEQPACKET) { + if (sk->type != SOCK_SEQPACKET && sk->type != SOCK_STREAM) { sock->state = SS_CONNECTED; sk->state = TCP_ESTABLISHED; return 0; } /* Move to connecting socket, ax.25 lapb WAIT_UA.. */ - sock->state = SS_CONNECTING; - sk->state = TCP_SYN_SENT; - - switch (sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(sk->protinfo.ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - sk->protinfo.ax25->modulus = AX25_MODULUS; - sk->protinfo.ax25->window = sk->protinfo.ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - if (sk->protinfo.ax25->ax25_dev->dama.slave) - ax25_ds_establish_data_link(sk->protinfo.ax25); - else - ax25_std_establish_data_link(sk->protinfo.ax25); - break; -#endif - } - - sk->protinfo.ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(sk->protinfo.ax25); + sock->state = SS_CONNECTING; + sk->state = TCP_SYN_SENT; + /* Start going SABM SABM until a UA or a give up and DM */ + ax25_establish_data_link(sk->protinfo.ax25); /* Now the loop */ if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) return -EINPROGRESS; + wait_for_con: cli(); /* To avoid races on the sleep */ /* A DM or timeout will go to closed, a UA will go to ABM */ @@ -1270,12 +906,11 @@ static int ax25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le } sock->state = SS_CONNECTED; - sti(); - return 0; } +/* ---------------------------------------------------------------------*/ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) { @@ -1310,12 +945,14 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) } } while (skb == NULL); - newsk = skb->sk; - newsk->pair = NULL; + newsk = skb->sk; + newsk->pair = NULL; newsk->socket = newsock; newsk->sleep = &newsock->wait; /* Now attach up the new socket */ + skb->sk = NULL; + skb->destructor = NULL; kfree_skb(skb); sk->ack_backlog--; newsock->sk = newsk; @@ -1324,132 +961,108 @@ static int ax25_accept(struct socket *sock, struct socket *newsock, int flags) return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer) { + struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)uaddr; struct sock *sk = sock->sk; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)uaddr; - unsigned char ndigi, i; + unsigned char dcount; - if (peer != 0) { + if (peer) { if (sk->state != TCP_ESTABLISHED) return -ENOTCONN; - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->dest_addr; - fsa->fsa_ax25.sax25_ndigis = 0; - - if (sk->protinfo.ax25->digipeat != NULL) { - ndigi = sk->protinfo.ax25->digipeat->ndigi; - fsa->fsa_ax25.sax25_ndigis = ndigi; - for (i = 0; i < ndigi; i++) - fsa->fsa_digipeater[i] = sk->protinfo.ax25->digipeat->calls[i]; - } + sax->fsax25_family = AF_AX25; + sax->fsax25_call = sk->protinfo.ax25->addr.dest; + dcount = sax->fsax25_ndigis = sk->protinfo.ax25->addr.dcount; + memcpy(sax->fsa_digipeater, sk->protinfo.ax25->addr.digipeater, dcount * AX25_ADDR_LEN); } else { - fsa->fsa_ax25.sax25_family = AF_AX25; - fsa->fsa_ax25.sax25_call = sk->protinfo.ax25->source_addr; - fsa->fsa_ax25.sax25_ndigis = 1; - if (sk->protinfo.ax25->ax25_dev != NULL) { - memcpy(&fsa->fsa_digipeater[0], sk->protinfo.ax25->ax25_dev->dev->dev_addr, AX25_ADDR_LEN); + sax->fsax25_family = AF_AX25; + sax->fsax25_call = sk->protinfo.ax25->addr.src; + + if (sk->protinfo.ax25->device != NULL) { + sax->fsax25_ndigis = 1; + memcpy(&sax->fsa_digipeater[0], sk->protinfo.ax25->device->dev_addr, AX25_ADDR_LEN); } else { - fsa->fsa_digipeater[0] = null_ax25_address; + sax->fsax25_ndigis = 0; } } *uaddr_len = sizeof (struct full_sockaddr_ax25); return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm) { struct sock *sk = sock->sk; struct sockaddr_ax25 *usax = (struct sockaddr_ax25 *)msg->msg_name; int err; - struct sockaddr_ax25 sax; struct sk_buff *skb; unsigned char *asmptr; int size; - ax25_digi *dp; - ax25_digi dtmp; int lv; int addr_len = msg->msg_namelen; + ax25_addr_t addr; if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR)) return -EINVAL; + /* socket must be bound to a name */ if (sk->zapped) return -EADDRNOTAVAIL; + /* socket ist shut down */ if (sk->shutdown & SEND_SHUTDOWN) { send_sig(SIGPIPE, current, 0); return -EPIPE; } - if (sk->protinfo.ax25->ax25_dev == NULL) + if (sk->protinfo.ax25->device == NULL) return -ENETUNREACH; - if (usax != NULL) { + if (addr_len != 0) { if (usax->sax25_family != AF_AX25) return -EINVAL; + if (sk->type == SOCK_SEQPACKET) + return -EISCONN; - if (addr_len == sizeof(struct sockaddr_ax25)) { + if (addr_len != sizeof(struct sockaddr_ax25)) { printk(KERN_WARNING "ax25_sendmsg(): %s uses obsolete socket structure\n", current->comm); - } - else if (addr_len != sizeof(struct full_sockaddr_ax25)) { - /* support for old structure may go away some time */ - if ((addr_len < sizeof(struct sockaddr_ax25) + sizeof(ax25_address) * 6) || - (addr_len > sizeof(struct full_sockaddr_ax25))) - return -EINVAL; - - printk(KERN_WARNING "ax25_sendmsg(): %s uses old (6 digipeater) socket structure.\n", - current->comm); - } + } else if (addr_len != sizeof(struct full_sockaddr_ax25)) + return -EINVAL; if (addr_len > sizeof(struct sockaddr_ax25) && usax->sax25_ndigis != 0) { - int ct = 0; struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)usax; + int ct; /* Valid number of digipeaters ? */ - if (usax->sax25_ndigis < 1 || usax->sax25_ndigis > AX25_MAX_DIGIS) + if (usax->sax25_ndigis < 0 || usax->sax25_ndigis > AX25_MAX_DIGIS) return -EINVAL; - dtmp.ndigi = usax->sax25_ndigis; - - while (ct < usax->sax25_ndigis) { - dtmp.repeated[ct] = 0; - dtmp.calls[ct] = fsa->fsa_digipeater[ct]; - ct++; - } - - dtmp.lastrepeat = 0; + for (ct = 0; ct < usax->sax25_ndigis; ct++) + addr.digipeater[ct] = fsa->fsa_digipeater[ct]; + addr.lastrepeat = -1; } - - sax = *usax; - if (sk->type == SOCK_SEQPACKET && ax25cmp(&sk->protinfo.ax25->dest_addr, &sax.sax25_call) != 0) - return -EISCONN; - if (usax->sax25_ndigis == 0) - dp = NULL; - else - dp = &dtmp; + addr.dcount = usax->sax25_ndigis; + addr.dest = usax->sax25_call; } else { - /* - * FIXME: 1003.1g - if the socket is like this because - * it has become closed (not started closed) and is VC - * we ought to SIGPIPE, EPIPE - */ - if (sk->state != TCP_ESTABLISHED) + if (sk->state != TCP_ESTABLISHED) { + if (sk->dead) { + send_sig(SIGPIPE, current, 0); + return -EPIPE; + } return -ENOTCONN; - sax.sax25_family = AF_AX25; - sax.sax25_call = sk->protinfo.ax25->dest_addr; - dp = sk->protinfo.ax25->digipeat; + } + addr = sk->protinfo.ax25->addr; } - SOCK_DEBUG(sk, "AX.25: sendto: Addresses built.\n"); - - /* Build a packet */ - SOCK_DEBUG(sk, "AX.25: sendto: building packet.\n"); + SOCK_DEBUG(sk, "AX.25: sendto: Addresses built, building packet.\n"); /* Assume the worst case */ - size = len + 3 + ax25_addr_size(dp) + AX25_BPQ_HEADER_LEN; + size = len + 3 + ax25_sizeof_addr(&addr) + AX25_BPQ_HEADER_LEN; if ((skb = sock_alloc_send_skb(sk, size, 0, msg->msg_flags & MSG_DONTWAIT, &err)) == NULL) return err; @@ -1463,10 +1076,8 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct skb->nh.raw = skb->data; /* Add the PID if one is not supplied by the user in the skb */ - if (!sk->protinfo.ax25->pidincl) { - asmptr = skb_push(skb, 1); - *asmptr = sk->protocol; - } + if (!sk->protinfo.ax25->pidincl) + *skb_push(skb, 1) = sk->protocol; SOCK_DEBUG(sk, "AX.25: Transmitting buffer\n"); @@ -1476,44 +1087,38 @@ static int ax25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct kfree_skb(skb); return -ENOTCONN; } - ax25_output(sk->protinfo.ax25, sk->protinfo.ax25->paclen, skb); /* Shove it onto the queue and kick */ - return len; - } else { - asmptr = skb_push(skb, 1 + ax25_addr_size(dp)); - - SOCK_DEBUG(sk, "Building AX.25 Header (dp=%p).\n", dp); - - if (dp != NULL) - SOCK_DEBUG(sk, "Num digipeaters=%d\n", dp->ndigi); - - /* Build an AX.25 header */ - asmptr += (lv = ax25_addr_build(asmptr, &sk->protinfo.ax25->source_addr, &sax.sax25_call, dp, AX25_COMMAND, AX25_MODULUS)); + } - SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); + addr.src = sk->protinfo.ax25->addr.src; + asmptr = skb_push(skb, ax25_sizeof_addr(&addr)+1); - skb->h.raw = asmptr; + SOCK_DEBUG(sk, "Num digipeaters=%d\n", addr.dcount); - SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); + /* Build an AX.25 header */ + lv = ax25_build_addr(asmptr, &addr, AX25_COMMAND, AX25_SEQMASK); + asmptr += lv; - *asmptr = AX25_UI; + SOCK_DEBUG(sk, "Built header (%d bytes)\n",lv); + SOCK_DEBUG(sk, "base=%p pos=%p\n", skb->data, asmptr); - /* Datagram frames go straight out of the door as UI */ - skb->dev = sk->protinfo.ax25->ax25_dev->dev; + *asmptr = AX25_UI; - ax25_queue_xmit(skb); + /* Datagram frames go straight out of the door as UI */ + ax25_send_unproto(skb, sk->protinfo.ax25->device); - return len; - } + return len; } +/* ---------------------------------------------------------------------*/ + static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags, struct scm_cookie *scm) { struct sock *sk = sock->sk; int copied; struct sk_buff *skb; - int er; + int err; /* * This works for seqpacket too. The receiver has ordered the @@ -1523,8 +1128,8 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f return -ENOTCONN; /* Now we can treat all alike */ - if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &er)) == NULL) - return er; + if ((skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &err)) == NULL) + return err; if (!sk->protinfo.ax25->pidincl) skb_pull(skb, 1); /* Remove PID */ @@ -1540,25 +1145,19 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); if (msg->msg_namelen != 0) { - struct sockaddr_ax25 *sax = (struct sockaddr_ax25 *)msg->msg_name; - ax25_digi digi; - ax25_address dest; + struct full_sockaddr_ax25 *sax = (struct full_sockaddr_ax25 *)msg->msg_name; + ax25_pktinfo pkt; - ax25_addr_parse(skb->mac.raw+1, skb->data-skb->mac.raw-1, NULL, &dest, &digi, NULL, NULL); + ax25_parse_addr(skb->mac.raw, skb->data-skb->mac.raw, &pkt); - sax->sax25_family = AF_AX25; - /* We set this correctly, even though we may not let the - application know the digi calls further down (because it - did NOT ask to know them). This could get political... **/ - sax->sax25_ndigis = digi.ndigi; - sax->sax25_call = dest; + sax->fsax25_family = AF_AX25; + sax->fsax25_ndigis = pkt.addr.dcount; + sax->fsax25_call = pkt.addr.dest; - if (sax->sax25_ndigis != 0) { + if (sax->fsax25_ndigis != 0) { int ct; - struct full_sockaddr_ax25 *fsa = (struct full_sockaddr_ax25 *)sax; - - for (ct = 0; ct < digi.ndigi; ct++) - fsa->fsa_digipeater[ct] = digi.calls[ct]; + for (ct = 0; ct < pkt.addr.dcount; ct++) + sax->fsa_digipeater[ct] = pkt.addr.digipeater[ct]; } msg->msg_namelen = sizeof(struct full_sockaddr_ax25); } @@ -1568,12 +1167,31 @@ static int ax25_recvmsg(struct socket *sock, struct msghdr *msg, int size, int f return copied; } -static int ax25_shutdown(struct socket *sk, int how) +/* ---------------------------------------------------------------------*/ + +static int ax25_shutdown(struct socket *sock, int how) { - /* FIXME - generate DM and RNR states */ - return -EOPNOTSUPP; + switch (how) { + case 0: + sock->sk->shutdown = RCV_SHUTDOWN; + break; + + case 1: + sock->sk->shutdown = SEND_SHUTDOWN; + break; + + case 2: + sock->sk->shutdown = SHUTDOWN_MASK; + break; + + default: + return -EINVAL; + } + return 0; } +/* ---------------------------------------------------------------------*/ + static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) { struct sock *sk = sock->sk; @@ -1584,7 +1202,9 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) amount = sk->sndbuf - atomic_read(&sk->wmem_alloc); if (amount < 0) amount = 0; - return put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; + return 0; } case TIOCINQ: { @@ -1593,15 +1213,19 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) /* These two are safe on a single CPU system as only user tasks fiddle here */ if ((skb = skb_peek(&sk->receive_queue)) != NULL) amount = skb->len; - return put_user(amount, (int *)arg); + if (put_user(amount, (int *)arg)) + return -EFAULT; + return 0; } case SIOCGSTAMP: if (sk != NULL) { if (sk->stamp.tv_sec == 0) return -ENOENT; - return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0; - } + if (copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval))) + return -EFAULT; + return 0; + } return -EINVAL; case SIOCAX25ADDUID: /* Add a uid to the uid/call map table */ @@ -1637,29 +1261,31 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return -EPERM; return ax25_ctl_ioctl(cmd, (void *)arg); - case SIOCAX25GETINFO: + case SIOCAX25GETINFO: case SIOCAX25GETINFOOLD: { struct ax25_info_struct ax25_info; - ax25_info.t1 = sk->protinfo.ax25->t1 / HZ; - ax25_info.t2 = sk->protinfo.ax25->t2 / HZ; - ax25_info.t3 = sk->protinfo.ax25->t3 / HZ; - ax25_info.idle = sk->protinfo.ax25->idle / (60 * HZ); + read_lock(&sk->protinfo.ax25->timer_lock); + ax25_info.t1 = sk->protinfo.ax25->t1; + ax25_info.t2 = sk->protinfo.ax25->t2; + ax25_info.t3 = sk->protinfo.ax25->t3 / AX25_SLOWHZ; + ax25_info.idle = sk->protinfo.ax25->idle / AX25_SLOWHZ; ax25_info.n2 = sk->protinfo.ax25->n2; - ax25_info.t1timer = ax25_display_timer(&sk->protinfo.ax25->t1timer) / HZ; - ax25_info.t2timer = ax25_display_timer(&sk->protinfo.ax25->t2timer) / HZ; - ax25_info.t3timer = ax25_display_timer(&sk->protinfo.ax25->t3timer) / HZ; - ax25_info.idletimer = ax25_display_timer(&sk->protinfo.ax25->idletimer) / (60 * HZ); + ax25_info.t1timer = sk->protinfo.ax25->wrt_timer; + ax25_info.t3timer = sk->protinfo.ax25->wrt_timer / AX25_SLOWHZ; + ax25_info.t2timer = sk->protinfo.ax25->ack_timer; + ax25_info.idletimer = sk->protinfo.ax25->idletimer / AX25_SLOWHZ; ax25_info.n2count = sk->protinfo.ax25->n2count; ax25_info.state = sk->protinfo.ax25->state; ax25_info.rcv_q = atomic_read(&sk->rmem_alloc); ax25_info.snd_q = atomic_read(&sk->wmem_alloc); ax25_info.vs = sk->protinfo.ax25->vs; - ax25_info.vr = sk->protinfo.ax25->vr; - ax25_info.va = sk->protinfo.ax25->va; - ax25_info.vs_max = sk->protinfo.ax25->vs; /* reserved */ + ax25_info.vr = sk->protinfo.ax25->vr; + ax25_info.va = sk->protinfo.ax25->va; + ax25_info.vs_max = sk->protinfo.ax25->vs_max; ax25_info.paclen = sk->protinfo.ax25->paclen; ax25_info.window = sk->protinfo.ax25->window; + read_unlock(&sk->protinfo.ax25->timer_lock); /* old structure? */ if (cmd == SIOCAX25GETINFOOLD) { @@ -1670,12 +1296,14 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) warned=1; } - if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) + if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct_depreciated))) return -EFAULT; } else { - if (copy_to_user((void *)arg, &ax25_info, sizeof(struct ax25_info_struct))) + if (copy_to_user((void *) arg, &ax25_info, sizeof(struct ax25_info_struct))) return -EINVAL; - } + } + + return 0; } @@ -1709,48 +1337,47 @@ static int ax25_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) return 0; } -static int ax25_get_info(char *buffer, char **start, off_t offset, int length) -{ - ax25_cb *ax25; - int k; - int len = 0; - off_t pos = 0; - off_t begin = 0; +/* ---------------------------------------------------------------------*/ - cli(); +static int ax25_print_list(char *buffer, off_t *begin, off_t offset, int length, off_t *pos, ax25_cb *ax25, char *devname) { + int len = 0; /* * New format: * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q inode */ - - for (ax25 = ax25_list; ax25 != NULL; ax25 = ax25->next) { + + for ( ; ax25 != NULL; ax25 = ax25->next) { + int k; + len += sprintf(buffer+len, "%8.8lx %s %s%s ", - (long) ax25, - ax25->ax25_dev == NULL? "???" : ax25->ax25_dev->dev->name, - ax2asc(&ax25->source_addr), - ax25->iamdigi? "*":""); - - len += sprintf(buffer+len, "%s", ax2asc(&ax25->dest_addr)); - - for (k=0; (ax25->digipeat != NULL) && (k < ax25->digipeat->ndigi); k++) { + (long) ax25, + devname, + ax2asc(&ax25->addr.src), + ax25->iamdigi? "*":""); + + len += sprintf(buffer+len, "%s", ax2asc(&ax25->addr.dest)); + + for (k=0; k < ax25->addr.dcount; k++) { len += sprintf(buffer+len, ",%s%s", - ax2asc(&ax25->digipeat->calls[k]), - ax25->digipeat->repeated[k]? "*":""); + ax2asc(&ax25->addr.digipeater[k]), + ax25->addr.lastrepeat == k ? "*":""); } - - len += sprintf(buffer+len, " %d %d %d %d %lu %lu %lu %lu %lu %lu %lu %lu %d %d %lu %d %d", + + read_lock(&ax25->timer_lock); + len += sprintf(buffer+len, " %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d", ax25->state, ax25->vs, ax25->vr, ax25->va, - ax25_display_timer(&ax25->t1timer) / HZ, ax25->t1 / HZ, - ax25_display_timer(&ax25->t2timer) / HZ, ax25->t2 / HZ, - ax25_display_timer(&ax25->t3timer) / HZ, ax25->t3 / HZ, - ax25_display_timer(&ax25->idletimer) / (60 * HZ), - ax25->idle / (60 * HZ), + ax25->wrt_timer , ax25->t1, + ax25->ack_timer , ax25->t2, + ax25->t3 , ax25->t3, + ax25->idletimer / AX25_SLOWHZ, + ax25->idle / AX25_SLOWHZ, ax25->n2count, ax25->n2, - ax25->rtt / HZ, + ax25->rtt / AX25_TICS, ax25->window, ax25->paclen); + read_unlock(&ax25->timer_lock); if (ax25->sk != NULL) { len += sprintf(buffer + len, " %d %d %ld\n", @@ -1761,121 +1388,70 @@ static int ax25_get_info(char *buffer, char **start, off_t offset, int length) len += sprintf(buffer + len, " * * *\n"); } - pos = begin + len; + *pos = *begin + len; - if (pos < offset) { - len = 0; - begin = pos; + if (*pos < offset) { + len = 0; + *begin = *pos; } - if (pos > offset + length) + if (*pos > offset + length) break; } - sti(); - - *start = buffer + (offset - begin); - len -= (offset - begin); - - if (len > length) len = length; - - return(len); + return len; } -static struct net_proto_family ax25_family_ops = { - family: PF_AX25, - create: ax25_create, -}; -static struct proto_ops SOCKOPS_WRAPPED(ax25_proto_ops) = { - family: PF_AX25, +/* ---------------------------------------------------------------------*/ - release: ax25_release, - bind: ax25_bind, - connect: ax25_connect, - socketpair: sock_no_socketpair, - accept: ax25_accept, - getname: ax25_getname, - poll: datagram_poll, - ioctl: ax25_ioctl, - listen: ax25_listen, - shutdown: ax25_shutdown, - setsockopt: ax25_setsockopt, - getsockopt: ax25_getsockopt, - sendmsg: ax25_sendmsg, - recvmsg: ax25_recvmsg, - mmap: sock_no_mmap, -}; +static int ax25_get_info(char *buffer, char **start, off_t offset, int length) +{ + struct net_device *dev; + int len = 0; + off_t begin = 0; + off_t pos = begin; + int i; -#include <linux/smp_lock.h> -SOCKOPS_WRAP(ax25_proto, PF_AX25); + read_lock(&ax25_dev_lock); -/* - * Called by socket.c on kernel start up - */ -static struct packet_type ax25_packet_type = { - type: __constant_htons(ETH_P_AX25), - func: ax25_kiss_rcv, -}; + len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_list, "*"); -static struct notifier_block ax25_dev_notifier = { - notifier_call: ax25_device_event, -}; + for (i = 0; i < AX25_MAX_DEVICES; i++) { + dev = ax25_devices[i]; + if (!dev || !(dev->flags & IFF_UP)) + continue; -EXPORT_SYMBOL(ax25_encapsulate); -EXPORT_SYMBOL(ax25_rebuild_header); -EXPORT_SYMBOL(ax25_findbyuid); -EXPORT_SYMBOL(ax25_find_cb); -EXPORT_SYMBOL(ax25_linkfail_register); -EXPORT_SYMBOL(ax25_linkfail_release); -EXPORT_SYMBOL(ax25_listen_register); -EXPORT_SYMBOL(ax25_listen_release); -EXPORT_SYMBOL(ax25_protocol_register); -EXPORT_SYMBOL(ax25_protocol_release); -EXPORT_SYMBOL(ax25_send_frame); -EXPORT_SYMBOL(ax25_uid_policy); -EXPORT_SYMBOL(ax25cmp); -EXPORT_SYMBOL(ax2asc); -EXPORT_SYMBOL(asc2ax); -EXPORT_SYMBOL(null_ax25_address); -EXPORT_SYMBOL(ax25_display_timer); + len += ax25_print_list(buffer + len, &begin, offset, length, &pos, ax25_dev_list(dev), dev->name); + } -static const char banner[] __initdata = KERN_INFO "NET4: G4KLX/GW4PTS AX.25 for Linux. Version 0.37 for Linux NET4.0\n"; + read_unlock(&ax25_dev_lock); -static int __init ax25_init(void) -{ - sock_register(&ax25_family_ops); - dev_add_pack(&ax25_packet_type); - register_netdevice_notifier(&ax25_dev_notifier); - ax25_register_sysctl(); + *start = buffer + (offset - begin); + len -= (offset - begin); - proc_net_create("ax25_route", 0, ax25_rt_get_info); - proc_net_create("ax25", 0, ax25_get_info); - proc_net_create("ax25_calls", 0, ax25_uid_get_info); + if (len > length) len = length; - printk(banner); - return 0; + return len; } -module_init(ax25_init); - -MODULE_AUTHOR("Jonathan Naylor G4KLX <g4klx@g4klx.demon.co.uk>"); -MODULE_DESCRIPTION("The amateur radio AX.25 link layer protocol"); +/* ---------------------------------------------------------------------*/ -static void __exit ax25_exit(void) +static int ax25_gifconf(struct net_device *dev, char *buf, int len) { - proc_net_remove("ax25_route"); - proc_net_remove("ax25"); - proc_net_remove("ax25_calls"); - ax25_rt_free(); - ax25_uid_free(); - ax25_dev_free(); + struct ifreq ifr; + int done=0; - ax25_unregister_sysctl(); - unregister_netdevice_notifier(&ax25_dev_notifier); - - dev_remove_pack(&ax25_packet_type); - - sock_unregister(PF_AX25); + if (!buf) { + done += sizeof(ifr); + return done; + } + if (len < (int) sizeof(ifr)) return done; + memset(&ifr, 0, sizeof(struct ifreq)); + strcpy(ifr.ifr_name, dev->name); + (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = AF_AX25; + if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) return -EFAULT; + done += sizeof(struct ifreq); + return done; } -module_exit(ax25_exit); + diff --git a/net/ax25/af_ax25.h b/net/ax25/af_ax25.h new file mode 100644 index 000000000..9980f54f6 --- /dev/null +++ b/net/ax25/af_ax25.h @@ -0,0 +1,21 @@ +/* + * Interface declaration for AF_AX25 base routines layer. + * + * Matthias Welwarsky (DG2FEF) 05/28/98 + * + */ + + +#ifndef _AF_AX25_H +#define _AF_AX25_H + +#include <linux/skbuff.h> +#include <net/ax25.h> + +extern struct net_device* ax25rtr_get_dev(ax25_address *); +extern struct sock* ax25_make_new(struct sock*, struct net_device*); +extern ax25_cb * ax25_list; +extern void ax25_kill_by_device(struct net_device*); +extern rwlock_t ax25_dev_lock; + +#endif diff --git a/net/ax25/ax25_addr.c b/net/ax25/ax25_addr.c deleted file mode 100644 index f96c65586..000000000 --- a/net/ax25/ax25_addr.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_subr.c. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -/* - * The null address is defined as a callsign of all spaces with an - * SSID of zero. - */ -ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; - -/* - * ax25 -> ascii conversion - */ -char *ax2asc(ax25_address *a) -{ - static char buf[11]; - char c, *s; - int n; - - for (n = 0, s = buf; n < 6; n++) { - c = (a->ax25_call[n] >> 1) & 0x7F; - - if (c != ' ') *s++ = c; - } - - *s++ = '-'; - - if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { - *s++ = '1'; - n -= 10; - } - - *s++ = n + '0'; - *s++ = '\0'; - - if (*buf == '\0' || *buf == '-') - return "*"; - - return buf; - -} - -/* - * ascii -> ax25 conversion - */ -ax25_address *asc2ax(char *callsign) -{ - static ax25_address addr; - char *s; - int n; - - for (s = callsign, n = 0; n < 6; n++) { - if (*s != '\0' && *s != '-') - addr.ax25_call[n] = *s++; - else - addr.ax25_call[n] = ' '; - addr.ax25_call[n] <<= 1; - addr.ax25_call[n] &= 0xFE; - } - - if (*s++ == '\0') { - addr.ax25_call[6] = 0x00; - return &addr; - } - - addr.ax25_call[6] = *s++ - '0'; - - if (*s != '\0') { - addr.ax25_call[6] *= 10; - addr.ax25_call[6] += *s++ - '0'; - } - - addr.ax25_call[6] <<= 1; - addr.ax25_call[6] &= 0x1E; - - return &addr; -} - -/* - * Compare two ax.25 addresses - */ -int ax25cmp(ax25_address *a, ax25_address *b) -{ - int ct = 0; - - while (ct < 6) { - if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ - return 1; - ct++; - } - - if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ - return 0; - - return 2; /* Partial match */ -} - -/* - * Compare two AX.25 digipeater paths. - */ -int ax25digicmp(ax25_digi *digi1, ax25_digi *digi2) -{ - int i; - - if (digi1->ndigi != digi2->ndigi) - return 1; - - if (digi1->lastrepeat != digi2->lastrepeat) - return 1; - - for (i = 0; i < digi1->ndigi; i++) - if (ax25cmp(&digi1->calls[i], &digi2->calls[i]) != 0) - return 1; - - return 0; -} - -/* - * Given an AX.25 address pull of to, from, digi list, command/response and the start of data - * - */ -unsigned char *ax25_addr_parse(unsigned char *buf, int len, ax25_address *src, ax25_address *dest, ax25_digi *digi, int *flags, int *dama) -{ - int d = 0; - - if (len < 14) return NULL; - - if (flags != NULL) { - *flags = 0; - - if (buf[6] & AX25_CBIT) - *flags = AX25_COMMAND; - if (buf[13] & AX25_CBIT) - *flags = AX25_RESPONSE; - } - - if (dama != NULL) - *dama = ~buf[13] & AX25_DAMA_FLAG; - - /* Copy to, from */ - if (dest != NULL) - memcpy(dest, buf + 0, AX25_ADDR_LEN); - if (src != NULL) - memcpy(src, buf + 7, AX25_ADDR_LEN); - - buf += 2 * AX25_ADDR_LEN; - len -= 2 * AX25_ADDR_LEN; - - digi->lastrepeat = -1; - digi->ndigi = 0; - - while (!(buf[-1] & AX25_EBIT)) { - if (d >= AX25_MAX_DIGIS) return NULL; /* Max of 6 digis */ - if (len < 7) return NULL; /* Short packet */ - - memcpy(&digi->calls[d], buf, AX25_ADDR_LEN); - digi->ndigi = d + 1; - - if (buf[6] & AX25_HBIT) { - digi->repeated[d] = 1; - digi->lastrepeat = d; - } else { - digi->repeated[d] = 0; - } - - buf += AX25_ADDR_LEN; - len -= AX25_ADDR_LEN; - d++; - } - - return buf; -} - -/* - * Assemble an AX.25 header from the bits - */ -int ax25_addr_build(unsigned char *buf, ax25_address *src, ax25_address *dest, ax25_digi *d, int flag, int modulus) -{ - int len = 0; - int ct = 0; - - memcpy(buf, dest, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] |= AX25_SSSID_SPARE; - - if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - memcpy(buf, src, AX25_ADDR_LEN); - buf[6] &= ~(AX25_EBIT | AX25_CBIT); - buf[6] &= ~AX25_SSSID_SPARE; - - if (modulus == AX25_MODULUS) - buf[6] |= AX25_SSSID_SPARE; - else - buf[6] |= AX25_ESSID_SPARE; - - if (flag == AX25_RESPONSE) buf[6] |= AX25_CBIT; - - /* - * Fast path the normal digiless path - */ - if (d == NULL || d->ndigi == 0) { - buf[6] |= AX25_EBIT; - return 2 * AX25_ADDR_LEN; - } - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - - while (ct < d->ndigi) { - memcpy(buf, &d->calls[ct], AX25_ADDR_LEN); - - if (d->repeated[ct]) - buf[6] |= AX25_HBIT; - else - buf[6] &= ~AX25_HBIT; - - buf[6] &= ~AX25_EBIT; - buf[6] |= AX25_SSSID_SPARE; - - buf += AX25_ADDR_LEN; - len += AX25_ADDR_LEN; - ct++; - } - - buf[-1] |= AX25_EBIT; - - return len; -} - -int ax25_addr_size(ax25_digi *dp) -{ - if (dp == NULL) - return 2 * AX25_ADDR_LEN; - - return AX25_ADDR_LEN * (2 + dp->ndigi); -} - -/* - * Reverse Digipeat List. May not pass both parameters as same struct - */ -void ax25_digi_invert(ax25_digi *in, ax25_digi *out) -{ - int ct; - - out->ndigi = in->ndigi; - out->lastrepeat = in->ndigi - in->lastrepeat - 2; - - /* Invert the digipeaters */ - for (ct = 0; ct < in->ndigi; ct++) { - out->calls[ct] = in->calls[in->ndigi - ct - 1]; - - if (ct <= out->lastrepeat) { - out->calls[ct].ax25_call[6] |= AX25_HBIT; - out->repeated[ct] = 1; - } else { - out->calls[ct].ax25_call[6] &= ~AX25_HBIT; - out->repeated[ct] = 0; - } - } -} - diff --git a/net/ax25/ax25_core.c b/net/ax25/ax25_core.c new file mode 100644 index 000000000..37d171d83 --- /dev/null +++ b/net/ax25/ax25_core.c @@ -0,0 +1,513 @@ +/* + * ax25_core.c: AX.25 core and support functions for NEW-AX.25 + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), + * Jonathan (G4KLX), Alan Cox (GW4PTS) + * + * Comment: + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/socket.h> +#include <linux/ax25.h> +#include <linux/spinlock.h> +#include <net/ax25.h> +#include <net/ax25dev.h> +#include <net/sock.h> + +#include "af_ax25.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_core.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_ddi.h" + + +rwlock_t ax25_list_lock = RW_LOCK_UNLOCKED; + +/* ---------------------------------------------------------------------*/ +/* + * The null address is defined as a callsign of all spaces with an + * SSID of zero. + */ +ax25_address null_ax25_address = {{0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00}}; + +/* --------------------------------------------------------------------- */ +/* + * ax25 -> ascii conversion + */ +char *ax2asc(ax25_address *a) +{ + static char buf[11]; + char c, *s; + int n; + + for (n = 0, s = buf; n < 6; n++) { + c = (a->ax25_call[n] >> 1) & 0x7F; + + if (c != ' ') *s++ = c; + } + + *s++ = '-'; + + if ((n = ((a->ax25_call[6] >> 1) & 0x0F)) > 9) { + *s++ = '1'; + n -= 10; + } + + *s++ = n + '0'; + *s++ = '\0'; + + if (*buf == '\0' || *buf == '-') + return "*"; + + return buf; + +} + +/* ---------------------------------------------------------------------*/ +/* + * ascii -> ax25 conversion + */ +ax25_address *asc2ax(char *callsign) +{ + static ax25_address addr; + char *s; + int n; + + for (s = callsign, n = 0; n < 6; n++) { + if (*s != '\0' && *s != '-') + addr.ax25_call[n] = *s++; + else + addr.ax25_call[n] = ' '; + addr.ax25_call[n] <<= 1; + addr.ax25_call[n] &= 0xFE; + } + + if (*s++ == '\0') { + addr.ax25_call[6] = 0x00; + return &addr; + } + + addr.ax25_call[6] = *s++ - '0'; + + if (*s != '\0') { + addr.ax25_call[6] *= 10; + addr.ax25_call[6] += *s++ - '0'; + } + + addr.ax25_call[6] <<= 1; + addr.ax25_call[6] &= 0x1E; + + return &addr; +} + +/* ---------------------------------------------------------------------*/ +/* + * Compare two ax.25 addresses + */ +int ax25cmp(ax25_address *a, ax25_address *b) +{ + int ct = 0; + + while (ct < 6) { + if ((a->ax25_call[ct] & 0xFE) != (b->ax25_call[ct] & 0xFE)) /* Clean off repeater bits */ + return 1; + ct++; + } + + if ((a->ax25_call[ct] & 0x1E) == (b->ax25_call[ct] & 0x1E)) /* SSID without control bit */ + return 0; + + return 2; /* Partial match */ +} + +/* ---------------------------------------------------------------------*/ +/* + * Add a socket to the bound sockets list. + */ +void ax25_insert_cb(ax25_cb *ax25) +{ + if (ax25->device != NULL) { + ax25_dev_insert_cb(ax25); + return; + } + + ax25->prev = NULL; + ax25->next = ax25_list; + + write_lock(&ax25_list_lock); + + if (ax25_list != NULL) + ax25_list->prev = ax25; + ax25_list = ax25; + + write_unlock(&ax25_list_lock); + + ax25->inserted = 1; +} + +/* ---------------------------------------------------------------------*/ +/* + * Socket removal is now protected agains bottom half ints + * with a start/end_bh_atomic bracket. There should be no + * need to mask interrupts on hardware level. + */ +void ax25_remove_cb(ax25_cb *ax25) +{ + /* + * unbound sockets are not in any list + */ + + if (!ax25->inserted) + return; + + if (ax25->device != NULL) { + ax25_dev_remove_cb(ax25); + return; + } + + + if (ax25_list != NULL) { + write_lock(&ax25_list_lock); + if (ax25->prev == NULL) + ax25_list = ax25->next; + else + ax25->prev->next = ax25->next; + + if (ax25->next != NULL) + ax25->next->prev = ax25->prev; + write_unlock(&ax25_list_lock); + } + + ax25->inserted = 0; + +} + +/* ---------------------------------------------------------------------*/ +/* + * Find an AX.25 control block given both ends. + */ +ax25_cb *ax25_find_cb(ax25_addr_t *addr, struct net_device *dev) +{ + ax25_cb *s; + + read_lock(&ax25_list_lock); + for (s = ax25_dev_list(dev) ; s != NULL; s = s->next) { + + if (s->sk != NULL && s->sk->type != SOCK_SEQPACKET) + continue; + + if (s->addr.dcount == addr->dcount && !ax25cmp(&s->addr.src, &addr->src) + && !ax25cmp(&s->addr.dest, &addr->dest)) + { + int i; + + if (addr->dcount == 0) + break; + if (addr->lastrepeat != s->addr.lastrepeat) + continue; + i = addr->dcount; + while (i--) { + if (ax25cmp(&s->addr.digipeater[i], &addr->digipeater[i])) + break; + } + if (i < 0) + break; + } + } + read_unlock(&ax25_list_lock); + return s; +} + +/* ---------------------------------------------------------------------*/ + +void ax25_destroy_cb(ax25_cb *ax25) +{ + ax25_remove_cb(ax25); + ax25_clear_queues(ax25); + ax25_free_cb(ax25); +} + +/* ---------------------------------------------------------------------*/ + +void ax25_destroy_socket(struct sock *sk) +{ + struct sk_buff *skb; + + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { + /* + * this may be a pending SABM, waiting in the receive queue + * to become accepted. But the listener just died :-) + */ + if (skb->sk != sk) { + /* + * signal the peer that we have closed the socket. + * if he has already disconnected himself, just mark + * the socket dead and move to TCP_CLOSE. This is only + * for security, as ax25_disconnect should have already + * done this. + */ + if (skb->sk->state == TCP_ESTABLISHED) + skb->sk->protinfo.ax25->condition |= AX25_COND_RELEASE; + else { + printk(KERN_DEBUG "ax25_destroy_socket: TCP_CLOSE\n"); + skb->sk->state = TCP_CLOSE; + } + skb->sk->dead = 1; + } + kfree_skb(skb); + } + sk_free(sk); +} + +/* ---------------------------------------------------------------------*/ +/* + * Fill in a created AX.25 control block with the default + * values for a particular device. + */ +void ax25_fillin_cb(ax25_cb *ax25, struct net_device *dev) +{ + ax25->device = dev; + ax25->vs_rtt = -1; + + if (dev != NULL) { + ax25->rtt = ax25_dev_get_value(dev, AX25_VALUES_T1) / 4; + ax25->t1 = ax25_dev_get_value(dev, AX25_VALUES_T1); + ax25->t2 = ax25_dev_get_value(dev, AX25_VALUES_T2); + ax25->t3 = ax25_dev_get_value(dev, AX25_VALUES_T3); + ax25->n2 = ax25_dev_get_value(dev, AX25_VALUES_N2); + ax25->paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); + ax25->idle = ax25_dev_get_value(dev, AX25_VALUES_IDLE); + ax25->backoff = ax25_dev_get_value(dev, AX25_VALUES_BACKOFF); + + if (ax25_dev_get_value(dev, AX25_VALUES_AXDEFMODE)) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } + } else { + ax25->rtt = AX25_DEF_T1 / 4; + ax25->t1 = AX25_DEF_T1; + ax25->t2 = AX25_DEF_T2; + ax25->t3 = AX25_DEF_T3; + ax25->n2 = AX25_DEF_N2; + ax25->paclen = AX25_DEF_PACLEN; + ax25->idle = AX25_DEF_IDLE; + ax25->backoff = AX25_DEF_BACKOFF; + + if (AX25_DEF_AXDEFMODE) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = AX25_DEF_EWINDOW; + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = AX25_DEF_WINDOW; + } + } +} + +/* ---------------------------------------------------------------------*/ +/* + * Create an empty AX.25 control block. + */ +ax25_cb *ax25_create_cb(void) +{ + ax25_cb *ax25; + + if ((ax25 = (ax25_cb *)kmalloc(sizeof(ax25_cb), GFP_ATOMIC)) == NULL) + return NULL; + + MOD_INC_USE_COUNT; + + memset(ax25, 0x00, sizeof(ax25_cb)); + + skb_queue_head_init(&ax25->write_queue); + skb_queue_head_init(&ax25->frag_queue); + skb_queue_head_init(&ax25->ack_queue); + skb_queue_head_init(&ax25->rcv_queue); + + ax25->state = AX25_LISTEN; + ax25->condition = AX25_COND_SETUP; + + return ax25; +} + +/* ---------------------------------------------------------------------*/ +/* + * Free an allocated ax25 control block. This is done to centralise + * the MOD count code. + */ +void ax25_free_cb(ax25_cb *ax25) +{ + if (ax25->slcomp != NULL) { + axhc_free(ax25->slcomp); + } + + if (ax25->peer != NULL) { + ax25->peer->peer = NULL; + } + + kfree(ax25); + + MOD_DEC_USE_COUNT; +} + +/* ---------------------------------------------------------------------*/ + +void ax25_free_sock(struct sock *sk) +{ + if (sk->protinfo.ax25 != NULL) + ax25_free_cb(sk->protinfo.ax25); +} + +/* ---------------------------------------------------------------------*/ +/* + * Given an AX.25 address pull of to, from, digi list, command/response and the start of data + * + */ +unsigned char *ax25_parse_addr(unsigned char *buf, int len, ax25_pktinfo *pkt_info) +{ + int d = 0; + + if (len < 15) + return NULL; + + pkt_info->cmdrsp = 0; + + memcpy(&pkt_info->addr.dest, buf, AX25_ADDR_LEN); + + if (buf[6] & AX25_CBIT) + pkt_info->cmdrsp = AX25_COMMAND; + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; + + memcpy(&pkt_info->addr.src, buf, AX25_ADDR_LEN); + + if (buf[6] & AX25_CBIT) + pkt_info->cmdrsp = AX25_RESPONSE; + + pkt_info->dama = !(buf[6] & AX25_DAMA_FLAG); + + pkt_info->addr.lastrepeat = -1; + pkt_info->addr.dcount = 0; + + while (!(buf[6] & AX25_EBIT)) { + buf += AX25_ADDR_LEN; + len -= AX25_ADDR_LEN; + + if (d < AX25_MAX_DIGIS && len >= 7) { + memcpy(&pkt_info->addr.digipeater[d], buf, AX25_ADDR_LEN); + if (buf[6] & AX25_HBIT) + pkt_info->addr.lastrepeat = d; + ++d; + pkt_info->addr.dcount = d; + } else + return NULL; + } + + return buf + AX25_ADDR_LEN; +} + +/* ---------------------------------------------------------------------*/ +/* + * Assemble an AX.25 header from the bits + */ +int ax25_build_addr(unsigned char *buf, ax25_addr_t *addr, int flag, int seqmask) +{ + int len = 0; + int ct = 0; + + memcpy(buf, &addr->dest, AX25_ADDR_LEN); + buf[6] &= ~(AX25_EBIT | AX25_CBIT); + buf[6] |= AX25_SSSID_SPARE; + + if (flag == AX25_COMMAND) buf[6] |= AX25_CBIT; + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + + memcpy(buf, &addr->src, AX25_ADDR_LEN); + buf[6] &= ~(AX25_EBIT | AX25_CBIT); + buf[6] &= ~AX25_SSSID_SPARE; + + if (seqmask == AX25_SEQMASK) + buf[6] |= AX25_SSSID_SPARE; + else + buf[6] |= AX25_ESSID_SPARE; + + if (flag == AX25_RESPONSE) + buf[6] |= AX25_CBIT; + + /* + * Fast path the normal digiless path + */ + if (addr->dcount == 0) { + buf[6] |= AX25_EBIT; + return 2 * AX25_ADDR_LEN; + } + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + + while (ct < addr->dcount) { + memcpy(buf, &addr->digipeater[ct], AX25_ADDR_LEN); + + if (ct <= addr->lastrepeat) + buf[6] |= AX25_HBIT; + else + buf[6] &= ~AX25_HBIT; + + buf[6] &= ~AX25_EBIT; + buf[6] |= AX25_SSSID_SPARE; + + buf += AX25_ADDR_LEN; + len += AX25_ADDR_LEN; + ct++; + } + + buf[-1] |= AX25_EBIT; + + return len; +} + +/* ---------------------------------------------------------------------*/ + +int ax25_sizeof_addr(ax25_addr_t *addr) +{ + return AX25_ADDR_LEN * (addr->dcount+2); +} + +/* ---------------------------------------------------------------------*/ +/* + * Invert AX.25 address. May not pass both parameters as same struct + */ +void ax25_invert_addr(ax25_addr_t *in, ax25_addr_t *out) +{ + ax25_address *ip, *op; + int dcount; + + dcount = out->dcount = in->dcount; + out->lastrepeat = dcount - in->lastrepeat - 2; + + /* source/destination */ + out->dest = in->src; + out->src = in->dest; + + /* Invert the digipeaters */ + if (dcount) { + ip = in->digipeater; + op = out->digipeater + (dcount-1); /* pointer scaled! */ + while (dcount--) + *op-- = *ip++; + } +} diff --git a/net/ax25/ax25_core.h b/net/ax25/ax25_core.h new file mode 100644 index 000000000..bbb31183f --- /dev/null +++ b/net/ax25/ax25_core.h @@ -0,0 +1,21 @@ +#ifndef _AX25_CORE_H +#define _AX25_CORE_H + +extern char* ax2asc(ax25_address*); +extern int ax25cmp(ax25_address*, ax25_address*); +extern ax25_address* asc2ax(char*); +extern void ax25_insert_cb(ax25_cb*); +extern void ax25_remove_cb(ax25_cb*); +extern ax25_cb* ax25_find_cb(ax25_addr_t*, struct net_device*); +extern void ax25_destroy_cb(ax25_cb*); +extern void ax25_destroy_socket(struct sock*); +extern void ax25_fillin_cb(ax25_cb*, struct net_device*); +extern ax25_cb* ax25_create_cb(void); +extern void ax25_free_cb(ax25_cb*); +extern void ax25_free_sock(struct sock*); +extern int ax25_build_addr(unsigned char*, ax25_addr_t*, int, int); +extern int ax25_sizeof_addr(ax25_addr_t*); +extern void ax25_invert_addr(ax25_addr_t*, ax25_addr_t*); + +extern ax25_address null_ax25_address; +#endif diff --git a/net/ax25/ax25_ctl.c b/net/ax25/ax25_ctl.c new file mode 100644 index 000000000..da4cd49c8 --- /dev/null +++ b/net/ax25/ax25_ctl.c @@ -0,0 +1,133 @@ +/* + * ax25_ctl.c: Implements ioctl()s on persisting VC sockets (NEW-AX.25) + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Joerg (DL1BKE) + * + * Comment: + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <asm/uaccess.h> +#include <linux/ax25.h> +#include <net/ax25.h> + +#include "af_ax25.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_timer.h" + +/* + * dl1bke 960311: set parameters for existing AX.25 connections, + * includes a KILL command to abort any connection. + * VERY useful for debugging ;-) + */ +int ax25_ctl_ioctl(const unsigned int cmd, void *arg) +{ + struct ax25_ctl_struct ax25_ctl; + struct net_device *dev; + ax25_cb *ax25; + ax25_addr_t addr; + int err; + + if ((err = verify_area(VERIFY_READ, arg, sizeof(ax25_ctl))) != 0) + return err; + + if (copy_from_user(&ax25_ctl, arg, sizeof(ax25_ctl))) + return -EFAULT;; + + if ((dev = ax25rtr_get_dev(&ax25_ctl.port_addr)) == NULL) + return -ENODEV; + + addr.src = ax25_ctl.source_addr; + addr.dest = ax25_ctl.dest_addr; + addr.dcount = 0; + + if ((ax25 = ax25_find_cb(&addr, dev)) == NULL) + return -ENOTCONN; + + switch (ax25_ctl.cmd) { + case AX25_KILL: + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, ENETRESET); + if (ax25->sk) + ax25_close_socket(ax25->sk, ENETRESET); + break; + + case AX25_WINDOW: + if (ax25->seqmask == AX25_SEQMASK) { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 7) + return -EINVAL; + } else { + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 63) + return -EINVAL; + } + ax25->window = ax25_ctl.arg; + break; + + case AX25_T1: + if (ax25_ctl.arg < 1) + return -EINVAL; + ax25->rtt = (ax25_ctl.arg * AX25_TICS) / 4; + ax25->t1 = ax25_ctl.arg; + write_lock(&ax25->timer_lock); + if (ax25->wrt_timer > ax25->t1) + ax25->wrt_timer = ax25->t1; + write_unlock(&ax25->timer_lock); + break; + + case AX25_T2: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->t2 = ax25_ctl.arg; + write_lock(&ax25->timer_lock); + if (ax25->ack_timer > ax25->t2) + ax25->ack_timer = ax25->t2; + write_unlock(&ax25->timer_lock); + break; + + case AX25_N2: + if (ax25_ctl.arg < 1 || ax25_ctl.arg > 31) + return -EINVAL; + ax25->n2count = 0; + ax25->n2 = ax25_ctl.arg; + break; + + case AX25_T3: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->t3 = ax25_ctl.arg * AX25_SLOWHZ; + write_lock(&ax25->timer_lock); + if (ax25->wrt_timer != 0) + ax25->wrt_timer = ax25->t3; + write_unlock(&ax25->timer_lock); + break; + + case AX25_IDLE: + if (ax25_ctl.arg < 0) + return -EINVAL; + ax25->idle = ax25_ctl.arg * AX25_SLOWHZ; + write_lock(&ax25->timer_lock); + if (ax25->idletimer != 0) + ax25->idletimer = ax25->idle; + write_unlock(&ax25->timer_lock); + break; + + case AX25_PACLEN: + if (ax25_ctl.arg < 16 || ax25_ctl.arg > 65535) + return -EINVAL; + ax25->paclen = ax25_ctl.arg; + break; + + default: + return -EINVAL; + } + + return 0; +} diff --git a/net/ax25/ax25_ctl.h b/net/ax25/ax25_ctl.h new file mode 100644 index 000000000..10a41d991 --- /dev/null +++ b/net/ax25/ax25_ctl.h @@ -0,0 +1,6 @@ +#ifndef _AX25_CTL_H +#define _AX25_CTL_H + +extern int ax25_ctl_ioctl(const unsigned int, void*); + +#endif diff --git a/net/ax25/ax25_ddi.c b/net/ax25/ax25_ddi.c new file mode 100644 index 000000000..594484c77 --- /dev/null +++ b/net/ax25/ax25_ddi.c @@ -0,0 +1,1186 @@ +/* + * ax25_ddi.c: Device Driver Independent Module for NEW-AX.25 + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), + * + * Comment: Contains device driver interface, scheduler, channel arbitration + * LAPB state machine is synchronized here to avoid race conditions + * Written from scratch by Matthias Welwarsky in 1998. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#include <linux/netdevice.h> +#include <linux/tqueue.h> +#include <linux/time.h> +#include <linux/spinlock.h> + +#include <net/tcp.h> +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include "af_ax25.h" +#include "ax25_ddi.h" +#include "ax25_core.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_route.h" +#include "ax25_timer.h" + +struct net_device *ax25_devices[AX25_MAX_DEVICES]; +rwlock_t ax25_dev_lock = RW_LOCK_UNLOCKED; + + + +/* + * ------------------------------------------------------------------------ + * declaration of private functions + * ------------------------------------------------------------------------ + */ + +static void clear_ax25devices(void); +static void ax25_dev_timer(unsigned long); +static void ax25_dev_tic(unsigned long); +static void ax25_transmit_buffer(ax25_cb*, struct sk_buff*, int); +static void ax25_send_iframe(ax25_cb*, struct sk_buff*, int); +static void ax25_send_control(ax25_cb*, int, int, int); +static void ax25_kick_device(struct ax25_dev*); +static __inline__ void ax25_dev_add_ready(struct ax25_dev *, ax25_cb *); +static __inline__ void ax25_dev_remove_active(struct ax25_dev *); +static __inline__ void ax25_dev_remove_ready(struct ax25_dev *, ax25_cb *); +static void ax25_dev_set_tic(struct ax25_dev *); +static void ax25_dev_set_timer(struct ax25_dev *, unsigned int); +static void ax25_queue_xmit(struct sk_buff *); +static struct ax25_dev *ax25_dev_get_dev(struct net_device *); + +/* + * ------------------------------------------------------------------------ + * Interface implementation + * All public functions of this module are defined here + * ------------------------------------------------------------------------ + */ + +void ax25_ddi_init(void) +{ + clear_ax25devices(); +} + +/* + * queue a fully assembled frame in the unproto queue of the + * device and mark the channel ready for transmission + */ +void ax25_send_unproto(struct sk_buff* skb, struct net_device* dev) +{ + struct ax25_dev* ax25_device = AX25_PTR(dev); + + skb->dev = dev; + skb_queue_tail(&ax25_device->unproto_queue, skb); + ax25_kick_device(ax25_device); +} + +void ax25_send_broadcast(struct sk_buff *skb) +{ + int i; + + read_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + struct net_device *dev = ax25_devices[i]; + + if (dev != NULL && (dev->flags & (IFF_UP|IFF_BROADCAST)) != 0) { + struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC); + if (newskb != NULL) + ax25_send_unproto(newskb, dev); + else + printk(KERN_ERR "ax25_send_broadcast: unable to clone packet.\n"); + } + } + read_unlock(&ax25_dev_lock); + + /* caller frees original packet */ +} + +/* + * put a connection on the ready list of it's device and mark the device + * ready for transmission. + */ +void ax25_kick(ax25_cb *ax25) +{ + if (ax25->device != NULL) { + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + + /* + * put the connection on the readylist of this channel, + * if it's not already there. + */ + ax25_dev_add_ready(ax25_device, ax25); + + /* + * mark the channel ready + */ + ax25_kick_device(ax25_device); + } +} + +/* + * return the connection list to a given device + */ +ax25_cb *ax25_dev_list(struct net_device *dev) +{ + struct ax25_dev *ax25_device; + + if (dev == NULL) + return ax25_list; + + if ((ax25_device = AX25_PTR(dev)) != NULL && ax25_device->magic == AX25_DEV_MAGIC) + return ax25_device->list.all; + + return NULL; +} + +/* + * insert a connection into a device queue + */ +void ax25_dev_insert_cb(ax25_cb *ax25) +{ + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + + if (ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_insert_cb: wrong magic number.\n"); + return; + } + + ax25->prev = NULL; + ax25->next = ax25_device->list.all; + + write_lock(&ax25_dev_lock); + if (ax25_device->list.all != NULL) + ax25_device->list.all->prev = ax25; + ax25_device->list.all = ax25; + write_unlock(&ax25_dev_lock); + + ax25->inserted = 1; +} + +/* + * remove a connection from a device queue + */ +void ax25_dev_remove_cb(ax25_cb *ax25) +{ + struct ax25_dev *ax25_device = AX25_PTR(ax25->device); + struct net_device *dev = ax25->device; + struct ax25_cb *axp; + + if (ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_remove_cb: wrong magic number.\n"); + return; + } + + if (ax25_device->list.all == NULL) { + printk(KERN_ERR "ax25_dev_remove_cb: empty list.\n"); + return; + } + + write_lock(&ax25_dev_lock); + if (ax25->prev == NULL) { + ax25_device->list.all = ax25->next; + } else { + ax25->prev->next = ax25->next; + } + + if (ax25->next != NULL) + ax25->next->prev = ax25->prev; + + ax25->inserted = 0; + + if (xchg(&ax25->ready.state, AX25_SCHED_IDLE) == AX25_SCHED_READY) + ax25_dev_remove_ready(ax25_device, ax25); + + /* search for active circuits and set DAMA flag accordingly */ + for (axp=ax25_device->list.all; axp!=NULL; axp=axp->next) + if ((axp->state == AX25_STATE_3) || (axp->state == AX25_STATE_4)) break; + if (axp == NULL) ax25_dev_set_dama(dev, 0); + + write_unlock(&ax25_dev_lock); +} + +/* + * Look for any matching address. + */ +int ax25_dev_match_addr(ax25_address *addr, struct net_device *dev) +{ + ax25_cb *s; + + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0) + return 1; + } + return 0; +} + +/* + * Find a control block that wants to accept the SABM we have just + * received. + */ +ax25_cb *ax25_dev_find_listener(ax25_address *addr, int digi, struct net_device *dev) +{ + ax25_cb *s; + + read_lock(&ax25_dev_lock); + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->state != AX25_LISTEN) + continue; + if ((s->iamdigi && !digi) || (!s->iamdigi && digi)) + continue; + if (ax25cmp(&s->addr.src, addr) == 0) + break; + } + read_unlock(&ax25_dev_lock); + return s; +} + +/* + * Find an AX.25 socket given both ends. + */ +struct sock *ax25_dev_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type) +{ + ax25_cb *s; + + read_lock(&ax25_dev_lock); + for (s = ax25_dev_list(dev); s != NULL; s = s->next) { + if (s->sk != NULL && ax25cmp(&s->addr.src, my_addr) == 0 + && ax25cmp(&s->addr.dest, dest_addr) == 0 && s->sk->type == type) { + read_unlock(&ax25_dev_lock); + return s->sk; + } + } + read_unlock(&ax25_dev_lock); + return NULL; +} + +/* + * This function is called whenever a parameter is modified using + * ax25_dev_set_value_notify or via the proc/sysctl interface. It + * decides whether to notify the device driver of the event. If the + * decision is positive, it uses the parameter_change downcall. + * The driver can then react and re-set the value or pick the + * closest value the hardware allows (e.g. by baud rate divider etc.). + * The most important values for the device driver are duplex, txdelay, + * txtail, {tx,rx}bitrate. Slottime and p-persistence are currently + * only "for info" since channel arbitration is done by DDI layer now. + */ +void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval) +{ + struct ax25_dev *ax_dev; + + if (!dev) return; /* paranoia */ + ax_dev = AX25_PTR(dev); + if (!ax_dev) return; /* paranoia */ + + switch (id) { + case AX25_VALUES_MEDIA_DUPLEX: + case AX25_VALUES_MEDIA_TXDELAY: + case AX25_VALUES_MEDIA_TXTAIL: + case AX25_VALUES_MEDIA_TXBITRATE: + case AX25_VALUES_MEDIA_RXBITRATE: + case AX25_VALUES_MEDIA_SLOTTIME: + case AX25_VALUES_MEDIA_PPERSISTENCE: + case AX25_VALUES_MEDIA_AUTO_ADJUST: + if (ax_dev->hw.parameter_change_notify) { + (ax_dev->hw.parameter_change_notify)(dev, id, oldval, newval); + } + break; + default: + break; + } + return; +} + +/* + * Call this function from AX.25 driver to check if driver has + * to be notified of the event. + */ +void ax25_dev_set_value_notify(struct net_device *dev, int valueno, int newvalue) +{ + int oldvalue; + + oldvalue = ax25_dev_get_value(dev, valueno); + ax25_dev_set_value(dev, valueno, newvalue); + if (oldvalue != newvalue) + ax25_notify_dispatcher(dev, valueno, oldvalue, newvalue); +} + +/* + * This is called when an interface is brought up. These are + * reasonable defaults. We try not to mess with the media parameters + * if they appear as having been set already. + */ +void ax25_dev_device_up(struct net_device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + int txbitrate; + + if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) + return; + + ax25_device->ready_lock = RW_LOCK_UNLOCKED; + ax25_device->forward = NULL; + ax25_device->list.all = NULL; + ax25_device->list.ready = NULL; + skb_queue_head_init(&ax25_device->unproto_queue); + ax25_device->bytes_sent = 0; + ax25_device->dama_mode = 0; + + ax25_dev_set_value_notify(dev, AX25_VALUES_IPDEFMODE, AX25_DEF_IPDEFMODE); + ax25_dev_set_value_notify(dev, AX25_VALUES_AXDEFMODE, AX25_DEF_AXDEFMODE); + ax25_dev_set_value_notify(dev, AX25_VALUES_BACKOFF, AX25_DEF_BACKOFF); + ax25_dev_set_value_notify(dev, AX25_VALUES_CONMODE, AX25_DEF_CONMODE); + ax25_dev_set_value_notify(dev, AX25_VALUES_WINDOW, AX25_DEF_WINDOW); + ax25_dev_set_value_notify(dev, AX25_VALUES_EWINDOW, AX25_DEF_EWINDOW); + ax25_dev_set_value_notify(dev, AX25_VALUES_T1, AX25_DEF_T1); + ax25_dev_set_value_notify(dev, AX25_VALUES_T3, AX25_DEF_T3); + ax25_dev_set_value_notify(dev, AX25_VALUES_IDLE, AX25_DEF_IDLE); + ax25_dev_set_value_notify(dev, AX25_VALUES_N2, AX25_DEF_N2); + ax25_dev_set_value_notify(dev, AX25_VALUES_PACLEN, AX25_DEF_PACLEN); + ax25_dev_set_value_notify(dev, AX25_VALUES_PROTOCOL, AX25_DEF_PROTOCOL); + ax25_dev_set_value_notify(dev, AX25_VALUES_DAMA_SLAVE_TIMEOUT, AX25_DEF_DAMA_SLAVE_TIMEOUT); + + txbitrate = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + ax25_dev_set_value_notify(dev, AX25_VALUES_T2, + txbitrate > 0 ? (3600 / AX25_TICS) * HZ / txbitrate : 0); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, AX25_DEF_MEDIA_PPERSISTENCE); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_SLOTTIME, AX25_DEF_MEDIA_SLOTTIME); + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_AUTO_ADJUST, AX25_DEF_MEDIA_AUTO_ADJUST); + + init_timer(&ax25_device->timer); + ax25_dev_set_timer(ax25_device, AX25_TICS); + init_timer(&ax25_device->tics); + ax25_dev_set_tic(ax25_device); +} + +/* + * this is called when a device is brought down. Delete the device + * timers and update the sysctl interface. + */ +void ax25_dev_device_down(struct net_device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + ax25_kill_by_device(dev); + ax25_rt_device_down(dev); + + if (!ax25_device || ax25_device->magic != AX25_DEV_MAGIC) { + printk(KERN_ERR "ax25_dev_device_down: not an AX.25 device.\n"); + return; + } + + del_timer(&ax25_device->timer); + del_timer(&ax25_device->tics); + + /* FIXME: do I have to lock this or not? */ + /* start_bh_atomic(); */ + skb_queue_purge(&ax25_device->unproto_queue); + /* end_bh_atomic(); */ +} + +/* + * Packet forwarding control IOCTL + * FIXME: does anybody really need this feature? + */ +int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) +{ + struct net_device *dev; + struct ax25_dev *ax25_dev; + + if ((dev = ax25rtr_get_dev(&fwd->port_from)) == NULL) + return -EINVAL; + + if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) + return -EINVAL; + + switch (cmd) { + case SIOCAX25ADDFWD: + if ((dev = ax25rtr_get_dev(&fwd->port_to)) == NULL) + return -EINVAL; + if (ax25_dev->forward != NULL) + return -EINVAL; + ax25_dev->forward = dev; + break; + + case SIOCAX25DELFWD: + if (ax25_dev->forward == NULL) + return -EINVAL; + ax25_dev->forward = NULL; + break; + + default: + return -EINVAL; + } + + return 0; +} + +struct net_device *ax25_fwd_dev(struct net_device *dev) +{ + struct ax25_dev *ax25_dev; + + if ((ax25_dev = ax25_dev_get_dev(dev)) == NULL) + return dev; + + if (ax25_dev->forward == NULL) + return dev; + + return ax25_dev->forward; +} + +int ax25_dev_get_info(char *buffer, char **start, off_t offset, int length) +{ + int i; + struct net_device *dev; + char devname[7]; + + int len = 0; + off_t pos = 0; + off_t begin = 0; + + len += sprintf(buffer, "device hwaddr rifr tifr rrej rkby tkby duplex tx-bps rx-bps ppers slot auto txd txt \n"); + + read_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if ((dev = ax25_devices[i]) != NULL) { + strncpy(devname, dev->name, 6); + devname[6] = 0; + len += sprintf(buffer+len, "%-6s %-9s %-6ld %-6ld %-6ld %-9ld %-9ld %-6s %-8d %-8d %-5d %-4d %-4s %-4d %-4d\n", + devname, ax2asc((ax25_address *)dev->dev_addr), + AX25_PTR(dev)->rx_iframes, AX25_PTR(dev)->tx_iframes, + AX25_PTR(dev)->rx_rejects, + AX25_PTR(dev)->rx_bytes/1024, + AX25_PTR(dev)->tx_bytes/1024, + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX) ? "full" : "half", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE), + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE), + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE), + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_SLOTTIME), + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST) ? "on" : "off", + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY), + ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL)); + + pos = begin + len; + if (pos < offset) { + len = 0; + begin = pos; + } + if (pos > offset + length) + break; + } + } + read_unlock(&ax25_dev_lock); + + *start = buffer + (offset - begin); + len -= offset - begin; + + if (len > length) len = length; + + return len; +} + +/* + * This function is called by core/dev.c whenever a new netdevice is + * being registerd. We initialize its ax25_dev structure and include + * it in our list. We also register the sysctl tree for it and initialize + * its parameters. + */ +void register_ax25device(struct net_device *dev) +{ + int i; + struct ax25_dev *axdev = AX25_PTR(dev); + + axdev->magic = AX25_DEV_MAGIC; + axdev->netdev = dev; + + memcpy((char *) dev->broadcast, (char *) asc2ax("QST-0"), AX25_ADDR_LEN); + + ax25_unregister_sysctl(); + write_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_devices[i] == NULL) { + ax25_devices[i] = dev; + break; + } + } + + ax25_register_sysctl(); + if (i == AX25_MAX_DEVICES) { + printk(KERN_ERR "AX.25: Too many devices, could not register.\n"); + goto done; + } + + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXDELAY) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXDELAY, AX25_DEF_MEDIA_TXDELAY); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXTAIL) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXTAIL, AX25_DEF_MEDIA_TXTAIL); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_TXBITRATE, AX25_DEF_MEDIA_TXBITRATE); + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_RXBITRATE) == 0) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_RXBITRATE, AX25_DEF_MEDIA_RXBITRATE); + /* + * slottime, p-persistence and auto-adjust defaults are + * loaded upon interface start + */ + +done: + write_unlock(&ax25_dev_lock); +} + +/* + * This function is executed when an interface is about to be removed. + * It must already have been downed before. We remove it from our + * list and remove sysctl directory entry. + */ +void unregister_ax25device(struct net_device *dev) +{ + int i; + + ax25_unregister_sysctl(); + write_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if (ax25_devices[i] == dev) { + ax25_devices[i] = NULL; + break; + } + } + write_unlock(&ax25_dev_lock); + ax25_register_sysctl(); +} + +/* + * Activate/Deactivate DAMA on a given interface. + * We automagically configure the media for full duplex if neccessary. + */ +void ax25_dev_set_dama(struct net_device *dev, int dama) +{ + if (dama && (ax25_dev_get_value(dev, AX25_VALUES_PROTOCOL) == 1)) { + if (!(AX25_PTR(dev)->dama_mode & DAMA_SLAVE)) { + AX25_PTR(dev)->dama_mode |= DAMA_SLAVE; + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 1); + } + } else { + if (AX25_PTR(dev)->dama_mode & DAMA_SLAVE) { + AX25_PTR(dev)->dama_mode &= ~DAMA_SLAVE; + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_DUPLEX, 0); + } + } + return; +} + +/* + * ------------------------------------------------------------------------ + * End of public area, all private functions of this module are defined + * here. + * ------------------------------------------------------------------------ + */ + +static void clear_ax25devices(void) +{ + int i; + + write_lock(&ax25_dev_lock); + for (i = 0; i < AX25_MAX_DEVICES; i++) + ax25_devices[i] = NULL; + write_unlock(&ax25_dev_lock); +} + +/* + * simple pseudo-random number generator, stolen from hdlcdrv.c :) + */ +static inline unsigned short random_num(void) +{ + static unsigned short random_seed; + + random_seed = 28629 * random_seed + 157; + return random_seed & 0xFF; +} + +/* + * add a connection to the channels readylist + */ +static inline void ax25_dev_add_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) +{ + write_lock(&ax25_device->ready_lock); + if (ax25->ready.state != AX25_SCHED_READY) { + ax25->ready.state = AX25_SCHED_READY; + if (ax25_device->list.ready == NULL) { + ax25->ready.prev = ax25; + ax25->ready.next = ax25; + ax25_device->list.ready = ax25; + } else { + ax25->ready.next = ax25_device->list.ready; + ax25->ready.prev = ax25_device->list.ready->ready.prev; + ax25_device->list.ready->ready.prev->ready.next = ax25; + ax25_device->list.ready->ready.prev = ax25; + } + } + write_unlock(&ax25_device->ready_lock); +} + +/* + * remove the active connection from the channels readylist + * NB: caller must do write_lock() on ax25_device->ready_lock! + */ +static inline void ax25_dev_remove_active(struct ax25_dev *ax25_device) +{ + ax25_cb *active = ax25_device->list.ready; + if (active->ready.next == active) { + ax25_device->list.ready = NULL; + } else { + ax25_device->list.ready = active->ready.next; + active->ready.next->ready.prev = active->ready.prev; + active->ready.prev->ready.next = active->ready.next; + } + active->ready.state = AX25_SCHED_IDLE; +} + +/* + * remove a connection from the channels readylist + */ +static inline void ax25_dev_remove_ready(struct ax25_dev *ax25_device, ax25_cb *ax25) +{ + write_lock(&ax25_device->ready_lock); + + if (ax25 == ax25_device->list.ready) { + ax25_dev_remove_active(ax25_device); + } else { + ax25->ready.next->ready.prev = ax25->ready.prev; + ax25->ready.prev->ready.next = ax25->ready.next; + ax25->ready.state = AX25_SCHED_IDLE; + } + + write_unlock(&ax25_device->ready_lock); +} + +/* + * Timer for a per device 100ms timing tic. AX.25 Timers of all + * connections on this device are driven by this timer. + */ +static void ax25_dev_set_tic(struct ax25_dev *this) +{ + this->tics.data = (unsigned long)this; + this->tics.function = &ax25_dev_tic; + this->tics.expires = jiffies + AX25_TICS; + + add_timer(&this->tics); +} + +static void ax25_dev_tic(unsigned long param) +{ + ax25_cb *active; + struct ax25_dev *this = (struct ax25_dev *) param; + + if (!this->needs_transmit && ((!this->hw.ptt) || (!this->hw.ptt(this->netdev)))) { + for (active = this->list.all; active; active = active->next) { + /* + * only run the timer on idle connections. + */ + if (!active->ready.state) + ax25_timer(active); + } + } + ax25_dev_set_tic(this); +} + +/* + * Timer for channel access arbitration. Fires every 100ms if the channel + * is idle (i.e. no connections need to transmit), and in intervals of + * half of a frame length if trying to transmit + */ +static void ax25_dev_set_timer(struct ax25_dev *this, unsigned int tics) +{ + this->timer.data = (unsigned long)this; + this->timer.function = &ax25_dev_timer; + this->timer.expires = jiffies + tics; + + add_timer(&this->timer); +} + +static void ax25_dev_timer(unsigned long param) +{ + struct ax25_dev *this = (struct ax25_dev *) param; + struct net_device *dev = this->netdev; + ax25_cb *active; + struct sk_buff *skb; + unsigned int bytes_sent = 0; + unsigned int max_bytes; + int ppers = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_PPERSISTENCE); + int br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE); + int duplex = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_DUPLEX); + int bit_per_jiffie; + int jiffies_per_slot; + + if (br == 0) { + printk(KERN_ERR "ax25_dev_timer(%s): TX-Bitrate unset!!!\n", dev->name); + } + bit_per_jiffie = br / HZ; + jiffies_per_slot = 1200 * HZ / br + 1; + + if (this->dama_mode & DAMA_SLAVE) { + /* >>>>> DAMA slave <<<<< + * + * we only transmit when we are asked to do so or when + * T3 ran out, which should only occur if the master forgot + * our circuits (i.e. had a reset or is broken otherwise). + */ + if (this->dama_polled) { + /* we have been polled, it's ok to transmit */ + this->dama_polled = 0; + goto arbitration_ok; + } else { + /* + * we are not allowed to transmit. Maybe next time. + */ + ax25_dev_set_timer(this, jiffies_per_slot); + return; + } + } else if (this->dama_mode & DAMA_MASTER) { + /* >>>>> DAMA master <<<<< + * + * insert code here + * this could have been your ad! :-) + */ + } else { + /* >>>>> CSMA <<<<< + * + * this implements a rather innovative channel access method. + * the basic idea is to run the usual slottime/persistence + * scheme, but with two significant changes: + * 1. slottime is derived from the bitrate of the channel + * 2. persistence is variable, depending on the dcd pattern + * of the channel. + * + * "Sample the dcd in intervals of half of a frames length and + * - increment persistence value if dcd is inactive, + * - decrement persistence value if dcd is active." + * + * simulations show that this scheme gives good collision + * avoidance and throughput without knowledge about the + * dcd propagation delay and station count. It will probably + * perform *much* too aggressive in a hidden station environment. + * + * Note: The check for hw.fast skips the channel arbitration + * stuff. Set this for KISS and ethernet devices. + */ + if (!this->hw.fast && !duplex && !this->hw.ptt(this->netdev)) { + /* decide whether this is a "good" slot or not */ + if (random_num() < ppers) { + /* ok, a good one, check the dcd now */ + if (this->hw.dcd(this->netdev)) { + this->dcd_memory = 1; + /* + * too bad, dcd is up. we're too aggressive, + * but we must wait for a falling edge of the dcd + * before we can decrement persistence + */ + if (this->dcd_dropped && ppers > 1) + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST)) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers); + if (this->needs_transmit) + ax25_dev_set_timer(this, jiffies_per_slot); + return; + } + /* update dcd memory */ + this->dcd_memory = 0; + this->dcd_dropped = 0; + goto arbitration_ok; + } else { + /* a bad slot, check the dcd */ + if (!this->hw.dcd(this->netdev)) { + /* um. dcd is down, we should have tx'd here. */ + if (ppers < 128) + if (ax25_dev_get_value(dev, AX25_VALUES_MEDIA_AUTO_ADJUST)) + ax25_dev_set_value_notify(dev, AX25_VALUES_MEDIA_PPERSISTENCE, ppers+1); + /* was it up the slot before? */ + if (this->dcd_memory) { + this->dcd_dropped = 1; + } + this->dcd_memory = 0; + } else { + this->dcd_memory = 1; + } + if (this->needs_transmit) + ax25_dev_set_timer(this, jiffies_per_slot); + return; + } + } + } + +arbitration_ok: + /* + * OK, we may transmit, arbitration successful. + */ + if (this->hw.rts) this->hw.rts(this->netdev); + + /* + * compute the amount of bytes to send during 100ms (AX25_TICS) + */ + max_bytes = (bit_per_jiffie * AX25_TICS); + + /* + * UI Frames + */ + while ((bytes_sent < max_bytes || this->hw.fast) + && ((skb = skb_dequeue(&this->unproto_queue)) != NULL)) { + ax25_queue_xmit(skb); + bytes_sent += skb->len; + } + + /* + * traverse our list of connections. we're messing with a + * private list here and we will not sleep and schedule, so no + * further protection should be necessary. + * + * we implement a simple round robin style packet scheduler here. + * each device has a list of cnnections ready to transmit packets, + * and we loop through the connections until + * a. the list becomes empty + * b. the transmit time limit is reached. + * if a connection has no more packets left or exceeds its window + * of outbound packets, it is removed from the list. + */ + while ((active = this->list.ready) != NULL && ((bytes_sent < max_bytes) || this->hw.fast)) { + unsigned short start; + unsigned short end; + struct sk_buff *skbn; + ax25_cb *peer; + int in_retransmit = 0; + + skbn = skb_peek(&active->ack_queue); + + /* transmit supervisory stuff first */ + if (active->tx_rsp) { + int poll_bit = active->tx_rsp & 0x100; + int frametype = active->tx_rsp & 0x0ff; + active->tx_rsp = 0; + ax25_send_control(active, frametype, poll_bit, AX25_RESPONSE); + + + /* + * supervisory stuff is all done, clear state-change flag + */ + ax25_clr_cond(active, AX25_COND_STATE_CHANGE); + + if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ + active->ack_timer = 0; + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + } + } + + if (active->tx_cmd) { + int poll_bit = active->tx_cmd & 0x100; + int frametype = active->tx_cmd & 0x0ff; + + active->tx_cmd = 0; + + /* + * a problem exists due to a race condition between linux' + * packet-scheduler and the timer routine: a write timeout might + * happen before the packet actually reaches the device and is copied + * for transmission. our transmit routine will then grab the first + * packet off the ack queue, put a header in front of the data and + * queue it for transmission. now we have the obscure situation that + * we have two packets in our transmit queue that share a single data + * segment. this isn't bad by itself, but since the first + * retransmitted frame will have the poll bit set and eventually will + * carry an updated N(r), we modify the header of a yet unsent packet, + * resulting in a protocol violation. + * + * we do the obvious thing to prevent this here: if the packet we + * got from the ack queue is cloned, we make a private copy of the + * data. + */ + if (poll_bit && skbn + && frametype == AX25_RR + && !(active->condition & (AX25_COND_PEER_RX_BUSY|AX25_COND_STATE_CHANGE)) + && active->n2count < 4) + { + if (skb_cloned(skbn)) { + skb = skb_copy(skbn, GFP_ATOMIC); + } else + skb = skb_clone(skbn, GFP_ATOMIC); + if (skb) { + active->vs = active->va; + ax25_send_iframe(active, skb, AX25_POLLON); + active->vs = active->vs_max; + } + } else { + ax25_send_control(active, frametype, poll_bit, AX25_COMMAND); + } + /* + * supervisory stuff is all done, clear state-change flag + */ + ax25_clr_cond(active, AX25_COND_STATE_CHANGE); + if ((frametype & AX25_U) == AX25_S) { /* S frames carry NR */ + active->ack_timer = 0; + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + } + } + + /* + * if the write queue and ack queue are both empty, + * or connection is not in info transfer state + * or the peer station is busy + * or the window is closed + * or the write queue is empty and we may not retransmit yet + * then remove connection from the devices' readylist; + * + * NOTE: ax25_dev_remove_active implicitly advances the + * round robin pointer to schedule the next connection + * on the readylist. + */ + skb = skb_peek(&active->write_queue); + if ((skb == NULL && skbn == NULL) + || active->state != AX25_STATE_3 + || (active->condition & AX25_COND_PEER_RX_BUSY) != 0 + || (start = active->vs) == (end = (active->va + active->window) & active->seqmask) + || (skb == NULL && start != active->va)) + { + if (active->condition & AX25_COND_START_T1) { + ax25_clr_cond(active, AX25_COND_START_T1); + write_lock(&active->timer_lock); + active->wrt_timer = active->t1 = ax25_calculate_t1(active); + write_unlock(&active->timer_lock); + } + write_lock(&this->ready_lock); /* paranoia */ + ax25_dev_remove_active(this); + write_unlock(&this->ready_lock); + continue; + } + + /* + * handle RTS/CTS handshaking. drivers can request TX-Delay + * by returning 0 in the cts method. Note, that the driver still + * has to handle handshaking itself, but it can prevent to be + * flooded with frames while it's not ready to send. + */ + if (this->needs_transmit < AX25_TX_STATE_CTS) { + if (this->hw.cts == NULL || this->hw.cts(this->netdev)) + this->needs_transmit = AX25_TX_STATE_CTS; + else if (this->needs_transmit == AX25_TX_STATE_RTS) + this->needs_transmit = AX25_TX_STATE_WAIT_CTS; + else + break; + } + + if (skbn != NULL && start == active->va) { + skb = skbn; + in_retransmit = 1; + } + + /* + * clone the buffer, put the original into the + * ack_queue and transmit the copy. That way the + * socket will be uncharged from the memory when + * the packet is acked, not when it's transmitted. + */ + if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) + break; + + /* advance pointer to current connection */ + this->list.ready = active->ready.next; + + ax25_send_iframe(active, skbn, AX25_POLLOFF); + if (!(DAMA_STATE(active) & DAMA_SLAVE)) { + ax25_start_t1(active); + } + + /* implicit ACK */ + ax25_clr_cond(active, AX25_COND_ACK_PENDING); + + if (!in_retransmit) { + active->vs_max = active->vs = (active->vs + 1) & active->seqmask; + skb_dequeue(&active->write_queue); + skb_queue_tail(&active->ack_queue, skb); + + if (active->vs_rtt == -1) { + active->rtt_timestamp = jiffies; + active->vs_rtt = active->vs; + } + + this->tx_iframes++; + this->tx_bytes += skbn->len; + } else { + active->vs = active->vs_max; + if (active->condition & AX25_COND_START_T1) { + ax25_clr_cond(active, AX25_COND_START_T1); + write_lock(&active->timer_lock); + active->wrt_timer = active->t1 = ax25_calculate_t1(active); + write_unlock(&active->timer_lock); + } + ax25_dev_remove_ready(this, active); + } + + bytes_sent += skbn->len; + + peer = active->peer; + if (peer && (peer->condition & AX25_COND_OWN_RX_BUSY) + && skb_queue_len(&active->write_queue) < 5) + { + ax25_clr_cond(peer, AX25_COND_OWN_RX_BUSY); + ax25_set_cond(peer, AX25_COND_STATE_CHANGE); + peer->state = AX25_STATE_4; + ax25_transmit_enquiry(peer); + } + } + + this->bytes_sent += bytes_sent; + + if (this->list.ready == NULL) { + this->bytes_sent = 0; + this->needs_transmit = AX25_TX_STATE_IDLE; + } else { + if (this->bytes_sent > this->max_bytes) { + this->bytes_sent = 0; + ax25_dev_set_timer(this, HZ/2); + } else + ax25_dev_set_timer(this, AX25_TICS); + } +} + +/* + * send a control frame + */ +static void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) +{ + struct sk_buff *skb; + unsigned char *dptr; + struct net_device *dev; + + if ((dev = ax25->device) == NULL) + return; /* Route died */ + + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr) + 2, GFP_ATOMIC)) == NULL) + return; + + skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_sizeof_addr(&ax25->addr)); + + /* Assume a response - address structure for DTE */ + if (ax25->seqmask == AX25_SEQMASK) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? AX25_PF : 0; + if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ + *dptr |= (ax25->vr << 5); + } else { + if ((frametype & AX25_U) == AX25_U) { + dptr = skb_put(skb, 1); + *dptr = frametype; + *dptr |= (poll_bit) ? AX25_PF : 0; + } else { + dptr = skb_put(skb, 2); + dptr[0] = frametype; + dptr[1] = (ax25->vr << 1); + dptr[1] |= (poll_bit) ? AX25_EPF : 0; + } + } + + skb->nh.raw = skb->data; + ax25_transmit_buffer(ax25, skb, type); + ax25->vl = ax25->vr; /* vl: last acked frame */ +} + +static void ax25_kick_device(struct ax25_dev* ax25_device) +{ + write_lock(&ax25_dev_lock); + if (!ax25_device->needs_transmit) { + ax25_device->needs_transmit = AX25_TX_STATE_RTS; + ax25_device->task_queue.routine = (void *) ax25_dev_timer; + ax25_device->task_queue.data = (void *)ax25_device; + ax25_device->task_queue.sync = 0; + queue_task(&ax25_device->task_queue, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } + write_unlock(&ax25_dev_lock); +} + +/* + * This procedure is passed a buffer descriptor for an iframe. It builds + * the rest of the control part of the frame and then writes it out. + * + */ +static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) +{ + unsigned char *frame; + + skb->nh.raw = skb->data; + + if (ax25->seqmask == AX25_SEQMASK) { + frame = skb_push(skb, 1); + + *frame = AX25_I; + *frame |= (poll_bit) ? AX25_PF : 0; + *frame |= (ax25->vr << 5); + *frame |= (ax25->vs << 1); + } else { + frame = skb_push(skb, 2); + + frame[0] = AX25_I; + frame[0] |= (ax25->vs << 1); + frame[1] = (poll_bit) ? AX25_EPF : 0; + frame[1] |= (ax25->vr << 1); + } + + ax25->idletimer = ax25->idle; + ax25_transmit_buffer(ax25, skb, AX25_COMMAND); + ax25->vl = ax25->vr; /* vl: last acked frame */ +} + +static void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) +{ + unsigned char *ptr; + + if (ax25->device == NULL) + return; + + if (skb_headroom(skb) < ax25_sizeof_addr(&ax25->addr)) { + printk(KERN_WARNING "ax25_transmit_buffer: not enough room for digi-peaters\n"); + kfree_skb(skb); + return; + } + + ptr = skb_push(skb, ax25_sizeof_addr(&ax25->addr)); + ax25_build_addr(ptr, &ax25->addr, type, ax25->seqmask); + skb->dev = ax25->device; + ax25_queue_xmit(skb); +} + +/* ---------------------------------------------------------------------*/ + +/* A small shim to dev_queue_xmit to do any packet forwarding in operation. */ +static void ax25_queue_xmit(struct sk_buff *skb) +{ + skb->protocol = htons(ETH_P_AX25); + skb->dev = ax25_fwd_dev(skb->dev); + + dev_queue_xmit(skb); +} + +/* ---------------------------------------------------------------------*/ + +static struct ax25_dev *ax25_dev_get_dev(struct net_device *dev) +{ + struct ax25_dev *ax25_device = AX25_PTR(dev); + + if (ax25_device == NULL) + return NULL; + + if (ax25_device->magic == AX25_DEV_MAGIC) + return ax25_device; + + return NULL; +} diff --git a/net/ax25/ax25_ddi.h b/net/ax25/ax25_ddi.h new file mode 100644 index 000000000..acaf4aa37 --- /dev/null +++ b/net/ax25/ax25_ddi.h @@ -0,0 +1,39 @@ +/* + * Interface declaration for the DDI layer. + * + * Matthias Welwarsky (DG2FEF) 05/25/98 + * + */ + + +#ifndef _AX25_DDI_H +#define _AX25_DDI_H + +enum { + AX25_TX_STATE_IDLE = 0, + AX25_TX_STATE_RTS, + AX25_TX_STATE_WAIT_CTS, + AX25_TX_STATE_CTS +}; + +extern void ax25_ddi_init(void); +extern ax25_cb* ax25_dev_list(struct net_device *); +extern void ax25_dev_insert_cb(ax25_cb *); +extern void ax25_dev_remove_cb(ax25_cb *); +extern ax25_cb* ax25_dev_find_listener(ax25_address *, int, struct net_device *); +extern struct sock* ax25_dev_find_socket(ax25_address *, ax25_address *, struct net_device *, int); +extern int ax25_dev_match_addr(ax25_address *, struct net_device *); +extern void ax25_kick(ax25_cb *); +extern void ax25_send_unproto(struct sk_buff*, struct net_device*); +extern void ax25_send_broadcast(struct sk_buff*); +extern void ax25_dev_set_value_notify(struct net_device *dev, int, int); +extern void ax25_dev_device_up(struct net_device *); +extern void ax25_dev_device_down(struct net_device *); +extern int ax25_fwd_ioctl(unsigned int, struct ax25_fwd_struct *); +extern struct net_device* ax25_fwd_dev(struct net_device *); +extern int ax25_dev_get_info(char*, char**, off_t, int); +extern void ax25_notify_dispatcher(struct net_device *dev, int id, int oldval, int newval); +extern void ax25_dev_set_dama(struct net_device *dev, int dama); + +extern struct net_device* ax25_devices[]; +#endif diff --git a/net/ax25/ax25_dev.c b/net/ax25/ax25_dev.c deleted file mode 100644 index efeec64e0..000000000 --- a/net/ax25/ax25_dev.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Other kernels modules in this kit are generally BSD derived. See the copyright headers. - * - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_route.c. - */ - -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/init.h> - -ax25_dev *ax25_dev_list; - -ax25_dev *ax25_dev_ax25dev(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - if (ax25_dev->dev == dev) - return ax25_dev; - - return NULL; -} - -ax25_dev *ax25_addr_ax25dev(ax25_address *addr) -{ - ax25_dev *ax25_dev; - - for (ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - if (ax25cmp(addr, (ax25_address *)ax25_dev->dev->dev_addr) == 0) - return ax25_dev; - - return NULL; -} - -/* - * This is called when an interface is brought up. These are - * reasonable defaults. - */ -void ax25_dev_device_up(struct net_device *dev) -{ - ax25_dev *ax25_dev; - unsigned long flags; - - if ((ax25_dev = kmalloc(sizeof(*ax25_dev), GFP_ATOMIC)) == NULL) { - printk(KERN_ERR "AX.25: ax25_dev_device_up - out of memory\n"); - return; - } - - ax25_unregister_sysctl(); - - memset(ax25_dev, 0x00, sizeof(*ax25_dev)); - - ax25_dev->dev = dev; - ax25_dev->forward = NULL; - - ax25_dev->values[AX25_VALUES_IPDEFMODE] = AX25_DEF_IPDEFMODE; - ax25_dev->values[AX25_VALUES_AXDEFMODE] = AX25_DEF_AXDEFMODE; - ax25_dev->values[AX25_VALUES_BACKOFF] = AX25_DEF_BACKOFF; - ax25_dev->values[AX25_VALUES_CONMODE] = AX25_DEF_CONMODE; - ax25_dev->values[AX25_VALUES_WINDOW] = AX25_DEF_WINDOW; - ax25_dev->values[AX25_VALUES_EWINDOW] = AX25_DEF_EWINDOW; - ax25_dev->values[AX25_VALUES_T1] = AX25_DEF_T1; - ax25_dev->values[AX25_VALUES_T2] = AX25_DEF_T2; - ax25_dev->values[AX25_VALUES_T3] = AX25_DEF_T3; - ax25_dev->values[AX25_VALUES_IDLE] = AX25_DEF_IDLE; - ax25_dev->values[AX25_VALUES_N2] = AX25_DEF_N2; - ax25_dev->values[AX25_VALUES_PACLEN] = AX25_DEF_PACLEN; - ax25_dev->values[AX25_VALUES_PROTOCOL] = AX25_DEF_PROTOCOL; - ax25_dev->values[AX25_VALUES_DS_TIMEOUT]= AX25_DEF_DS_TIMEOUT; - - save_flags(flags); cli(); - ax25_dev->next = ax25_dev_list; - ax25_dev_list = ax25_dev; - restore_flags(flags); - - ax25_register_sysctl(); -} - -void ax25_dev_device_down(struct net_device *dev) -{ - ax25_dev *s, *ax25_dev; - unsigned long flags; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return; - - ax25_unregister_sysctl(); - - save_flags(flags); cli(); - -#ifdef CONFIG_AX25_DAMA_SLAVE - ax25_ds_del_timer(ax25_dev); -#endif - - /* - * Remove any packet forwarding that points to this device. - */ - for (s = ax25_dev_list; s != NULL; s = s->next) - if (s->forward == dev) - s->forward = NULL; - - if ((s = ax25_dev_list) == ax25_dev) { - ax25_dev_list = s->next; - restore_flags(flags); - kfree(ax25_dev); - ax25_register_sysctl(); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == ax25_dev) { - s->next = ax25_dev->next; - restore_flags(flags); - kfree(ax25_dev); - ax25_register_sysctl(); - return; - } - - s = s->next; - } - - restore_flags(flags); - ax25_register_sysctl(); -} - -int ax25_fwd_ioctl(unsigned int cmd, struct ax25_fwd_struct *fwd) -{ - ax25_dev *ax25_dev, *fwd_dev; - - if ((ax25_dev = ax25_addr_ax25dev(&fwd->port_from)) == NULL) - return -EINVAL; - - switch (cmd) { - case SIOCAX25ADDFWD: - if ((fwd_dev = ax25_addr_ax25dev(&fwd->port_to)) == NULL) - return -EINVAL; - if (ax25_dev->forward != NULL) - return -EINVAL; - ax25_dev->forward = fwd_dev->dev; - break; - - case SIOCAX25DELFWD: - if (ax25_dev->forward == NULL) - return -EINVAL; - ax25_dev->forward = NULL; - break; - - default: - return -EINVAL; - } - - return 0; -} - -struct net_device *ax25_fwd_dev(struct net_device *dev) -{ - ax25_dev *ax25_dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return dev; - - if (ax25_dev->forward == NULL) - return dev; - - return ax25_dev->forward; -} - -/* - * Free all memory associated with device structures. - */ -void __exit ax25_dev_free(void) -{ - ax25_dev *s, *ax25_dev = ax25_dev_list; - - while (ax25_dev != NULL) { - s = ax25_dev; - ax25_dev = ax25_dev->next; - - kfree(s); - } -} diff --git a/net/ax25/ax25_ds_in.c b/net/ax25/ax25_ds_in.c deleted file mode 100644 index 6c65baea5..000000000 --- a/net/ax25/ax25_ds_in.c +++ /dev/null @@ -1,312 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c - * Joerg(DL1BKE) Fixed it. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Joerg(DL1BKE) ax25->n2count never got reset - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <net/ip.h> /* For ip_rcv */ -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_ds_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - } - ax25_dama_on(ax25); - - /* according to DK4EG´s spec we are required to - * send a RR RESPONSE FINAL NR=0. - */ - - ax25_std_enquiry_response(ax25); - break; - - case AX25_DM: - if (pf) ax25_disconnect(ax25, ECONNREFUSED); - break; - - default: - if (pf) ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_ds_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) { - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - } - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_ds_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - ax25_dama_on(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_dama_off(ax25); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_dama_off(ax25); - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count=0; - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - - if (ax25_validate_nr(ax25, nr)) { - if (ax25->va != nr) - ax25->n2count=0; - - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - - if (type == AX25_COMMAND && pf) - ax25_ds_enquiry_response(ax25); - } else { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_ds_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - ax25->n2count = 0; - } else { - if (ax25_check_iframes_acked(ax25, nr)) - ax25->n2count = 0; - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_ds_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_ds_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_ds_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_ds_enquiry_response(ax25); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_ds_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_ds_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_ds_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_ds_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_ds_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - return queued; -} - diff --git a/net/ax25/ax25_ds_subr.c b/net/ax25/ax25_ds_subr.c deleted file mode 100644 index e3e88d771..000000000 --- a/net/ax25/ax25_ds_subr.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_out.c and ax25_subr.c. - * Joerg(DL1BKE) Changed ax25_ds_enquiry_response(), - * fixed ax25_dama_on() and ax25_dama_off(). - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -void ax25_ds_nr_error_recovery(ax25_cb *ax25) -{ - ax25_ds_establish_data_link(ax25); -} - -/* - * dl1bke 960114: transmit I frames on DAMA poll - */ -void ax25_ds_enquiry_response(ax25_cb *ax25) -{ - ax25_cb *ax25o; - - /* Please note that neither DK4EG´s nor DG2FEF´s - * DAMA spec mention the following behaviour as seen - * with TheFirmware: - * - * DB0ACH->DL1BKE <RR C P R0> [DAMA] - * DL1BKE->DB0ACH <I NR=0 NS=0> - * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> - * DL1BKE->DB0ACH <RR R F R0> - * - * The Flexnet DAMA Master implementation apparently - * insists on the "proper" AX.25 behaviour: - * - * DB0ACH->DL1BKE <RR C P R0> [DAMA] - * DL1BKE->DB0ACH <RR R F R0> - * DL1BKE->DB0ACH <I NR=0 NS=0> - * DL1BKE-7->DB0PRA-6 DB0ACH <I C S3 R5> - * - * Flexnet refuses to send us *any* I frame if we send - * a REJ in case AX25_COND_REJECT is set. It is superfluous in - * this mode anyway (a RR or RNR invokes the retransmission). - * Is this a Flexnet bug? - */ - - ax25_std_enquiry_response(ax25); - - if (!(ax25->condition & AX25_COND_PEER_RX_BUSY)) { - ax25_requeue_frames(ax25); - ax25_kick(ax25); - } - - if (ax25->state == AX25_STATE_1 || ax25->state == AX25_STATE_2 || skb_peek(&ax25->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25); - else - ax25->n2count = 0; - - ax25_start_t3timer(ax25); - ax25_ds_set_timer(ax25->ax25_dev); - - for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) { - if (ax25o == ax25) - continue; - - if (ax25o->ax25_dev != ax25->ax25_dev) - continue; - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2) { - ax25_ds_t1_timeout(ax25o); - continue; - } - - if (!(ax25o->condition & AX25_COND_PEER_RX_BUSY) && ax25o->state == AX25_STATE_3) { - ax25_requeue_frames(ax25o); - ax25_kick(ax25o); - } - - if (ax25o->state == AX25_STATE_1 || ax25o->state == AX25_STATE_2 || skb_peek(&ax25o->ack_queue) != NULL) - ax25_ds_t1_timeout(ax25o); - - /* do not start T3 for listening sockets (tnx DD8NE) */ - - if (ax25o->state != AX25_STATE_0) - ax25_start_t3timer(ax25o); - } -} - -void ax25_ds_establish_data_link(ax25_cb *ax25) -{ - ax25->condition &= AX25_COND_DAMA_MODE; - ax25->n2count = 0; - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); -} - -/* - * :::FIXME::: - * This is a kludge. Not all drivers recognize kiss commands. - * We need a driver level request to switch duplex mode, that does - * either SCC changing, PI config or KISS as required. Currently - * this request isn't reliable. - */ -static void ax25_kiss_cmd(ax25_dev *ax25_dev, unsigned char cmd, unsigned char param) -{ - struct sk_buff *skb; - unsigned char *p; - - if (ax25_dev->dev == NULL) - return; - - if ((skb = alloc_skb(2, GFP_ATOMIC)) == NULL) - return; - - skb->nh.raw = skb->data; - p = skb_put(skb, 2); - - *p++ = cmd; - *p++ = param; - - skb->dev = ax25_dev->dev; - skb->protocol = htons(ETH_P_AX25); - - dev_queue_xmit(skb); -} - -/* - * A nasty problem arises if we count the number of DAMA connections - * wrong, especially when connections on the device already existed - * and our network node (or the sysop) decides to turn on DAMA Master - * mode. We thus flag the 'real' slave connections with - * ax25->dama_slave=1 and look on every disconnect if still slave - * connections exist. - */ -static int ax25_check_dama_slave(ax25_dev *ax25_dev) -{ - ax25_cb *ax25; - - for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next) - if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) - return 1; - - return 0; -} - -void ax25_dev_dama_on(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave == 0) - ax25_kiss_cmd(ax25_dev, 5, 1); - - ax25_dev->dama.slave = 1; - ax25_ds_set_timer(ax25_dev); -} - -void ax25_dev_dama_off(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) - return; - - if (ax25_dev->dama.slave && !ax25_check_dama_slave(ax25_dev)) { - ax25_kiss_cmd(ax25_dev, 5, 0); - ax25_dev->dama.slave = 0; - ax25_ds_del_timer(ax25_dev); - } -} - -void ax25_dama_on(ax25_cb *ax25) -{ - ax25_dev_dama_on(ax25->ax25_dev); - ax25->condition |= AX25_COND_DAMA_MODE; -} - -void ax25_dama_off(ax25_cb *ax25) -{ - ax25->condition &= ~AX25_COND_DAMA_MODE; - ax25_dev_dama_off(ax25->ax25_dev); -} - diff --git a/net/ax25/ax25_ds_timer.c b/net/ax25/ax25_ds_timer.c deleted file mode 100644 index 3c5b2ea9c..000000000 --- a/net/ax25/ax25_ds_timer.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_timer.c. - * Joerg(DL1BKE) Added DAMA Slave Timeout timer - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -static void ax25_ds_timeout(unsigned long); - -/* - * Add DAMA slave timeout timer to timer list. - * Unlike the connection based timers the timeout function gets - * triggered every second. Please note that NET_AX25_DAMA_SLAVE_TIMEOUT - * (aka /proc/sys/net/ax25/{dev}/dama_slave_timeout) is still in - * 1/10th of a second. - */ - -static void ax25_ds_add_timer(ax25_dev *ax25_dev) -{ - struct timer_list *t = &ax25_dev->dama.slave_timer; - t->data = (unsigned long) ax25_dev; - t->function = &ax25_ds_timeout; - t->expires = jiffies + HZ; - add_timer(t); -} - -void ax25_ds_del_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev) del_timer(&ax25_dev->dama.slave_timer); -} - -void ax25_ds_set_timer(ax25_dev *ax25_dev) -{ - if (ax25_dev == NULL) /* paranoia */ - return; - - del_timer(&ax25_dev->dama.slave_timer); - ax25_dev->dama.slave_timeout = ax25_dev->values[AX25_VALUES_DS_TIMEOUT] / 10; - ax25_ds_add_timer(ax25_dev); -} - -/* - * DAMA Slave Timeout - * Silently discard all (slave) connections in case our master forgot us... - */ - -static void ax25_ds_timeout(unsigned long arg) -{ - ax25_dev *ax25_dev = (struct ax25_dev *) arg; - ax25_cb *ax25; - - if (ax25_dev == NULL || !ax25_dev->dama.slave) - return; /* Yikes! */ - - if (!ax25_dev->dama.slave_timeout || --ax25_dev->dama.slave_timeout) { - ax25_ds_set_timer(ax25_dev); - return; - } - - for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) { - if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE)) - continue; - - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - } - - ax25_dev_dama_off(ax25_dev); -} - -void ax25_ds_heartbeat_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - /* - * Check the state of the receive buffer. - */ - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - break; - } - } - break; - } - - ax25_start_heartbeat(ax25); -} - -/* dl1bke 960114: T3 works much like the IDLE timeout, but - * gets reloaded with every frame for this - * connection. - */ -void ax25_ds_t3timer_expiry(ax25_cb *ax25) -{ - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_dama_off(ax25); - ax25_disconnect(ax25, ETIMEDOUT); -} - -/* dl1bke 960228: close the connection when IDLE expires. - * unlike T3 this timer gets reloaded only on - * I frames. - */ -void ax25_ds_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } -} - -/* dl1bke 960114: The DAMA protocol requires to send data and SABM/DISC - * within the poll of any connected channel. Remember - * that we are not allowed to send anything unless we - * get polled by the Master. - * - * Thus we'll have to do parts of our T1 handling in - * ax25_enquiry_response(). - */ -void ax25_ds_t1_timeout(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLOFF, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLOFF, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - - case AX25_STATE_3: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_iface.c b/net/ax25/ax25_iface.c deleted file mode 100644 index 15ca9c453..000000000 --- a/net/ax25/ax25_iface.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. - */ - -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -static struct protocol_struct { - struct protocol_struct *next; - unsigned int pid; - int (*func)(struct sk_buff *, ax25_cb *); -} *protocol_list; - -static struct linkfail_struct { - struct linkfail_struct *next; - void (*func)(ax25_cb *, int); -} *linkfail_list; - -static struct listen_struct { - struct listen_struct *next; - ax25_address callsign; - struct net_device *dev; -} *listen_list; - -int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) -{ - struct protocol_struct *protocol; - unsigned long flags; - - if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) - return 0; -#ifdef CONFIG_INET - if (pid == AX25_P_IP || pid == AX25_P_ARP) - return 0; -#endif - if ((protocol = kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) - return 0; - - protocol->pid = pid; - protocol->func = func; - - save_flags(flags); - cli(); - - protocol->next = protocol_list; - protocol_list = protocol; - - restore_flags(flags); - - return 1; -} - -void ax25_protocol_release(unsigned int pid) -{ - struct protocol_struct *s, *protocol = protocol_list; - unsigned long flags; - - if (protocol == NULL) - return; - - save_flags(flags); - cli(); - - if (protocol->pid == pid) { - protocol_list = protocol->next; - restore_flags(flags); - kfree(protocol); - return; - } - - while (protocol != NULL && protocol->next != NULL) { - if (protocol->next->pid == pid) { - s = protocol->next; - protocol->next = protocol->next->next; - restore_flags(flags); - kfree(s); - return; - } - - protocol = protocol->next; - } - - restore_flags(flags); -} - -int ax25_linkfail_register(void (*func)(ax25_cb *, int)) -{ - struct linkfail_struct *linkfail; - unsigned long flags; - - if ((linkfail = kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) - return 0; - - linkfail->func = func; - - save_flags(flags); - cli(); - - linkfail->next = linkfail_list; - linkfail_list = linkfail; - - restore_flags(flags); - - return 1; -} - -void ax25_linkfail_release(void (*func)(ax25_cb *, int)) -{ - struct linkfail_struct *s, *linkfail = linkfail_list; - unsigned long flags; - - if (linkfail == NULL) - return; - - save_flags(flags); - cli(); - - if (linkfail->func == func) { - linkfail_list = linkfail->next; - restore_flags(flags); - kfree(linkfail); - return; - } - - while (linkfail != NULL && linkfail->next != NULL) { - if (linkfail->next->func == func) { - s = linkfail->next; - linkfail->next = linkfail->next->next; - restore_flags(flags); - kfree(s); - return; - } - - linkfail = linkfail->next; - } - - restore_flags(flags); -} - -int ax25_listen_register(ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - unsigned long flags; - - if (ax25_listen_mine(callsign, dev)) - return 0; - - if ((listen = kmalloc(sizeof(*listen), GFP_ATOMIC)) == NULL) - return 0; - - listen->callsign = *callsign; - listen->dev = dev; - - save_flags(flags); - cli(); - - listen->next = listen_list; - listen_list = listen; - - restore_flags(flags); - - return 1; -} - -void ax25_listen_release(ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *s, *listen = listen_list; - unsigned long flags; - - if (listen == NULL) - return; - - save_flags(flags); - cli(); - - if (ax25cmp(&listen->callsign, callsign) == 0 && listen->dev == dev) { - listen_list = listen->next; - restore_flags(flags); - kfree(listen); - return; - } - - while (listen != NULL && listen->next != NULL) { - if (ax25cmp(&listen->next->callsign, callsign) == 0 && listen->next->dev == dev) { - s = listen->next; - listen->next = listen->next->next; - restore_flags(flags); - kfree(s); - return; - } - - listen = listen->next; - } - - restore_flags(flags); -} - -int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) -{ - struct protocol_struct *protocol; - - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return protocol->func; - - return NULL; -} - -int ax25_listen_mine(ax25_address *callsign, struct net_device *dev) -{ - struct listen_struct *listen; - - for (listen = listen_list; listen != NULL; listen = listen->next) - if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) - return 1; - - return 0; -} - -void ax25_link_failed(ax25_cb *ax25, int reason) -{ - struct linkfail_struct *linkfail; - - for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) - (linkfail->func)(ax25, reason); -} - -int ax25_protocol_is_registered(unsigned int pid) -{ - struct protocol_struct *protocol; - - for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) - if (protocol->pid == pid) - return 1; - - return 0; -} - diff --git a/net/ax25/ax25_in.c b/net/ax25/ax25_in.c index 7c1484d6e..1e51005c4 100644 --- a/net/ax25/ax25_in.c +++ b/net/ax25/ax25_in.c @@ -1,68 +1,333 @@ /* - * AX.25 release 037 + * ax25_in.c: Routines for processing of incoming frames (NEW-AX.25) * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), + * Jonathan (G4KLX), Alan Cox (GW4PTS), HaJo (DD8NE) * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: Most of this code is based on the SDL diagrams published in the + * ARRL Computer Networking Conference papers. The diagrams have mi + * in them, but are mostly correct. Before you modify the code coul + * read the SDL diagrams as the code is not obvious and probably ve + * easy to break; * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; + * Changelog: * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from - * the sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Added IP mode registration. - * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. - * Upgraded state machine for SABME. - * Added arbitrary protocol id support. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * HaJo(DD8NE) Added Idle Disc Timer T5 - * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly - * different behaviour. Fixed defrag - * routine (I hope) - * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Modularisation changes. - * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. - * AX.25 036 Jonathan(G4KLX) Move DAMA code into own file. - * Joerg(DL1BKE) Fixed DAMA Slave. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Thomas(DL9SAU) Fixed missing initialization of skb->protocol. + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ #include <linux/config.h> -#include <linux/errno.h> #include <linux/types.h> #include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/netfilter.h> +#include <linux/netdevice.h> #include <net/sock.h> -#include <net/ip.h> /* For ip_rcv */ -#include <net/arp.h> /* For arp_rcv */ -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> +#include <net/ip.h> +#include <net/arp.h> +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include "af_ax25.h" +#include "ax25_route.h" +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_lapb.h" +#include "ax25_core.h" +#include "ax25_subr.h" +#include "ax25_netlink.h" + +static int ax25_rx_fragment(ax25_cb*, struct sk_buff*); +static void ax25_decode(ax25_cb*, struct sk_buff*, ax25_pktinfo*); +static struct sock* ax25_find_socket(ax25_address*, ax25_address*, struct net_device*, int); +static int ax25_match_addr(ax25_address*, struct net_device*); +/* static void ax25_send_to_raw(struct sock*, struct sk_buff*, int); */ +static int ax25_rcv(struct sk_buff*, struct net_device*, struct packet_type*); + +/* + * Packet type registration data + */ +struct packet_type ax25_packet_type = +{ + 0, /* MUTTER ntohs(ETH_P_AX25),*/ + 0, /* copy */ + ax25_rcv, + NULL, + NULL, +}; + +/* + * Higher level upcall for a LAPB frame + */ +int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo* pkt_info) +{ + struct net_device *dev = skb->dev; + + ax25_decode(ax25, skb, pkt_info); + ax25_dev_set_dama(dev, pkt_info->dama); + if (pkt_info->pf && (pkt_info->cmdrsp == AX25_COMMAND)) + AX25_PTR(dev)->dama_polled = 1; + return ax25_lapb_table[ax25->state](ax25, skb, pkt_info); +} + +/* + * Resequencer + */ +static inline unsigned int ax25_csum_skb(struct sk_buff *skb) +{ + unsigned char *cp = skb->data; + unsigned int csum = 0; + + while (cp < (skb->data + skb->len)) + csum += *cp++; + + return csum; +} + +void ax25_reseq_update(ax25_cb* ax25, struct sk_buff *skb, int ns) +{ + if (ax25->reseq[ns].skb != NULL) + return; + + ax25->reseq[ns].csum = ax25_csum_skb(skb); +} + +int ax25_reseq_in(ax25_cb *ax25, struct sk_buff *skb, int ns, int pf) +{ + unsigned int csum; + + /* + * if either + * - the poll flag is set + * - the slot is not empty + * - we've already seen this frame + * drop it + */ + + if (pf || ax25->reseq[ns].skb != NULL || + ax25->reseq[ns].csum == (csum = ax25_csum_skb(skb))) + return 0; + + /* + * otherwise queue it + */ + ax25->reseq[ns].skb = skb; + ax25->reseq[ns].csum = csum; + + return 1; +} + +void ax25_reseq_out(ax25_cb *ax25) +{ + struct sk_buff *skb; + + while ((skb = ax25->reseq[ax25->vr].skb) != NULL && ax25_rx_iframe(ax25, skb)) { + ax25->reseq[ax25->vr].skb = NULL; + ax25->vr = (ax25->vr+1) & ax25->seqmask; + } +} + +/* + * This is where all valid I frames are sent to, to be dispatched to + * whichever protocol requires them. + */ +int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) +{ + ax25_cb *peer; + unsigned char pid = *skb->data; + struct sock *sk = NULL; + int queued = 0; + + int (*func)(struct sk_buff *, ax25_cb *); + + if (ax25->device) { + AX25_PTR(ax25->device)->rx_iframes++; + AX25_PTR(ax25->device)->rx_bytes += skb->len; + } + + ax25_clr_cond(ax25, AX25_COND_SETUP); + + /* + * don't take any more data if we're about to close + * the connection. + */ + if (ax25->condition & AX25_COND_RELEASE) { + ax25_set_cond(ax25, AX25_COND_OWN_RX_BUSY); + kfree_skb(skb); + return 1; + } + + /* + * we're only digipeating, don't care about pid + * and idle timeouts. + */ + if ((peer = ax25->peer) != NULL) { + int max_qlen; + + /* + * don't queue any more data if the peer is just about to + * be disconnected. + */ + if (peer->state < AX25_STATE_3 || peer->condition & AX25_COND_RELEASE) { + kfree_skb(skb); + return 1; + } + + skb_queue_tail(&peer->write_queue, skb); + + /* + * check the length of write_queue, set OWN_RX_BUSY if + * enough queued. + */ + max_qlen = peer->seqmask << 1; + if (max_qlen > 16) + max_qlen = 16; + if (skb_queue_len(&peer->write_queue) > max_qlen) { + ax25->condition |= AX25_COND_OWN_RX_BUSY; + } + ax25_kick(peer); + + return 1; + } + + ax25->idletimer = ax25->idle; + + if (pid == AX25_P_SEGMENT) { + skb_pull(skb, 1); + queued = ax25_rx_fragment(ax25, skb); + + } else if ((func = ax25_protocol_function(pid)) != NULL) { + skb_pull(skb, 1); /* Remove PID */ + queued = func(skb, ax25); + + } else if ((sk = ax25->sk) != NULL) { + struct sk_buff *oskb; + /* + * check if we have frames left in ax25->rcv_queue, these + * must be delivered first to maintain the sequence. + */ + while ((oskb = skb_peek(&ax25->rcv_queue)) != NULL) { + if (sk->shutdown & RCV_SHUTDOWN) + break; + if (atomic_read(&sk->rmem_alloc) + oskb->truesize < sk->rcvbuf) { + skb_dequeue(&ax25->rcv_queue); + sock_queue_rcv_skb(sk, oskb); + } else + break; + } + if (oskb == NULL) + ax25->condition &= ~AX25_COND_OWN_RX_BUSY; + + /* + * now handle the frame that has just come in. + */ + if (ax25->pidincl || sk->protocol == pid) { + if (sk->shutdown & RCV_SHUTDOWN) { + ax25->condition |= AX25_COND_OWN_RX_BUSY; + kfree_skb(skb); + } else if (sock_queue_rcv_skb(sk, skb) != 0) { + /* + * no space left on the socket, become busy + * but keep queueing up the data. + */ + ax25->condition |= AX25_COND_OWN_RX_BUSY; + + /* + * don't queue an infinite amount of + * data, 10 frames should be enough + * and we'll send an RNR anyway. If + * the peer keeps bombing us with + * valid iframes, "return 0" forces + * the l2 to drop the frame without + * ACK'ng it. This will produce heavy + * load on the channel due to constantly + * retransmitted frames, but we cannot + * just drop the packets, the application + * cannot handle the data loss induced. + */ + if (skb_queue_len(&ax25->rcv_queue) < ax25->seqmask) + skb_queue_tail(&ax25->rcv_queue, skb); + else + return 0; + } + queued = 1; + } + } + + /* + * this is a kludge to satisfy the IMHO broken interface + * between the L2 and upper protocol handlers. There was no + * handler for the PID and the socket didn't accept the PID + * either, so we free the buffer and pretend to have queued + * it. + */ + if (!queued) + kfree_skb(skb); + return 1; +} + +/* ---------------------------------------------------------------------*/ + +struct sock *ax25_make_new(struct sock *osk, struct net_device *dev) +{ + struct sock *sk; + ax25_cb *ax25; + + if ((sk = sk_alloc(PF_AX25, GFP_ATOMIC, 1)) == NULL) + return NULL; + + if ((ax25 = ax25_create_cb()) == NULL) { + sk_free(sk); + return NULL; + } + + switch (osk->type) { + case SOCK_DGRAM: + break; + case SOCK_SEQPACKET: + break; + default: + sk_free(sk); + ax25_free_cb(ax25); + return NULL; + } + + sock_init_data(NULL, sk); + + sk->destruct = ax25_free_sock; + sk->type = osk->type; + sk->socket = osk->socket; + sk->priority = osk->priority; + sk->protocol = osk->protocol; + sk->rcvbuf = osk->rcvbuf; + sk->sndbuf = osk->sndbuf; + sk->debug = osk->debug; + sk->state = TCP_ESTABLISHED; + sk->sleep = osk->sleep; + sk->zapped = osk->zapped; + + ax25->seqmask = osk->protinfo.ax25->seqmask; + ax25->backoff = osk->protinfo.ax25->backoff; + ax25->pidincl = osk->protinfo.ax25->pidincl; + ax25->iamdigi = osk->protinfo.ax25->iamdigi; + ax25->rtt = osk->protinfo.ax25->rtt; + ax25->t1 = osk->protinfo.ax25->t1; + ax25->t2 = osk->protinfo.ax25->t2; + ax25->t3 = osk->protinfo.ax25->t3; + ax25->n2 = osk->protinfo.ax25->n2; + ax25->idle = osk->protinfo.ax25->idle; + ax25->paclen = osk->protinfo.ax25->paclen; + ax25->window = osk->protinfo.ax25->window; + + ax25->device = dev; + ax25->addr = osk->protinfo.ax25->addr; + + sk->protinfo.ax25 = ax25; + ax25->sk = sk; + + return sk; +} /* * Given a fragment, queue it on the fragment queue and if the fragment @@ -83,16 +348,15 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) /* Last fragment received ? */ if (ax25->fragno == 0) { - if ((skbn = alloc_skb(AX25_MAX_HEADER_LEN + ax25->fraglen, GFP_ATOMIC)) == NULL) { + if ((skbn = alloc_skb(MAX_HEADER + ax25->fraglen, GFP_ATOMIC)) == NULL) { while ((skbo = skb_dequeue(&ax25->frag_queue)) != NULL) kfree_skb(skbo); return 1; } - skb_reserve(skbn, AX25_MAX_HEADER_LEN); + skb_reserve(skbn, MAX_HEADER); - skbn->dev = ax25->ax25_dev->dev; - skbn->h.raw = skbn->data; + skbn->dev = ax25->device; skbn->nh.raw = skbn->data; /* Copy data from the fragments */ @@ -126,360 +390,422 @@ static int ax25_rx_fragment(ax25_cb *ax25, struct sk_buff *skb) return 0; } + /* - * This is where all valid I frames are sent to, to be dispatched to - * whichever protocol requires them. + * This routine is the centralised routine for parsing the control + * information for the different frame formats. */ -int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb) +static void ax25_decode(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt_info) { - int (*func)(struct sk_buff *, ax25_cb *); - volatile int queued = 0; - unsigned char pid; - - if (skb == NULL) return 0; - - ax25_start_idletimer(ax25); + unsigned char *frame; + + frame = skb->data; + pkt_info->frametype = AX25_ILLEGAL; + pkt_info->ns = pkt_info->nr = pkt_info->pf = 0; + + if (ax25->seqmask == AX25_SEQMASK) { + if ((frame[0] & AX25_S) == 0) { + pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */ + pkt_info->ns = (frame[0] >> 1) & 0x07; + pkt_info->nr = (frame[0] >> 5) & 0x07; + pkt_info->pf = frame[0] & AX25_PF; + } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ + pkt_info->frametype = frame[0] & 0x0F; + pkt_info->nr = (frame[0] >> 5) & 0x07; + pkt_info->pf = frame[0] & AX25_PF; + } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ + pkt_info->frametype = frame[0] & ~AX25_PF; + pkt_info->pf = frame[0] & AX25_PF; + } + skb_pull(skb, 1); + } else { + if ((frame[0] & AX25_S) == 0) { + pkt_info->frametype = AX25_I; /* I frame - carries NR/NS/PF */ + pkt_info->ns = (frame[0] >> 1) & 0x7F; + pkt_info->nr = (frame[1] >> 1) & 0x7F; + pkt_info->pf = frame[1] & AX25_EPF; + skb_pull(skb, 2); + } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ + pkt_info->frametype = frame[0] & 0x0F; + pkt_info->nr = (frame[1] >> 1) & 0x7F; + pkt_info->pf = frame[1] & AX25_EPF; + skb_pull(skb, 2); + } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ + pkt_info->frametype = frame[0] & ~AX25_PF; + pkt_info->pf = frame[0] & AX25_PF; + skb_pull(skb, 1); + } + } +} - pid = *skb->data; +/* ---------------------------------------------------------------------*/ +/* + * Find a control block that wants to accept the SABM we have just + * received. + */ +ax25_cb *ax25_find_listener(ax25_address *addr, int digi, struct net_device *dev) +{ + ax25_cb *s; -#ifdef CONFIG_INET - if (pid == AX25_P_IP) { - /* working around a TCP bug to keep additional listeners - * happy. TCP re-uses the buffer and destroys the original - * content. - */ - struct sk_buff *skbn = skb_copy(skb, GFP_ATOMIC); - if (skbn != NULL) { - kfree_skb(skb); - skb = skbn; - } + if (dev != NULL) + if ((s = ax25_dev_find_listener(addr, digi, dev)) != NULL) + return s; - skb_pull(skb, 1); /* Remove PID */ - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = ax25->ax25_dev->dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - ip_rcv(skb, skb->dev, NULL); /* Wrong ptype */ - return 1; - } -#endif - if (pid == AX25_P_SEGMENT) { - skb_pull(skb, 1); /* Remove PID */ - return ax25_rx_fragment(ax25, skb); + for (s = ax25_list; s != NULL; s = s->next) { + if (s->state == AX25_LISTEN + && s->iamdigi == digi + && !ax25cmp(&s->addr.src, addr)) + return s; } + return NULL; +} - if ((func = ax25_protocol_function(pid)) != NULL) { - skb_pull(skb, 1); /* Remove PID */ - return (*func)(skb, ax25); - } +/* ---------------------------------------------------------------------*/ +/* + * Find an AX.25 socket given both ends. + */ +static struct sock *ax25_find_socket(ax25_address *my_addr, ax25_address *dest_addr, struct net_device *dev, int type) +{ + ax25_cb *s; + struct sock *sk; - if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) { - if ((!ax25->pidincl && ax25->sk->protocol == pid) || ax25->pidincl) { - if (sock_queue_rcv_skb(ax25->sk, skb) == 0) - queued = 1; - else - ax25->condition |= AX25_COND_OWN_RX_BUSY; + if (dev != NULL) + if ((sk = ax25_dev_find_socket(my_addr, dest_addr, dev, type)) != NULL) + return sk; + + for (s = ax25_list; s != NULL; s = s->next) { + if (s->sk != NULL && s->sk->type == type + && !ax25cmp(&s->addr.src, my_addr) + && !ax25cmp(&s->addr.dest, dest_addr)) + { + return s->sk; } } + return NULL; +} + +/* ---------------------------------------------------------------------*/ - return queued; +static int ax25_match_addr(ax25_address *addr, struct net_device *dev) +{ + ax25_cb *s; + + if (dev != NULL && ax25_dev_match_addr(addr, dev)) + return 1; + + for (s = ax25_list; s != NULL; s = s->next) { + if (s->state == AX25_LISTEN && s->sk == NULL && ax25cmp(&s->addr.src, addr) == 0) + return 1; + } + return 0; } +/* ---------------------------------------------------------------------*/ + +#ifdef notdef /* - * Higher level upcall for a LAPB frame + * this is completely broken. + * + * it depends on that sk is the first of a linked list of sockets + * and traverses the list to find them all, cloning the packet if the + * protocol and socket type both match. */ -static int ax25_process_rx_frame(ax25_cb *ax25, struct sk_buff *skb, int type, int dama) +static void ax25_send_to_raw(struct sock *sk, struct sk_buff *skb, int proto) { - int queued = 0; + struct sk_buff *copy; - if (ax25->state == AX25_STATE_0) - return 0; + while (sk != NULL) { + if (sk->type == SOCK_RAW && + sk->protocol == proto && + atomic_read(&sk->rmem_alloc) <= sk->rcvbuf) { + if ((copy = skb_clone(skb, GFP_ATOMIC)) == NULL) + return; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - queued = ax25_std_frame_in(ax25, skb, type); - break; + if (sock_queue_rcv_skb(sk, copy) != 0) + kfree_skb(copy); + } -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (dama || ax25->ax25_dev->dama.slave) - queued = ax25_ds_frame_in(ax25, skb, type); - else - queued = ax25_std_frame_in(ax25, skb, type); - break; -#endif + sk = sk->next; } - - return queued; } +#endif -static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, ax25_address *dev_addr, struct packet_type *ptype) +/* ---------------------------------------------------------------------*/ + +static int ax25_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) { struct sock *make; struct sock *sk; - int type = 0; - ax25_digi dp, reverse_dp; - ax25_cb *ax25; - ax25_address src, dest; - ax25_address *next_digi = NULL; - ax25_dev *ax25_dev; - struct sock *raw; - int mine = 0; - int dama; + ax25_cb *ax25, *peer = NULL; + ax25_address *next_digi; + ax25_pktinfo pinf; + ax25_addr_t reverse_addr; + int queued; /* * Process the AX.25/LAPB frame. */ - skb->h.raw = skb->data; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) { - kfree_skb(skb); - return 0; - } + skb->sk = NULL; + queued = 0; + AX25_PTR(dev)->dama_polled = 0; /* - * Parse the address header. + * TODO: packet filter */ - - if (ax25_addr_parse(skb->data, skb->len, &src, &dest, &dp, &type, &dama) == NULL) { - kfree_skb(skb); - return 0; - } +#ifdef undef + if (call_in_firewall(PF_AX25, skb->dev, skb->data, NULL, &skb) != FW_ACCEPT) + goto out_normal; +#endif /* - * Ours perhaps ? + * Parse the frame and Pull of the AX.25 headers + * leaving the CTRL/PID byte */ - if (dp.lastrepeat + 1 < dp.ndigi) /* Not yet digipeated completely */ - next_digi = &dp.calls[dp.lastrepeat + 1]; + if (ax25_parse_addr(skb->data, skb->len, &pinf) == NULL) + goto out_normal; + skb_pull(skb, ax25_sizeof_addr(&pinf.addr)); + /* - * Pull of the AX.25 headers leaving the CTRL/PID bytes + * Steps to perform while processing the frame, fast path'd for + * digipeating. */ - skb_pull(skb, ax25_addr_size(&dp)); - - /* For our port addresses ? */ - if (ax25cmp(&dest, dev_addr) == 0 && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* Also match on any registered callsign from L3/4 */ - if (!mine && ax25_listen_mine(&dest, dev) && dp.lastrepeat + 1 == dp.ndigi) - mine = 1; - - /* UI frame - bypass LAPB processing */ - if ((*skb->data & ~0x10) == AX25_UI && dp.lastrepeat + 1 == dp.ndigi) { - skb->h.raw = skb->data + 2; /* skip control and pid */ - - if ((raw = ax25_addr_match(&dest)) != NULL) - ax25_send_to_raw(raw, skb, skb->data[1]); - - if (!mine && ax25cmp(&dest, (ax25_address *)dev->broadcast) != 0) { - kfree_skb(skb); + next_digi = NULL; + if (pinf.addr.lastrepeat + 1 < pinf.addr.dcount) { + /* Not yet digipeated completely */ + struct net_device *out_dev; + next_digi = &pinf.addr.digipeater[pinf.addr.lastrepeat + 1]; + + /* check the frame type. do the easy thing first */ + if ((*skb->data & ~AX25_PF) == AX25_UI) { + /* digipeat UI frame */ + + /* check if next_digi matches one of our interfaces */ + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + + /* rebuild the header and transmit the frame */ + out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev); + skb->nh.raw = skb->data; + skb_push(skb, ax25_sizeof_addr(&reverse_addr)); + ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK); + ax25_send_unproto(skb, out_dev); return 0; } - /* Now we are pointing at the pid byte */ - switch (skb->data[1]) { -#ifdef CONFIG_INET - case AX25_P_IP: - skb_pull(skb,2); /* drop PID/CTRL */ - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_IP); - ip_rcv(skb, dev, ptype); /* Note ptype here is the wrong one, fix me later */ - break; + ax25_invert_addr(&pinf.addr, &reverse_addr); - case AX25_P_ARP: - skb_pull(skb,2); - skb->h.raw = skb->data; - skb->nh.raw = skb->data; - skb->dev = dev; - skb->pkt_type = PACKET_HOST; - skb->protocol = htons(ETH_P_ARP); - arp_rcv(skb, dev, ptype); /* Note ptype here is wrong... */ - break; -#endif - case AX25_P_TEXT: - /* Now find a suitable dgram socket */ - if ((sk = ax25_find_socket(&dest, &src, SOCK_DGRAM)) != NULL) { - if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { - kfree_skb(skb); - } else { - /* - * Remove the control and PID. - */ - skb_pull(skb, 2); - if (sock_queue_rcv_skb(sk, skb) != 0) - kfree_skb(skb); - } - } else { - kfree_skb(skb); - } - break; - - default: - kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ - break; + if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) { + /* no UI, but matches a given context */ + queued = ax25_process_rx_frame(ax25, skb, &pinf); + goto out_queued; } - return 0; - } + if ((*skb->data & ~AX25_PF) == AX25_SABM || (*skb->data & ~AX25_PF) == AX25_SABME) { + if ((ax25 = ax25_find_listener(next_digi, 1, dev)) != NULL) + goto listener_found; + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + if ((peer = ax25_create_cb()) == NULL) + goto out_normal; + if ((ax25 = ax25_create_cb()) == NULL) { + ax25_free_cb(peer); + goto out_normal; + } - /* - * Is connected mode supported on this device ? - * If not, should we DM the incoming frame (except DMs) or - * silently ignore them. For now we stay quiet. - */ - if (ax25_dev->values[AX25_VALUES_CONMODE] == 0) { - kfree_skb(skb); - return 0; - } + out_dev = ax25_rt_set_addr(&peer->addr, &pinf.addr, dev, out_dev); + ax25_fillin_cb(peer, out_dev); + ax25_nlpost_route(&pinf, dev); - /* LAPB */ + /* set up the new control block */ + ax25_fillin_cb(ax25, dev); + ax25->addr = reverse_addr; - /* AX.25 state 1-4 */ + if ((*skb->data & ~AX25_PF) == AX25_SABME) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } - ax25_digi_invert(&dp, &reverse_dp); + /* crosslink the peers */ + ax25->peer = peer; + peer->peer = ax25; - if ((ax25 = ax25_find_cb(&dest, &src, &reverse_dp, dev)) != NULL) { - /* - * Process the frame. If it is queued up internally it returns one otherwise we - * free it immediately. This routine itself wakes the user context layers so we - * do no further work - */ - if (ax25_process_rx_frame(ax25, skb, type, dama) == 0) - kfree_skb(skb); + /* set window and modulus */ + peer->window = ax25->window; + peer->seqmask = ax25->seqmask; - return 0; - } + ax25->killtimer = 30 * AX25_SLOWHZ; + ax25->state = AX25_STATE_0; + ax25_insert_cb(ax25); - /* AX.25 state 0 (disconnected) */ + peer->state = AX25_STATE_1; + ax25_insert_cb(peer); + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); - /* a) received not a SABM(E) */ + goto out_normal; + } - if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { /* - * Never reply to a DM. Also ignore any connects for - * addresses that are not our interfaces and not a socket. + * the packet was no UI or SABM(E) and didn't match an existing + * context. If it's via us, digipeat blindly. */ - if ((*skb->data & ~AX25_PF) != AX25_DM && mine) - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); + /* check if next_digi matches one of our interfaces */ + if ((out_dev = ax25rtr_get_dev(next_digi)) == NULL) + goto out_normal; + + /* rebuild the header and transmit the frame */ + out_dev = ax25_rt_set_addr(&reverse_addr, &pinf.addr, dev, out_dev); + skb->nh.raw = skb->data; + skb_push(skb, ax25_sizeof_addr(&reverse_addr)); + ax25_build_addr(skb->data, &reverse_addr, pinf.cmdrsp, AX25_SEQMASK); + ax25_send_unproto(skb, out_dev); return 0; } - /* b) received SABM(E) */ - - if (dp.lastrepeat + 1 == dp.ndigi) - sk = ax25_find_listener(&dest, 0, dev, SOCK_SEQPACKET); - else - sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET); + /* + * UI frame - bypass LAPB processing + */ + if ((*skb->data & ~0x10) == AX25_UI) { + int queued = 0; + int pid; + /* skip control and pid */ + skb->nh.raw = skb->data + 2; - if (sk != NULL) { - if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, ax25_dev)) == NULL) { - if (mine) ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - return 0; - } + /* copy the datagram to all raw sockets */ +/* ax25_send_to_raw(&dest, skb, dev); */ - ax25 = make->protinfo.ax25; - skb_set_owner_r(skb, make); - skb_queue_head(&sk->receive_queue, skb); + /* if it's not a broadcast or one of our floating listeners, drop it */ + if (!ax25_match_addr(&pinf.addr.dest, dev) + && ax25cmp(&pinf.addr.dest, (ax25_address *)dev->broadcast)) + goto out_normal; - make->state = TCP_ESTABLISHED; - make->pair = sk; + /* Now we are pointing at the pid byte */ + switch (pid = skb->data[1]) { + int (*func)(struct sk_buff *, ax25_cb *); - sk->ack_backlog++; - } else { - if (!mine) { - kfree_skb(skb); - return 0; - } + case AX25_P_TEXT: + /* Now find a suitable dgram socket */ + if ((sk = ax25_find_socket(&pinf.addr.dest, &pinf.addr.src, dev, SOCK_DGRAM)) != NULL) { + if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf) { + kfree_skb(skb); + } else { + /* + * Remove the control and PID. + */ + skb_pull(skb, 2); + if (sock_queue_rcv_skb(sk, skb) != 0) + kfree_skb(skb); + } + } else { + kfree_skb(skb); + } + break; - if ((ax25 = ax25_create_cb()) == NULL) { - ax25_return_dm(dev, &src, &dest, &dp); - kfree_skb(skb); - return 0; + default: + if ((func = ax25_protocol_function(pid)) != NULL) { + skb_pull(skb, 2); /* Remove CTL/PID */ + queued = func(skb, NULL); + } + if (!queued) + kfree_skb(skb); /* Will scan SOCK_AX25 RAW sockets */ + break; } - ax25_fillin_cb(ax25, ax25_dev); + return 0; } - ax25->source_addr = dest; - ax25->dest_addr = src; - + /* LAPB */ /* - * Sort out any digipeated paths. + * invert the digipeater path and try to find a context for the + * received packet. */ - if (dp.ndigi != 0 && ax25->digipeat == NULL && (ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - ax25_destroy_socket(ax25); - return 0; + ax25_invert_addr(&pinf.addr, &reverse_addr); + if ((ax25 = ax25_find_cb(&reverse_addr, dev)) != NULL) { + /* + * Process the frame. If it is queued up internally it returns one otherwise we + * free it immediately. This routine itself wakes the user context layers so we + * do no further work + */ + queued = ax25_process_rx_frame(ax25, skb, &pinf); + goto out_queued; } - if (dp.ndigi == 0) { - if (ax25->digipeat != NULL) { - kfree(ax25->digipeat); - ax25->digipeat = NULL; + if ((ax25 = ax25_find_listener(&pinf.addr.dest, 0, dev)) != NULL) { + listener_found: + /* + * if this frame is not a SABM(E) return DM to the sender. + * it belongs to a stale connection. maybe we have rebootet or the + * peer didn't see our UA/DM + */ + if ((*skb->data & ~AX25_PF) != AX25_SABM && (*skb->data & ~AX25_PF) != AX25_SABME) { + ax25_return_dm(dev, &pinf); + goto out_normal; } - } else { - /* Reverse the source SABM's path */ - memcpy(&ax25->digipeat, &reverse_dp, sizeof(ax25_digi)); - } - if ((*skb->data & ~AX25_PF) == AX25_SABME) { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_EWINDOW]; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25_dev->values[AX25_VALUES_WINDOW]; - } - - ax25_send_control(ax25, AX25_UA, AX25_POLLON, AX25_RESPONSE); + /* ok, we have a listener */ + ax25_nlpost_route(&pinf, dev); + if ((sk = ax25->sk) != NULL) { + /* the listener is a socket */ + if (sk->type != SOCK_SEQPACKET || sk->state != TCP_LISTEN) + goto out_normal; -#ifdef CONFIG_AX25_DAMA_SLAVE - if (dama && ax25->ax25_dev->values[AX25_VALUES_PROTOCOL] == AX25_PROTO_DAMA_SLAVE) - ax25_dama_on(ax25); -#endif + if (sk->ack_backlog == sk->max_ack_backlog || (make = ax25_make_new(sk, dev)) == NULL) { + ax25_return_dm(dev, &pinf); + goto out_normal; + } + skb->sk = make; + make->state = TCP_ESTABLISHED; + make->pair = sk; + ax25 = make->protinfo.ax25; + skb_queue_head(&sk->receive_queue, skb); + sk->ack_backlog++; + if (!sk->dead) + sk->data_ready(sk, skb->len); + queued = 1; + } else { + /* + * the listener is no socket, must be a callsign registered + * by a higher protocol, we just take the connection + */ + if ((ax25 = ax25_create_cb()) == NULL) { + ax25_return_dm(dev, &pinf); + goto out_normal; + } + ax25->slcomp_enable = ax25_rt_mode_get(&pinf.addr.src) == 'C'; + } - ax25->state = AX25_STATE_3; + /* set up the new control block */ + ax25_fillin_cb(ax25, dev); + ax25->addr = reverse_addr; - ax25_insert_socket(ax25); + if ((*skb->data & ~AX25_PF) == AX25_SABME) { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_EWINDOW); + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(dev, AX25_VALUES_WINDOW); + } - ax25_start_heartbeat(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); + ax25->device = dev; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->state = AX25_STATE_3; + ax25_insert_cb(ax25); + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + } - if (sk != NULL) { - if (!sk->dead) - sk->data_ready(sk, skb->len); - } else { + out_queued: + if (!queued) { + out_normal: kfree_skb(skb); } - return 0; } - -/* - * Receive an AX.25 frame via a SLIP interface. - */ -int ax25_kiss_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) -{ - skb->sk = NULL; /* Initially we don't know who it's for */ - skb->destructor = NULL; /* Who initializes this, dammit?! */ - - if ((*skb->data & 0x0F) != 0) { - kfree_skb(skb); /* Not a KISS data frame */ - return 0; - } - - skb_pull(skb, AX25_KISS_HEADER_LEN); /* Remove the KISS byte */ - - return ax25_rcv(skb, dev, (ax25_address *)dev->dev_addr, ptype); -} - diff --git a/net/ax25/ax25_in.h b/net/ax25/ax25_in.h new file mode 100644 index 000000000..90aa12b1e --- /dev/null +++ b/net/ax25/ax25_in.h @@ -0,0 +1,20 @@ +#ifndef _AX25_IN_H +#define _AX25_IN_H + +/* + * prototype for state machine functions. + */ + +typedef int (*ax25_statefunc_t)(ax25_cb*, struct sk_buff*, ax25_pktinfo*); + +extern int ax25_process_rx_frame(ax25_cb*, struct sk_buff*, ax25_pktinfo*); +extern int ax25_rx_iframe(ax25_cb*, struct sk_buff*); +extern void ax25_reseq_update(ax25_cb*, struct sk_buff*, int); +extern int ax25_reseq_in(ax25_cb*, struct sk_buff*, int, int); +extern void ax25_reseq_out(ax25_cb*); +extern ax25_cb* ax25_find_listener(ax25_address*, int, struct net_device*); +extern struct sock* ax25_make_new(struct sock*, struct net_device*); + +extern struct packet_type ax25_packet_type; + +#endif diff --git a/net/ax25/ax25_ip.c b/net/ax25/ax25_ip.c deleted file mode 100644 index e6dcda83d..000000000 --- a/net/ax25/ax25_ip.c +++ /dev/null @@ -1,211 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. - */ - -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/termios.h> /* For TIOCINQ/OUTQ */ -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/netfilter.h> -#include <linux/sysctl.h> -#include <net/ip.h> -#include <net/arp.h> - -/* - * IP over AX.25 encapsulation. - */ - -/* - * Shove an AX.25 UI header on an IP packet and handle ARP - */ - -#ifdef CONFIG_INET - -int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) -{ - /* header is an AX.25 UI frame from us to them */ - unsigned char *buff = skb_push(skb, AX25_HEADER_LEN); - - *buff++ = 0x00; /* KISS DATA */ - - if (daddr != NULL) - memcpy(buff, daddr, dev->addr_len); /* Address specified */ - - buff[6] &= ~AX25_CBIT; - buff[6] &= ~AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - if (saddr != NULL) - memcpy(buff, saddr, dev->addr_len); - else - memcpy(buff, dev->dev_addr, dev->addr_len); - - buff[6] &= ~AX25_CBIT; - buff[6] |= AX25_EBIT; - buff[6] |= AX25_SSSID_SPARE; - buff += AX25_ADDR_LEN; - - *buff++ = AX25_UI; /* UI */ - - /* Append a suitable AX.25 PID */ - switch (type) { - case ETH_P_IP: - *buff++ = AX25_P_IP; - break; - case ETH_P_ARP: - *buff++ = AX25_P_ARP; - break; - default: - printk(KERN_ERR "AX.25: ax25_encapsulate - wrong protocol type 0x%x2.2\n", type); - *buff++ = 0; - break; - } - - if (daddr != NULL) - return AX25_HEADER_LEN; - - return -AX25_HEADER_LEN; /* Unfinished header */ -} - -int ax25_rebuild_header(struct sk_buff *skb) -{ - struct sk_buff *ourskb; - unsigned char *bp = skb->data; - struct net_device *dev; - ax25_address *src, *dst; - ax25_route *route; - ax25_dev *ax25_dev; - - dst = (ax25_address *)(bp + 1); - src = (ax25_address *)(bp + 8); - - if (arp_find(bp + 1, skb)) - return 1; - - route = ax25_rt_find_route(dst, NULL); - dev = route->dev; - - if (dev == NULL) - dev = skb->dev; - - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return 1; - - if (bp[16] == AX25_P_IP) { - if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) { - /* - * We copy the buffer and release the original thereby - * keeping it straight - * - * Note: we report 1 back so the caller will - * not feed the frame direct to the physical device - * We don't want that to happen. (It won't be upset - * as we have pulled the frame from the queue by - * freeing it). - * - * NB: TCP modifies buffers that are still - * on a device queue, thus we use skb_copy() - * instead of using skb_clone() unless this - * gets fixed. - */ - - ax25_address src_c; - ax25_address dst_c; - - if ((ourskb = skb_copy(skb, GFP_ATOMIC)) == NULL) { - kfree_skb(skb); - return 1; - } - - if (skb->sk != NULL) - skb_set_owner_w(ourskb, skb->sk); - - kfree_skb(skb); - - src_c = *src; - dst_c = *dst; - - skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */ - skb->nh.raw = skb->data; - - ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c, -&dst_c, route->digipeat, dev); - - return 1; - } - } - - bp[7] &= ~AX25_CBIT; - bp[7] &= ~AX25_EBIT; - bp[7] |= AX25_SSSID_SPARE; - - bp[14] &= ~AX25_CBIT; - bp[14] |= AX25_EBIT; - bp[14] |= AX25_SSSID_SPARE; - - skb_pull(skb, AX25_KISS_HEADER_LEN); - - if (route->digipeat != NULL) { - if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) { - kfree_skb(skb); - return 1; - } - - skb = ourskb; - } - - skb->dev = dev; - - ax25_queue_xmit(skb); - - return 1; -} - -#else /* INET */ - -int ax25_encapsulate(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) -{ - return -AX25_HEADER_LEN; -} - -int ax25_rebuild_header(struct sk_buff *skb) -{ - return 1; -} - -#endif - diff --git a/net/ax25/ax25_ipax.c b/net/ax25/ax25_ipax.c new file mode 100644 index 000000000..65e8090a3 --- /dev/null +++ b/net/ax25/ax25_ipax.c @@ -0,0 +1,708 @@ +/* + * ax25_ipax.c: implements "virtual" interface ipax0 and en/decapsulation of IP + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF) + * + * Comment: Includes "Protocol Booster" and VJ compression switch. + * Written from scratch by Matthias Welwarsky in 1998. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#define AX25_ENCAP_MODE_IGNORE_PROTOCOL + +#define __NO_VERSION__ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/malloc.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <net/sock.h> +#include <net/arp.h> +#include <net/ip.h> +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include "ax25_route.h" +#include "ax25_core.h" +#include "ax25_ddi.h" +#include "ax25_vj.h" +#include "ax25_netlink.h" +#include "ax25_subr.h" + + + +static int ipax_hard_header(struct sk_buff*, struct net_device*, unsigned short, void*, void*, unsigned); +static int ipax_set_mac_address(struct net_device*, void*); +static int ipax_probe(struct net_device*); +static int ipax_device_event(struct notifier_block*, unsigned long, void*); +static int ipax_open(struct net_device*); +static int ipax_close(struct net_device*); +static int ipax_rcv(struct sk_buff*, ax25_cb*); +static int ipax_arp_rcv(struct sk_buff*, ax25_cb*); +static int ipax_vjc_rcv(struct sk_buff*, struct ax25_cb*); +static int ipax_vjunc_rcv(struct sk_buff*, struct ax25_cb*); +static int ipax_send_packet(struct sk_buff*, struct net_device*); +static struct net_device_stats *ipax_get_stats(struct net_device*); + +/* --------------------------------------------------------------------- */ + +static struct net_device ipax_device; + +static struct ipax_local_t { + struct net_device_stats stats; +} ipax_local; + +/* + * Device up/down notifier block + */ +static struct notifier_block ipax_dev_notifier = { + ipax_device_event, + 0 +}; + +static char ax25_bcast[7] = +{'Q' << 1, 'S' << 1, 'T' << 1, ' ' << 1, ' ' << 1, ' ' << 1, '0' << 1}; +static char ax25_test[7] = +{'L' << 1, 'I' << 1, 'N' << 1, 'U' << 1, 'X' << 1, ' ' << 1, '1' << 1}; + +/* ---------------------------------------------------------------------*/ + +int ipax_init(void) +{ + struct net_device *dev = &ipax_device; + + memset(dev, 0, sizeof (struct net_device)); + strcpy(dev->name, "ipax0"); + dev->if_port = 0; + dev->init = ipax_probe; + dev->base_addr = 0; + dev->irq = 0; + dev->dma = 0; + + register_netdevice_notifier(&ipax_dev_notifier); + + if (register_netdev(dev)) { + printk(KERN_WARNING "ipax: cannot register net device\n"); + return -ENXIO; + } + + return 0; +} + +int ipax_cleanup(void) +{ + unregister_netdevice_notifier(&ipax_dev_notifier); + unregister_netdev(&ipax_device); + return 0; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_device_event(struct notifier_block *this, unsigned long event, void *ptr) +{ + return NOTIFY_DONE; +} + +/* ---------------------------------------------------------------------*/ +/* + * set the MAC layer address of the interface. (i.e. the callsign) + * + */ +static int ipax_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *sa = (struct sockaddr *)addr; + + if (netif_running(dev)) + ax25_listen_release((ax25_address *)dev->dev_addr, NULL); + memcpy(dev->dev_addr, sa->sa_data, dev->addr_len); + if (netif_running(dev)) + ax25_listen_register((ax25_address *)dev->dev_addr, NULL); + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Interface startup. + * + */ +static int ipax_probe(struct net_device *dev) +{ + if (dev == NULL) + return -ENXIO; + + dev->base_addr = 0; + dev->irq = 0; + dev->dma = 0; + + /* Initialize the device structure. */ + dev->priv = &ipax_local; + + memset(dev->priv, 0, sizeof(struct ipax_local_t)); + + dev->open = ipax_open; + dev->stop = ipax_close; + dev->hard_start_xmit = ipax_send_packet; + dev->get_stats = ipax_get_stats; + + dev->hard_header = ipax_hard_header; + dev->set_mac_address = ipax_set_mac_address; + + dev->type = ARPHRD_AX25; + dev->hard_header_len = AX25_MAX_HEADER_LEN; + dev->mtu = 1500; + dev->addr_len = AX25_ADDR_LEN; + dev->tx_queue_len = 8; + + memcpy(dev->broadcast, ax25_bcast, dev->addr_len); + memcpy(dev->dev_addr, ax25_test, dev->addr_len); + + dev->flags = IFF_BROADCAST; + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + */ +static int ipax_open(struct net_device *dev) +{ + if (netif_running(dev)) + return 0; + + if (!ax25_listen_register((ax25_address *)dev->dev_addr, NULL)) + return 0; + + if (!ax25_protocol_register(AX25_P_IP, ipax_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_ARP, ipax_arp_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_VJCOMP, ipax_vjc_rcv)) + return 0; + + if (!ax25_protocol_register(AX25_P_VJUNCOMP, ipax_vjunc_rcv)) + return 0; + + /* dev_restart(dev); */ /* FIXME: anything to do here at all? */ + + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * the inverse routine to ipax_open. close the interface end + * decrement the module use count. + */ +static int ipax_close(struct net_device *dev) +{ + if (!netif_running(dev)) + return 0; + + netif_stop_queue(dev); + + ax25_listen_release((ax25_address *)dev->dev_addr, NULL); + ax25_protocol_release(AX25_P_IP); + ax25_protocol_release(AX25_P_ARP); + ax25_protocol_release(AX25_P_VJCOMP); + ax25_protocol_release(AX25_P_VJUNCOMP); + + return 0; + +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_rcv(struct sk_buff *skb, ax25_cb *ax25) +{ + struct net_device *dev = &ipax_device; + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_IP); + if (ax25) /* FIXME: works only for VC */ + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); /* Wrong ptype */ + lp->stats.rx_packets++; + + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_arp_rcv(struct sk_buff *skb, ax25_cb *ax25) +{ + struct net_device *dev = &ipax_device; + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_ARP); + arp_rcv(skb, dev, NULL); /* Wrong ptype */ + + lp->stats.rx_packets++; + + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_vjc_rcv(struct sk_buff *skb, struct ax25_cb *ax25) +{ + struct net_device *dev = &ipax_device; + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + + if (!ax25) + return 0; + + ax25->slcomp_enable = 1; + + /* + * check if we already have initialized slots, + * if not, it's too late. flush the frame. + */ + if (ax25->slcomp == NULL) { + ax25->slcomp = axhc_init(32, 32); + printk(KERN_DEBUG "ax25_vjc_recv: no vjc-slots allocated, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + + /* + * the data will grow upwards while decompressing, + * crippling the ax.25 header. copy the packet only if + * it's smaller than 256 bytes, else the defragmenter has + * already copied it, we don't do that twice. + */ + if (skb->len <= 256) { + struct sk_buff *skbn; + if ((skbn = skb_copy(skb, GFP_ATOMIC)) != NULL) { + kfree_skb(skb); + skb = skbn; + } + } + + /* set device the packet came in */ + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + + /* + * make headroom for MAX_HEADER bytes of TCP/IP header + */ + if (skb_headroom(skb) < MAX_HEADER) { + struct sk_buff *skbn; + if ((skbn = skb_realloc_headroom(skb, MAX_HEADER)) == NULL) { + printk(KERN_DEBUG "ax25_vjc_recv: cannot reallocate headroom, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + kfree_skb(skb); + skb = skbn; + } + + if (axhc_uncompress(ax25->slcomp, skb) <= 0) { + printk(KERN_DEBUG "ipax_vjc_rcv: error decompressing packet, dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + /* adjust pointer to network header */ + skb->nh.raw = skb->data; + skb->protocol = __constant_htons(ETH_P_IP); + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); + lp->stats.rx_packets++; + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_vjunc_rcv(struct sk_buff *skb, struct ax25_cb *ax25) +{ + struct net_device *dev = &ipax_device; + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + + if (!ax25) + return 0; + + ax25->slcomp_enable = 1; + + /* + * MW: + * check if we already have initialized slots, + * do so if not. + */ + if (ax25->slcomp == NULL) + ax25->slcomp = axhc_init(32, 32); + + if (axhc_remember(ax25->slcomp, skb) <= 0) { + printk(KERN_DEBUG "ipax_vjunc_rcv: unable to remember slot, packet dropped.\n"); + lp->stats.rx_dropped++; + return 0; + } + skb->nh.raw = skb->data; + skb->dev = dev; + skb->pkt_type = PACKET_HOST; + skb->protocol = __constant_htons(ETH_P_IP); + ax25_nlpost_armsg(skb->nh.iph->saddr, &ax25->addr.dest, dev); + ip_rcv(skb, dev, NULL); + lp->stats.rx_packets++; + return 1; +} + +/* ---------------------------------------------------------------------*/ + +static void ipax_fragment(struct sk_buff *skb, struct net_device *dev, ax25_addr_t *addr) +{ + struct iphdr *iph; + unsigned char *raw; + unsigned char *ptr; + struct sk_buff *skb2; + unsigned int mtu, hlen, left, len; + int offset; + int not_last_frag; + + /* + * Point into the IP datagram header. + */ + + raw = skb->nh.raw; + iph = (struct iphdr*)raw; + + /* + * Setup starting values. + */ + + hlen = iph->ihl * 4; + left = ntohs(iph->tot_len) - hlen; /* Space per frame */ + mtu = dev->mtu; /* Size of data space */ + ptr = raw + hlen; /* Where to start from */ + + /* + * The protocol doesn't seem to say what to do in the case that the + * frame + options doesn't fit the mtu. As it used to fall down dead + * in this case we were fortunate it didn't happen + * + * It is impossible, because mtu>=68. --ANK (980801) + */ + +#ifdef CONFIG_NET_PARANOIA + if (mtu<8) + goto fail; +#endif + + /* + * Fragment the datagram. + */ + + offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3; + not_last_frag = iph->frag_off & htons(IP_MF); + + /* + * Keep copying data until we run out. + */ + + while(left > 0) { + len = left; + /* IF: it doesn't fit, use 'mtu' - the data space left */ + if (len > mtu) + len = mtu; + /* IF: we are not sending upto and including the packet end + then align the next start on an eight byte boundary */ + if (len < left) { + len &= ~7; + } + /* + * Allocate buffer. + */ + + if ((skb2 = alloc_skb(len+hlen+AX25_MAX_HEADER_LEN+15,GFP_ATOMIC)) == NULL) { + NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n")); + goto fail; + } + + /* + * Set up data on packet + */ + + skb2->pkt_type = skb->pkt_type; + skb2->priority = skb->priority; + skb_reserve(skb2, (AX25_MAX_HEADER_LEN+15)&~15); + skb_put(skb2, len + hlen); + skb2->nh.raw = skb2->data; + skb2->h.raw = skb2->data + hlen; + + /* + * Charge the memory for the fragment to any owner + * it might possess + */ + + if (skb->sk) + skb_set_owner_w(skb2, skb->sk); + skb2->dst = dst_clone(skb->dst); + + /* + * Copy the packet header into the new buffer. + */ + + memcpy(skb2->nh.raw, raw, hlen); + + /* + * Copy a block of the IP datagram. + */ + memcpy(skb2->h.raw, ptr, len); + left -= len; + + /* + * Fill in the new header fields. + */ + iph = skb2->nh.iph; + iph->frag_off = htons((offset >> 3)); + + /* ANK: dirty, but effective trick. Upgrade options only if + * the segment to be fragmented was THE FIRST (otherwise, + * options are already fixed) and make it ONCE + * on the initial skb, so that all the following fragments + * will inherit fixed options. + */ + if (offset == 0) + ip_options_fragment(skb); + + /* + * Added AC : If we are fragmenting a fragment that's not the + * last fragment then keep MF on each bit + */ + if (left > 0 || not_last_frag) + iph->frag_off |= htons(IP_MF); + ptr += len; + offset += len; + + /* + * Put this fragment into the sending queue. + */ + + /* FIXME: where did this go? + ip_statistics.IpFragCreates++; + */ + + iph->tot_len = htons(len + hlen); + + ip_send_check(iph); + + /* build UI packet header */ + *skb_push(skb2, 1) = AX25_P_IP; + *skb_push(skb2, 1) = AX25_UI; + skb_push(skb2, ax25_sizeof_addr(addr)); + ax25_build_addr(skb2->data, addr, AX25_COMMAND, AX25_SEQMASK); + ax25_send_unproto(skb2, dev); + } + kfree_skb(skb); + + /* FIXME: where did this go? + ip_statistics.IpFragOKs++; + */ + return; + +fail: + kfree_skb(skb); + /* FIXME: where did this go? + ip_statistics.IpFragFails++; + */ + +} + +/* ---------------------------------------------------------------------*/ + +static unsigned char ipax_try_compress(ax25_cb *ax25, struct sk_buff *skb) +{ + unsigned char pid; + + if (ax25->slcomp == NULL) + ax25->slcomp = axhc_init(32, 32); + /* attempt to compress the frame */ + pid = axhc_compress(ax25->slcomp, skb, ax25->slcomp_enable); + if (pid) + *skb_push(skb, 1) = pid; + return pid; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + ax25_address *dest, *source; + struct ax25_route *axrt; + struct net_device *axdev; + ax25_addr_t addr; + + /* + * send the packet down to the tunneling protocol + */ + dest = (ax25_address *)skb->data; + source = (ax25_address *)(skb->data+AX25_ADDR_LEN); + + if (!ax25cmp(dest, (ax25_address *)ax25_bcast)) { + ax25_send_broadcast(skb); + kfree_skb(skb); + lp->stats.tx_packets++; + return 0; + } + + axrt = ax25_find_route(dest); + if (axrt == NULL) { + printk(KERN_DEBUG "ax25: ipax_send_packet(): no route found, discarding packet.\n"); + kfree_skb(skb); + lp->stats.tx_dropped++; + return 0; + } + + axdev = axrt->dev; + + addr.dest = *dest; + addr.src = *source; + addr.dcount = axrt->path.dcount; + addr.lastrepeat = -1; + memcpy(addr.digipeater, axrt->path.digipeater, sizeof(ax25_address)*addr.dcount); + + if (skb->data[15] == AX25_P_IP) { +#ifndef AX25_ENCAP_MODE_IGNORE_PROTOCOL + struct iphdr *iph = (struct iphdr *)(skb->data+16); +#endif + int mode = axrt->ip_mode; + +#ifdef AX25_ENCAP_MODE_IGNORE_PROTOCOL + if (1) { +#else + if (iph->protocol != IPPROTO_UDP) { +#endif + if (mode == 'V' || mode == 'C' + || (mode == ' ' && ax25_dev_get_value(axdev, AX25_VALUES_IPDEFMODE))) + { + int paclen = ax25_dev_get_value(axdev, AX25_VALUES_PACLEN); + ax25_cb* ax25 = ax25_find_cb(&addr, axdev); + + if (ax25 != NULL) { + /* reuse a just disconnected control block */ + if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) { + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + goto out_reused; + } + goto out_ok; + } + if ((ax25 = ax25_create_cb()) == NULL) + goto out_dropped; + ax25_fillin_cb(ax25, axdev); + ax25->addr = addr; + ax25_insert_cb(ax25); + out_reused: + ax25->slcomp_enable = (mode == 'C'); + ax25_establish_data_link(ax25); + out_ok: + /* completely strip the header */ + skb_pull(skb, AX25_MIN_HEADER_LEN+1); + if (!ipax_try_compress(ax25, skb)) + goto out_dropped; + ax25_output(ax25, paclen, skb); + dev->trans_start = jiffies; + /* netif_stop_queue(dev) */ + lp->stats.tx_packets++; + return 0; + out_dropped: + lp->stats.tx_dropped++; + kfree_skb(skb); + /* netif_stop_queue(dev) */ + return 0; + } + } else if (skb->len > axdev->mtu) { + /* completely strip the header */ + skb_pull(skb, AX25_MIN_HEADER_LEN+1); + ipax_fragment(skb, axdev, &addr); + return 0; + } + } + + /* rebuild the header if digipeaters are to be used */ + if (addr.dcount != 0) { + /* strip address field, keep the control byte */ + skb_pull(skb, AX25_MIN_HEADER_LEN-1); + skb_push(skb, ax25_sizeof_addr(&addr)); + ax25_build_addr(skb->data, &addr, AX25_COMMAND, AX25_SEQMASK); + } + + /* + * queue a completely assembled frame to the unproto + * queue of an interface + */ + ax25_send_unproto(skb, axdev); + dev->trans_start = jiffies; + lp->stats.tx_packets++; + /* netif_stop_queue(dev) */ + return 0; +} + +/* ---------------------------------------------------------------------*/ +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats *ipax_get_stats(struct net_device *dev) +{ + struct ipax_local_t *lp = (struct ipax_local_t *) dev->priv; + return &lp->stats; +} + +/* ---------------------------------------------------------------------*/ + +static int ipax_hard_header(struct sk_buff* skb, struct net_device* dev, unsigned short type, void* daddr, void* saddr, unsigned len) +{ + unsigned char pid; + unsigned char *buf; + int hlen; + + if (type == ETH_P_IP) + pid = AX25_P_IP; + else if (type == ETH_P_ARP) + pid = AX25_P_ARP; + else { + printk(KERN_ERR "ax25: ipax_hard_header: unknown packet type %d.\n", type); + pid = 0; + } + *skb_push(skb, 1) = pid; + *skb_push(skb, 1) = AX25_UI; + + if (saddr != NULL) + memcpy(skb_push(skb, AX25_ADDR_LEN), saddr, AX25_ADDR_LEN); + else + memcpy(skb_push(skb, dev->addr_len), dev->dev_addr, dev->addr_len); + + if (daddr != NULL) { + memcpy(skb_push(skb, AX25_ADDR_LEN), daddr, AX25_ADDR_LEN); + hlen = (AX25_MIN_HEADER_LEN+1); + } else { + memcpy(skb_push(skb, AX25_ADDR_LEN), &null_ax25_address, AX25_ADDR_LEN); + hlen = -(AX25_MIN_HEADER_LEN+1); + } + + buf = skb->data; + buf[6] &= ~(AX25_CBIT|AX25_EBIT); + buf[6] |= AX25_SSSID_SPARE; + buf[13] &= ~AX25_CBIT; + buf[13] |= (AX25_EBIT|AX25_SSSID_SPARE); + + return hlen; +} diff --git a/net/ax25/ax25_lapb.c b/net/ax25/ax25_lapb.c new file mode 100644 index 000000000..849eeb44d --- /dev/null +++ b/net/ax25/ax25_lapb.c @@ -0,0 +1,789 @@ +/* + * ax25_lapb.c: NEW-AX.25 state machine + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF) + * + * Comment: Most of this code is based on the SDL diagrams published in the + * ARRL Computer Networking Conference papers. The diagrams have mi + * in them, but are mostly correct. Before you modify the code coul + * read the SDL diagrams as the code is not obvious and probably ve + * easy to break; + * Rewritten from scratch by Matthias in 1998. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/socket.h> +#include <linux/time.h> +#include <net/ax25.h> +#include <net/ax25dev.h> +#include <net/tcp.h> + +#include "ax25_ddi.h" +#include "ax25_vj.h" +#include "ax25_route.h" +#include "ax25_in.h" +#include "ax25_lapb.h" +#include "ax25_core.h" +#include "ax25_subr.h" +#include "ax25_timer.h" + +/* + * Calculate the Round Trip Time + */ +static void ax25_calculate_rtt(ax25_cb *ax25, unsigned long rtt_raw) +{ + switch (ax25->backoff) { + case 0: + ax25->rtt = rtt_raw; + break; + + case 1: + case 2: + ax25->rtt = (9 * ax25->rtt + rtt_raw) / 10; + break; + } +} + +/* + * This routine purges the input queue of those frames that have been + * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the + * SDL diagram. + */ +static void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) +{ + struct sk_buff *skb; + /* + * Remove all the ack-ed frames from ack_queue. + */ + while (ax25->va != nr && (skb = skb_dequeue(&ax25->ack_queue)) != NULL) { + ax25->va = (ax25->va + 1) & ax25->seqmask; + kfree_skb(skb); + + if (ax25->vs_rtt == ax25->va) { + ax25_calculate_rtt(ax25, jiffies - ax25->rtt_timestamp + 1); + ax25->vs_rtt = -1; + } + } + if (ax25->condition & AX25_COND_SETUP) + ax25_clr_cond(ax25, AX25_COND_SETUP); +} + +/* + * This routine decides whether to restart T1 or not on an incoming + * frame. If the frame acks all outstanding frames, T1 is + * stopped, T3 is started. Else if it acks at least one + * outstanding frame T1 is restarted. + */ +static void ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) +{ + write_lock(&ax25->timer_lock); + if (ax25->va != nr) { + /* at least one frame acked */ + ax25_frames_acked(ax25, nr); + if ((ax25->vs != nr) && !(DAMA_STATE(ax25) & DAMA_SLAVE)) + ax25->wrt_timer = ax25->t1; + } + if (ax25->vs == ax25->va) ax25->wrt_timer = ax25->t3; + write_unlock(&ax25->timer_lock); +} + +/* + * Due to the changed retransmission handling frames are not + * requeued from the ack_queue into the write queue. All + * retransmission stuff is done in ax25_dev_timer() now. This + * function is for clarity only, it will either be removed + * completely or turn into an inline function. + */ +static void ax25_requeue_frames(ax25_cb *ax25) +{ + /* + * reset the send sequence counter. + */ + ax25->vs = ax25->va; +} + +/* + * Validate that the value of nr is between va and vs. Return true or + * false for testing. + */ +static int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) +{ + unsigned short ds; + unsigned short dr; + + ds = ax25->vs_max - ax25->va; + ds &= ax25->seqmask; + + dr = nr - ax25->va; + dr &= ax25->seqmask; + + return (dr <= ds); +} + +/* + * link reset handling. This is a rather complex routine, as it + * has to cope with a tricky situation in the protocol flow. + */ +static int ax25_reset_link(ax25_cb* ax25, struct sk_buff* skb) +{ + ax25_cb* peer = ax25->peer; + + write_lock(&ax25->timer_lock); + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->idletimer = ax25->idle; + ax25->n2count = 0; + write_unlock(&ax25->timer_lock); + + /* + * peer just didn't get our UA, handle gracefully. + */ + if (ax25->condition & AX25_COND_SETUP) { + ax25_requeue_frames(ax25); + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + return 0; + } + + /* + * Ok, this is a *real* reset. If we have an attached socket, + * disconnect it and produce a new, accept-ready one. + */ + if (ax25->sk != NULL) { + struct sock* make; + struct sock* sk; + ax25_cb* tmp = ax25_find_listener(&ax25->addr.src, 0, ax25->device); + + printk(KERN_DEBUG "ax25_lapb.c: resetting socket\n"); + + if (!tmp || !(sk = tmp->sk) + || sk->ack_backlog == sk->max_ack_backlog + || (make = ax25_make_new(sk, ax25->device)) == NULL) + { + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25->state = AX25_STATE_0; + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + return 0; + } + + /* set the destination address */ + make->protinfo.ax25->addr = ax25->addr; + + /* + * ax25_make_new() has produced a new ax25_cb, attached to + * a struct sock, we just throw away the old one the regular + * way and insert the new one into the appropriate device queue + * + * It should be enough to disconnect() the old control block, + * as this will wake up the application which then in turn + * does a close() on the respective socket descriptor. + */ + ax25_remove_cb(ax25); + ax25_disconnect(ax25, ECONNRESET); + ax25_close_socket(ax25->sk, ECONNRESET); + + /* + * done with the old one, now for the new one + */ + write_lock(&ax25->timer_lock); + ax25 = make->protinfo.ax25; + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->vs_rtt = -1; + write_unlock(&ax25->timer_lock); + + make->pair = sk; + skb->sk = make; + skb_queue_head(&sk->receive_queue, skb); + sk->ack_backlog++; + ax25_dev_insert_cb(ax25); + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + sk->data_ready(sk, skb->len); + return 1; + } + + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + + /* + * check if we have a peer connection to notify + */ + if (peer) { + + printk(KERN_DEBUG "ax25_lapb.c: resetting digipeated connection, state:%d peer state:%d\n", + ax25->state, peer->state); + + ax25_clear_queues(ax25); + + write_lock(&ax25->timer_lock); + + ax25->killtimer = 30 * AX25_SLOWHZ; + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->state = AX25_STATE_0; + ax25->condition = AX25_COND_SETUP; + + write_unlock(&ax25->timer_lock); + + ax25_clear_queues(peer); + + write_lock(&peer->timer_lock); + + peer->wrt_timer = 0; + peer->ack_timer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->vl = 0; + peer->condition = AX25_COND_SETUP; + peer->state = AX25_STATE_1; + + write_unlock(&peer->timer_lock); + + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + return 0; + } + + printk(KERN_DEBUG "ax25_lapb.c: resetting protocol uplink\n"); + + if (ax25->slcomp != NULL) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25->wrt_timer = ax25->t3; + ax25->condition = AX25_COND_SETUP; + ax25_tx_response(ax25, AX25_UA, AX25_POLLON); + + return 0; +} + +/* + * State machine for state 0, Disconnected State. + * we need this now for the hop-2-hop acknowledgement + */ +static int ax25_state0_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (peer) { + if (peer->condition & AX25_COND_SETUP) { + if (peer->seqmask == AX25_SEQMASK) + ax25_tx_command(peer, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(peer, AX25_SABME, AX25_POLLON); + ax25->killtimer = 30 * AX25_SLOWHZ; + } else { + struct net_device *dev; + if ((dev = ax25_rt_set_addr(&peer->addr, &pkt->addr, ax25->device, peer->device)) == NULL) + ax25_tx_command(ax25, AX25_DM, AX25_POLLON); + else { + ax25_fillin_cb(peer, dev); + ax25_reset_link(ax25, skb); + } + } + } else if (!ax25->sk) + ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + default: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + } + return 0; /* we never queue */ +} + + +/* + * State machine for state 1, Awaiting Connection State. + * The handling of the timer(s) is in file ax25_timer.c. + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state1_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + if (peer || ax25->sk == NULL) { + if (ax25->seqmask != AX25_SEQMASK) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_SABME: + if (ax25->seqmask == AX25_ESEQMASK && (peer || ax25->sk == NULL)) { + ax25->state = AX25_STATE_3; + ax25->vs_rtt = -1; + ax25_tx_response(ax25, AX25_UA, pkt->pf); + if (peer) { + peer->state = AX25_STATE_3; + peer->vs_rtt = -1; + ax25_tx_response(peer, AX25_UA, pkt->pf); + } + } + break; + + case AX25_DISC: + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_UA: + if (pkt->pf) { + write_lock(&ax25->timer_lock); + ax25->state = AX25_STATE_3; + ax25->wrt_timer = ax25->t3; + ax25->idletimer = ax25->idle; + ax25->killtimer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + ax25->vs_rtt = -1; + ax25->n2count = 0; + ax25->tx_cmd = 0; + ax25->tx_rsp = 0; + write_unlock(&ax25->timer_lock); + + if (peer != NULL) { + write_lock(&peer->timer_lock); + peer->wrt_timer = peer->t3; + peer->idletimer = 0; + peer->killtimer = 0; + peer->vs = 0; + peer->va = 0; + peer->vr = 0; + peer->vl = 0; + peer->vs_rtt = -1; + peer->n2count = 0; + peer->state = AX25_STATE_3; + ax25_clr_cond(peer, AX25_COND_STATE_CHANGE); + ax25_tx_response(peer, AX25_UA, pkt->pf); + write_unlock(&peer->timer_lock); + } else if (ax25->sk != NULL) { + ax25->sk->state = TCP_ESTABLISHED; + /* For WAIT_SABM connections we will produce an accept ready socket here */ + ax25->sk->state_change(ax25->sk); + } + + ax25_kick(ax25); + } + break; + + case AX25_DM: + if (peer != NULL) { + ax25_set_cond(peer, AX25_COND_STATE_CHANGE); + ax25_disconnect(peer, 0); + ax25_tx_response(peer, AX25_DM, pkt->pf); + } else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNREFUSED); + ax25_disconnect(ax25, ECONNREFUSED); + break; + + default: + break; + } + + return 0; +} + +/* + * State machine for state 2, Awaiting Release State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state2_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_DM, pkt->pf); + break; + + case AX25_DM: + case AX25_UA: + if (pkt->pf) { + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_disconnect(ax25, 0); + ax25->killtimer=0; + ax25->tx_cmd=0; + } + break; + + case AX25_I: + case AX25_REJ: + case AX25_RNR: + case AX25_RR: + if (pkt->pf && pkt->cmdrsp == AX25_COMMAND) + ax25_tx_response(ax25, AX25_DM, AX25_POLLON); + break; + + default: + break; + } + return queued; +} + +/* + * State machine for state 3, Connected State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state3_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->pf) { + if (pkt->cmdrsp == AX25_COMMAND) { + ax25_enquiry_response(ax25); + } + if (ax25->vs == ax25->va) { + ax25->n2count=0; + } else { + ax25_requeue_frames(ax25); + if (DAMA_STATE(ax25) & DAMA_SLAVE) { + /* + * in DAMA mode we use n2count in state 3 + * to track the number of DAMA polls + * ack'ing only part of our I frames. This + * is neccessary to prevent hangs with buggy + * masters. + */ + if (++ax25->n2count == ax25->n2) { + ax25->vs_rtt = -1; + ax25->n2count = 1; + ax25_start_t1(ax25); + ax25->state = AX25_STATE_4; + ax25_transmit_enquiry(ax25); + } + } + } + } + + ax25_kick(ax25); + break; + + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_check_iframes_acked(ax25, pkt->nr); + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + ax25_requeue_frames(ax25); + ax25_kick(ax25); + AX25_PTR(ax25->device)->rx_rejects++; + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_check_iframes_acked(ax25, pkt->nr); + + if (pkt->ns == ax25->vr) { + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + + if (pkt->pf) { + ax25_enquiry_response(ax25); +#ifdef notdef + } else if (((pkt->ns+2) & ax25->seqmask) == ax25->vl) { + /* + * optimization (fast ack): + * If our peer's TX window is sent out + * completely we ack immediately without + * waiting for T2. + */ + ax25_timeout_response(ax25); + } else { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } +#else + } else ax25_timeout_response(ax25); +#endif + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + ax25_kick(ax25); + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +/* + * State machine for state 4, Timer Recovery State. + * The handling of the timer(s) is in file ax25_timer.c + * Handling of state 0 and connection release is in ax25.c. + */ +static int ax25_state4_machine(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + int queued = 0; + ax25_cb *peer = ax25->peer; + + switch (pkt->frametype) { + case AX25_SABM: + case AX25_SABME: + if (pkt->frametype == AX25_SABM) { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + } else { + ax25->seqmask = AX25_ESEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_EWINDOW); + } + queued = ax25_reset_link(ax25, skb); + break; + + case AX25_DISC: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25_disconnect(ax25, 0); + ax25_tx_response(ax25, AX25_UA, pkt->pf); + break; + + case AX25_DM: + if (peer) + ax25_set_cond(peer, AX25_COND_RELEASE); + else if (ax25->sk) + ax25_close_socket(ax25->sk, ECONNRESET); + ax25_disconnect(ax25, ECONNRESET); + break; + + case AX25_RR: + case AX25_RNR: + case AX25_REJ: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + + ax25_frames_acked(ax25, pkt->nr); + + if (pkt->frametype == AX25_RR) + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + else if (pkt->frametype == AX25_REJ) { + ax25_clr_cond(ax25, AX25_COND_PEER_RX_BUSY); + AX25_PTR(ax25->device)->rx_rejects++; + } else + ax25_set_cond(ax25, AX25_COND_PEER_RX_BUSY); + + if (pkt->pf) { + if (pkt->cmdrsp == AX25_RESPONSE) { + write_lock(&ax25->timer_lock); + if (ax25->vs == ax25->va) { + ax25->n2count = 0; + ax25->wrt_timer = 0; + ax25->state = AX25_STATE_3; + ax25->tx_cmd = 0; + ax25->wrt_timer = ax25->t3; + } + write_unlock(&ax25->timer_lock); + ax25_requeue_frames(ax25); + } else { + if (DAMA_STATE(ax25) & DAMA_SLAVE) + ax25_requeue_frames(ax25); + ax25_enquiry_response(ax25); + } + } + ax25_kick(ax25); + break; + + case AX25_I: + if (!ax25_validate_nr(ax25, pkt->nr)) { + ax25_nr_error_recovery(ax25); + break; + } + ax25_frames_acked(ax25, pkt->nr); + write_lock(&ax25->timer_lock); + + if (pkt->ns == ax25->vr) { + /* frame is in sequence */ + if ((queued = ax25_rx_iframe(ax25, skb)) != 0) { + ax25_reseq_update(ax25, skb, pkt->ns); + ax25->vr = (ax25->vr+1) & ax25->seqmask; + ax25_reseq_out(ax25); + ax25_clr_cond(ax25, AX25_COND_REJECT); + } + if (pkt->pf) { + ax25_enquiry_response(ax25); + } else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + /* frame is not in sequence */ + queued = ax25_reseq_in(ax25, skb, pkt->ns, pkt->pf); + if (ax25->condition & AX25_COND_REJECT) { + if (pkt->pf) + ax25_enquiry_response(ax25); + else if (ax25->ack_timer == 0) { + ax25->ack_timer = ax25->t2; + ax25_set_cond(ax25, AX25_COND_ACK_PENDING); + } + } else { + ax25->ack_timer = 0; + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + ax25_set_cond(ax25, AX25_COND_REJECT); + ax25_tx_response(ax25, AX25_REJ, pkt->pf); + } + } + + write_unlock(&ax25->timer_lock); + break; + + case AX25_FRMR: + case AX25_ILLEGAL: + ax25_nr_error_recovery(ax25); + break; + + default: + break; + } + + return queued; +} + +static int ax25_state_nop(ax25_cb *ax25, struct sk_buff *skb, ax25_pktinfo *pkt) +{ + printk(KERN_DEBUG "ax25_state_nop()\n"); + return 0; +} + +ax25_statefunc_t ax25_lapb_table[] = +{ + ax25_state0_machine, + ax25_state1_machine, + ax25_state2_machine, + ax25_state3_machine, + ax25_state4_machine, + ax25_state_nop +}; diff --git a/net/ax25/ax25_lapb.h b/net/ax25/ax25_lapb.h new file mode 100644 index 000000000..989eab629 --- /dev/null +++ b/net/ax25/ax25_lapb.h @@ -0,0 +1,11 @@ +#ifndef _AX25_LAPB_H +#define _AX25_LAPB_H + + +/* + * state machine tables for lapb and dama slave + */ +extern ax25_statefunc_t ax25_lapb_table[]; +extern ax25_statefunc_t ax25_dama_table[]; + +#endif diff --git a/net/ax25/ax25_netlink.c b/net/ax25/ax25_netlink.c new file mode 100644 index 000000000..73e25e234 --- /dev/null +++ b/net/ax25/ax25_netlink.c @@ -0,0 +1,110 @@ +/* + * ax25_netlink.c: NETLINK interface for NEW-AX.25 + * + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan ( + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al + * + * Comment: It is intended to develop a new AX.25 routing daemon using t + * method to communicate with the kernel part. Recent developme + * Linux' realtime abilities, however, suggest removing AX.25 c + * from kernel space. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. +*/ + +#include <linux/skbuff.h> +#include <linux/netlink.h> +#include <linux/ax25.h> +#include <net/sock.h> +#include <net/ax25.h> + +#include "ax25_netlink.h" +#include "ax25_route.h" + +static struct sock *axrtnl; + +static void ax25_netlink_rcv(struct sock *sk, int len) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + struct net_device *dev; + + while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) { + if (skb->len < sizeof(struct ax25_nlmsg)) { + kfree_skb(skb); + break; + } + nlmsg = (struct ax25_nlmsg *)skb->data; + + switch (nlmsg->msg_type) { + case AX25_MSG_SETRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL + && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS) + ax25_add_route(&nlmsg->msg.pathmsg.path, dev); + break; + + case AX25_MSG_DELRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL) + ax25_del_route(&nlmsg->msg.pathmsg.path.addr); + break; + + case AX25_MSG_OPTRT: + if ((dev = dev_get(nlmsg->msg.pathmsg.port_name)) != NULL + && nlmsg->msg.pathmsg.path.dcount <= AX25_MAX_DIGIS) + { + ax25_add_route(&nlmsg->msg.pathmsg.path, dev); + ax25_ipopt_route(&nlmsg->msg.pathmsg.path.addr, nlmsg->msg.pathmsg.mode); + } + } + kfree_skb(skb); + } +} + +void ax25_nlpost_route(ax25_pktinfo *pkt, struct net_device *dev) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC); + if (!skb) + return; + nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg)); + + nlmsg->msg_type = AX25_MSG_RTINFO; + strncpy(nlmsg->msg.rtmsg.port_name, dev->name, sizeof(nlmsg->msg.rtmsg.port_name)); + nlmsg->msg.rtmsg.addr = pkt->addr; + netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL); +} + +void ax25_nlpost_armsg(unsigned int ip_addr, ax25_address *ax_addr, struct net_device *dev) +{ + struct ax25_nlmsg *nlmsg; + struct sk_buff *skb; + + skb = alloc_skb(sizeof(struct ax25_nlmsg), GFP_ATOMIC); + if (!skb) + return; + nlmsg = (struct ax25_nlmsg *)skb_put(skb, sizeof(struct ax25_nlmsg)); + + nlmsg->msg_type = AX25_MSG_ARINFO; + strncpy(nlmsg->msg.armsg.port_name, dev->name, sizeof(nlmsg->msg.armsg.port_name)); + nlmsg->msg.armsg.ip_addr = ip_addr; + nlmsg->msg.armsg.ax_addr = *ax_addr; + + netlink_broadcast(axrtnl, skb, 0, ~0, GFP_KERNEL); +} + +void ax25_netlink_init(void) +{ + axrtnl = netlink_kernel_create(NETLINK_AX25, ax25_netlink_rcv); +} + +void ax25_netlink_cleanup(void) +{ + sock_release(axrtnl->socket); +} diff --git a/net/ax25/ax25_netlink.h b/net/ax25/ax25_netlink.h new file mode 100644 index 000000000..c100ed212 --- /dev/null +++ b/net/ax25/ax25_netlink.h @@ -0,0 +1,10 @@ +#ifndef _AX25_NETLINK_H +#define _AX25_NETLINK_H + +extern void ax25_netlink_init(void); +extern void ax25_netlink_cleanup(void); + +extern void ax25_nlpost_route(ax25_pktinfo*, struct net_device*); +extern void ax25_nlpost_armsg(unsigned int, ax25_address*, struct net_device*); + +#endif diff --git a/net/ax25/ax25_out.c b/net/ax25/ax25_out.c index 231c170e7..a222ad1e8 100644 --- a/net/ax25/ax25_out.c +++ b/net/ax25/ax25_out.c @@ -1,129 +1,74 @@ /* - * AX.25 release 037 + * ax25_out.c: Subroutines for outgoing packets * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: Most of this code is based on the SDL diagrams published in the + * ARRL Computer Networking Conference papers. The diagrams have mi + * in them, but are mostly correct. Before you modify the code coul + * read the SDL diagrams as the code is not obvious and probably ve + * easy to break; * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; + * Changelog: * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Only poll when window is full. - * AX.25 030 Jonathan(G4KLX) Added fragmentation to ax25_output. - * Added support for extended AX.25. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * Joerg(DL1BKE) Modified fragmenter to fragment vanilla - * AX.25 I-Frames. Added PACLEN parameter. - * Joerg(DL1BKE) Fixed a problem with buffer allocation - * for fragments. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Joerg(DL1BKE) Fixed DAMA Slave mode: will work - * on non-DAMA interfaces like AX25L2V2 - * again (this behaviour is _required_). - * Joerg(DL1BKE) ax25_check_iframes_acked() returns a - * value now (for DAMA n2count handling) + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ #include <linux/config.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> #include <linux/skbuff.h> -#include <linux/netfilter.h> +#include <net/ax25.h> +#include <net/ax25dev.h> #include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax25_address *dest, ax25_digi *digi, struct net_device *dev) +#include "ax25_core.h" +#include "ax25_route.h" +#include "ax25_vj.h" +#include "ax25_ddi.h" +#include "ax25_subr.h" + +/* ---------------------------------------------------------------------*/ +/* + * send an iframe on a connection, maybe establish the connection first. + */ +ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_addr_t *addr, struct net_device *dev) { - ax25_dev *ax25_dev; ax25_cb *ax25; - /* - * Take the default packet length for the device if zero is - * specified. - */ - if (paclen == 0) { - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return NULL; - - paclen = ax25_dev->values[AX25_VALUES_PACLEN]; - } + if (paclen == 0) + paclen = ax25_dev_get_value(dev, AX25_VALUES_PACLEN); /* * Look for an existing connection. */ - if ((ax25 = ax25_find_cb(src, dest, digi, dev)) != NULL) { + if ((ax25 = ax25_find_cb(addr, dev)) != NULL) { + /* reuse a just disconnected control block */ + if (ax25->state == AX25_STATE_0 || ax25->state == AX25_STATE_2) { + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } + ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C'; + ax25_establish_data_link(ax25); + } ax25_output(ax25, paclen, skb); return ax25; /* It already existed */ } - if ((ax25_dev = ax25_dev_ax25dev(dev)) == NULL) - return NULL; - if ((ax25 = ax25_create_cb()) == NULL) return NULL; - ax25_fillin_cb(ax25, ax25_dev); + ax25_fillin_cb(ax25, dev); - ax25->source_addr = *src; - ax25->dest_addr = *dest; - - if (digi != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - ax25_free_cb(ax25); - return NULL; - } - memcpy(ax25->digipeat, digi, sizeof(ax25_digi)); - } - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_establish_data_link(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25_dev->dama.slave) - ax25_ds_establish_data_link(ax25); - else - ax25_std_establish_data_link(ax25); - break; -#endif - } - - ax25_insert_socket(ax25); - - ax25->state = AX25_STATE_1; - - ax25_start_heartbeat(ax25); + ax25->addr = *addr; + ax25->slcomp_enable = ax25_rt_mode_get(&addr->dest) == 'C'; + ax25_establish_data_link(ax25); + ax25_insert_cb(ax25); ax25_output(ax25, paclen, skb); - return ax25; /* We had to create it */ } @@ -138,7 +83,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) struct sk_buff *skbn; unsigned char *p; int frontlen, len, fragno, ka9qfrag, first = 1; - long flags; if ((skb->len - 1) > paclen) { if (*skb->data == AX25_P_TEXT) { @@ -155,11 +99,7 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) frontlen = skb_headroom(skb); /* Address space + CTRL */ while (skb->len > 0) { - save_flags(flags); - cli(); - if ((skbn = alloc_skb(paclen + 2 + frontlen, GFP_ATOMIC)) == NULL) { - restore_flags(flags); printk(KERN_CRIT "AX.25: ax25_output - out of memory\n"); return; } @@ -167,13 +107,11 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) if (skb->sk != NULL) skb_set_owner_w(skbn, skb->sk); - restore_flags(flags); - len = (paclen > skb->len) ? skb->len : paclen; if (ka9qfrag == 1) { skb_reserve(skbn, frontlen + 2); - skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); + memcpy(skb_put(skbn, len), skb->data, len); p = skb_push(skbn, 2); @@ -186,7 +124,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) } } else { skb_reserve(skbn, frontlen + 1); - skbn->nh.raw = skbn->data + (skb->nh.raw - skb->data); memcpy(skb_put(skbn, len), skb->data, len); p = skb_push(skbn, 1); *p = AX25_P_TEXT; @@ -201,209 +138,6 @@ void ax25_output(ax25_cb *ax25, int paclen, struct sk_buff *skb) skb_queue_tail(&ax25->write_queue, skb); /* Throw it on the queue */ } - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_kick(ax25); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - /* - * A DAMA slave is _required_ to work as normal AX.25L2V2 - * if no DAMA master is available. - */ - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) ax25_kick(ax25); - break; -#endif - } -} - -/* - * This procedure is passed a buffer descriptor for an iframe. It builds - * the rest of the control part of the frame and then writes it out. - */ -static void ax25_send_iframe(ax25_cb *ax25, struct sk_buff *skb, int poll_bit) -{ - unsigned char *frame; - - if (skb == NULL) - return; - - skb->nh.raw = skb->data; - - if (ax25->modulus == AX25_MODULUS) { - frame = skb_push(skb, 1); - - *frame = AX25_I; - *frame |= (poll_bit) ? AX25_PF : 0; - *frame |= (ax25->vr << 5); - *frame |= (ax25->vs << 1); - } else { - frame = skb_push(skb, 2); - - frame[0] = AX25_I; - frame[0] |= (ax25->vs << 1); - frame[1] = (poll_bit) ? AX25_EPF : 0; - frame[1] |= (ax25->vr << 1); - } - - ax25_start_idletimer(ax25); - - ax25_transmit_buffer(ax25, skb, AX25_COMMAND); -} - -void ax25_kick(ax25_cb *ax25) -{ - struct sk_buff *skb, *skbn; - int last = 1; - unsigned short start, end, next; - - if (ax25->state != AX25_STATE_3 && ax25->state != AX25_STATE_4) - return; - - if (ax25->condition & AX25_COND_PEER_RX_BUSY) - return; - - if (skb_peek(&ax25->write_queue) == NULL) - return; - - start = (skb_peek(&ax25->ack_queue) == NULL) ? ax25->va : ax25->vs; - end = (ax25->va + ax25->window) % ax25->modulus; - - if (start == end) - return; - - ax25->vs = start; - - /* - * Transmit data until either we're out of data to send or - * the window is full. Send a poll on the final I frame if - * the window is filled. - */ - - /* - * Dequeue the frame and copy it. - */ - skb = skb_dequeue(&ax25->write_queue); - - do { - if ((skbn = skb_clone(skb, GFP_ATOMIC)) == NULL) { - skb_queue_head(&ax25->write_queue, skb); - break; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - next = (ax25->vs + 1) % ax25->modulus; - last = (next == end); - - /* - * Transmit the frame copy. - * bke 960114: do not set the Poll bit on the last frame - * in DAMA mode. - */ - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_send_iframe(ax25, skbn, (last) ? AX25_POLLON : AX25_POLLOFF); - break; - -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - ax25_send_iframe(ax25, skbn, AX25_POLLOFF); - break; -#endif - } - - ax25->vs = next; - - /* - * Requeue the original data frame. - */ - skb_queue_tail(&ax25->ack_queue, skb); - - } while (!last && (skb = skb_dequeue(&ax25->write_queue)) != NULL); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - if (!ax25_t1timer_running(ax25)) { - ax25_stop_t3timer(ax25); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - } -} - -void ax25_transmit_buffer(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - struct sk_buff *skbn; - unsigned char *ptr; - int headroom; - - if (ax25->ax25_dev == NULL) { - ax25_disconnect(ax25, ENETUNREACH); - return; - } - - headroom = ax25_addr_size(ax25->digipeat); - - if (skb_headroom(skb) < headroom) { - if ((skbn = skb_realloc_headroom(skb, headroom)) == NULL) { - printk(KERN_CRIT "AX.25: ax25_transmit_buffer - out of memory\n"); - kfree_skb(skb); - return; - } - - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); - - kfree_skb(skb); - skb = skbn; - } - - ptr = skb_push(skb, headroom); - - ax25_addr_build(ptr, &ax25->source_addr, &ax25->dest_addr, ax25->digipeat, type, ax25->modulus); - - skb->dev = ax25->ax25_dev->dev; - - ax25_queue_xmit(skb); -} - -/* - * A small shim to dev_queue_xmit to add the KISS control byte, and do - * any packet forwarding in operation. - */ -void ax25_queue_xmit(struct sk_buff *skb) -{ - unsigned char *ptr; - - skb->protocol = htons(ETH_P_AX25); - skb->dev = ax25_fwd_dev(skb->dev); - - ptr = skb_push(skb, 1); - *ptr = 0x00; /* KISS */ - - dev_queue_xmit(skb); -} - -int ax25_check_iframes_acked(ax25_cb *ax25, unsigned short nr) -{ - if (ax25->vs == nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - return 1; - } else { - if (ax25->va != nr) { - ax25_frames_acked(ax25, nr); - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - return 1; - } - } - return 0; + ax25_kick(ax25); } diff --git a/net/ax25/ax25_route.c b/net/ax25/ax25_route.c index 254ff36fb..cf468c65f 100644 --- a/net/ax25/ax25_route.c +++ b/net/ax25/ax25_route.c @@ -1,278 +1,246 @@ /* - * AX.25 release 037 + * ax25_route.c: Routing table management for NEW-AX.25 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4K + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: * - * Other kernels modules in this kit are generally BSD derived. See the copyright headers. + * Changelog: * - * - * History - * AX.25 020 Jonathan(G4KLX) First go. - * AX.25 022 Jonathan(G4KLX) Added the actual meat to this - we now have a nice heard list. - * AX.25 025 Alan(GW4PTS) First cut at autobinding by route scan. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the - * sock structure. Device removal now - * removes the heard structure. - * AX.25 029 Steven(GW7RRM) Added /proc information for uid/callsign mapping. - * Jonathan(G4KLX) Handling of IP mode in the routing list and /proc entry. - * AX.25 030 Jonathan(G4KLX) Added digi-peaters to routing table, and - * ioctls to manipulate them. Added port - * configuration. - * AX.25 031 Jonathan(G4KLX) Added concept of default route. - * Joerg(DL1BKE) ax25_rt_build_path() find digipeater list and device by - * destination call. Needed for IP routing via digipeater - * Jonathan(G4KLX) Added routing for IP datagram packets. - * Joerg(DL1BKE) Changed routing for IP datagram and VC to use a default - * route if available. Does not overwrite default routes - * on route-table overflow anymore. - * Joerg(DL1BKE) Fixed AX.25 routing of IP datagram and VC, new ioctl() - * "SIOCAX25OPTRT" to set IP mode and a 'permanent' flag - * on routes. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Joerg(DL1BKE) Moved BPQ Ethernet driver to separate device. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * Jonathan(G4KLX) Support for packet forwarding. - * Arnaldo C. Melo s/suser/capable/ + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ -#include <linux/errno.h> +#include <linux/config.h> #include <linux/types.h> #include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/init.h> +#include <net/sock.h> +#include <net/ax25_uid.h> -static ax25_route *ax25_route_list; +#include "af_ax25.h" +#include "ax25_core.h" +#include "ax25_route.h" -static ax25_route *ax25_find_route(ax25_address *, struct net_device *); +static struct ax25_route *ax25_route = NULL; +rwlock_t ax25_rt_lock = RW_LOCK_UNLOCKED; /* - * small macro to drop non-digipeated digipeaters and reverse path + * delete all routes on a given device */ -static inline void ax25_route_invert(ax25_digi *in, ax25_digi *out) +void ax25_rt_device_down(struct net_device *dev) { - int k; + struct ax25_route *this, *last; - for (k = 0; k < in->ndigi; k++) - if (!in->repeated[k]) - break; + write_lock(&ax25_rt_lock); + + this = ax25_route; + last = NULL; - in->ndigi = k; + while (this) { + if (this->dev != dev) { + last = this; + this = this->next; + continue; + } + if (!last) { + ax25_route = this->next; + kfree(this); + this = ax25_route; + continue; + } + last->next = this->next; + kfree(this); + this = last->next; + } - ax25_digi_invert(in, out); + write_unlock(&ax25_rt_lock); } -void ax25_rt_device_down(struct net_device *dev) +int ax25_add_route(ax25_path_t *path, struct net_device *dev) { - ax25_route *s, *t, *ax25_rt = ax25_route_list; - - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; + struct ax25_route *this; + + write_lock(&ax25_rt_lock); + + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, &path->addr) == 0) { + this->path = *path; + this->dev = dev; + return 0; + } + } + + if ((this = (struct ax25_route *)kmalloc(sizeof(struct ax25_route), GFP_KERNEL)) == NULL) + return -ENOMEM; + this->path = *path; + this->dev = dev; + this->ip_mode = ' '; + this->next = ax25_route; + ax25_route = this; + + write_unlock(&ax25_rt_lock); + + return 0; +} + +int ax25_del_route(ax25_address *addr) +{ + struct ax25_route *this, *last; + + write_lock(&ax25_rt_lock); + + this = ax25_route; + last = NULL; + while (this != NULL) { + if (ax25cmp(&this->path.addr, addr) != 0) { + last = this; + this = this->next; + continue; + } + if (!last) { + ax25_route = this->next; + kfree(this); + return 0; + } + last->next = this->next; + kfree(this); + return 0; + } + + write_unlock(&ax25_rt_lock); + + return -EINVAL; +} + +int ax25_ipopt_route(ax25_address *addr, unsigned char opt) +{ + struct ax25_route *this; + int err = -EINVAL; + + write_lock(&ax25_rt_lock); - if (s->dev == dev) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } + for (this = ax25_route; this; this = this->next) { + if (ax25cmp(&this->path.addr, addr) == 0) { + switch(opt) { + case ' ': + case 'D': + case 'V': + case 'C': + this->ip_mode = opt; + err = 0; + break; } } } + + write_unlock(&ax25_rt_lock); + + return err; } int ax25_rt_ioctl(unsigned int cmd, void *arg) { - unsigned long flags; - ax25_route *s, *t, *ax25_rt; struct ax25_routes_struct route; struct ax25_route_opt_struct rt_option; - ax25_dev *ax25_dev; + struct net_device *dev; + ax25_path_t ax25_path; + int err = -EINVAL; int i; switch (cmd) { - case SIOCADDRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - if (route.digi_count > AX25_MAX_DIGIS) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &route.dest_addr) == 0 && ax25_rt->dev == ax25_dev->dev) { - if (ax25_rt->digipeat != NULL) { - kfree(ax25_rt->digipeat); - ax25_rt->digipeat = NULL; - } - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - return 0; - } - } - if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) - return -ENOMEM; - ax25_rt->callsign = route.dest_addr; - ax25_rt->dev = ax25_dev->dev; - ax25_rt->digipeat = NULL; - ax25_rt->ip_mode = ' '; - if (route.digi_count != 0) { - if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { - kfree(ax25_rt); - return -ENOMEM; - } - ax25_rt->digipeat->lastrepeat = -1; - ax25_rt->digipeat->ndigi = route.digi_count; - for (i = 0; i < route.digi_count; i++) { - ax25_rt->digipeat->repeated[i] = 0; - ax25_rt->digipeat->calls[i] = route.digi_addr[i]; - } - } - save_flags(flags); cli(); - ax25_rt->next = ax25_route_list; - ax25_route_list = ax25_rt; - restore_flags(flags); + case SIOCADDRT: + /* do some sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) + break; + if (route.digi_count > AX25_MAX_DIGIS) break; - case SIOCDELRT: - if (copy_from_user(&route, arg, sizeof(route))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&route.port_addr)) == NULL) - return -EINVAL; - ax25_rt = ax25_route_list; - while (ax25_rt != NULL) { - s = ax25_rt; - ax25_rt = ax25_rt->next; - if (s->dev == ax25_dev->dev && ax25cmp(&route.dest_addr, &s->callsign) == 0) { - if (ax25_route_list == s) { - ax25_route_list = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - } else { - for (t = ax25_route_list; t != NULL; t = t->next) { - if (t->next == s) { - t->next = s->next; - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); - break; - } - } - } - } - } + ax25_path.addr = route.dest_addr; + for (i = 0; i < route.digi_count; i++) + ax25_path.digipeater[i] = route.digi_addr[i]; + ax25_path.dcount = route.digi_count; + err = ax25_add_route(&ax25_path, dev); + break; + + case SIOCDELRT: + /* sanity checks */ + if (copy_from_user(&route, arg, sizeof(route))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&route.port_addr)) == NULL) break; - case SIOCAX25OPTRT: - if (copy_from_user(&rt_option, arg, sizeof(rt_option))) - return -EFAULT; - if ((ax25_dev = ax25_addr_ax25dev(&rt_option.port_addr)) == NULL) - return -EINVAL; - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25_rt->dev == ax25_dev->dev && ax25cmp(&rt_option.dest_addr, &ax25_rt->callsign) == 0) { - switch (rt_option.cmd) { - case AX25_SET_RT_IPMODE: - switch (rt_option.arg) { - case ' ': - case 'D': - case 'V': - ax25_rt->ip_mode = rt_option.arg; - break; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } - } - } + err = ax25_del_route(&route.dest_addr); + break; + + case SIOCAX25OPTRT: + /* sanity checks */ + if (copy_from_user(&rt_option, arg, sizeof(rt_option))) { + err = -EFAULT; + break; + } + if ((dev = ax25rtr_get_dev(&rt_option.port_addr)) == NULL) break; - default: - return -EINVAL; + switch (rt_option.cmd) { + case AX25_SET_RT_IPMODE: + err = ax25_ipopt_route(&rt_option.dest_addr, rt_option.arg); + break; + } } - return 0; + return err; } int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) { - ax25_route *ax25_rt; + struct ax25_route *ax25_rt; int len = 0; off_t pos = 0; off_t begin = 0; char *callsign; int i; - cli(); + read_lock(&ax25_rt_lock); len += sprintf(buffer, "callsign dev mode digipeaters\n"); - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0) + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) callsign = "default"; else - callsign = ax2asc(&ax25_rt->callsign); + callsign = ax2asc(&ax25_rt->path.addr); len += sprintf(buffer + len, "%-9s %-4s", - callsign, - ax25_rt->dev ? ax25_rt->dev->name : "???"); + callsign, + ax25_rt->dev ? ax25_rt->dev->name : "???"); switch (ax25_rt->ip_mode) { - case 'V': - len += sprintf(buffer + len, " vc"); - break; - case 'D': - len += sprintf(buffer + len, " dg"); - break; - default: - len += sprintf(buffer + len, " *"); - break; + case 'V': + len += sprintf(buffer + len, " vc"); + break; + case 'D': + len += sprintf(buffer + len, " dg"); + break; + case 'C': + len += sprintf(buffer + len, " vj"); + break; + default: + len += sprintf(buffer + len, " *"); + break; } - if (ax25_rt->digipeat != NULL) - for (i = 0; i < ax25_rt->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->digipeat->calls[i])); + for (i = 0; i < ax25_rt->path.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&ax25_rt->path.digipeater[i])); len += sprintf(buffer + len, "\n"); @@ -287,7 +255,7 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); + read_unlock(&ax25_rt_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -300,153 +268,149 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) /* * Find AX.25 route */ -static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) +struct ax25_route *ax25_find_route(ax25_address *addr) { - ax25_route *ax25_spe_rt = NULL; - ax25_route *ax25_def_rt = NULL; - ax25_route *ax25_rt; + struct ax25_route *ax25_spe_rt = NULL; + struct ax25_route *ax25_def_rt = NULL; + struct ax25_route *ax25_rt; - /* - * Bind to the physical interface we heard them on, or the default - * route if none is found; - */ - for (ax25_rt = ax25_route_list; ax25_rt != NULL; ax25_rt = ax25_rt->next) { - if (dev == NULL) { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev != NULL) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev != NULL) - ax25_def_rt = ax25_rt; - } else { - if (ax25cmp(&ax25_rt->callsign, addr) == 0 && ax25_rt->dev == dev) - ax25_spe_rt = ax25_rt; - if (ax25cmp(&ax25_rt->callsign, &null_ax25_address) == 0 && ax25_rt->dev == dev) - ax25_def_rt = ax25_rt; - } + read_lock(&ax25_rt_lock); + + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) { + if (ax25cmp(&ax25_rt->path.addr, addr) == 0) + ax25_spe_rt = ax25_rt; + if (ax25cmp(&ax25_rt->path.addr, &null_ax25_address) == 0) + ax25_def_rt = ax25_rt; } if (ax25_spe_rt != NULL) return ax25_spe_rt; + read_unlock(&ax25_rt_lock); + return ax25_def_rt; } /* - * Adjust path: If you specify a default route and want to connect - * a target on the digipeater path but w/o having a special route - * set before, the path has to be truncated from your target on. + * MW: This is the core of the digipeating stuff. For a given + * src/dest it finds the appropriate device and digipeaterpath + * to route to. */ -static inline void ax25_adjust_path(ax25_address *addr, ax25_digi *digipeat) +struct net_device *ax25_rt_set_addr(ax25_addr_t *out, ax25_addr_t *in, struct net_device *dev, struct net_device *out_dev) { - int k; + struct ax25_route *ax25rt; + ax25_address *next_dest; + ax25_address *dptr; + int more_digis; - for (k = 0; k < digipeat->ndigi; k++) { - if (ax25cmp(addr, &digipeat->calls[k]) == 0) - break; - } + /* + * find out where to go next. we route the packet either + * to the next digi behind us or to the destination. We + * NEVER route to the destination if there are digipeaters + * left. + */ + more_digis = in->dcount - (in->lastrepeat+2); + if (more_digis > 0) + next_dest = &in->digipeater[in->lastrepeat+2]; + else + next_dest = &in->dest; - digipeat->ndigi = k; -} - + /* + * check for a route. + */ + if ((ax25rt = ax25_find_route(next_dest)) != NULL) + out_dev = ax25rt->dev; -/* - * Find which interface to use. - */ -int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) -{ - ax25_route *ax25_rt; - ax25_address *call; + /* + * set up the digipeater path. + * for now we just copy the path of the incoming SABM + * up to the digipeater before us, if any. + */ + out->dest = in->dest; + out->src = in->src; - if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) - return -EHOSTUNREACH; + if (in->lastrepeat >= 0) + memcpy(out->digipeater, in->digipeater, sizeof(ax25_address) * (in->lastrepeat+1)); - if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) - return -EHOSTUNREACH; + /* + * then we fill in the callsign of the device the frame + * came in. + */ + out->lastrepeat = in->lastrepeat+1; + out->dcount = out->lastrepeat+1; + dptr = &out->digipeater[(int) out->lastrepeat]; + *dptr++ = *((ax25_address *)dev->dev_addr); - if ((call = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; + /* + * insert the route to next_dest, if any. + */ + if (ax25rt != NULL && ax25rt->path.dcount != 0) { + memcpy(dptr, ax25rt->path.digipeater, sizeof(ax25_address) * ax25rt->path.dcount); + out->dcount += ax25rt->path.dcount; + dptr += ax25rt->path.dcount; } - ax25->source_addr = *call; - - if (ax25_rt->digipeat != NULL) { - if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) - return -ENOMEM; - memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); - ax25_adjust_path(addr, ax25->digipeat); + /* + * append next_dest if we route to a waypoint + */ + while (more_digis-- > 0 && out->dcount <= AX25_MAX_DIGIS) { + *dptr++ = *next_dest++; + out->dcount++; } - - if (ax25->sk != NULL) - ax25->sk->zapped = 0; - - return 0; + return out_dev; } /* - * dl1bke 960117: build digipeater path - * dl1bke 960301: use the default route if it exists + * Find the device to use */ -ax25_route *ax25_rt_find_route(ax25_address *addr, struct net_device *dev) +int ax25_rt_fillin_dev(ax25_cb *ax25, ax25_address *addr) { - static ax25_route route; - ax25_route *ax25_rt; - - if ((ax25_rt = ax25_find_route(addr, dev)) == NULL) { - route.next = NULL; - route.callsign = *addr; - route.dev = dev; - route.digipeat = NULL; - route.ip_mode = ' '; - return &route; - } + struct ax25_route *ax25_rt; - return ax25_rt; -} + if ((ax25_rt = ax25_find_route(addr)) == NULL) + return -EHOSTUNREACH; -struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src, ax25_address *dest, ax25_digi *digi) -{ - struct sk_buff *skbn; - unsigned char *bp; - int len; +/* ax25_remove_cb(ax25); */ + ax25_fillin_cb(ax25, ax25_rt->dev); +/* ax25_insert_cb(ax25); */ - len = digi->ndigi * AX25_ADDR_LEN; + return 0; +} - if (skb_headroom(skb) < len) { - if ((skbn = skb_realloc_headroom(skb, len)) == NULL) { - printk(KERN_CRIT "AX.25: ax25_dg_build_path - out of memory\n"); - return NULL; - } +/* + * Return the IP mode of a given callsign/device pair. + */ +char ax25_rt_mode_get(ax25_address *callsign) +{ + struct ax25_route *ax25_rt; - if (skb->sk != NULL) - skb_set_owner_w(skbn, skb->sk); + read_lock(&ax25_rt_lock); - kfree_skb(skb); + for (ax25_rt = ax25_route; ax25_rt != NULL; ax25_rt = ax25_rt->next) + if (ax25cmp(&ax25_rt->path.addr, callsign) == 0) + return ax25_rt->ip_mode; - skb = skbn; - } + read_unlock(&ax25_rt_lock); - bp = skb_push(skb, len); + return ' '; +} - ax25_addr_build(bp, src, dest, digi, AX25_COMMAND, AX25_MODULUS); - return skb; -} /* - * Free all memory associated with routing structures. + * Free all memory associated with routing and device structures. */ -void __exit ax25_rt_free(void) +void ax25_rt_free(void) { - ax25_route *s, *ax25_rt = ax25_route_list; + struct ax25_route *s, *ax25_rt = ax25_route; + + write_lock(&ax25_rt_lock); while (ax25_rt != NULL) { s = ax25_rt; ax25_rt = ax25_rt->next; - - if (s->digipeat != NULL) - kfree(s->digipeat); - kfree(s); } + + write_unlock(&ax25_rt_lock); } diff --git a/net/ax25/ax25_route.h b/net/ax25/ax25_route.h new file mode 100644 index 000000000..edcb41a6e --- /dev/null +++ b/net/ax25/ax25_route.h @@ -0,0 +1,25 @@ +#ifndef _AX25_ROUTE_H +#define _AX25_ROUTE_H + +struct ax25_route { + struct ax25_route *next; + ax25_path_t path; + struct net_device *dev; + char ip_mode; +}; + +extern void ax25_rt_device_down(struct net_device*); +extern int ax25_rt_ioctl(unsigned int, void*); +extern int ax25_rt_get_info(char*, char**, off_t, int); +extern struct net_device* ax25_rt_set_addr(ax25_addr_t*, ax25_addr_t*, struct net_device*, struct net_device*); +extern int ax25_rt_autobind(ax25_cb*, ax25_address*); +extern int ax25_rt_fillin_dev(ax25_cb*, ax25_address*); +extern char ax25_rt_mode_get(ax25_address*); +extern struct ax25_route* ax25_find_route(ax25_address*); + +extern int ax25_add_route(ax25_path_t*, struct net_device*); +extern int ax25_del_route(ax25_address*); +extern int ax25_ipopt_route(ax25_address*, unsigned char); +void ax25_rt_free(void); + +#endif diff --git a/net/ax25/ax25_std_in.c b/net/ax25/ax25_std_in.c deleted file mode 100644 index d1cfc3ff9..000000000 --- a/net/ax25/ax25_std_in.c +++ /dev/null @@ -1,467 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from - * the sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * Jonathan(G4KLX) Added IP mode registration. - * AX.25 030 Jonathan(G4KLX) Added AX.25 fragment reception. - * Upgraded state machine for SABME. - * Added arbitrary protocol id support. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * HaJo(DD8NE) Added Idle Disc Timer T5 - * Joerg(DL1BKE) Renamed it to "IDLE" with a slightly - * different behaviour. Fixed defrag - * routine (I hope) - * AX.25 032 Darryl(G7LED) AX.25 segmentation fixed. - * AX.25 033 Jonathan(G4KLX) Remove auto-router. - * Modularisation changes. - * AX.25 035 Hans(PE1AYX) Fixed interface to IP layer. - * AX.25 036 Jonathan(G4KLX) Cloned from ax25_in.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <net/ip.h> /* For ip_rcv */ -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -/* - * State machine for state 1, Awaiting Connection State. - * The handling of the timer(s) is in file ax25_std_timer.c. - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_SABME: - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_UA: - if (pf) { - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - if (ax25->sk != NULL) { - ax25->sk->state = TCP_ESTABLISHED; - /* For WAIT_SABM connections we will produce an accept ready socket here */ - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - } - } - break; - - case AX25_DM: - if (pf) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ECONNREFUSED); - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } - } - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 2, Awaiting Release State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state2_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int pf, int type) -{ - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - ax25_send_control(ax25, AX25_DM, pf, AX25_RESPONSE); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - case AX25_UA: - if (pf) ax25_disconnect(ax25, 0); - break; - - case AX25_I: - case AX25_REJ: - case AX25_RNR: - case AX25_RR: - if (pf) ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - break; - - default: - break; - } - - return 0; -} - -/* - * State machine for state 3, Connected State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state3_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_check_iframes_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_calculate_rtt(ax25); - ax25_stop_t1timer(ax25); - ax25_start_t3timer(ax25); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - if (ax25->condition & AX25_COND_PEER_RX_BUSY) { - ax25_frames_acked(ax25, nr); - } else { - ax25_check_iframes_acked(ax25, nr); - } - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * State machine for state 4, Timer Recovery State. - * The handling of the timer(s) is in file ax25_std_timer.c - * Handling of state 0 and connection release is in ax25.c. - */ -static int ax25_std_state4_machine(ax25_cb *ax25, struct sk_buff *skb, int frametype, int ns, int nr, int pf, int type) -{ - int queued = 0; - - switch (frametype) { - case AX25_SABM: - case AX25_SABME: - if (frametype == AX25_SABM) { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - } else { - ax25->modulus = AX25_EMODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_EWINDOW]; - } - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t3timer(ax25); - ax25_start_idletimer(ax25); - ax25->condition = 0x00; - ax25->vs = 0; - ax25->va = 0; - ax25->vr = 0; - ax25->state = AX25_STATE_3; - ax25->n2count = 0; - ax25_requeue_frames(ax25); - break; - - case AX25_DISC: - ax25_send_control(ax25, AX25_UA, pf, AX25_RESPONSE); - ax25_disconnect(ax25, 0); - break; - - case AX25_DM: - ax25_disconnect(ax25, ECONNRESET); - break; - - case AX25_RR: - case AX25_RNR: - if (frametype == AX25_RR) - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - else - ax25->condition |= AX25_COND_PEER_RX_BUSY; - if (type == AX25_RESPONSE && pf) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_REJ: - ax25->condition &= ~AX25_COND_PEER_RX_BUSY; - if (pf && type == AX25_RESPONSE) { - ax25_stop_t1timer(ax25); - ax25->n2count = 0; - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - if (ax25->vs == ax25->va) { - ax25_start_t3timer(ax25); - ax25->state = AX25_STATE_3; - } else { - ax25_requeue_frames(ax25); - } - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - } - if (type == AX25_COMMAND && pf) - ax25_std_enquiry_response(ax25); - if (ax25_validate_nr(ax25, nr)) { - ax25_frames_acked(ax25, nr); - ax25_requeue_frames(ax25); - } else { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - } - break; - - case AX25_I: - if (!ax25_validate_nr(ax25, nr)) { - ax25_std_nr_error_recovery(ax25); - ax25->state = AX25_STATE_1; - break; - } - ax25_frames_acked(ax25, nr); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) { - if (pf) ax25_std_enquiry_response(ax25); - break; - } - if (ns == ax25->vr) { - ax25->vr = (ax25->vr + 1) % ax25->modulus; - queued = ax25_rx_iframe(ax25, skb); - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25->vr = ns; /* ax25->vr - 1 */ - ax25->condition &= ~AX25_COND_REJECT; - if (pf) { - ax25_std_enquiry_response(ax25); - } else { - if (!(ax25->condition & AX25_COND_ACK_PENDING)) { - ax25->condition |= AX25_COND_ACK_PENDING; - ax25_start_t2timer(ax25); - } - } - } else { - if (ax25->condition & AX25_COND_REJECT) { - if (pf) ax25_std_enquiry_response(ax25); - } else { - ax25->condition |= AX25_COND_REJECT; - ax25_send_control(ax25, AX25_REJ, pf, AX25_RESPONSE); - ax25->condition &= ~AX25_COND_ACK_PENDING; - } - } - break; - - case AX25_FRMR: - case AX25_ILLEGAL: - ax25_std_establish_data_link(ax25); - ax25->state = AX25_STATE_1; - break; - - default: - break; - } - - return queued; -} - -/* - * Higher level upcall for a LAPB frame - */ -int ax25_std_frame_in(ax25_cb *ax25, struct sk_buff *skb, int type) -{ - int queued = 0, frametype, ns, nr, pf; - - frametype = ax25_decode(ax25, skb, &ns, &nr, &pf); - - switch (ax25->state) { - case AX25_STATE_1: - queued = ax25_std_state1_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_2: - queued = ax25_std_state2_machine(ax25, skb, frametype, pf, type); - break; - case AX25_STATE_3: - queued = ax25_std_state3_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - case AX25_STATE_4: - queued = ax25_std_state4_machine(ax25, skb, frametype, ns, nr, pf, type); - break; - } - - ax25_kick(ax25); - - return queued; -} diff --git a/net/ax25/ax25_std_subr.c b/net/ax25/ax25_std_subr.c deleted file mode 100644 index c868e0507..000000000 --- a/net/ax25/ax25_std_subr.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; - * - * History - * AX.25 036 Jonathan(G4KLX) Split from ax25_out.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -/* - * The following routines are taken from page 170 of the 7th ARRL Computer - * Networking Conference paper, as is the whole state machine. - */ - -void ax25_std_nr_error_recovery(ax25_cb *ax25) -{ - ax25_std_establish_data_link(ax25); -} - -void ax25_std_establish_data_link(ax25_cb *ax25) -{ - ax25->condition = 0x00; - ax25->n2count = 0; - - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - - ax25_calculate_t1(ax25); - ax25_stop_idletimer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_t2timer(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_transmit_enquiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_COMMAND); - - ax25->condition &= ~AX25_COND_ACK_PENDING; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} - -void ax25_std_enquiry_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLON, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLON, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} - -void ax25_std_timeout_response(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_OWN_RX_BUSY) - ax25_send_control(ax25, AX25_RNR, AX25_POLLOFF, AX25_RESPONSE); - else - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - - ax25->condition &= ~AX25_COND_ACK_PENDING; -} diff --git a/net/ax25/ax25_std_timer.c b/net/ax25/ax25_std_timer.c deleted file mode 100644 index 5a2d8771c..000000000 --- a/net/ax25/ax25_std_timer.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * AX.25 release 037 - * - * This code REQUIRES 2.1.15 or higher/ NET3.038 - * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the - * sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug - * AX.25 033 Jonathan(G4KLX) Modularisation functions. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 036 Jonathan(G4KLX) Split from ax25_timer.c. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - */ - -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -void ax25_std_heartbeat_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - - case AX25_STATE_0: - /* Magic here: If we listen() and a new link dies before it - is accepted() it isn't 'dead' so doesn't get removed. */ - if (ax25->sk == NULL || ax25->sk->destroy || (ax25->sk->state == TCP_LISTEN && ax25->sk->dead)) { - ax25_destroy_socket(ax25); - return; - } - break; - - case AX25_STATE_3: - case AX25_STATE_4: - /* - * Check the state of the receive buffer. - */ - if (ax25->sk != NULL) { - if (atomic_read(&ax25->sk->rmem_alloc) < (ax25->sk->rcvbuf / 2) && - (ax25->condition & AX25_COND_OWN_RX_BUSY)) { - ax25->condition &= ~AX25_COND_OWN_RX_BUSY; - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_send_control(ax25, AX25_RR, AX25_POLLOFF, AX25_RESPONSE); - break; - } - } - } - - ax25_start_heartbeat(ax25); -} - -void ax25_std_t2timer_expiry(ax25_cb *ax25) -{ - if (ax25->condition & AX25_COND_ACK_PENDING) { - ax25->condition &= ~AX25_COND_ACK_PENDING; - ax25_std_timeout_response(ax25); - } -} - -void ax25_std_t3timer_expiry(ax25_cb *ax25) -{ - ax25->n2count = 0; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; -} - -void ax25_std_idletimer_expiry(ax25_cb *ax25) -{ - ax25_clear_queues(ax25); - - ax25->n2count = 0; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25->state = AX25_STATE_2; - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = 0; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } -} - -void ax25_std_t1timer_expiry(ax25_cb *ax25) -{ - switch (ax25->state) { - case AX25_STATE_1: - if (ax25->n2count == ax25->n2) { - if (ax25->modulus == AX25_MODULUS) { - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->modulus = AX25_MODULUS; - ax25->window = ax25->ax25_dev->values[AX25_VALUES_WINDOW]; - ax25->n2count = 0; - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - } - } else { - ax25->n2count++; - if (ax25->modulus == AX25_MODULUS) - ax25_send_control(ax25, AX25_SABM, AX25_POLLON, AX25_COMMAND); - else - ax25_send_control(ax25, AX25_SABME, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_2: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_send_control(ax25, AX25_DISC, AX25_POLLON, AX25_COMMAND); - } - break; - - case AX25_STATE_3: - ax25->n2count = 1; - ax25_std_transmit_enquiry(ax25); - ax25->state = AX25_STATE_4; - break; - - case AX25_STATE_4: - if (ax25->n2count == ax25->n2) { - ax25_send_control(ax25, AX25_DM, AX25_POLLON, AX25_RESPONSE); - ax25_disconnect(ax25, ETIMEDOUT); - return; - } else { - ax25->n2count++; - ax25_std_transmit_enquiry(ax25); - } - break; - } - - ax25_calculate_t1(ax25); - ax25_start_t1timer(ax25); -} diff --git a/net/ax25/ax25_subr.c b/net/ax25/ax25_subr.c index 669789646..f8700bd8f 100644 --- a/net/ax25/ax25_subr.c +++ b/net/ax25/ax25_subr.c @@ -1,223 +1,77 @@ /* - * AX.25 release 037 + * ax25_subr.c: Subroutines for NEW-AX.25 state machine * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KL + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: Most of this code is based on the SDL diagrams published in the + * ARRL Computer Networking Conference papers. The diagrams have mi + * in them, but are mostly correct. Before you modify the code coul + * read the SDL diagrams as the code is not obvious and probably ve + * easy to break; * - * Most of this code is based on the SDL diagrams published in the 7th - * ARRL Computer Networking Conference papers. The diagrams have mistakes - * in them, but are mostly correct. Before you modify the code could you - * read the SDL diagrams as the code is not obvious and probably very - * easy to break; + * Changelog: * - * History - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. Removed - * old BSD code. - * AX.25 030 Jonathan(G4KLX) Added support for extended AX.25. - * Added fragmentation support. - * Darryl(G7LED) Added function ax25_requeue_frames() to split - * it up from ax25_frames_acked(). - * AX.25 031 Joerg(DL1BKE) DAMA needs KISS Fullduplex ON/OFF. - * Thus we have ax25_kiss_cmd() now... ;-) - * Dave Brown(N2RJT) - * Killed a silly bug in the DAMA code. - * Joerg(DL1BKE) Found the real bug in ax25.h, sri. - * AX.25 032 Joerg(DL1BKE) Added ax25_queue_length to count the number of - * enqueued buffers of a socket.. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 037 Jonathan(G4KLX) New timer architecture. + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> #include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> +#include <linux/tcp.h> #include <linux/mm.h> -#include <linux/interrupt.h> +#include <net/sock.h> +#include <net/ax25.h> +#include <net/ax25dev.h> + +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_core.h" +#include "ax25_vj.h" +#include "ax25_timer.h" /* * This routine purges all the queues of frames. */ void ax25_clear_queues(ax25_cb *ax25) { - struct sk_buff *skb; - - while ((skb = skb_dequeue(&ax25->write_queue)) != NULL) - kfree_skb(skb); + int i; - while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) - kfree_skb(skb); + skb_queue_purge(&ax25->frag_queue); + skb_queue_purge(&ax25->rcv_queue); + skb_queue_purge(&ax25->write_queue); + skb_queue_purge(&ax25->ack_queue); - while ((skb = skb_dequeue(&ax25->reseq_queue)) != NULL) - kfree_skb(skb); + for (i = 0; i <= AX25_SEQMASK; i++) { + struct sk_buff *skb; - while ((skb = skb_dequeue(&ax25->frag_queue)) != NULL) - kfree_skb(skb); -} - -/* - * This routine purges the input queue of those frames that have been - * acknowledged. This replaces the boxes labelled "V(a) <- N(r)" on the - * SDL diagram. - */ -void ax25_frames_acked(ax25_cb *ax25, unsigned short nr) -{ - struct sk_buff *skb; - - /* - * Remove all the ack-ed frames from the ack queue. - */ - if (ax25->va != nr) { - while (skb_peek(&ax25->ack_queue) != NULL && ax25->va != nr) { - skb = skb_dequeue(&ax25->ack_queue); + if ((skb = ax25->reseq[i].skb) != NULL) { + ax25->reseq[i].skb = NULL; + ax25->reseq[i].csum = 0; kfree_skb(skb); - ax25->va = (ax25->va + 1) % ax25->modulus; } } } -void ax25_requeue_frames(ax25_cb *ax25) -{ - struct sk_buff *skb, *skb_prev = NULL; - - /* - * Requeue all the un-ack-ed frames on the output queue to be picked - * up by ax25_kick called from the timer. This arrangement handles the - * possibility of an empty output queue. - */ - while ((skb = skb_dequeue(&ax25->ack_queue)) != NULL) { - if (skb_prev == NULL) - skb_queue_head(&ax25->write_queue, skb); - else - skb_append(skb_prev, skb); - skb_prev = skb; - } -} - /* - * Validate that the value of nr is between va and vs. Return true or - * false for testing. + * this is new */ -int ax25_validate_nr(ax25_cb *ax25, unsigned short nr) +void ax25_tx_command(ax25_cb *ax25, int frametype, int poll_bit) { - unsigned short vc = ax25->va; - - while (vc != ax25->vs) { - if (nr == vc) return 1; - vc = (vc + 1) % ax25->modulus; - } - - if (nr == ax25->vs) return 1; - - return 0; + if (poll_bit) + frametype |= 0x100; + ax25->tx_cmd = frametype; + ax25_kick(ax25); } -/* - * This routine is the centralised routine for parsing the control - * information for the different frame formats. - */ -int ax25_decode(ax25_cb *ax25, struct sk_buff *skb, int *ns, int *nr, int *pf) +void ax25_tx_response(ax25_cb *ax25, int frametype, int poll_bit) { - unsigned char *frame; - int frametype = AX25_ILLEGAL; - - frame = skb->data; - *ns = *nr = *pf = 0; - - if (ax25->modulus == AX25_MODULUS) { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x07; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[0] >> 5) & 0x07; - *pf = frame[0] & AX25_PF; - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - } - skb_pull(skb, 1); - } else { - if ((frame[0] & AX25_S) == 0) { - frametype = AX25_I; /* I frame - carries NR/NS/PF */ - *ns = (frame[0] >> 1) & 0x7F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 1) { /* S frame - take out PF/NR */ - frametype = frame[0] & 0x0F; - *nr = (frame[1] >> 1) & 0x7F; - *pf = frame[1] & AX25_EPF; - skb_pull(skb, 2); - } else if ((frame[0] & AX25_U) == 3) { /* U frame - take out PF */ - frametype = frame[0] & ~AX25_PF; - *pf = frame[0] & AX25_PF; - skb_pull(skb, 1); - } - } - - return frametype; -} - -/* - * This routine is called when the HDLC layer internally generates a - * command or response for the remote machine ( eg. RR, UA etc. ). - * Only supervisory or unnumbered frames are processed. - */ -void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) -{ - struct sk_buff *skb; - unsigned char *dptr; - - if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat) + 2, GFP_ATOMIC)) == NULL) - return; - - skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(ax25->digipeat)); - - skb->nh.raw = skb->data; - - /* Assume a response - address structure for DTE */ - if (ax25->modulus == AX25_MODULUS) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - if ((frametype & AX25_U) == AX25_S) /* S frames carry NR */ - *dptr |= (ax25->vr << 5); - } else { - if ((frametype & AX25_U) == AX25_U) { - dptr = skb_put(skb, 1); - *dptr = frametype; - *dptr |= (poll_bit) ? AX25_PF : 0; - } else { - dptr = skb_put(skb, 2); - dptr[0] = frametype; - dptr[1] = (ax25->vr << 1); - dptr[1] |= (poll_bit) ? AX25_EPF : 0; - } - } - - ax25_transmit_buffer(ax25, skb, type); + if (poll_bit) + frametype |= 0x100; + ax25->tx_rsp = frametype; + ax25_kick(ax25); } /* @@ -225,44 +79,39 @@ void ax25_send_control(ax25_cb *ax25, int frametype, int poll_bit, int type) * * Note: src here is the sender, thus it's the target of the DM */ -void ax25_return_dm(struct net_device *dev, ax25_address *src, ax25_address *dest, ax25_digi *digi) +void ax25_return_dm(struct net_device *dev, ax25_pktinfo *pkt) { struct sk_buff *skb; - char *dptr; - ax25_digi retdigi; + unsigned char *dptr; + int sizeof_addr; + ax25_addr_t addr; - if (dev == NULL) - return; + sizeof_addr = ax25_sizeof_addr(&pkt->addr); - if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + ax25_addr_size(digi) + 1, GFP_ATOMIC)) == NULL) + if ((skb = alloc_skb(AX25_BPQ_HEADER_LEN + sizeof_addr + 1, GFP_ATOMIC)) == NULL) return; /* Next SABM will get DM'd */ - skb_reserve(skb, AX25_BPQ_HEADER_LEN + ax25_addr_size(digi)); - skb->nh.raw = skb->data; - - ax25_digi_invert(digi, &retdigi); - - dptr = skb_put(skb, 1); + skb_reserve(skb, AX25_BPQ_HEADER_LEN + sizeof_addr); + ax25_invert_addr(&pkt->addr, &addr); - *dptr = AX25_DM | AX25_PF; + *skb_put(skb, 1) = AX25_DM|AX25_PF; /* * Do the address ourselves */ - dptr = skb_push(skb, ax25_addr_size(digi)); - dptr += ax25_addr_build(dptr, dest, src, &retdigi, AX25_RESPONSE, AX25_MODULUS); - - skb->dev = dev; - - ax25_queue_xmit(skb); + skb->nh.raw = skb->data; + dptr = skb_push(skb, sizeof_addr); + dptr += ax25_build_addr(dptr, &addr, AX25_RESPONSE, AX25_SEQMASK); + ax25_send_unproto(skb, dev); } /* * Exponential backoff for AX.25 */ -void ax25_calculate_t1(ax25_cb *ax25) +unsigned short ax25_calculate_t1(ax25_cb *ax25) { - int n, t = 2; + int n; + int t = 1; switch (ax25->backoff) { case 0: @@ -273,52 +122,103 @@ void ax25_calculate_t1(ax25_cb *ax25) break; case 2: - for (n = 0; n < ax25->n2count; n++) - t *= 2; - if (t > 8) t = 8; + t <<= (ax25->n2count < 8 ? ax25->n2count : 8); break; } - ax25->t1 = t * ax25->rtt; -} + n = (t * ax25->rtt); -/* - * Calculate the Round Trip Time - */ -void ax25_calculate_rtt(ax25_cb *ax25) -{ - if (ax25->backoff == 0) - return; + if (n > AX25_T1CLAMPHI) + return AX25_T1CLAMPHI; - if (ax25_t1timer_running(ax25) && ax25->n2count == 0) - ax25->rtt = (9 * ax25->rtt + ax25->t1 - ax25_display_timer(&ax25->t1timer)) / 10; + if (n < AX25_T1CLAMPLO) + return AX25_T1CLAMPLO; - if (ax25->rtt < AX25_T1CLAMPLO) - ax25->rtt = AX25_T1CLAMPLO; + return n; +} - if (ax25->rtt > AX25_T1CLAMPHI) - ax25->rtt = AX25_T1CLAMPHI; +void ax25_close_socket(struct sock *sk, int reason) +{ + sk->err = reason; + sk->shutdown = SHUTDOWN_MASK; + sk->state = TCP_CLOSE; + sk->state_change(sk); + sk->dead = 1; } void ax25_disconnect(ax25_cb *ax25, int reason) { ax25_clear_queues(ax25); + ax25_link_failed(ax25, reason); + write_lock(&ax25->timer_lock); + ax25->wrt_timer = 0; + ax25->ack_timer = 0; + ax25->idletimer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + ax25->killtimer = 90 * AX25_SLOWHZ; + ax25->state = AX25_STATE_0; + write_unlock(&ax25->timer_lock); + if (ax25->peer && ax25->peer->state > AX25_STATE_2) + ax25_set_cond(ax25->peer, AX25_COND_RELEASE); + if (ax25->slcomp) { + axhc_free(ax25->slcomp); + ax25->slcomp = NULL; + } +} - ax25_stop_t1timer(ax25); - ax25_stop_t2timer(ax25); - ax25_stop_t3timer(ax25); - ax25_stop_idletimer(ax25); +void ax25_nr_error_recovery(ax25_cb *ax25) +{ + if (ax25->peer != NULL) + ax25->peer->condition |= AX25_COND_RELEASE; - ax25->state = AX25_STATE_0; + ax25_clear_queues(ax25); + ax25_start_t1(ax25); + ax25->state = AX25_STATE_2; + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); +} - ax25_link_failed(ax25, reason); +void ax25_establish_data_link(ax25_cb *ax25) +{ + ax25->timer_lock= RW_LOCK_UNLOCKED; + ax25->state = AX25_STATE_1; + ax25->n2count = 0; + ax25->ack_timer = 0; + ax25->vs = 0; + ax25->va = 0; + ax25->vr = 0; + ax25->vl = 0; + ax25_set_cond(ax25, AX25_COND_SETUP); + ax25_start_t1(ax25); + + if (ax25->seqmask == AX25_SEQMASK) + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(ax25, AX25_SABME, AX25_POLLON); - if (ax25->sk != NULL) { - ax25->sk->state = TCP_CLOSE; - ax25->sk->err = reason; - ax25->sk->shutdown |= SEND_SHUTDOWN; - if (!ax25->sk->dead) - ax25->sk->state_change(ax25->sk); - ax25->sk->dead = 1; - } +} + +void ax25_transmit_enquiry(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_command(ax25, ft, AX25_POLLON); + ax25_start_t1(ax25); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); + if (DAMA_STATE(ax25) & DAMA_SLAVE) AX25_PTR(ax25->device)->dama_polled = 1; +} + +void ax25_enquiry_response(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_response(ax25, ft, AX25_POLLON); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); +} + +void ax25_timeout_response(ax25_cb *ax25) +{ + int ft = (ax25->condition & AX25_COND_OWN_RX_BUSY) ? AX25_RNR:AX25_RR; + ax25_tx_response(ax25, ft, AX25_POLLOFF); + ax25_clr_cond(ax25, AX25_COND_ACK_PENDING); } diff --git a/net/ax25/ax25_subr.h b/net/ax25/ax25_subr.h new file mode 100644 index 000000000..026217ae1 --- /dev/null +++ b/net/ax25/ax25_subr.h @@ -0,0 +1,38 @@ +#ifndef _AX25_SUBR_H +#define _AX25_SUBR_H + +#include <net/ax25.h> +#include <net/ax25dev.h> + +extern unsigned char *ax25_parse_addr(unsigned char*, int, ax25_pktinfo*); +extern unsigned short ax25_calculate_t1(ax25_cb*); +extern void ax25_return_dm(struct net_device*, ax25_pktinfo*); +extern void ax25_tx_command(ax25_cb*, int, int); +extern void ax25_tx_response(ax25_cb*, int, int); +extern void ax25_clear_queues(ax25_cb*); +extern void ax25_disconnect(ax25_cb*, int); +extern void ax25_nr_error_recovery(ax25_cb*); +extern void ax25_establish_data_link(ax25_cb*); +extern void ax25_transmit_enquiry(ax25_cb*); +extern void ax25_enquiry_response(ax25_cb*); +extern void ax25_timeout_response(ax25_cb*); +extern void ax25_close_socket(struct sock*, int); + + +extern inline void ax25_set_cond(ax25_cb* ax25, unsigned int cond) +{ + ax25->condition |= cond; +} + +extern inline void ax25_clr_cond(ax25_cb* ax25, unsigned int cond) +{ + ax25->condition &= ~cond; +} + +extern inline void ax25_start_t1(ax25_cb* ax25) +{ + ax25_set_cond(ax25, AX25_COND_START_T1); + ax25->wrt_timer = 0; +} + +#endif diff --git a/net/ax25/ax25_timer.c b/net/ax25/ax25_timer.c index 01e7596f3..fdc871bc1 100644 --- a/net/ax25/ax25_timer.c +++ b/net/ax25/ax25_timer.c @@ -1,256 +1,455 @@ /* - * AX.25 release 037 + * ax25_timer.c: timer subroutines for NEW-AX.25 * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Jonathan (G4KLX + * Alan Cox (GW4PTS), Joerg (DL1BKE), et al * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: * - * History - * AX.25 028a Jonathan(G4KLX) New state machine based on SDL diagrams. - * AX.25 028b Jonathan(G4KLX) Extracted AX25 control block from the - * sock structure. - * AX.25 029 Alan(GW4PTS) Switched to KA9Q constant names. - * AX.25 031 Joerg(DL1BKE) Added DAMA support - * AX.25 032 Joerg(DL1BKE) Fixed DAMA timeout bug - * AX.25 033 Jonathan(G4KLX) Modularisation functions. - * AX.25 035 Frederic(F1OAT) Support for pseudo-digipeating. - * AX.25 036 Jonathan(G4KLX) Split Standard and DAMA code into separate files. - * Joerg(DL1BKE) Fixed DAMA Slave. We are *required* to start with - * standard AX.25 mode. - * AX.25 037 Jonathan(G4KLX) New timer architecture. - * Tomi(OH2BNS) Fixed heartbeat expiry (check ax25_dev). + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ + #include <linux/config.h> -#include <linux/errno.h> -#include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> -#include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> #include <linux/netdevice.h> -#include <linux/skbuff.h> +#include <linux/tcp.h> #include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/interrupt.h> - -static void ax25_heartbeat_expiry(unsigned long); -static void ax25_t1timer_expiry(unsigned long); -static void ax25_t2timer_expiry(unsigned long); -static void ax25_t3timer_expiry(unsigned long); -static void ax25_idletimer_expiry(unsigned long); - -void ax25_start_heartbeat(ax25_cb *ax25) -{ - del_timer(&ax25->timer); +#include <net/ax25dev.h> +#include <net/ax25.h> - ax25->timer.data = (unsigned long)ax25; - ax25->timer.function = &ax25_heartbeat_expiry; - ax25->timer.expires = jiffies + 5 * HZ; +#include "ax25_core.h" +#include "ax25_ddi.h" +#include "ax25_in.h" +#include "ax25_subr.h" +#include "ax25_timer.h" - add_timer(&ax25->timer); -} +static void ax25_wrt_timeout(ax25_cb *); -void ax25_start_t1timer(ax25_cb *ax25) +/* + * AX.25 TIMER + * + * This routine is called every 100ms. Decrement timer by this + * amount - if expired then process the event. + */ +void ax25_timer(ax25_cb *ax25) { - del_timer(&ax25->t1timer); + int wrt_timeout; + + switch (ax25->state) { + case AX25_LISTEN: + /* + * never kill listening sockets. let the be moved to + * AX25_STATE_0 first. ax25_release() does this. + */ + return; + + case AX25_STATE_0: + /* + * don't kill if a frame signalling a state change is + * still pending + */ + if (ax25->condition & AX25_COND_STATE_CHANGE) + break; + + if (ax25->sk) { + /* + * ax25_release() sets ax25->sk = NULL, releasing the + * connection between the socket and the underlying control + * structure, we handle that further down + */ + + /* almost dead, notify socket */ + if (!ax25->sk->dead) + ax25_close_socket(ax25->sk, 0); + break; + } + + /* + * wait a certain time before destroying the control block + */ + if (ax25->killtimer > 0 && --ax25->killtimer > 0) + break; + + /* + * if a peer exists in STATE [12], disconnect it. this handles + * "connection timed out" for digipeated connections. + * + * else if no peer exists, destroy this control block. + */ + if (ax25->peer) { + if (ax25->peer->state < AX25_STATE_3) + ax25_destroy_cb(ax25->peer); + else + break; + } + + ax25_destroy_cb(ax25); + return; + + case AX25_STATE_3: + write_lock(&ax25->timer_lock); + if (!ax25->peer && !ax25->sk && ax25->idletimer > 0 && --ax25->idletimer == 0) + ax25_set_cond(ax25, AX25_COND_RELEASE); + write_unlock(&ax25->timer_lock); + /* fall through */ + case AX25_STATE_4: + write_lock(&ax25->timer_lock); + if (ax25->condition & AX25_COND_ACK_PENDING) { + if (ax25->ack_timer > 0) + ax25->ack_timer--; + else + ax25_timeout_response(ax25); + } + + /* + * Our peer connection has seen a DISC or DM and we're about to change to + * state 2. We don't DISC until we've delivered all queued data + */ + if (ax25->condition & AX25_COND_RELEASE) { + if (!skb_queue_len(&ax25->write_queue) && !skb_queue_len(&ax25->ack_queue)) { + ax25->n2count = 1; + ax25->ack_timer = 0; + ax25_start_t1(ax25); + ax25_clr_cond(ax25, AX25_COND_RELEASE); + ax25->state = AX25_STATE_2; + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + } + + /* + * Check the state of the receive buffer. This is part of the flow control + * and should be done in ax25_rcvmsg(); + */ + } else if (ax25->sk && skb_queue_len(&ax25->rcv_queue)) { + struct sk_buff *skb; + + while ((skb = skb_peek(&ax25->rcv_queue)) != NULL) { + if (ax25->sk->shutdown & RCV_SHUTDOWN) + break; + if (atomic_read(&ax25->sk->rmem_alloc) + skb->truesize < ax25->sk->rcvbuf) { + skb_dequeue(&ax25->rcv_queue); + sock_queue_rcv_skb(ax25->sk, skb); + } else + break; + } + if (skb == NULL) { + ax25_clr_cond(ax25, AX25_COND_OWN_RX_BUSY); + ax25_set_cond(ax25, AX25_COND_STATE_CHANGE); + ax25->state = AX25_STATE_4; + ax25_transmit_enquiry(ax25); + } + } + write_unlock(&ax25->timer_lock); + break; + + default: /* state 1/2 */ + break; + } + + /* ax25_wrt_timout() must be called unlocked as ax25_disconnect() + * sets ax25->timer_lock + */ + + write_lock(&ax25->timer_lock); + wrt_timeout = (ax25->wrt_timer > 0 && --ax25->wrt_timer == 0); + write_unlock(&ax25->timer_lock); + + if (wrt_timeout) + ax25_wrt_timeout(ax25); - ax25->t1timer.data = (unsigned long)ax25; - ax25->t1timer.function = &ax25_t1timer_expiry; - ax25->t1timer.expires = jiffies + ax25->t1; - add_timer(&ax25->t1timer); } -void ax25_start_t2timer(ax25_cb *ax25) +static void ax25_wrt_timeout(ax25_cb *ax25) { - del_timer(&ax25->t2timer); + ax25_cb *peer = ax25->peer; + switch (ax25->state) { + case AX25_STATE_1: + if (ax25->n2count == ax25->n2) { + if (ax25->seqmask == AX25_SEQMASK) { + ax25_disconnect(ax25, ETIMEDOUT); + if (ax25->sk) + ax25_close_socket(ax25->sk, ETIMEDOUT); + else if (peer) { + ax25_disconnect(peer, 0); + } + } else { + ax25->seqmask = AX25_SEQMASK; + ax25->window = ax25_dev_get_value(ax25->device, AX25_VALUES_WINDOW); + ax25->n2count = 0; + ax25_start_t1(ax25); + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + } + } else { + ax25->n2count++; + ax25_start_t1(ax25); + if (ax25->seqmask == AX25_SEQMASK) + ax25_tx_command(ax25, AX25_SABM, AX25_POLLON); + else + ax25_tx_command(ax25, AX25_SABME, AX25_POLLON); + } + break; + + case AX25_STATE_2: + if (ax25->n2count == ax25->n2) { + if (ax25->sk) + ax25_close_socket(ax25->sk, 0); + ax25_disconnect(ax25, 0); + } else { + ax25->n2count++; + ax25_start_t1(ax25); + ax25_tx_command(ax25, AX25_DISC, AX25_POLLON); + } + break; + + case AX25_STATE_3: + ax25->vs_rtt = -1; + ax25->n2count = 1; + ax25_start_t1(ax25); + ax25->state = AX25_STATE_4; + + /* + * We are angry now. + * Use CSMA. If master responds to poll, circuit will be saved + * and dama_mode will be re-enabled automagically in ax25_in.c + */ + ax25_dev_set_dama(ax25->device, 0); + ax25_transmit_enquiry(ax25); + break; + + case AX25_STATE_4: + if (ax25->n2count == ax25->n2) { + if (peer) { + ax25_set_cond(peer, AX25_COND_RELEASE); + ax25->killtimer = 90 * AX25_SLOWHZ; + } else if (ax25->sk) + ax25_close_socket(ax25->sk, ETIMEDOUT); + ax25_disconnect(ax25, ETIMEDOUT); + } else { + ax25->n2count++; + ax25_transmit_enquiry(ax25); + } + break; + } +} - ax25->t2timer.data = (unsigned long)ax25; - ax25->t2timer.function = &ax25_t2timer_expiry; - ax25->t2timer.expires = jiffies + ax25->t2; - add_timer(&ax25->t2timer); -} +/* -------------------------------------------------------------------- */ + +/************************************************************************/ +/* Module support functions follow. */ +/************************************************************************/ + +static struct protocol_struct { + struct protocol_struct *next; + unsigned int pid; + int (*func)(struct sk_buff *, ax25_cb *); +} *protocol_list = NULL; + +static struct linkfail_struct { + struct linkfail_struct *next; + void (*func)(ax25_cb *, int); +} *linkfail_list = NULL; -void ax25_start_t3timer(ax25_cb *ax25) +static struct listen_struct { + struct listen_struct *next; + ax25_address callsign; + struct net_device *dev; +} *listen_list = NULL; + +int ax25_protocol_register(unsigned int pid, int (*func)(struct sk_buff *, ax25_cb *)) { - del_timer(&ax25->t3timer); + struct protocol_struct *protocol; + unsigned long flags; - if (ax25->t3 > 0) { - ax25->t3timer.data = (unsigned long)ax25; - ax25->t3timer.function = &ax25_t3timer_expiry; - ax25->t3timer.expires = jiffies + ax25->t3; + if (pid == AX25_P_TEXT || pid == AX25_P_SEGMENT) + return 0; - add_timer(&ax25->t3timer); - } + if ((protocol = (struct protocol_struct *)kmalloc(sizeof(*protocol), GFP_ATOMIC)) == NULL) + return 0; + + protocol->pid = pid; + protocol->func = func; + + save_flags(flags); + cli(); + + protocol->next = protocol_list; + protocol_list = protocol; + + restore_flags(flags); + + return 1; } -void ax25_start_idletimer(ax25_cb *ax25) +void ax25_protocol_release(unsigned int pid) { - del_timer(&ax25->idletimer); + struct protocol_struct *s, *protocol = protocol_list; + unsigned long flags; + + if (protocol == NULL) + return; - if (ax25->idle > 0) { - ax25->idletimer.data = (unsigned long)ax25; - ax25->idletimer.function = &ax25_idletimer_expiry; - ax25->idletimer.expires = jiffies + ax25->idle; + save_flags(flags); + cli(); - add_timer(&ax25->idletimer); + if (protocol->pid == pid) { + protocol_list = protocol->next; + restore_flags(flags); + kfree(protocol); + return; } -} -void ax25_stop_heartbeat(ax25_cb *ax25) -{ - del_timer(&ax25->timer); -} + while (protocol != NULL && protocol->next != NULL) { + if (protocol->next->pid == pid) { + s = protocol->next; + protocol->next = protocol->next->next; + restore_flags(flags); + kfree(s); + return; + } -void ax25_stop_t1timer(ax25_cb *ax25) -{ - del_timer(&ax25->t1timer); -} + protocol = protocol->next; + } -void ax25_stop_t2timer(ax25_cb *ax25) -{ - del_timer(&ax25->t2timer); + restore_flags(flags); } -void ax25_stop_t3timer(ax25_cb *ax25) +int ax25_linkfail_register(void (*func)(ax25_cb *, int)) { - del_timer(&ax25->t3timer); -} + struct linkfail_struct *linkfail; + unsigned long flags; -void ax25_stop_idletimer(ax25_cb *ax25) -{ - del_timer(&ax25->idletimer); + if ((linkfail = (struct linkfail_struct *)kmalloc(sizeof(*linkfail), GFP_ATOMIC)) == NULL) + return 0; + + linkfail->func = func; + + save_flags(flags); + cli(); + + linkfail->next = linkfail_list; + linkfail_list = linkfail; + + restore_flags(flags); + + return 1; } -int ax25_t1timer_running(ax25_cb *ax25) +void ax25_linkfail_release(void (*func)(ax25_cb *, int)) { - return timer_pending(&ax25->t1timer); + struct linkfail_struct *s, *linkfail = linkfail_list; + unsigned long flags; + + if (linkfail == NULL) + return; + + save_flags(flags); + cli(); + + if (linkfail->func == func) { + linkfail_list = linkfail->next; + restore_flags(flags); + kfree(linkfail); + return; + } + + while (linkfail != NULL && linkfail->next != NULL) { + if (linkfail->next->func == func) { + s = linkfail->next; + linkfail->next = linkfail->next->next; + restore_flags(flags); + kfree(s); + return; + } + + linkfail = linkfail->next; + } + + restore_flags(flags); } -unsigned long ax25_display_timer(struct timer_list *timer) +static char empty_addr[AX25_ADDR_LEN] = {0, 0, 0, 0, 0, 0, 0}; + +int ax25_listen_register(ax25_address *callsign, struct net_device *dev) { - if (!timer_pending(timer)) + ax25_cb *ax25; + + ax25_addr_t addr; + + addr.dcount = 0; + addr.src = *callsign; + memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN); + + if (ax25_find_cb(&addr, dev) != NULL) + return 0; + + if ((ax25 = ax25_create_cb()) == NULL) return 0; - return timer->expires - jiffies; + ax25->addr.src = *callsign; + + ax25_fillin_cb(ax25, dev); + ax25_insert_cb(ax25); + + return 1; } -static void ax25_heartbeat_expiry(unsigned long param) +void ax25_listen_release(ax25_address *callsign, struct net_device *dev) { - ax25_cb *ax25 = (ax25_cb *)param; - int proto = AX25_PROTO_STD_SIMPLEX; + ax25_cb *ax25; - if (ax25->ax25_dev) - proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]; + ax25_addr_t addr; - switch (proto) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_heartbeat_expiry(ax25); - break; + addr.dcount = 0; + addr.src = *callsign; + memcpy(&addr.dest, empty_addr, AX25_ADDR_LEN); -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_heartbeat_expiry(ax25); - else - ax25_std_heartbeat_expiry(ax25); - break; -#endif - } + if ((ax25 = ax25_find_cb(&addr, dev)) != NULL) + ax25_destroy_cb(ax25); } -static void ax25_t1timer_expiry(unsigned long param) +int (*ax25_protocol_function(unsigned int pid))(struct sk_buff *, ax25_cb *) { - ax25_cb *ax25 = (ax25_cb *)param; + struct protocol_struct *protocol; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t1timer_expiry(ax25); - break; + for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) + if (protocol->pid == pid) + return protocol->func; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t1timer_expiry(ax25); - break; -#endif - } + return NULL; } -static void ax25_t2timer_expiry(unsigned long param) +int ax25_listen_mine(ax25_address *callsign, struct net_device *dev) { - ax25_cb *ax25 = (ax25_cb *)param; + struct listen_struct *listen; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t2timer_expiry(ax25); - break; + for (listen = listen_list; listen != NULL; listen = listen->next) + if (ax25cmp(&listen->callsign, callsign) == 0 && (listen->dev == dev || listen->dev == NULL)) + return 1; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (!ax25->ax25_dev->dama.slave) - ax25_std_t2timer_expiry(ax25); - break; -#endif - } + return 0; } -static void ax25_t3timer_expiry(unsigned long param) +void ax25_link_failed(ax25_cb *ax25, int reason) { - ax25_cb *ax25 = (ax25_cb *)param; - - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_t3timer_expiry(ax25); - break; + struct linkfail_struct *linkfail; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_t3timer_expiry(ax25); - else - ax25_std_t3timer_expiry(ax25); - break; -#endif - } + for (linkfail = linkfail_list; linkfail != NULL; linkfail = linkfail->next) + (linkfail->func)(ax25, reason); } -static void ax25_idletimer_expiry(unsigned long param) +int ax25_protocol_is_registered(unsigned int pid) { - ax25_cb *ax25 = (ax25_cb *)param; + struct protocol_struct *protocol; - switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) { - case AX25_PROTO_STD_SIMPLEX: - case AX25_PROTO_STD_DUPLEX: - ax25_std_idletimer_expiry(ax25); - break; + for (protocol = protocol_list; protocol != NULL; protocol = protocol->next) + if (protocol->pid == pid) + return 1; -#ifdef CONFIG_AX25_DAMA_SLAVE - case AX25_PROTO_DAMA_SLAVE: - if (ax25->ax25_dev->dama.slave) - ax25_ds_idletimer_expiry(ax25); - else - ax25_std_idletimer_expiry(ax25); - break; -#endif - } + return 0; } diff --git a/net/ax25/ax25_timer.h b/net/ax25/ax25_timer.h new file mode 100644 index 000000000..010e8e876 --- /dev/null +++ b/net/ax25/ax25_timer.h @@ -0,0 +1,11 @@ +/* + * Interface declaration for timer functions + * + * Joerg Reuter DL1BKE 2000-07-06 + * + */ + + +#ifndef _AX25_TIMER_H +#define _AX25_TIMER_H +#endif diff --git a/net/ax25/ax25_uid.c b/net/ax25/ax25_uid.c index 603d8b8cc..4a41313de 100644 --- a/net/ax25/ax25_uid.c +++ b/net/ax25/ax25_uid.c @@ -1,124 +1,146 @@ /* - * AX.25 release 037 + * ax25_uid.c: Callsign/UID mapper. This is in kernel space for security on mul * - * This code REQUIRES 2.1.15 or higher/ NET3.038 + * Authors: Matthias Welwarsky (DG2FEF) + * Joerg Reuter (DL1BKE) * - * This module: - * This module is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + * Comment: * - * History - * AX.25 036 Jonathan(G4KLX) Split from af_ax25.c. + * Changelog: + * 2001-02-06 Joerg Reuter DL1BKE <jreuter@yaina.de> + * extended policy scheme implemented + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ -#include <linux/errno.h> #include <linux/types.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/timer.h> -#include <linux/string.h> +#include <linux/errno.h> #include <linux/sockios.h> -#include <linux/net.h> -#include <net/ax25.h> -#include <linux/inet.h> -#include <linux/netdevice.h> -#include <linux/if_arp.h> -#include <linux/skbuff.h> -#include <net/sock.h> -#include <asm/uaccess.h> -#include <asm/system.h> -#include <linux/fcntl.h> #include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/notifier.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/netfilter.h> -#include <linux/sysctl.h> -#include <net/ip.h> -#include <net/arp.h> +#include <linux/netdevice.h> +#include <linux/ax25.h> -/* - * Callsign/UID mapper. This is in kernel space for security on multi-amateur machines. - */ +#include <net/ax25_uid.h> + +#include "af_ax25.h" +t_ax25_uid_policy ax25_uid_policy = AX25_UID_POLICY_ARBITRARY_CALLSIGN; static ax25_uid_assoc *ax25_uid_list; -int ax25_uid_policy = 0; +ax25_address *ax25_find_by_uid(uid_t uid) +{ + ax25_uid_assoc *a; -ax25_address *ax25_findbyuid(uid_t uid) + for (a = ax25_uid_list; a != NULL; a = a->next) { + if (a->uid == uid) + return &a->call; + } + + return NULL; +} + +ax25_address *ax25_find_match_for_uid(uid_t uid, ax25_address *provided, char *device) { - ax25_uid_assoc *ax25_uid; + int res; + ax25_uid_assoc *a; + + /* no callsign given? find one! */ + if (ax25cmp(provided, &null_ax25_address) == 0) + { + for (a = ax25_uid_list; a != NULL; a = a->next) { + if (a->uid == uid) + { + if (device != NULL && strcmp(device, a->device)) + continue; + return &a->call; + } + } + } + + /* any user can choose an arbitrary callsign? */ + if (ax25_uid_policy == 0) + return provided; + + /* walk the list... */ + for (a = ax25_uid_list; a != NULL ; a = a->next) { + if (a->uid == uid) + { + /* limited to a device? */ + if (device != NULL && strcmp(device, a->device)) + continue; - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25_uid->uid == uid) - return &ax25_uid->call; + /* user can choose any callsign? */ + if (ax25cmp(&a->call, &null_ax25_address) == 0) + return provided; + + res = ax25cmp(provided, &a->call); + + /* exact match, or only SSID differ (when allowed): okay */ + if ( res == 0 || + (res == 2 && ax25_uid_policy == AX25_UID_POLICY_ANY_SSID) ) + return provided; + } } return NULL; } +/* + * TODO: move this stuff to procfs + * general idea: echo "add 503 dl1bke scc0" >/proc/net/ax25_calls + * echo "del 502 dk0tux *" >/proc/net/ax25_calls + */ + int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) { - ax25_uid_assoc *s, *ax25_uid; - unsigned long flags; + ax25_uid_assoc *a; switch (cmd) { case SIOCAX25GETUID: - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - return ax25_uid->uid; + for (a = ax25_uid_list; a != NULL; a = a->next) { + if (ax25cmp(&sax->sax25_call, &a->call) == 0) + return a->uid; } return -ENOENT; case SIOCAX25ADDUID: if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (ax25_findbyuid(sax->sax25_uid)) + if (ax25_find_by_uid(sax->sax25_uid)) return -EEXIST; if (sax->sax25_uid == 0) return -EINVAL; - if ((ax25_uid = kmalloc(sizeof(*ax25_uid), GFP_KERNEL)) == NULL) + a = (ax25_uid_assoc *)kmalloc(sizeof(*a), GFP_KERNEL); + if (a == NULL) return -ENOMEM; - ax25_uid->uid = sax->sax25_uid; - ax25_uid->call = sax->sax25_call; - save_flags(flags); cli(); - ax25_uid->next = ax25_uid_list; - ax25_uid_list = ax25_uid; - restore_flags(flags); + a->uid = sax->sax25_uid; + a->call = sax->sax25_call; + a->device[0] = '\0'; + a->next = ax25_uid_list; + ax25_uid_list = a; return 0; - case SIOCAX25DELUID: + case SIOCAX25DELUID: { + ax25_uid_assoc **l; + if (!capable(CAP_NET_ADMIN)) return -EPERM; - for (ax25_uid = ax25_uid_list; ax25_uid != NULL; ax25_uid = ax25_uid->next) { - if (ax25cmp(&sax->sax25_call, &ax25_uid->call) == 0) - break; - } - if (ax25_uid == NULL) - return -ENOENT; - save_flags(flags); cli(); - if ((s = ax25_uid_list) == ax25_uid) { - ax25_uid_list = s->next; - restore_flags(flags); - kfree(ax25_uid); - return 0; - } - while (s != NULL && s->next != NULL) { - if (s->next == ax25_uid) { - s->next = ax25_uid->next; - restore_flags(flags); - kfree(ax25_uid); + l = &ax25_uid_list; + while ((*l) != NULL) { + if (ax25cmp(&((*l)->call), &(sax->sax25_call)) == 0) { + a = *l; + *l = (*l)->next; + kfree(a); return 0; } - s = s->next; + + l = &((*l)->next); } - restore_flags(flags); return -ENOENT; + } default: return -EINVAL; @@ -127,19 +149,17 @@ int ax25_uid_ioctl(int cmd, struct sockaddr_ax25 *sax) return -EINVAL; /*NOTREACHED */ } -int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) +int ax25_cs_get_info(char *buffer, char **start, off_t offset, int length) { ax25_uid_assoc *pt; int len = 0; off_t pos = 0; off_t begin = 0; - cli(); - len += sprintf(buffer, "Policy: %d\n", ax25_uid_policy); for (pt = ax25_uid_list; pt != NULL; pt = pt->next) { - len += sprintf(buffer + len, "%6d %s\n", pt->uid, ax2asc(&pt->call)); + len += sprintf(buffer + len, "%6d %-9s %s\n", pt->uid, ax2asc(&pt->call), pt->device); pos = begin + len; @@ -152,7 +172,6 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) break; } - sti(); *start = buffer + (offset - begin); len -= offset - begin; @@ -162,17 +181,3 @@ int ax25_uid_get_info(char *buffer, char **start, off_t offset, int length) return len; } -/* - * Free all memory associated with UID/Callsign structures. - */ -void __exit ax25_uid_free(void) -{ - ax25_uid_assoc *s, *ax25_uid = ax25_uid_list; - - while (ax25_uid != NULL) { - s = ax25_uid; - ax25_uid = ax25_uid->next; - - kfree(s); - } -} diff --git a/net/ax25/ax25_vj.c b/net/ax25/ax25_vj.c new file mode 100644 index 000000000..4fa2dc1ff --- /dev/null +++ b/net/ax25/ax25_vj.c @@ -0,0 +1,724 @@ +/* + * ax25_vj.c: VJ compression routines for VJ-compressed IP via NEW-AX.25 + * + * Authors: Matthias Welwarsky (DG2FEF) + * + * Comment: Routines to compress TCP/IP packets according to RFC 1144 and to + * suppress redundant retransmissions on a reliable virtual circuit + * Largely based on code by Van Jacobson, Phil Karn et.al. Directly + * derived from WAMPES' slhc.c written by Dieter Deyke, DK5SG. + * + * Changelog: + * 1998-02-25 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de> + * Adapted to Linux. contains code from drivers/net/slhc.c + * + * 1998-03-04 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de> + * fixed problem with nonatomically calling kmalloc from interrupt + * + * 1998-03-08 Matthias Welwarsky DG2FEF <dg2fef@afthd.tu-darmstadt.de> + * fixed problem in axhc_recv_vjc() that lead to a system panic when + * the incoming sk_buff didn't contain enough headroom to rebuild the + * TCP/IP header after decompression + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/config.h> +#if defined(CONFIG_INET) +#include <linux/skbuff.h> +#include <asm/unaligned.h> +#include <linux/kernel.h> + +#include "ax25_vj.h" + +#ifdef DEBUG +#define PRINTK(x) printk x +#else +#define PRINTK(x) +#endif + +/* Bits in first octet of compressed packet */ +#define NEW_C 0x40 /* flag bits for what changed in a packet */ +#define NEW_I 0x20 +#define NEW_S 0x08 +#define NEW_A 0x04 +#define NEW_W 0x02 +#define NEW_U 0x01 + +/* reserved, special-case values of above */ +#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ +#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ +#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) + +#define TCP_PUSH_BIT 0x10 + +static int axhc_toss(struct axvj_slcomp *); + +/* Put a short in host order into a char array in network order */ +static inline unsigned char * +put16(unsigned char *cp, unsigned short x) +{ + *cp++ = x >> 8; + *cp++ = x; + + return cp; +} + +/* Pull a 16-bit integer in host order from buffer in network byte order */ +static unsigned short pull16(unsigned char **cpp) +{ + short rval; + + rval = *(*cpp)++; + rval <<= 8; + rval |= *(*cpp)++; + return rval; +} + +/* Encode a number */ +static unsigned char * +encode(unsigned char *cp, unsigned short n) +{ + if (n >= 256 || n == 0) { + *cp++ = 0; + cp = put16(cp, n); + } else { + *cp++ = n; + } + return cp; +} + +/* Decode a number */ +static long decode(unsigned char **cpp) +{ + register int x; + + x = *(*cpp)++; + if (x == 0) { + return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ + } else { + return x & 0xff; /* -1 if PULLCHAR returned error */ + } +} + + +/* Initialize compression data structure + * slots must be in range 0 to 255 (zero meaning no compression) + */ +struct axvj_slcomp * +axhc_init(int rslots, int tslots) +{ + register short i; + register struct axvj_cstate *ts; + struct axvj_slcomp *comp; + + comp = (struct axvj_slcomp *)kmalloc(sizeof(struct axvj_slcomp), + GFP_ATOMIC); + if (! comp) + return NULL; + + memset(comp, 0, sizeof(struct axvj_slcomp)); + + if ( rslots > 0 && rslots < 256 ) { + size_t rsize = rslots * sizeof(struct axvj_cstate); + comp->rstate = (struct axvj_cstate *) kmalloc(rsize, GFP_ATOMIC); + if (! comp->rstate) + { + kfree((unsigned char *)comp); + return NULL; + } + memset(comp->rstate, 0, rsize); + comp->rslot_limit = rslots - 1; + } + + if ( tslots > 0 && tslots < 256 ) { + size_t tsize = tslots * sizeof(struct axvj_cstate); + comp->tstate = (struct axvj_cstate *) kmalloc(tsize, GFP_ATOMIC); + if (! comp->tstate) + { + kfree((unsigned char *)comp->rstate); + kfree((unsigned char *)comp); + return NULL; + } + memset(comp->tstate, 0, tsize); + comp->tslot_limit = tslots - 1; + } + + comp->xmit_oldest = 0; + comp->xmit_current = 255; + comp->recv_current = 255; + /* + * don't accept any packets with implicit index until we get + * one with an explicit index. Otherwise the uncompress code + * will try to use connection 255, which is almost certainly + * out of range + */ + comp->flags |= SLF_TOSS; + + if ( tslots > 0 ) { + ts = comp->tstate; + for(i = comp->tslot_limit; i > 0; --i){ + ts[i].cs_this = i; + ts[i].next = &(ts[i - 1]); + } + ts[0].next = &(ts[comp->tslot_limit]); + ts[0].cs_this = 0; + } + return comp; +} + + +/* Free a compression data structure */ +void +axhc_free(struct axvj_slcomp *comp) +{ + if ( comp == NULL ) + return; + + if ( comp->rstate != NULL ) + kfree( comp->rstate ); + + if ( comp->tstate != NULL ) + kfree( comp->tstate ); + + kfree( comp ); +} + +/* Dear hacker. I assume that you have read and understood RFC 1144 + * and the original slhc_compress() procedure before tinkering with + * this code. + * + * procedure is as follows: + * 1. check if packet is TCP. return AX25_P_IP if not. + * 2. check if SYN, FIN, MSS, WSCALE, TSTAMP or RST is set, or if ACK is not + * set. deny compression for these packets (do_compression = 0). + * 3. try to find the appopriate slot, reuse an old one if no match is found + * 4. attempt to compress the packet and check the following rules: + * - if the packet contains an old (outdated) seq and no new ack or + * window or urgent data, drop it (return 0). + * - if nothing changed since the last frame sent (no new seq, ack, + * window, urgent data, or changing TCP flags), drop it. + * - before dropping a packet, check if any packet made it through the + * filter within the last 120sec. If not, assume a packet loss and + * transmit the packet. + * 5. transmit a compressed, uncompressed or regular packet, depending + * on do_compression and cs->deny_compression. + */ + +int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression) +{ + struct axvj_cstate *ocs = &(comp->tstate[comp->xmit_oldest]); + struct axvj_cstate *lcs = ocs; + struct axvj_cstate *cs = lcs->next; + unsigned int hlen; + struct tcphdr *th, *oth; + struct iphdr *iph; + unsigned long deltaS, deltaA; + unsigned short changes = 0; + unsigned char new_seq[16]; + unsigned char *cp = new_seq; + + /* Peek at IP header */ + iph = (struct iphdr *) skb->data; + + /* Bail if this packet isn't TCP, or is an IP fragment */ + if (iph->protocol != IPPROTO_TCP || + (ntohs(iph->frag_off) & 0x1fff) || + (iph->frag_off & 32)) { + /* Send as regular IP */ + if (iph->protocol != IPPROTO_TCP) + comp->sls_o_nontcp++; + else + comp->sls_o_tcp++; + return AX25_P_IP; + } + /* Extract TCP header */ + th = (struct tcphdr *) (((unsigned char *) iph) + iph->ihl * 4); + hlen = iph->ihl * 4 + th->doff * 4; + + PRINTK((KERN_DEBUG "ax25_vj.c: th.seq=%0x\n", ntohl(th->seq))); + /* + * check if packet may be compressed. + */ + if (th->syn || th->fin || th->rst || !th->ack) { + comp->sls_o_tcp++; + do_compression = 0; + } + /* + * locate the connection state slot + */ + for (;;) { + if (iph->saddr == cs->cs_ip.saddr + && iph->daddr == cs->cs_ip.daddr + && th->source == cs->cs_tcp.source + && th->dest == cs->cs_tcp.dest) + goto found; + + /* if current equal oldest, at end of list */ + if (cs == ocs) + break; + lcs = cs; + cs = cs->next; + comp->sls_o_searches++; + } + /* + * Didn't find it -- re-use oldest axvj_cstate. Send an + * uncompressed packet that tells the other side what + * connection number we're using for this conversation. + * + * Note that since the state list is circular, the oldest + * state points to the newest and we only need to set + * xmit_oldest to update the lru linkage. + */ + comp->sls_o_misses++; + comp->xmit_oldest = lcs->cs_this; + cs->deny_compression = 0; + cs->lastdropped = 0; + PRINTK((KERN_DEBUG "ax25_vj.c: new slot %d\n", cs->cs_this)); + goto uncompressed; + + found: + /* + * Found it -- move to the front on the connection list. + */ + if (lcs == ocs) { + /* found at most recently used */ + } else if (cs == ocs) { + /* found at least recently used */ + comp->xmit_oldest = lcs->cs_this; + } else { + /* more than 2 elements */ + lcs->next = cs->next; + cs->next = ocs->next; + ocs->next = cs; + } + PRINTK((KERN_DEBUG "ax25_vj.c: found slot %d\n", cs->cs_this)); + /* + * Make sure that only what we expect to change changed. + * Check the following: + * IP protocol version, header length & type of service. + * The "Don't fragment" bit. + * The time-to-live field. + * The TCP header length. + * IP options, if any. + * TCP options, if any. + * If any of these things are different between the previous & + * current datagram, we send the current datagram `uncompressed'. + */ + oth = &cs->cs_tcp; + + if (iph->version != cs->cs_ip.version || iph->ihl != cs->cs_ip.ihl + || iph->tos != cs->cs_ip.tos + || (iph->frag_off & 64) != (cs->cs_ip.frag_off & 64) + || iph->ttl != cs->cs_ip.ttl + || th->doff != cs->cs_tcp.doff + || (iph->ihl > 5 && memcmp(iph + 1, cs->cs_ipopt, ((iph->ihl) - 5) * 4) != 0) + || (th->doff > 5 && memcmp(th + 1, cs->cs_tcpopt, ((th->doff) - 5) * 4) != 0)) { + PRINTK((KERN_DEBUG "ax25_vj.c: packet uncompressable\n")); + goto uncompressed; + } + /* + * Figure out which of the changing fields changed. The + * receiver expects changes in the order: urgent, window, + * ack, seq (the order minimizes the number of temporaries + * needed in this section of code). + */ + if (th->urg) { + deltaS = ntohs(th->urg_ptr); + cp = encode(cp, deltaS); + changes |= NEW_U; + } else if (th->urg_ptr != oth->urg_ptr) { + /* argh! URG not set but urp changed -- a sensible + * implementation should never do this but RFC793 + * doesn't prohibit the change so we have to deal + * with it. */ + goto uncompressed; + } + if ((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0) { + cp = encode(cp, deltaS); + changes |= NEW_W; + } + if ((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L) { + if (deltaA > 0x0000ffff) + goto uncompressed; + cp = encode(cp, deltaA); + changes |= NEW_A; + } + if ((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L) { + if (deltaS > 0x0000ffff) { + + /* + * - if the packet contains an old (outdated) seq and no + * new ack or window or urgent data, drop it (return 0) + */ + if (before(ntohl(th->seq), ntohl(oth->seq)) && !changes) { + if (cs->lastdropped != 0) { + if (jiffies - cs->lastdropped > 120 * HZ) { + goto uncompressed; + } + } else { + cs->lastdropped = jiffies; + } + PRINTK((KERN_DEBUG "ax25_vj.c: old packet, dS=%0x th.seq=%0x oth.seq=%0x\n", deltaS, ntohl(th->seq), ntohl(oth->seq))); + return 0; + } + goto uncompressed; + } + cp = encode(cp, deltaS); + changes |= NEW_S; + } + switch (changes) { + case 0: /* Nothing changed. If this packet contains data and the + * last one didn't, this is probably a data packet following + * an ack (normal on an interactive connection) and we send + * it compressed. Otherwise it's probably a retransmit, + * retransmitted ack or window probe. Send it uncompressed + * in case the other side missed the compressed version. */ + if (iph->tot_len != cs->cs_ip.tot_len + && ntohs(cs->cs_ip.tot_len) == hlen) { + PRINTK((KERN_DEBUG "ax25_vj.c: data following ack\n")); + break; + } + /* + * MW: drop retransmitted packet. seq and ack did not change, + * check if flags have changed. + */ + if (th->fin != oth->fin || th->syn != oth->syn || th->rst != oth->rst + || th->ack != oth->ack) { + PRINTK((KERN_DEBUG "ax25_vj.c: tcp flags changed\n")); + goto uncompressed; + } + if (cs->lastdropped != 0) { + if (jiffies - cs->lastdropped > 120 * HZ) { + goto uncompressed; + } + } else { + cs->lastdropped = jiffies; + } + PRINTK((KERN_DEBUG "ax25_vj.c: no changes detected\n")); + return 0; + + case SPECIAL_I: + case SPECIAL_D: + /* actual changes match one of our special case encodings -- + * send packet uncompressed. + */ + goto uncompressed; + case NEW_S | NEW_A: + if (deltaS == deltaA && + deltaS == ntohs(cs->cs_ip.tot_len) - hlen) { + /* special case for echoed terminal traffic */ + changes = SPECIAL_I; + cp = new_seq; + } + break; + case NEW_S: + if (deltaS == ntohs(cs->cs_ip.tot_len) - hlen) { + /* special case for data xfer */ + changes = SPECIAL_D; + cp = new_seq; + } + break; + } + + /* + * The Packet contains new information, it has not been dropped + * until here. But compression has been denied, so we transmit an + * uncompressed packet instead. + */ + if (cs->deny_compression) { + goto uncompressed; + } + deltaS = ntohs(iph->id) - ntohs(cs->cs_ip.id); + if (deltaS != 1) { + cp = encode(cp, deltaS); + changes |= NEW_I; + } + if (th->psh) + changes |= TCP_PUSH_BIT; + /* Grab the cksum before we overwrite it below. Then update our + * state with this packet's header. + */ + deltaA = ntohs(th->check); + memcpy(&cs->cs_ip, iph, 20); + memcpy(&cs->cs_tcp, th, 20); + cs->lastdropped = 0; + + /* + * MW: We don't actually perform the compression if we run on an + * uncompressible stream. + */ + if (!do_compression) { + cs->deny_compression = 1; + return AX25_P_IP; + } + /* We want to use the original packet as our compressed packet. + * (cp - new_seq) is the number of bytes we need for compressed + * sequence numbers. In addition we need one byte for the change + * mask, one for the connection id and two for the tcp checksum. + * So, (cp - new_seq) + 4 bytes of header are needed. + */ + deltaS = cp - new_seq; + skb_pull(skb, hlen); /* Strip TCP/IP headers */ + if (comp->xmit_current != cs->cs_this) { + cp = skb_push(skb, deltaS + 4); + *cp++ = changes | NEW_C; + *cp++ = cs->cs_this; + comp->xmit_current = cs->cs_this; + } else { + cp = skb_push(skb, deltaS + 3); + *cp++ = changes; + } + cp = put16(cp, (short) deltaA); /* Write TCP checksum */ + memcpy(cp, new_seq, deltaS); /* Write list of deltas */ + comp->sls_o_compressed++; + return AX25_P_VJCOMP; + + /* Update connection state cs & send uncompressed packet (i.e., + * a regular ip/tcp packet but with the 'conversation id' we hope + * to use on future compressed packets in the protocol field). + */ + uncompressed: + memcpy(&cs->cs_ip, iph, 20); + memcpy(&cs->cs_tcp, th, 20); + if (iph->ihl > 5) + memcpy(cs->cs_ipopt, iph + 1, ((iph->ihl) - 5) * 4); + if (th->doff > 5) + memcpy(cs->cs_tcpopt, th + 1, ((th->doff) - 5) * 4); + comp->xmit_current = cs->cs_this; + cs->lastdropped = 0; + + if (!do_compression) { + cs->deny_compression = 1; + return AX25_P_IP; + } + iph->protocol = cs->cs_this; + cs->deny_compression = 0; + comp->sls_o_uncompressed++; + return AX25_P_VJUNCOMP; +} + +int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb) +{ + register int changes; + long x; + register struct tcphdr *thp; + register struct iphdr *ip; + register struct axvj_cstate *cs; + int len, hdrlen; + + int isize = skb->len; + unsigned char *icp = skb->data; + unsigned char *cp = icp; + + /* We've got a compressed packet; read the change byte */ + comp->sls_i_compressed++; + if (isize < 3) { + comp->sls_i_error++; + return 0; + } + changes = *cp++; + if (changes & NEW_C) { + /* Make sure the state index is in range, then grab the state. + * If we have a good state index, clear the 'discard' flag. + */ + x = *cp++; /* Read conn index */ + if (x < 0 || x > comp->rslot_limit) + goto bad; + + comp->flags &= ~SLF_TOSS; + comp->recv_current = x; + } else { + /* this packet has an implicit state index. If we've + * had a line error since the last time we got an + * explicit state index, we have to toss the packet. */ + if (comp->flags & SLF_TOSS) { + comp->sls_i_tossed++; + return 0; + } + } + cs = &comp->rstate[comp->recv_current]; + thp = &cs->cs_tcp; + ip = &cs->cs_ip; + + if ((x = pull16(&cp)) == -1) { /* Read the TCP checksum */ + goto bad; + } + thp->check = htons(x); + + thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; +/* + * we can use the same number for the length of the saved header and + * the current one, because the packet wouldn't have been sent + * as compressed unless the options were the same as the previous one + */ + + hdrlen = ip->ihl * 4 + thp->doff * 4; + + switch (changes & SPECIALS_MASK) { + case SPECIAL_I: /* Echoed terminal traffic */ + { + register short i; + i = ntohs(ip->tot_len) - hdrlen; + thp->ack_seq = htonl(ntohl(thp->ack_seq) + i); + thp->seq = htonl(ntohl(thp->seq) + i); + } + break; + + case SPECIAL_D: /* Unidirectional data */ + thp->seq = htonl(ntohl(thp->seq) + + ntohs(ip->tot_len) - hdrlen); + break; + + default: + if (changes & NEW_U) { + thp->urg = 1; + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->urg_ptr = htons(x); + } else + thp->urg = 0; + if (changes & NEW_W) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->window = htons(ntohs(thp->window) + x); + } + if (changes & NEW_A) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->ack_seq = htonl(ntohl(thp->ack_seq) + x); + } + if (changes & NEW_S) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + thp->seq = htonl(ntohl(thp->seq) + x); + } + break; + } + if (changes & NEW_I) { + if ((x = decode(&cp)) == -1) { + goto bad; + } + ip->id = htons(ntohs(ip->id) + x); + } else + ip->id = htons(ntohs(ip->id) + 1); + + /* + * At this point, cp points to the first byte of data in the + * packet. Put the reconstructed TCP and IP headers back on the + * packet. Recalculate IP checksum (but not TCP checksum). + */ + + len = isize - (cp - icp); + if (len < 0) + goto bad; + len += hdrlen; + ip->tot_len = htons(len); + ip->check = 0; + + /* + * MW: + * we are working on sk_buffs here, so we can spare the memmove() + * and simply skb_push() the hdrlen + */ + + skb_push(skb, hdrlen - (cp - icp)); + + cp = icp = skb->data; + memcpy(cp, ip, 20); + cp += 20; + + if (ip->ihl > 5) { + memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); + cp += (ip->ihl - 5) * 4; + } + put_unaligned(ip_fast_csum(icp, ip->ihl), + &((struct iphdr *) icp)->check); + + memcpy(cp, thp, 20); + cp += 20; + + if (thp->doff > 5) { + memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); + cp += ((thp->doff) - 5) * 4; + } + return len; + bad: + comp->sls_i_error++; + return axhc_toss(comp); +} + + +int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb) +{ + register struct axvj_cstate *cs; + unsigned ihl; + + unsigned char index; + + int isize = skb->len; + unsigned char *icp = skb->data; + + if (isize < 20) { + /* The packet is shorter than a legal IP header */ + printk(KERN_DEBUG "axhc_remember: short packet from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_runt++; + return axhc_toss(comp); + } + /* Peek at the IP header's IHL field to find its length */ + ihl = icp[0] & 0xf; + if (ihl < 20 / 4) { + /* The IP header length field is too small */ + printk(KERN_DEBUG "axhc_remember: ihl too small from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_runt++; + return axhc_toss(comp); + } + index = icp[9]; + icp[9] = IPPROTO_TCP; + + if (ip_fast_csum(icp, ihl)) { + /* Bad IP header checksum; discard */ + printk(KERN_DEBUG "axhc_remember: bad ip header checksum from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_badcheck++; + return axhc_toss(comp); + } + if (index > comp->rslot_limit) { + printk(KERN_DEBUG "axhc_remember: illegal slot from %d.%d.%d.%d\n", NIPQUAD(((struct iphdr*)icp)->saddr)); + comp->sls_i_error++; + return axhc_toss(comp); + } + /* Update local state */ + cs = &comp->rstate[comp->recv_current = index]; + comp->flags &= ~SLF_TOSS; + memcpy(&cs->cs_ip, icp, 20); + memcpy(&cs->cs_tcp, icp + ihl * 4, 20); + if (ihl > 5) + memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); + if (cs->cs_tcp.doff > 5) + memcpy(cs->cs_tcpopt, icp + ihl * 4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); + cs->cs_hsize = ihl * 2 + cs->cs_tcp.doff * 2; + /* Put headers back on packet + * Neither header checksum is recalculated + */ + comp->sls_i_uncompressed++; + return isize; +} + +static int +axhc_toss(struct axvj_slcomp *comp) +{ + if ( comp == NULL ) + return 0; + + comp->flags |= SLF_TOSS; + return 0; +} + +#endif diff --git a/net/ax25/ax25_vj.h b/net/ax25/ax25_vj.h new file mode 100644 index 000000000..da059392c --- /dev/null +++ b/net/ax25/ax25_vj.h @@ -0,0 +1,73 @@ +/* + * Interface declaration of the VJ compression code + * + * Matthias Welwarsky (DG2FEF) 05/25/98 + */ + +#ifndef _AX25_VJ_H +#define _AX25_VJ_H + +#include <net/ip.h> +#include <net/tcp.h> + +/* + * MW: copied this into ax25.h to unclobber the original struct cstate. + * "state" data for each active tcp conversation on the "wire". This is + * basically a copy of the entire IP/TCP header from the last packet + * we saw from the conversation together with a small identifier + * the transmit & receive ends of the line use to locate saved header. + */ +struct axvj_cstate { + unsigned char cs_this; /* connection id number (xmit) */ + struct axvj_cstate *next; /* next in ring (xmit) */ + struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */ + struct tcphdr cs_tcp; + unsigned char cs_ipopt[64]; + unsigned char cs_tcpopt[64]; + int cs_hsize; + unsigned char deny_compression; /* MW: for vj compression via AX.25 */ + unsigned long lastdropped; +}; + +/* + * all the state data for one VC (we need one of these per VC). + */ +struct axvj_slcomp { + struct axvj_cstate *tstate; /* transmit connection states (array)*/ + struct axvj_cstate *rstate; /* receive connection states (array)*/ + + unsigned char tslot_limit; /* highest transmit slot id (0-l)*/ + unsigned char rslot_limit; /* highest receive slot id (0-l)*/ + + unsigned char xmit_oldest; /* oldest xmit in ring */ + unsigned char xmit_current; /* most recent xmit id */ + unsigned char recv_current; /* most recent rcvd id */ + + unsigned char flags; +#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */ + + int sls_o_nontcp; /* outbound non-TCP packets */ + int sls_o_tcp; /* outbound TCP packets */ + int sls_o_uncompressed; /* outbound uncompressed packets */ + int sls_o_compressed; /* outbound compressed packets */ + int sls_o_searches; /* searches for connection state */ + int sls_o_misses; /* times couldn't find conn. state */ + + int sls_i_uncompressed; /* inbound uncompressed packets */ + int sls_i_compressed; /* inbound compressed packets */ + int sls_i_error; /* inbound error packets */ + int sls_i_tossed; /* inbound packets tossed because of error */ + + int sls_i_runt; + int sls_i_badcheck; +}; + +extern struct axvj_slcomp* axhc_init(int, int); +extern void axhc_free(struct axvj_slcomp*); +extern int axhc_compress(struct axvj_slcomp *comp, struct sk_buff *skb, int do_compression); +extern void ax25_vj_init(void); +extern void ax25_vj_cleanup(void); +extern int axhc_uncompress(struct axvj_slcomp *comp, struct sk_buff *skb); +extern int axhc_remember(struct axvj_slcomp *comp, struct sk_buff *skb); + +#endif diff --git a/net/ax25/sysctl_net_ax25.c b/net/ax25/sysctl_net_ax25.c index e6016f93d..416f4cc80 100644 --- a/net/ax25/sysctl_net_ax25.c +++ b/net/ax25/sysctl_net_ax25.c @@ -1,14 +1,31 @@ -/* -*- linux-c -*- - * sysctl_net_ax25.c: sysctl interface to net AX.25 subsystem. +/* + * sysctl_net_ax25.c: sysctl interface for NEW-AX.25 * - * Begun April 1, 1996, Mike Shaver. - * Added /proc/sys/net/ax25 directory entry (empty =) ). [MS] + * Authors: Jens David (DG1KJD), Matthias Welwarsky (DG2FEF), Mike Shaver + * + * Comment: Needs cleanup, most parameters are not needed and/or should be au + * adjusting. + * + * Changelog: + * + * License: This module is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. */ -#include <linux/config.h> #include <linux/mm.h> #include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/if_arp.h> #include <net/ax25.h> +#include <net/ax25dev.h> +#include "ax25_ddi.h" + +int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp); +int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context); static int min_ipdefmode[] = {0}, max_ipdefmode[] = {1}; static int min_axdefmode[] = {0}, max_axdefmode[] = {1}; @@ -16,22 +33,29 @@ static int min_backoff[] = {0}, max_backoff[] = {2}; static int min_conmode[] = {0}, max_conmode[] = {2}; static int min_window[] = {1}, max_window[] = {7}; static int min_ewindow[] = {1}, max_ewindow[] = {63}; -static int min_t1[] = {1}, max_t1[] = {30 * HZ}; -static int min_t2[] = {1}, max_t2[] = {20 * HZ}; -static int min_t3[] = {0}, max_t3[] = {3600 * HZ}; -static int min_idle[] = {0}, max_idle[] = {65535 * HZ}; +static int min_t1[] = {1}, max_t1[] = {30 * AX25_SLOWHZ * 10}; +static int min_t2[] = {0}, max_t2[] = {10 * AX25_SLOWHZ * 10}; +static int min_t3[] = {0}, max_t3[] = {3600 * AX25_SLOWHZ * 10}; +static int min_idle[] = {0}, max_idle[] = {65535 * AX25_SLOWHZ * 10}; static int min_n2[] = {1}, max_n2[] = {31}; static int min_paclen[] = {1}, max_paclen[] = {512}; -static int min_proto[] = {0}, max_proto[] = {3}; -static int min_ds_timeout[] = {0}, max_ds_timeout[] = {65535 * HZ}; +static int min_protocol[] = {0}, max_protocol[] = {2}; +static int min_dama_slave_timeout[] = {0}, max_dama_slave_timeout[] = {3600 * AX25_SLOWHZ}; +static int min_media_duplex[] = {0}, max_media_duplex[] = {2}; +static int min_media_txdelay[] = {0}, max_media_txdelay[] = {10000}; +static int min_media_txtail[] = {0}, max_media_txtail[] = {10000}; +static int min_media_txbitrate[] = {0}, max_media_txbitrate[] = {80000000}; +static int min_media_rxbitrate[] = {0}, max_media_rxbitrate[] = {80000000}; +static int min_media_slottime[] = {0}, max_media_slottime[] = {1000}; +static int min_media_ppersist[] = {0}, max_media_ppersist[] = {255}; +static int min_media_autoadj[] = {0}, max_media_autoadj[] = {1}; static struct ctl_table_header *ax25_table_header; -static ctl_table *ax25_table; -static int ax25_table_size; +static ctl_table ax25_table[AX25_MAX_DEVICES + 1]; static ctl_table ax25_dir_table[] = { - {NET_AX25, "ax25", NULL, 0, 0555, NULL}, + {NET_AX25, "ax25", NULL, 0, 0555, ax25_table}, {0} }; @@ -92,71 +116,121 @@ static const ctl_table ax25_param_table[] = { {NET_AX25_PROTOCOL, "protocol", NULL, sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_proto, &max_proto}, + &min_protocol, &max_protocol}, {NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout", NULL, sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL, - &min_ds_timeout, &max_ds_timeout}, + &min_dama_slave_timeout, &max_dama_slave_timeout}, + {NET_AX25_MEDIA_DUPLEX, "media_duplex", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_duplex, &max_media_duplex}, + {NET_AX25_MEDIA_TXDELAY, "media_txdelay", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_txdelay, &max_media_txdelay}, + {NET_AX25_MEDIA_TXTAIL, "media_txtail", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_txtail, &max_media_txtail}, + {NET_AX25_MEDIA_TXBITRATE, "media_txbitrate", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_txbitrate, &max_media_txbitrate}, + {NET_AX25_MEDIA_RXBITRATE, "media_rxbitrate", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_rxbitrate, &max_media_rxbitrate}, + {NET_AX25_MEDIA_SLOTTIME, "media_slottime", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_slottime, &max_media_slottime}, + {NET_AX25_MEDIA_PPERSISTENCE, "media_ppersistence", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_ppersist, &max_media_ppersist}, + {NET_AX25_MEDIA_AUTO_ADJUST, "media_autoadjust", + NULL, sizeof(int), 0644, NULL, + &ax25_proc_dointvec_minmax, &ax25_sysctl_intvec, NULL, + &min_media_autoadj, &max_media_autoadj}, {0} /* that's all, folks! */ }; -void ax25_register_sysctl(void) +int ax25_proc_dointvec_minmax(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) { - ax25_dev *ax25_dev; - int n, k; + int id = table->ctl_name; + int ret; + int oldval, newval; + struct net_device *dev; + + oldval = * (int *) table->data; + ret = proc_dointvec_minmax(table, write, filp, buffer, lenp); + newval = * (int *) table->data; + if (oldval != newval) { + dev = dev_get(table->de->parent->name); + if (!dev) return ret; /* paranoia */ + ax25_notify_dispatcher(dev, id-1, oldval, newval); + } + return ret; +} + +int ax25_sysctl_intvec(ctl_table *table, int *name, int nlen, void *oldval, + size_t *oldlenp, void *newval, size_t newlen, void **context) +{ + int id = table->ctl_name; + int ret; + struct net_device *dev; + + ret = sysctl_intvec(table, name, nlen, oldval, oldlenp, newval, + newlen, context); + if (* (int *) oldval != * (int *) newval) { + dev = dev_get(table->de->parent->name); + if (!dev) return ret; /* paranoia */ + ax25_notify_dispatcher(dev, id-1, * (int *) oldval, * (int *) newval); + } + return ret; +} - for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) - ax25_table_size += sizeof(ctl_table); +void ax25_register_sysctl(void) +{ + struct net_device *dev; + struct ax25_dev *ax25_device; - if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL) - return; + int n, k, i; - memset(ax25_table, 0x00, ax25_table_size); + memset(ax25_table, 0x00, (AX25_MAX_DEVICES + 1) * sizeof(ctl_table)); - for (n = 0, ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next) { - ctl_table *child = kmalloc(sizeof(ax25_param_table), GFP_ATOMIC); - if (!child) { - while (n--) - kfree(ax25_table[n].child); - kfree(ax25_table); - return; - } - memcpy(child, ax25_param_table, sizeof(ax25_param_table)); - ax25_table[n].child = ax25_dev->systable = child; - ax25_table[n].ctl_name = n + 1; - ax25_table[n].procname = ax25_dev->dev->name; - ax25_table[n].mode = 0555; + n = 0; -#ifndef CONFIG_AX25_DAMA_SLAVE - /* - * We do not wish to have a representation of this parameter - * in /proc/sys/ when configured *not* to include the - * AX.25 DAMA slave code, do we? - */ + for (i = 0; i < AX25_MAX_DEVICES; i++) { + if ((dev = ax25_devices[i]) != NULL) { + ax25_device = AX25_PTR(dev); + if (n <= AX25_MAX_DEVICES) { + ax25_table[n].ctl_name = n + 1; + ax25_table[n].procname = dev->name; + ax25_table[n].data = NULL; + ax25_table[n].maxlen = 0; + ax25_table[n].mode = 0555; + ax25_table[n].child = ax25_device->systable; + ax25_table[n].proc_handler = NULL; - child[AX25_VALUES_DS_TIMEOUT].procname = NULL; -#endif + memcpy(ax25_device->systable, ax25_param_table, sizeof(ax25_device->systable)); - child[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ + ax25_device->systable[AX25_MAX_VALUES].ctl_name = 0; /* just in case... */ - for (k = 0; k < AX25_MAX_VALUES; k++) - child[k].data = &ax25_dev->values[k]; + for (k = 0; k < AX25_MAX_VALUES; k++) + ax25_device->systable[k].data = &ax25_device->values[k]; - n++; + n++; + } + } } - ax25_dir_table[0].child = ax25_table; - ax25_table_header = register_sysctl_table(ax25_root_table, 1); } void ax25_unregister_sysctl(void) { - ctl_table *p; unregister_sysctl_table(ax25_table_header); - - ax25_dir_table[0].child = NULL; - for (p = ax25_table; p->ctl_name; p++) - kfree(p->child); - kfree(ax25_table); } diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index c43892ea5..e3f98e671 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -257,33 +257,6 @@ static int arp_constructor(struct neighbour *neigh) in old paradigm. */ -#if 1 - /* So... these "amateur" devices are hopeless. - The only thing, that I can say now: - It is very sad that we need to keep ugly obsolete - code to make them happy. - - They should be moved to more reasonable state, now - they use rebuild_header INSTEAD OF hard_start_xmit!!! - Besides that, they are sort of out of date - (a lot of redundant clones/copies, useless in 2.1), - I wonder why people believe that they work. - */ - switch (dev->type) { - default: - break; - case ARPHRD_ROSE: -#if defined(CONFIG_AX25) || defined(CONFIG_AX25_MODULE) - case ARPHRD_AX25: -#if defined(CONFIG_NETROM) || defined(CONFIG_NETROM_MODULE) - case ARPHRD_NETROM: -#endif - neigh->ops = &arp_broken_ops; - neigh->output = neigh->ops->output; - return 0; -#endif - ;} -#endif if (neigh->type == RTN_MULTICAST) { neigh->nud_state = NUD_NOARP; arp_mc_map(addr, neigh->ha, dev, 1); diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c index a4b89d294..3ba0b8c81 100644 --- a/net/netrom/af_netrom.c +++ b/net/netrom/af_netrom.c @@ -47,6 +47,7 @@ #include <linux/net.h> #include <linux/stat.h> #include <net/ax25.h> +#include <net/ax25_uid.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -347,6 +348,8 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; + char devname[IFNAMSIZ]; + struct net_device *dev; int opt; if (level != SOL_NETROM) @@ -389,6 +392,21 @@ static int nr_setsockopt(struct socket *sock, int level, int optname, sk->protinfo.nr->idle = opt * 60 * HZ; return 0; + case SO_BINDTODEVICE: + if (optlen > IFNAMSIZ) optlen = IFNAMSIZ; + if (copy_from_user(devname, optval, optlen)) + return -EFAULT; + + dev = dev_get_by_name(devname); + if (dev == NULL) return -ENODEV; + + if (sk->type == SOCK_SEQPACKET && + (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) + return -EADDRNOTAVAIL; + + sk->protinfo.nr->device=dev; + return 0; + default: return -ENOPROTOOPT; } @@ -398,15 +416,24 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; + void *valptr; int val = 0; - int len; + int maxlen, length; if (level != SOL_NETROM) return -ENOPROTOOPT; - if (get_user(len, optlen)) + if (get_user(maxlen, optlen)) return -EFAULT; + if (maxlen < 1) + return -EFAULT; + + valptr = (void *) &val; + length = min(maxlen, sizeof(int)); + switch (optname) { case NETROM_T1: val = sk->protinfo.nr->t1 / HZ; @@ -428,16 +455,33 @@ static int nr_getsockopt(struct socket *sock, int level, int optname, val = sk->protinfo.nr->idle / (60 * HZ); break; + case SO_BINDTODEVICE: + dev = sk->protinfo.nr->device; + + if (dev != NULL) { + strncpy(devname, dev->name, IFNAMSIZ); + length = min(strlen(dev->name)+1, maxlen); + devname[length-1] = '\0'; + } else { + *devname = '\0'; + length = 1; + } + + valptr = (void *) devname; + break; + + default: return -ENOPROTOOPT; } - len = min(len, sizeof(int)); + if (put_user(length, optlen)) + return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_user(optval, valptr, length)) return -EFAULT; - return copy_to_user(optval, &val, len) ? -EFAULT : 0; + return 0; } static int nr_listen(struct socket *sock, int backlog) @@ -589,13 +633,12 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct sock *sk = sock->sk; struct full_sockaddr_ax25 *addr = (struct full_sockaddr_ax25 *)uaddr; struct net_device *dev; - ax25_address *user, *source; + ax25_address *user = NULL, *source = NULL; if (sk->zapped == 0) return -EINVAL; - if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct -full_sockaddr_ax25)) + if (addr_len < sizeof(struct sockaddr_ax25) || addr_len > sizeof(struct full_sockaddr_ax25)) return -EINVAL; if (addr_len < (addr->fsa_ax25.sax25_ndigis * sizeof(ax25_address) + sizeof(struct sockaddr_ax25))) @@ -604,32 +647,42 @@ full_sockaddr_ax25)) if (addr->fsa_ax25.sax25_family != AF_NETROM) return -EINVAL; - if ((dev = nr_dev_get(&addr->fsa_ax25.sax25_call)) == NULL) { - SOCK_DEBUG(sk, "NET/ROM: bind failed: invalid node callsign\n"); - return -EADDRNOTAVAIL; - } - /* - * Only the super user can set an arbitrary user callsign. + * User did not set the interfave with SO_BINDTODEVICE, + * use compatibility code. */ - if (addr->fsa_ax25.sax25_ndigis == 1) { - if (!capable(CAP_NET_BIND_SERVICE)) - return -EACCES; - sk->protinfo.nr->user_addr = addr->fsa_digipeater[0]; - sk->protinfo.nr->source_addr = addr->fsa_ax25.sax25_call; - } else { - source = &addr->fsa_ax25.sax25_call; - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) - return -EPERM; - user = source; + dev = sk->protinfo.nr->device; + if (dev == NULL) { + if (addr_len > sizeof(struct sockaddr_ax25) && addr->fsax25_ndigis == 1) { + /* device callsign provided */ + if(ax25cmp(&addr->fsa_digipeater[0], &null_ax25_address) && + (dev = nr_dev_get(&addr->fsa_digipeater[0])) == NULL) + return -EADDRNOTAVAIL; + source = &addr->fsa_digipeater[0]; + } else { + if ((dev = nr_dev_get(&addr->fsax25_call)) == NULL) + return -EADDRNOTAVAIL; + source = &addr->fsa_ax25.sax25_call; } - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; + sk->protinfo.nr->device = dev; + } else { + source = (ax25_address *) dev->dev_addr; } + if (!capable(CAP_NET_BIND_SERVICE)) + { + /* FIXME: should not be coupled with AX.25 */ + user = ax25_find_match_for_uid(current->euid, &addr->fsax25_call, dev->name); + if (user == NULL && ax25_uid_policy) + return -EACCES; + } + + if (user == NULL) user = &addr->fsax25_call; + sk->protinfo.nr->user_addr = *user; + sk->protinfo.nr->source_addr = *source; + sk->protinfo.nr->device = dev; nr_insert_socket(sk); @@ -643,7 +696,6 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, { struct sock *sk = sock->sk; struct sockaddr_ax25 *addr = (struct sockaddr_ax25 *)uaddr; - ax25_address *user, *source = NULL; struct net_device *dev; if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { @@ -668,26 +720,8 @@ static int nr_connect(struct socket *sock, struct sockaddr *uaddr, if (addr->sax25_family != AF_NETROM) return -EINVAL; - if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ - sk->zapped = 0; - - if ((dev = nr_dev_first()) == NULL) - return -ENETUNREACH; - - source = (ax25_address *)dev->dev_addr; - - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_ADMIN)) - return -EPERM; - user = source; - } - - sk->protinfo.nr->user_addr = *user; - sk->protinfo.nr->source_addr = *source; - sk->protinfo.nr->device = dev; - - nr_insert_socket(sk); /* Finish the bind */ - } + if (sk->zapped) + return -EADDRNOTAVAIL; sk->protinfo.nr->dest_addr = addr->sax25_call; diff --git a/net/netrom/nr_loopback.c b/net/netrom/nr_loopback.c index 5290ae024..519ca55da 100644 --- a/net/netrom/nr_loopback.c +++ b/net/netrom/nr_loopback.c @@ -44,10 +44,10 @@ int nr_loopback_queue(struct sk_buff *skb) { struct sk_buff *skbn; - if ((skbn = alloc_skb(skb->len, GFP_ATOMIC)) != NULL) { - memcpy(skb_put(skbn, skb->len), skb->data, skb->len); - skbn->h.raw = skbn->data; + skbn = skb_clone(skb, GFP_ATOMIC); + kfree(skb); + if (skbn != NULL) { skb_queue_tail(&loopback_queue, skbn); if (!nr_loopback_running()) diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index ec0578b51..e6793fb0b 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -60,8 +60,8 @@ static void nr_remove_neigh(struct nr_neigh *); * Add a new route to a node, and in the process add the node and the * neighbour if it is new. */ -static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax25, - ax25_digi *ax25_digi, struct net_device *dev, int quality, int obs_count) +static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_addr_t *ax25_addr, + struct net_device *dev, int quality, int obs_count) { struct nr_node *nr_node; struct nr_neigh *nr_neigh; @@ -77,7 +77,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 break; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; /* @@ -106,8 +106,8 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *ax25; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address *) dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = sysctl_netrom_default_path_quality; @@ -116,13 +116,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); - } + /* FIXME: not SMP safe! */ save_flags(flags); cli(); @@ -133,7 +127,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 restore_flags(flags); } - if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) + if (quality != 0 && ax25cmp(nr, &ax25_addr->dest) == 0 && !nr_neigh->locked) nr_neigh->quality = quality; if (nr_node == NULL) { @@ -294,8 +288,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) if ((s = nr_neigh_list) == nr_neigh) { nr_neigh_list = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -304,8 +296,6 @@ static void nr_remove_neigh(struct nr_neigh *nr_neigh) if (s->next == nr_neigh) { s->next = nr_neigh->next; restore_flags(flags); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); kfree(nr_neigh); return; } @@ -333,7 +323,7 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n if (nr_node == NULL) return -EINVAL; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(neighbour, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -370,13 +360,13 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n /* * Lock a neighbour with a quality. */ -static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net_device *dev, unsigned int quality) +static int nr_add_neigh(ax25_addr_t *ax25_addr, struct net_device *dev, unsigned int quality) { struct nr_neigh *nr_neigh; unsigned long flags; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) { + if (ax25cmp(&ax25_addr->dest, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) { nr_neigh->quality = quality; nr_neigh->locked = 1; return 0; @@ -386,8 +376,8 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - nr_neigh->callsign = *callsign; - nr_neigh->digipeat = NULL; + nr_neigh->addr = *ax25_addr; + nr_neigh->addr.src = *((ax25_address *) dev->dev_addr); nr_neigh->ax25 = NULL; nr_neigh->dev = dev; nr_neigh->quality = quality; @@ -396,14 +386,6 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; - if (ax25_digi != NULL && ax25_digi->ndigi > 0) { - if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { - kfree(nr_neigh); - return -ENOMEM; - } - memcpy(nr_neigh->digipeat, ax25_digi, sizeof(ax25_digi)); - } - save_flags(flags); cli(); @@ -424,7 +406,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned struct nr_neigh *nr_neigh; for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) + if (ax25cmp(callsign, &nr_neigh->addr.dest) == 0 && nr_neigh->dev == dev) break; if (nr_neigh == NULL) return -EINVAL; @@ -586,31 +568,26 @@ struct net_device *nr_dev_get(ax25_address *addr) for (dev = dev_base; dev != NULL; dev = dev->next) { if ((dev->flags & IFF_UP) && dev->type == ARPHRD_NETROM && ax25cmp(addr, (ax25_address *)dev->dev_addr) == 0) { dev_hold(dev); - goto out; + break; } } -out: + read_unlock(&dev_base_lock); return dev; } -static ax25_digi *nr_call_to_digi(int ndigis, ax25_address *digipeaters) +static ax25_addr_t *nr_neigh_to_addr(ax25_address *callsign, int ndigis, ax25_address *digipeater) { - static ax25_digi ax25_digi; + static ax25_addr_t ax25_addr; int i; - if (ndigis == 0) - return NULL; + ax25_addr.dest = *callsign; + ax25_addr.dcount = ndigis; - for (i = 0; i < ndigis; i++) { - ax25_digi.calls[i] = digipeaters[i]; - ax25_digi.repeated[i] = 0; - } + for (i = 0; i < ndigis; i++) + ax25_addr.digipeater[i] = digipeater[i]; - ax25_digi.ndigi = ndigis; - ax25_digi.lastrepeat = -1; - - return &ax25_digi; + return &ax25_addr; } /* @@ -632,16 +609,13 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) return -EINVAL; switch (nr_route.type) { case NETROM_NODE: - return nr_add_node(&nr_route.callsign, - nr_route.mnemonic, - &nr_route.neighbour, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), + return nr_add_node(&nr_route.callsign, nr_route.mnemonic, + nr_neigh_to_addr(&nr_route.neighbour, nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality, nr_route.obs_count); case NETROM_NEIGH: - return nr_add_neigh(&nr_route.callsign, - nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), - dev, nr_route.quality); + return nr_add_neigh(nr_neigh_to_addr(&nr_route.callsign, nr_route.ndigis, nr_route.digipeaters), + dev, nr_route.quality); default: return -EINVAL; } @@ -713,8 +687,8 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) nr_dest = (ax25_address *)(skb->data + 7); if (ax25 != NULL) - nr_add_node(nr_src, "", &ax25->dest_addr, ax25->digipeat, - ax25->ax25_dev->dev, 0, sysctl_netrom_obsolescence_count_initialiser); + nr_add_node(nr_src, "", &ax25->addr, ax25->device, 0, + sysctl_netrom_obsolescence_count_initialiser); if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ if (ax25 == NULL) /* Its from me */ @@ -745,7 +719,13 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + /* The next line is dirty hotfix. There is a bug in nr_add_neigh() - + * src callsign is taken from dev->dev_addr of device on which the + * broadcast was received. But this is AX25 device (and callsign), + * not the netrom device, so the callsign is bad. + */ + nr_neigh->addr.src = *((ax25_address*)dev->dev_addr); + nr_neigh->ax25 = ax25_send_frame(skb, 256, &nr_neigh->addr, nr_neigh->dev); return (nr_neigh->ax25 != NULL); } @@ -814,16 +794,16 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d", nr_neigh->number, - ax2asc(&nr_neigh->callsign), + ax2asc(&nr_neigh->addr.dest), nr_neigh->dev ? nr_neigh->dev->name : "???", nr_neigh->quality, nr_neigh->locked, nr_neigh->count, nr_neigh->failed); - if (nr_neigh->digipeat != NULL) { - for (i = 0; i < nr_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->digipeat->calls[i])); + if (nr_neigh->addr.dcount) { + for (i = 0; i < nr_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&nr_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n"); diff --git a/net/netsyms.c b/net/netsyms.c index 8adcef849..5d56062f3 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -246,6 +246,7 @@ EXPORT_SYMBOL(ip_dev_find); EXPORT_SYMBOL(inetdev_by_index); EXPORT_SYMBOL(in_dev_finish_destroy); EXPORT_SYMBOL(ip_defrag); +EXPORT_SYMBOL(ip_options_fragment); /* Route manipulation */ EXPORT_SYMBOL(ip_rt_ioctl); diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c index a92bf86f5..07acd3946 100644 --- a/net/rose/af_rose.c +++ b/net/rose/af_rose.c @@ -39,6 +39,7 @@ #include <linux/net.h> #include <linux/stat.h> #include <net/ax25.h> +#include <net/ax25_uid.h> #include <linux/inet.h> #include <linux/netdevice.h> #include <linux/if_arp.h> @@ -404,6 +405,8 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; int opt; if (level != SOL_ROSE) @@ -454,6 +457,22 @@ static int rose_setsockopt(struct socket *sock, int level, int optname, sk->protinfo.rose->qbitincl = opt ? 1 : 0; return 0; + + case SO_BINDTODEVICE: + if (optlen > IFNAMSIZ) optlen = IFNAMSIZ; + if (copy_from_user(devname, optval, optlen)) + return -EFAULT; + + dev = dev_get_by_name(devname); + if (dev == NULL) return -ENODEV; + + if (sk->type == SOCK_SEQPACKET && + (sock->state != SS_UNCONNECTED || sk->state == TCP_LISTEN)) + return -EADDRNOTAVAIL; + + sk->protinfo.rose->device = dev; + return 0; + default: return -ENOPROTOOPT; } @@ -463,14 +482,23 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen) { struct sock *sk = sock->sk; + struct net_device *dev; + char devname[IFNAMSIZ]; + void *valptr; int val = 0; - int len; + int maxlen, length; if (level != SOL_ROSE) return -ENOPROTOOPT; - if (get_user(len, optlen)) + if (get_user(maxlen, optlen)) return -EFAULT; + + if (maxlen < 1) + return -EFAULT; + + valptr = (void *) &val; + length = min(maxlen, sizeof(int)); switch (optname) { case ROSE_DEFER: @@ -501,16 +529,32 @@ static int rose_getsockopt(struct socket *sock, int level, int optname, val = sk->protinfo.rose->qbitincl; break; + case SO_BINDTODEVICE: + dev = sk->protinfo.rose->device; + + if (dev != NULL) { + strncpy(devname, dev->name, IFNAMSIZ); + length = min(strlen(dev->name)+1, maxlen); + devname[length-1] = '\0'; + } else { + *devname = '\0'; + length = 1; + } + + valptr = (void *) devname; + break; + default: return -ENOPROTOOPT; } - len = min(len, sizeof(int)); + if (put_user(length, optlen)) + return -EFAULT; - if (put_user(len, optlen)) + if (copy_to_user(optval, valptr, length)) return -EFAULT; - return copy_to_user(optval, &val, len) ? -EFAULT : 0; + return 0; } static int rose_listen(struct socket *sock, int backlog) @@ -666,7 +710,8 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct sock *sk = sock->sk; struct sockaddr_rose *addr = (struct sockaddr_rose *)uaddr; struct net_device *dev; - ax25_address *user, *source; + ax25_address *user = NULL; + rose_address *source = NULL; int n; if (sk->zapped == 0) @@ -683,21 +728,36 @@ static int rose_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if (addr->srose_ndigis > ROSE_MAX_DIGIS) return -EINVAL; + /* + * User did not set interface with SO_BINDTODEVICE + * thus we'll use the compatibility code + */ + + dev = sk->protinfo.rose->device; + if (dev == NULL) { + if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { + SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); + return -EADDRNOTAVAIL; + } - if ((dev = rose_dev_get(&addr->srose_addr)) == NULL) { - SOCK_DEBUG(sk, "ROSE: bind failed: invalid address\n"); - return -EADDRNOTAVAIL; + source = &addr->srose_addr; + sk->protinfo.rose->device = dev; + } else { + source = (rose_address *) dev->dev_addr; } - source = &addr->srose_call; - - if ((user = ax25_findbyuid(current->euid)) == NULL) { - if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) + /* root can do whatever (s)he likes, but anyone else... */ + if (!capable(CAP_NET_BIND_SERVICE)) + { + /* FIXME: strictly speaking this has nothing to do with AX.25 */ + user = ax25_find_match_for_uid(current->euid, &addr->srose_call, dev->name); + if (user == NULL && ax25_uid_policy) return -EACCES; - user = source; } - sk->protinfo.rose->source_addr = addr->srose_addr; + if (user == NULL) user = &addr->srose_call; + + sk->protinfo.rose->source_addr = *source; sk->protinfo.rose->source_call = *user; sk->protinfo.rose->device = dev; sk->protinfo.rose->source_ndigis = addr->srose_ndigis; @@ -766,21 +826,8 @@ static int rose_connect(struct socket *sock, struct sockaddr *uaddr, int addr_le if ((sk->protinfo.rose->lci = rose_new_lci(sk->protinfo.rose->neighbour)) == 0) return -ENETUNREACH; - if (sk->zapped) { /* Must bind first - autobinding in this may or may not work */ - sk->zapped = 0; - - if ((dev = rose_dev_first()) == NULL) - return -ENETUNREACH; - - if ((user = ax25_findbyuid(current->euid)) == NULL) - return -EINVAL; - - memcpy(&sk->protinfo.rose->source_addr, dev->dev_addr, ROSE_ADDR_LEN); - sk->protinfo.rose->source_call = *user; - sk->protinfo.rose->device = dev; - - rose_insert_socket(sk); /* Finish the bind */ - } + if (sk->zapped) + return -EINVAL; sk->protinfo.rose->dest_addr = addr->srose_addr; sk->protinfo.rose->dest_call = addr->srose_call; diff --git a/net/rose/rose_link.c b/net/rose/rose_link.c index 2c793e4e6..a4e5f71bd 100644 --- a/net/rose/rose_link.c +++ b/net/rose/rose_link.c @@ -104,14 +104,12 @@ static void rose_t0timer_expiry(unsigned long param) */ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_send_frame(skb, 260, rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_send_frame(skb, 260, &neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } @@ -123,14 +121,12 @@ static int rose_send_frame(struct sk_buff *skb, struct rose_neigh *neigh) */ static int rose_link_up(struct rose_neigh *neigh) { - ax25_address *rose_call; - if (ax25cmp(&rose_callsign, &null_ax25_address) == 0) - rose_call = (ax25_address *)neigh->dev->dev_addr; + neigh->addr.src = *((ax25_address *) neigh->dev->dev_addr); else - rose_call = &rose_callsign; + neigh->addr.src = rose_callsign; - neigh->ax25 = ax25_find_cb(rose_call, &neigh->callsign, neigh->digipeat, neigh->dev); + neigh->ax25 = ax25_find_cb(&neigh->addr, neigh->dev); return (neigh->ax25 != NULL); } diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c index 1bedfdf0d..4de549586 100644 --- a/net/rose/rose_route.c +++ b/net/rose/rose_route.c @@ -77,23 +77,23 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) { if ((rose_neigh = kmalloc(sizeof(*rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_neigh->callsign = rose_route->neighbour; - rose_neigh->digipeat = NULL; - rose_neigh->ax25 = NULL; - rose_neigh->dev = dev; - rose_neigh->count = 0; - rose_neigh->use = 0; - rose_neigh->dce_mode = 0; - rose_neigh->loopback = 0; - rose_neigh->number = rose_neigh_no++; - rose_neigh->restarted = 0; + rose_neigh->addr.dest = rose_route->neighbour; + rose_neigh->addr.dcount = 0; + rose_neigh->ax25 = NULL; + rose_neigh->dev = dev; + rose_neigh->count = 0; + rose_neigh->use = 0; + rose_neigh->dce_mode = 0; + rose_neigh->loopback = 0; + rose_neigh->number = rose_neigh_no++; + rose_neigh->restarted = 0; skb_queue_head_init(&rose_neigh->queue); @@ -101,18 +101,11 @@ static int rose_add_node(struct rose_route_struct *rose_route, struct net_device init_timer(&rose_neigh->t0timer); if (rose_route->ndigis != 0) { - if ((rose_neigh->digipeat = kmalloc(sizeof(ax25_digi), GFP_KERNEL)) == NULL) { - kfree(rose_neigh); - return -ENOMEM; - } - - rose_neigh->digipeat->ndigi = rose_route->ndigis; - rose_neigh->digipeat->lastrepeat = -1; + rose_neigh->addr.dcount = rose_route->ndigis; + rose_neigh->addr.lastrepeat = -1; - for (i = 0; i < rose_route->ndigis; i++) { - rose_neigh->digipeat->calls[i] = rose_route->digipeaters[i]; - rose_neigh->digipeat->repeated[i] = 0; - } + for (i = 0; i < rose_route->ndigis; i++) + rose_neigh->addr.digipeater[i] = rose_route->digipeaters[i]; } save_flags(flags); cli(); @@ -234,8 +227,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) if ((s = rose_neigh_list) == rose_neigh) { rose_neigh_list = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -244,8 +235,6 @@ static void rose_remove_neigh(struct rose_neigh *rose_neigh) if (s->next == rose_neigh) { s->next = rose_neigh->next; restore_flags(flags); - if (rose_neigh->digipeat != NULL) - kfree(rose_neigh->digipeat); kfree(rose_neigh); return; } @@ -309,7 +298,7 @@ static int rose_del_node(struct rose_route_struct *rose_route, struct net_device if (rose_node->loopback) return -EINVAL; for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&rose_route->neighbour, &rose_neigh->callsign) == 0 && rose_neigh->dev == dev) + if (ax25cmp(&rose_route->neighbour, &rose_neigh->addr.dest) == 0 && rose_neigh->dev == dev) break; if (rose_neigh == NULL) return -EINVAL; @@ -353,16 +342,16 @@ int rose_add_loopback_neigh(void) if ((rose_loopback_neigh = kmalloc(sizeof(struct rose_neigh), GFP_ATOMIC)) == NULL) return -ENOMEM; - rose_loopback_neigh->callsign = null_ax25_address; - rose_loopback_neigh->digipeat = NULL; - rose_loopback_neigh->ax25 = NULL; - rose_loopback_neigh->dev = NULL; - rose_loopback_neigh->count = 0; - rose_loopback_neigh->use = 0; - rose_loopback_neigh->dce_mode = 1; - rose_loopback_neigh->loopback = 1; - rose_loopback_neigh->number = rose_neigh_no++; - rose_loopback_neigh->restarted = 1; + rose_loopback_neigh->addr.dest = null_ax25_address; + rose_loopback_neigh->addr.dcount = 0; + rose_loopback_neigh->ax25 = NULL; + rose_loopback_neigh->dev = NULL; + rose_loopback_neigh->count = 0; + rose_loopback_neigh->use = 0; + rose_loopback_neigh->dce_mode = 1; + rose_loopback_neigh->loopback = 1; + rose_loopback_neigh->number = rose_neigh_no++; + rose_loopback_neigh->restarted = 1; skb_queue_head_init(&rose_loopback_neigh->queue); @@ -787,11 +776,11 @@ int rose_route_frame(struct sk_buff *skb, ax25_cb *ax25) dest_addr = (rose_address *)(skb->data + 4); for (rose_neigh = rose_neigh_list; rose_neigh != NULL; rose_neigh = rose_neigh->next) - if (ax25cmp(&ax25->dest_addr, &rose_neigh->callsign) == 0 && ax25->ax25_dev->dev == rose_neigh->dev) + if (ax25cmp(&ax25->addr.dest, &rose_neigh->addr.dest) == 0 && ax25->device == rose_neigh->dev) break; if (rose_neigh == NULL) { - printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->dest_addr)); + printk("rose_route : unknown neighbour or device %s\n", ax2asc(&ax25->addr.dest)); return 0; } @@ -1034,7 +1023,7 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) /* if (!rose_neigh->loopback) { */ len += sprintf(buffer + len, "%05d %-9s %-4s %3d %3d %3s %3s %3lu %3lu", rose_neigh->number, - (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->callsign), + (rose_neigh->loopback) ? "RSLOOP-0" : ax2asc(&rose_neigh->addr.dest), rose_neigh->dev ? rose_neigh->dev->name : "???", rose_neigh->count, rose_neigh->use, @@ -1043,9 +1032,9 @@ int rose_neigh_get_info(char *buffer, char **start, off_t offset, int length) ax25_display_timer(&rose_neigh->t0timer) / HZ, ax25_display_timer(&rose_neigh->ftimer) / HZ); - if (rose_neigh->digipeat != NULL) { - for (i = 0; i < rose_neigh->digipeat->ndigi; i++) - len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->digipeat->calls[i])); + if (rose_neigh->addr.dcount != 0) { + for (i = 0; i < rose_neigh->addr.dcount; i++) + len += sprintf(buffer + len, " %s", ax2asc(&rose_neigh->addr.digipeater[i])); } len += sprintf(buffer + len, "\n"); diff --git a/net/rose/rose_subr.c b/net/rose/rose_subr.c index 384347a0e..f881e5da5 100644 --- a/net/rose/rose_subr.c +++ b/net/rose/rose_subr.c @@ -119,8 +119,9 @@ void rose_write_internal(struct sock *sk, int frametype) unsigned char lci1, lci2; char buffer[100]; int len, faclen = 0; + int ax25_header_len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1; - len = AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + ROSE_MIN_LEN + 1; + len = ax25_header_len + ROSE_MIN_LEN; switch (frametype) { case ROSE_CALL_REQUEST: @@ -141,9 +142,9 @@ void rose_write_internal(struct sock *sk, int frametype) /* * Space for AX.25 header and PID. */ - skb_reserve(skb, AX25_BPQ_HEADER_LEN + AX25_MAX_HEADER_LEN + 1); + skb_reserve(skb, ax25_header_len); - dptr = skb_put(skb, skb_tailroom(skb)); + dptr = skb_put(skb, len - ax25_header_len); lci1 = (sk->protinfo.rose->lci >> 8) & 0x0F; lci2 = (sk->protinfo.rose->lci >> 0) & 0xFF; |