--- /dev/null
+Generic HDLC layer for Linux kernel 2.4/2.5
+Krzysztof Halasa <khc@pm.waw.pl>
+May, 2001
+
+
+Generic HDLC layer currently supports:
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP),
+- raw HDLC (IPv4 only),
+- Cisco HDLC,
+- PPP (uses syncppp.c),
+- X.25 (uses X.25 routines).
+
+There are hardware drivers for the following cards:
+- C101 by Moxa Technologies Co., Ltd.
+- RISCom/N2 by SDL Communications Inc.
+- and others, some not in the official kernel.
+
+Make sure the hdlc.o and the hardware driver are loaded. It should
+create a number of "hdlc" (hdlc0 etc) network devices, one for each
+WAN port. You'll need the "sethdlc" utility, get it from:
+ http://hq.pm.waw.pl/hdlc/
+
+Compile sethdlc.c utility:
+ gcc -O2 -Wall -o sethdlc sethdlc.c
+Make sure you're using a correct version of sethdlc for your kernel.
+
+Use sethdlc to set physical interface, clock rate, HDLC mode used,
+and add any required PVCs if using Frame Relay.
+Usually you want something like:
+
+ sethdlc hdlc0 clock int rate 128000
+ sethdlc hdlc0 cisco interval 10 timeout 25
+or
+ sethdlc hdlc0 rs232 clock ext
+ sethdlc fr lmi ansi
+ sethdlc create 99
+
+In Frame Relay mode, ifconfig master hdlc device up (without assigning
+any IP address to it) before using pvc devices.
+
+
+Setting interface:
+
+* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
+ if the card has software-selectable interfaces
+ loopback - activate hardware loopback (for testing only)
+* clock ext - external clock (uses DTE RX and TX clock)
+* clock int - internal clock (provides clock signal on DCE clock output)
+* clock txint - TX internal, RX external (provides TX clock on DCE output)
+* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output)
+* rate - sets clock rate in bps (not required for external clock or
+ for txfromrx)
+
+Setting protocol:
+
+* hdlc - sets raw HDLC (IP-only) mode
+ nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code
+ no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
+ crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
+
+* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
+ interval - time in seconds between keepalive packets
+ timeout - time in seconds after last received keepalive packet before
+ we assume the link is down
+
+* ppp - sets synchronous PPP mode
+
+* x25 - sets X.25 mode
+
+* fr - Frame Relay mode
+ lmi ansi / ccitt / none - LMI (link management) type
+ dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
+ It has nothing to do with clocks!
+ t391 - link integrity verification polling timer (in seconds) - user
+ t392 - polling verification timer (in seconds) - network
+ n391 - full status polling counter - user
+ n392 - error threshold - both user and network
+ n393 - monitored events count - both user and network
+
+* create | delete n - FR only - adds / deletes PVC interface with DLCI #n.
+
+
+
+
+Board-specific issues
+---------------------
+
+n2.o and c101.o need parameters to work (note double quotes):
+
+ insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"'
+example:
+ insmod n2 hw='"0x300,10,0xD0000,01"'
+
+or
+ insmod c101 hw='"irq,ram[:irq,...]"
+example:
+ insmod c101 hw='"9,0xdc000"'
+
+If built into the kernel, these drivers need kernel (command line) parameters:
+ n2=io,irq,ram,ports:...
+or
+ c101=irq,ram:...
+
+
+
+If you have a problem with N2 or C101 card, you can issue the "private"
+command to see port's packet descriptor rings:
+
+ sethdlc hdlc0 private
+
+The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS.
+Attaching this info to bug reports would be helpful. Anyway, let me know
+if you have problems using this.
+
+For patches and other info look at http://hq.pm.waw.pl/hdlc/
This driver supports the FarSync T-Series X.21 (and V.35/V.24) cards
from FarSite Communications Ltd.
Synchronous communication is supported on all ports at speeds up to
- 8Mb/s (128K on V.24) using synchronous PPP or Cisco HDLC.
+ 8Mb/s (128K on V.24) using synchronous PPP, Cisco HDLC, raw HDLC,
+ Frame Relay or X.25/LAPB.
If you want to compile this driver as a module ( = code which can be
inserted in and removed from the running kernel whenever you want)
say M here and read <file:Documentation/modules.txt>.
The module will be called farsync.o and if you want the module to be
automatically loaded when the interface is referenced then you
- should add "alias syncX farsync" to /etc/modules.conf for each
+ should add "alias hdlcX farsync" to /etc/modules.conf for each
interface, where X is 0, 1, 2, ...
CONFIG_DLCI
dep_tristate ' Etinc PCISYNC serial board support (EXPERIMENTAL)' CONFIG_DSCC4 m
-# FarSite Communications' cards
-
- tristate ' FarSync T-Series X.21 (and V.35/V.24) cards' CONFIG_FARSYNC
#
# Lan Media's board. Currently 1000, 1200, 5200, 5245
tristate ' SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP
- tristate ' Generic HDLC driver' CONFIG_HDLC
+# Generic HDLC
+
+ tristate ' Generic HDLC layer' CONFIG_HDLC
if [ "$CONFIG_HDLC" != "n" ]; then
+ bool ' Raw HDLC support' CONFIG_HDLC_RAW
+ bool ' Cisco HDLC support' CONFIG_HDLC_CISCO
+ bool ' Frame Relay support' CONFIG_HDLC_FR
bool ' Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP
if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then
bool ' X.25 protocol support' CONFIG_HDLC_X25
fi
dep_tristate ' SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
dep_tristate ' Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
+ dep_tristate ' FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC
+ bool ' Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT
+ bool ' Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER
+ bool ' Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN
+ bool ' Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS
fi
tristate ' Frame relay DLCI support' CONFIG_DLCI
O_TARGET := wan.o
-export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o
+export-objs = z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o
list-multi = wanpipe.o cyclomx.o
wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y)
cyclomx-objs = cycx_main.o $(cyclomx-y)
cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o
+hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o
+hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
+hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o
+hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
+hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o
obj-$(CONFIG_HOSTESS_SV11) += z85230.o syncppp.o hostess_sv11.o
obj-$(CONFIG_SEALEVEL_4021) += z85230.o syncppp.o sealevel.o
obj-$(CONFIG_LAPBETHER) += lapbether.o
obj-$(CONFIG_SBNI) += sbni.o
obj-$(CONFIG_HDLC) += hdlc.o
-obj-$(CONFIG_HDLC_PPP) += syncppp.o
+ifeq ($(CONFIG_HDLC_PPP),y)
+ obj-$(CONFIG_HDLC) += syncppp.o
+endif
obj-$(CONFIG_N2) += n2.o
obj-$(CONFIG_C101) += c101.o
include $(TOPDIR)/Rules.make
+hdlc.o: hdlc_generic.o $(hdlc-y)
+ $(LD) -r -o $@ hdlc_generic.o $(hdlc-y)
+
wanpipe.o: $(wanpipe-objs)
$(LD) -r -o $@ $(wanpipe-objs)
/*
* Moxa C101 synchronous serial card driver for Linux
*
- * Copyright (C) 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2001 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
* Moxa C101 User's Manual
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "hd64570.h"
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
-static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4";
+static const char* version = "Moxa C101 driver version: 1.09";
static const char* devname = "C101";
#define C101_PAGE 0x1D00
typedef struct card_s {
hdlc_device hdlc; /* HDLC device struct - must be first */
spinlock_t lock; /* TX lock */
- int clkmode; /* clock mode */
- int clkrate; /* clock speed */
- int line; /* loopback only */
u8 *win0base; /* ISA window base address */
u32 phy_winbase; /* ISA physical base address */
u16 buff_offset; /* offset of first buffer of first channel */
+ sync_serial_settings settings;
+ unsigned short encoding;
+ unsigned short parity;
u8 rxs, txs, tmc; /* SCA registers */
u8 irq; /* IRQ (3-15) */
u8 ring_buffers; /* number of buffers in a ring */
typedef card_t port_t;
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
#define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg))
#define sca_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg))
#include "hd6457x.c"
-static int c101_set_clock(port_t *port, int value)
+static int c101_set_iface(port_t *port)
{
u8 msci = get_msci(port);
u8 rxs = port->rxs & CLK_BRG_MASK;
u8 txs = port->txs & CLK_BRG_MASK;
- switch(value) {
+ switch(port->settings.clock_type) {
case CLOCK_EXT:
rxs |= CLK_LINE_RX; /* RXC input */
txs |= CLK_LINE_TX; /* TXC input */
port->txs = txs;
sca_out(rxs, msci + RXS, port);
sca_out(txs, msci + TXS, port);
- port->clkmode = value;
+ sca_set_port(port);
return 0;
}
-static int c101_open(hdlc_device *hdlc)
+static int c101_open(struct net_device *dev)
{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
+ int result = hdlc_open(hdlc);
+ if (result)
+ return result;
MOD_INC_USE_COUNT;
writeb(1, port->win0base + C101_DTR);
sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
sca_open(hdlc);
- c101_set_clock(port, port->clkmode);
- return 0;
+ return c101_set_iface(port);
}
-static void c101_close(hdlc_device *hdlc)
+static int c101_close(struct net_device *dev)
{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
sca_close(hdlc);
writeb(0, port->win0base + C101_DTR);
sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
+ hdlc_close(hdlc);
MOD_DEC_USE_COUNT;
+ return 0;
}
-static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- int value = ifr->ifr_ifru.ifru_ivalue;
- int result = 0;
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
+ const size_t size = sizeof(sync_serial_settings);
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- switch(cmd) {
- case HDLCSCLOCK:
- result = c101_set_clock(port, value);
- case HDLCGCLOCK:
- value = port->clkmode;
- break;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+ if (cmd == SIOCDEVPRIVATE) {
+ sca_dump_rings(hdlc);
+ return 0;
+ }
+#endif
+ if (cmd != SIOCDEVICE)
+ return hdlc_ioctl(dev, ifr, cmd);
+
+ switch(ifr->ifr_settings.type) {
+ case IF_GET_IFACE:
+ ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+ if (ifr->ifr_settings.data_length == 0)
+ return 0; /* return interface type only */
+ if (ifr->ifr_settings.data_length < size)
+ return -ENOMEM; /* buffer too small */
+ if (copy_to_user(ifr->ifr_settings.data,
+ &port->settings, size))
+ return -EFAULT;
+ ifr->ifr_settings.data_length = size;
+ return 0;
- case HDLCSCLOCKRATE:
- port->clkrate = value;
- sca_set_clock(port);
- case HDLCGCLOCKRATE:
- value = port->clkrate;
- break;
+ case IF_IFACE_SYNC_SERIAL:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
- case HDLCSLINE:
- result = sca_set_loopback(port, value);
- case HDLCGLINE:
- value = port->line;
- break;
+ if (ifr->ifr_settings.data_length != size)
+ return -ENOMEM; /* incorrect data length */
-#ifdef DEBUG_RINGS
- case HDLCRUN:
- sca_dump_rings(hdlc);
- return 0;
-#endif /* DEBUG_RINGS */
+ if (copy_from_user(&port->settings,
+ ifr->ifr_settings.data, size))
+ return -EFAULT;
+ /* FIXME - put sanity checks here */
+ return c101_set_iface(port);
default:
- return -EINVAL;
+ return hdlc_ioctl(dev, ifr, cmd);
}
-
- ifr->ifr_ifru.ifru_ivalue = value;
- return result;
}
static int c101_run(unsigned long irq, unsigned long winbase)
{
+ struct net_device *dev;
card_t *card;
int result;
sca_init(card, 0);
+ dev = hdlc_to_dev(&card->hdlc);
+
spin_lock_init(&card->lock);
- hdlc_to_dev(&card->hdlc)->irq = irq;
- hdlc_to_dev(&card->hdlc)->mem_start = winbase;
- hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
- hdlc_to_dev(&card->hdlc)->tx_queue_len = 50;
- card->hdlc.ioctl = c101_ioctl;
- card->hdlc.open = c101_open;
- card->hdlc.close = c101_close;
+ dev->irq = irq;
+ dev->mem_start = winbase;
+ dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
+ dev->tx_queue_len = 50;
+ dev->do_ioctl = c101_ioctl;
+ dev->open = c101_open;
+ dev->stop = c101_close;
+ card->hdlc.attach = sca_attach;
card->hdlc.xmit = sca_xmit;
+ card->settings.clock_type = CLOCK_EXT;
result = register_hdlc_device(&card->hdlc);
if (result) {
return -ENOSYS; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s\n", version);
+ printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
do {
unsigned long irq, ram;
* the documentation/chipset releases. An on-line errata would be welcome.
*
* TODO:
- * - syncppp oopses. X25 untested.
+ * - test X25.
* - use polling at high irq/s,
* - performance analysis,
* - endianness.
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
+#include <linux/list.h>
#include <linux/ioport.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/hdlc.h>
/* Version */
-static const char version[] = "$Id: dscc4.c,v 1.157 2002/01/28 01:54:19 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.158 2002/01/30 00:40:37 romieu Exp $\n";
static int debug;
static int quartz;
#define BRR_DIVIDER_MAX 64*0x00008000
#define dev_per_card 4
-#define SOURCE_ID(flags) ((flags >> 28 ) & 0x03)
-#define TO_SIZE(state) ((state >> 16) & 0x1fff)
-#define TO_STATE(len) cpu_to_le32((len & TxSizeMax) << 16)
+#define SOURCE_ID(flags) (((flags) >> 28 ) & 0x03)
+#define TO_SIZE(state) (((state) >> 16) & 0x1fff)
+#define TO_STATE(len) cpu_to_le32(((len) & TxSizeMax) << 16)
#define RX_MAX(len) ((((len) >> 5) + 1)<< 5)
#define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET
static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *);
#endif
+static inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev)
+{
+ return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev);
+}
+
static inline void dscc4_patch_register(u32 ioaddr, u32 mask, u32 value)
{
u32 state;
static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv)
{
- int cur, ret = 0;
- s16 i;
+ int cur = dpriv->iqtx_current%IRQ_RING_SIZE;
+ s16 i = 0;
- cur = dpriv->iqtx_current%IRQ_RING_SIZE;
- for (i = 0; i >= 0; i++) {
+ do {
if (!(dpriv->flags & (NeedIDR | NeedIDT)) ||
(dpriv->iqtx[cur] & Xpr))
break;
smp_rmb();
- }
- if (i < 0) {
- printk(KERN_ERR "%s: %s timeout\n", "dscc4", "XPR");
- ret = -1;
- }
- return ret;
+ } while (i++ >= 0);
+
+ return i;
}
static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur,
struct RxFD *rx_fd, struct net_device *dev)
{
+ struct net_device_stats *stats = &dev_to_hdlc(dev)->stats;
struct pci_dev *pdev = dpriv->pci_priv->pdev;
struct sk_buff *skb;
int pkt_len;
skb = dpriv->rx_skbuff[cur];
- pkt_len = TO_SIZE(rx_fd->state2) - 1;
- pci_dma_sync_single(pdev, rx_fd->data, pkt_len + 1, PCI_DMA_FROMDEVICE);
- if((skb->data[pkt_len] & FrameOk) == FrameOk) {
+ pkt_len = TO_SIZE(rx_fd->state2);
+ pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
+ if((skb->data[--pkt_len] & FrameOk) == FrameOk) {
pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE);
- dev_to_hdlc(dev)->stats.rx_packets++;
- dev_to_hdlc(dev)->stats.rx_bytes += pkt_len;
+ stats->rx_packets++;
+ stats->rx_bytes += pkt_len;
skb->tail += pkt_len;
skb->len = pkt_len;
- if (netif_running(hdlc_to_dev(&dpriv->hdlc)))
+ if (netif_running(dev))
skb->protocol = htons(ETH_P_HDLC);
netif_rx(skb);
try_get_rx_skb(dpriv, cur, dev);
} else {
if(skb->data[pkt_len] & FrameRdo)
- dev_to_hdlc(dev)->stats.rx_fifo_errors++;
+ stats->rx_fifo_errors++;
else if(!(skb->data[pkt_len] | ~FrameCrc))
- dev_to_hdlc(dev)->stats.rx_crc_errors++;
+ stats->rx_crc_errors++;
else if(!(skb->data[pkt_len] | ~(FrameVfr | FrameRab)))
- dev_to_hdlc(dev)->stats.rx_length_errors++;
+ stats->rx_length_errors++;
else
- dev_to_hdlc(dev)->stats.rx_errors++;
+ stats->rx_errors++;
}
rx_fd->state1 |= Hold;
rx_fd->state2 = 0x00000000;
* SCC 0-3 private rx/tx irq structures
* IQRX/TXi needs to be set soon. Learned it the hard way...
*/
- for(i = 0; i < dev_per_card; i++) {
+ for (i = 0; i < dev_per_card; i++) {
dpriv = priv->root + i;
dpriv->iqtx = (u32 *) pci_alloc_consistent(pdev,
IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma);
goto err_out_free_iqtx;
writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4);
}
- for(i = 0; i < dev_per_card; i++) {
+ for (i = 0; i < dev_per_card; i++) {
dpriv = priv->root + i;
dpriv->iqrx = (u32 *) pci_alloc_consistent(pdev,
IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma);
dpriv->dev_id = i;
dpriv->pci_priv = ppriv;
spin_lock_init(&dpriv->lock);
- d->priv = dpriv;
hdlc->xmit = dscc4_start_xmit;
hdlc->attach = dscc4_hdlc_attach;
struct dscc4_dev_priv *dpriv;
struct dscc4_pci_priv *ppriv;
- dpriv = dev->priv;
+ dpriv = dscc4_priv(dev);
if (netif_queue_stopped(dev) &&
((jiffies - dev->trans_start) > TX_TIMEOUT)) {
ppriv = dpriv->pci_priv;
static int dscc4_open(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
hdlc_device *hdlc = &dpriv->hdlc;
struct dscc4_pci_priv *ppriv;
u32 ioaddr;
* WARNING, a really missing XPR usually means a hardware
* reset is needed. Suggestions anyone ?
*/
- if (dscc4_xpr_ack(dpriv))
+ if (dscc4_xpr_ack(dpriv) < 0) {
+ printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
goto err_free_ring;
+ }
netif_start_queue(dev);
static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
struct dscc4_pci_priv *ppriv;
struct TxFD *tx_fd;
int cur, next;
next = dpriv->tx_current%TX_RING_SIZE;
dpriv->tx_skbuff[next] = skb;
tx_fd = dpriv->tx_fd + next;
- printk(KERN_DEBUG "%s: %d sent\n", dev->name, skb->len);
tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len);
tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len,
PCI_DMA_TODEVICE);
static int dscc4_close(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
u32 ioaddr = dev->base_addr;
int dev_id;
hdlc_device *hdlc = dev_to_hdlc(dev);
static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
{
- struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
u32 brr;
*state &= ~Ccr0ClockMask;
static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
struct if_settings *if_s = &ifr->ifr_settings;
const size_t size = sizeof(dpriv->settings);
int ret = 0;
static int dscc4_clock_setting(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
sync_serial_settings *settings = &dpriv->settings;
u32 bps, state;
u32 ioaddr;
static int dscc4_encoding_setting(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
struct thingie encoding[] = {
{ ENCODING_NRZ, 0x00000000 },
{ ENCODING_NRZI, 0x00200000 },
static int dscc4_loopback_setting(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
sync_serial_settings *settings = &dpriv->settings;
u32 ioaddr, state;
static int dscc4_crc_setting(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
struct thingie crc[] = {
{ PARITY_CRC16_PR0_CCITT, 0x00000010 },
{ PARITY_CRC16_PR1_CCITT, 0x00000000 },
}
} else { /* ! SccEvt */
#ifdef DEBUG_PARANOIA
- int i;
static struct {
u32 mask;
const char *irq_name;
{ 0x00000008, "PLLA"},
{ 0x00000004, "CDSC"},
{ 0, NULL}
- };
+ }, *evt;
#endif /* DEBUG_PARANOIA */
state &= 0x00ffffff;
#ifdef DEBUG_PARANOIA
- for (i = 0; evts[i].irq_name; i++) {
- if (state & evts[i].mask) {
+ for (evt = evts; evt->irq_name; evt++) {
+ if (state & evt->mask) {
printk(KERN_DEBUG "%s: %s\n", dev->name,
- evts[i].irq_name);
- if (!(state &= ~evts[i].mask))
+ evt->irq_name);
+ if (!(state &= ~evt->mask))
goto try;
}
}
#endif /* DEBUG_PARANOIA */
/*
- * Receive Data Overflow (FIXME: untested)
+ * Receive Data Overflow (FIXME: fscked)
*/
if (state & Rdo) {
u32 ioaddr, scc_offset, scc_addr;
static int dscc4_init_ring(struct net_device *dev)
{
- struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
struct TxFD *tx_fd;
struct RxFD *rx_fd;
int i;
dpriv->tx_dirty = 0;
/* the dma core of the dscc4 will be locked on the first desc */
- for(i = 0; i < TX_RING_SIZE; ) {
+ for (i = 0; i < TX_RING_SIZE; ) {
reset_TxFD(tx_fd);
/* FIXME: NULL should be ok - to be tried */
tx_fd->data = dpriv->tx_fd_dma;
skb->len, PCI_DMA_TODEVICE);
dpriv->tx_skbuff[0] = skb;
}
- for (i = 0; i < RX_RING_SIZE;) {
+ for (i = 0; i < RX_RING_SIZE; ) {
/* size set by the host. Multiple of 4 bytes please */
rx_fd->state1 = HiDesc; /* Hi, no Hold */
rx_fd->state2 = 0x00000000;
static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding,
unsigned short parity)
{
- struct dscc4_dev_priv *dpriv = hdlc_to_dev(hdlc)->priv;
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
if (encoding != ENCODING_NRZ &&
encoding != ENCODING_NRZI &&
MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl);
static struct pci_driver dscc4_driver = {
- name: "dscc4",
+ name: DRV_NAME,
id_table: dscc4_pci_tbl,
probe: dscc4_init_one,
remove: dscc4_remove_one,
/*
- * FarSync X21 driver for Linux (2.4.x kernel version)
+ * FarSync X21 driver for Linux (generic HDLC version)
*
* Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards
*
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/ioport.h>
-#include <linux/netdevice.h>
#include <linux/init.h>
-#include <linux/if_arp.h>
#include <asm/uaccess.h>
-#include <net/syncppp.h>
+#include <linux/if.h>
+#include <linux/hdlc.h>
#include "farsync.h"
*/
MODULE_AUTHOR("R.J.Dunlop <bob.dunlop@farsite.co.uk>");
MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd.");
+MODULE_LICENSE("GPL");
EXPORT_NO_SYMBOLS;
/* Per port (line or channel) information
*/
struct fst_port_info {
- void *if_ptr; /* Some drivers describe this as a
- * general purpose pointer. However if
- * using syncPPP it has a very specific
- * purpose: it must be the first item in
- * the structure pointed to by dev->priv
- * and must in turn point to the
- * associated ppp_device structure.
- */
+ hdlc_device hdlc; /* HDLC device struct - must be first */
struct fst_card_info *card; /* Card we're associated with */
int index; /* Port index on the card */
- int proto; /* Protocol we are running */
int hwif; /* Line hardware (lineInterface copy) */
int run; /* Port is running */
int rxpos; /* Next Rx buffer to use */
int txpos; /* Next Tx buffer to use */
int txipos; /* Next Tx buffer to check for free */
int txcnt; /* Count of Tx buffers in use */
- struct net_device *dev; /* Kernel network device entry */
- struct net_device_stats stats; /* Standard statistics */
- struct ppp_device pppdev; /* Link to syncPPP */
};
/* Per card information
struct fst_port_info ports[ FST_MAX_PORTS ];
};
+/* Convert an HDLC device pointer into a port info pointer and similar */
+#define hdlc_to_port(H) ((struct fst_port_info *)(H))
+#define dev_to_port(D) hdlc_to_port(dev_to_hdlc(D))
+#define port_to_dev(P) hdlc_to_dev(&(P)->hdlc)
+
/*
* Shared memory window access macros
if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD ))
{
- if ( ! netif_carrier_ok ( port->dev ))
+ if ( ! netif_carrier_ok ( port_to_dev ( port )))
{
dbg ( DBG_INTR,"DCD active\n");
-
- /* Poke sPPP to renegotiate */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- {
- sppp_reopen ( port->dev );
- }
-
- netif_carrier_on ( port->dev );
+ netif_carrier_on ( port_to_dev ( port ));
}
}
else
{
- if ( netif_carrier_ok ( port->dev ))
+ if ( netif_carrier_ok ( port_to_dev ( port )))
{
dbg ( DBG_INTR,"DCD lost\n");
- netif_carrier_off ( port->dev );
+ netif_carrier_off ( port_to_dev ( port ));
}
}
}
len );
if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 )
{
- port->stats.rx_errors++;
+ port->hdlc.stats.rx_errors++;
/* Update error stats and discard buffer */
if ( dmabits & RX_OFLO )
{
- port->stats.rx_fifo_errors++;
+ port->hdlc.stats.rx_fifo_errors++;
}
if ( dmabits & RX_CRC )
{
- port->stats.rx_crc_errors++;
+ port->hdlc.stats.rx_crc_errors++;
}
if ( dmabits & RX_FRAM )
{
- port->stats.rx_frame_errors++;
+ port->hdlc.stats.rx_frame_errors++;
}
if ( dmabits == ( RX_STP | RX_ENP ))
{
- port->stats.rx_length_errors++;
+ port->hdlc.stats.rx_length_errors++;
}
/* Discard buffer descriptors until we see the end of packet
{
dbg ( DBG_RX,"intr_rx: can't allocate buffer\n");
- port->stats.rx_dropped++;
+ port->hdlc.stats.rx_dropped++;
/* Return descriptor to card */
FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
port->rxpos = rxp;
/* Update stats */
- port->stats.rx_packets++;
- port->stats.rx_bytes += len;
+ port->hdlc.stats.rx_packets++;
+ port->hdlc.stats.rx_bytes += len;
/* Push upstream */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- {
- /* Mark for further processing by sPPP module */
- skb->protocol = htons ( ETH_P_WAN_PPP );
- }
- else
- {
- /* DEC customer specific protocol (since nothing defined for
- * marking raw data), at least one other driver uses this value
- * for this purpose.
- */
- skb->protocol = htons ( ETH_P_CUST );
- skb->pkt_type = PACKET_HOST;
- }
skb->mac.raw = skb->data;
- skb->dev = port->dev;
+ skb->dev = hdlc_to_dev ( &port->hdlc );
+ skb->protocol = htons ( ETH_P_HDLC );
netif_rx ( skb );
- port->dev->last_rx = jiffies;
+ port_to_dev ( port )->last_rx = jiffies;
}
case CTLB_CHG:
case CTLC_CHG:
case CTLD_CHG:
- if ( port->run && port->dev != NULL )
+ if ( port->run )
fst_intr_ctlchg ( card, port );
break;
* always load up the entire packet for DMA.
*/
dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 );
-
- port->stats.tx_errors++;
- port->stats.tx_fifo_errors++;
+ port->hdlc.stats.tx_errors++;
+ port->hdlc.stats.tx_fifo_errors++;
break;
case INIT_CPLT:
for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ )
{
- if ( port->dev == NULL || ! port->run )
+ if ( ! port->run )
continue;
/* Check for rx completions */
--port->txcnt;
if ( ++port->txipos >= NUM_TX_BUFFER )
port->txipos = 0;
- netif_wake_queue ( port->dev );
+ netif_wake_queue ( port_to_dev ( port ));
}
}
}
-static int
-fst_change_mtu ( struct net_device *dev, int new_mtu )
-{
- if ( new_mtu < 128 || new_mtu > FST_MAX_MTU )
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-
-/* Sooner or later you can't avoid a forward declaration */
-static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd );
-
-static int
-switch_proto ( struct fst_port_info *port, int new_proto )
-{
- int err;
- int orig_mtu;
- struct net_device *dev;
-
- dev = port->dev;
-
- /* Turn off sPPP module ? */
- if (( new_proto != FST_HDLC && new_proto != FST_PPP )
- && ( port->proto == FST_HDLC || port->proto == FST_PPP ))
- {
- sppp_close ( port->pppdev.dev );
- sppp_detach ( port->pppdev.dev );
-
- /* Reset some fields overwritten by sPPP */
- dev->hard_header = NULL;
- dev->rebuild_header = NULL;
- dev->tx_queue_len = FST_TX_QUEUE_LEN;
- dev->type = ARPHRD_MYTYPE;
- dev->addr_len = 0;
- dev->hard_header_len = 0;
- dev->do_ioctl = fst_ioctl;
- dev->change_mtu = fst_change_mtu;
- dev->flags = IFF_POINTOPOINT|IFF_NOARP;
-
- return 0;
- }
-
- /* Turn on sPPP ? */
- if (( new_proto == FST_HDLC || new_proto == FST_PPP )
- && ( port->proto != FST_HDLC && port->proto != FST_PPP ))
- {
- orig_mtu = dev->mtu;
-
- /* Attach to sync PPP module */
- port->pppdev.dev = dev;
- sppp_attach ( &port->pppdev );
-
- if ( orig_mtu < dev->mtu )
- dev->change_mtu ( dev, orig_mtu );
-
- /* Claw back the ioctl routine. We promise to be good and call
- * the sync PPP routines once we've eliminated our functions.
- */
- dev->do_ioctl = fst_ioctl;
-
- /* Set the mode */
- if ( new_proto == FST_HDLC )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL,
- SPPPIOCCISCO );
- }
- else
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL,
- SPPPIOCPPP );
- }
-
- /* Open the device */
- if ( err == 0 )
- {
- (void) sppp_open ( port->dev );
- }
-
- return err;
- }
-
- /* Switch sPPP mode to that desired */
- err = 0;
- if ( new_proto == FST_HDLC && port->pppdev.dev != NULL )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO );
- }
- else if ( new_proto == FST_PPP && port->pppdev.dev != NULL )
- {
- err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP );
- }
-
- /* Anything else is switching from one raw mode to another which is
- * basically a NOP
- */
-
- return err;
-}
-
-
static int
set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port,
struct fstioc_info *info )
{
int err;
- /* Set things according to the user set valid flags */
+ /* Set things according to the user set valid flags.
+ * Several of the old options have been invalidated/replaced by the
+ * generic HDLC package.
+ */
err = 0;
if ( info->valid & FSTVAL_PROTO )
- {
- if ( port->proto != info->proto )
- {
- err = switch_proto ( port, info->proto );
- if ( err == 0 )
- port->proto = info->proto;
- }
- }
+ err = -EINVAL;
if ( info->valid & FSTVAL_CABLE )
- {
- FST_WRB ( card, portConfig[port->index].lineInterface,
- info->lineInterface );
- port->hwif = info->lineInterface;
- }
+ err = -EINVAL;
if ( info->valid & FSTVAL_SPEED )
- {
- FST_WRL ( card, portConfig[port->index].lineSpeed,
- info->lineSpeed );
- FST_WRB ( card, portConfig[port->index].internalClock,
- info->internalClock );
- }
+ err = -EINVAL;
+
if ( info->valid & FSTVAL_MODE )
- {
FST_WRW ( card, cardMode, info->cardMode );
- }
#if FST_DEBUG
if ( info->valid & FSTVAL_DEBUG )
- {
fst_debug_mask = info->debug;
- }
#endif
return err;
info->nports = card->nports;
info->type = card->type;
info->state = card->state;
- info->proto = port->proto;
+ info->proto = FST_GEN_HDLC;
info->index = i;
#if FST_DEBUG
info->debug = fst_debug_mask;
}
+static int
+fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port,
+ struct ifreq *ifr )
+{
+ sync_serial_settings sync;
+ int i;
+
+ if ( ifr->ifr_settings.data_length != sizeof ( sync ))
+ {
+ return -ENOMEM;
+ }
+
+ if ( copy_from_user ( &sync, ifr->ifr_settings.data, sizeof ( sync )))
+ {
+ return -EFAULT;
+ }
+
+ if ( sync.loopback )
+ return -EINVAL;
+
+ i = port->index;
+
+ switch ( ifr->ifr_settings.type )
+ {
+ case IF_IFACE_V35:
+ FST_WRW ( card, portConfig[i].lineInterface, V35 );
+ port->hwif = V35;
+ break;
+
+ case IF_IFACE_V24:
+ FST_WRW ( card, portConfig[i].lineInterface, V24 );
+ port->hwif = V24;
+ break;
+
+ case IF_IFACE_X21:
+ FST_WRW ( card, portConfig[i].lineInterface, X21 );
+ port->hwif = X21;
+ break;
+
+ case IF_IFACE_SYNC_SERIAL:
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch ( sync.clock_type )
+ {
+ case CLOCK_EXT:
+ FST_WRB ( card, portConfig[i].internalClock, EXTCLK );
+ break;
+
+ case CLOCK_INT:
+ FST_WRB ( card, portConfig[i].internalClock, INTCLK );
+ break;
+
+ default:
+ return -EINVAL;
+ }
+ FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate );
+ return 0;
+}
+
+static int
+fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port,
+ struct ifreq *ifr )
+{
+ sync_serial_settings sync;
+ int i;
+
+ /* First check what line type is set, we'll default to reporting X.21
+ * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be
+ * changed
+ */
+ switch ( port->hwif )
+ {
+ case V35:
+ ifr->ifr_settings.type = IF_IFACE_V35;
+ break;
+ case V24:
+ ifr->ifr_settings.type = IF_IFACE_V24;
+ break;
+ case X21:
+ default:
+ ifr->ifr_settings.type = IF_IFACE_X21;
+ break;
+ }
+ if ( ifr->ifr_settings.data_length == 0 )
+ {
+ return 0; /* only type requested */
+ }
+
+ if ( ifr->ifr_settings.data_length < sizeof ( sync ))
+ {
+ return -ENOMEM;
+ }
+
+ i = port->index;
+ sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed );
+ /* Lucky card and linux use same encoding here */
+ sync.clock_type = FST_RDB ( card, portConfig[i].internalClock );
+ sync.loopback = 0;
+
+ if ( copy_to_user ( ifr->ifr_settings.data, &sync, sizeof ( sync )))
+ {
+ return -EFAULT;
+ }
+
+ ifr->ifr_settings.data_length = sizeof ( sync );
+ return 0;
+}
+
+
static int
fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
{
dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data );
- port = dev->priv;
+ port = dev_to_port ( dev );
card = port->card;
if ( !capable ( CAP_NET_ADMIN ))
* when going over the top
*/
if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE
- || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
+ || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
{
return -ENXIO;
}
case FSTSETCONF:
+ /* Most of the setting have been moved to the generic ioctls
+ * this just covers debug and board ident mode now
+ */
if ( copy_from_user ( &info, ifr->ifr_data, sizeof ( info )))
{
return -EFAULT;
return set_conf_from_info ( card, port, &info );
+ case SIOCDEVICE:
+ switch ( ifr->ifr_settings.type )
+ {
+ case IF_GET_IFACE:
+ return fst_get_iface ( card, port, ifr );
+
+ case IF_IFACE_SYNC_SERIAL:
+ case IF_IFACE_V35:
+ case IF_IFACE_V24:
+ case IF_IFACE_X21:
+ return fst_set_iface ( card, port, ifr );
+
+ default:
+ return hdlc_ioctl ( dev, ifr, cmd );
+ }
+
default:
- /* Not one of ours. Pass it through to sPPP package */
- if ( port->proto == FST_HDLC || port->proto == FST_PPP )
- return sppp_do_ioctl ( dev, ifr, cmd );
- else
- return -EINVAL;
+ /* Not one of ours. Pass through to HDLC package */
+ return hdlc_ioctl ( dev, ifr, cmd );
}
}
signals = FST_RDL ( port->card, v24DebouncedSts[port->index]);
if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE
: IPSTS_DCD ))
- netif_carrier_on ( port->dev );
+ netif_carrier_on ( port_to_dev ( port ));
else
- netif_carrier_off ( port->dev );
+ netif_carrier_off ( port_to_dev ( port ));
}
}
static int
fst_open ( struct net_device *dev )
{
- struct fst_card_info *card;
- struct fst_port_info *port;
- int orig_mtu;
int err;
- MOD_INC_USE_COUNT;
-
- port = dev->priv;
- card = port->card;
-
- switch ( port->proto )
- {
- case FST_HDLC:
- case FST_PPP:
-
- orig_mtu = dev->mtu;
-
- /* Attach to sync PPP module */
- port->pppdev.dev = dev;
- sppp_attach ( &port->pppdev );
-
- if ( orig_mtu < dev->mtu )
- dev->change_mtu ( dev, orig_mtu );
-
- /* Claw back the ioctl routine. We promise to be good and call
- * the sync PPP routines once we've eliminated our functions.
- */
- dev->do_ioctl = fst_ioctl;
-
- err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC
- ? SPPPIOCCISCO : SPPPIOCPPP );
- if ( err )
- {
- sppp_detach ( dev );
- MOD_DEC_USE_COUNT;
- return err;
- }
-
- err = sppp_open ( dev );
- if ( err )
- {
- sppp_detach ( dev );
- MOD_DEC_USE_COUNT;
- return err;
- }
- break;
-
- case FST_MONITOR:
- case FST_RAW:
- break;
-
- default:
- dbg ( DBG_OPEN,"open: Unknown proto %d\n", port->proto );
- break;
- }
+ err = hdlc_open ( dev_to_hdlc ( dev ));
+ if ( err )
+ return err;
- fst_openport ( port );
+ MOD_INC_USE_COUNT;
+ fst_openport ( dev_to_port ( dev ));
netif_wake_queue ( dev );
return 0;
}
static int
fst_close ( struct net_device *dev )
{
- struct fst_port_info *port;
-
- port = dev->priv;
-
netif_stop_queue ( dev );
- switch ( port->proto )
- {
- case FST_HDLC:
- case FST_PPP:
- sppp_close ( dev );
- sppp_detach ( dev );
- break;
-
- case FST_MONITOR:
- case FST_RAW:
- break;
-
- default:
- dbg ( DBG_OPEN,"close: Unknown proto %d\n", port->proto );
- break;
- }
-
- fst_closeport ( port );
-
+ fst_closeport ( dev_to_port ( dev ));
+ hdlc_close ( dev_to_hdlc ( dev ));
MOD_DEC_USE_COUNT;
return 0;
}
+static int
+fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity )
+{
+ /* Setting currently fixed in FarSync card so we check and forget */
+ if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT )
+ return -EINVAL;
+ return 0;
+}
+
static void
fst_tx_timeout ( struct net_device *dev )
dbg ( DBG_INTR | DBG_TX,"tx_timeout\n");
- port = dev->priv;
+ port = dev_to_port ( dev );
- port->stats.tx_errors++;
- port->stats.tx_aborted_errors++;
+ port->hdlc.stats.tx_errors++;
+ port->hdlc.stats.tx_aborted_errors++;
if ( port->txcnt > 0 )
fst_issue_cmd ( port, ABORTTX );
int pi;
int txp;
- port = dev->priv;
+ port = dev_to_port ( dev );
card = port->card;
- /* Drop packet if in monitor (rx only) mode */
- if ( port->proto == FST_MONITOR )
- {
- dev_kfree_skb ( skb );
- return 0;
- }
-
/* Drop packet with error if we don't have carrier */
if ( ! netif_carrier_ok ( dev ))
{
dev_kfree_skb ( skb );
- port->stats.tx_errors++;
- port->stats.tx_carrier_errors++;
+ port->hdlc.stats.tx_errors++;
+ port->hdlc.stats.tx_carrier_errors++;
return 0;
}
dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len,
LEN_TX_BUFFER );
dev_kfree_skb ( skb );
- port->stats.tx_errors++;
+ port->hdlc.stats.tx_errors++;
return 0;
}
spin_unlock_irqrestore ( &card->card_lock, flags );
dbg ( DBG_TX,"Out of Tx buffers\n");
dev_kfree_skb ( skb );
- port->stats.tx_errors++;
+ port->hdlc.stats.tx_errors++;
return 0;
}
if ( ++port->txpos >= NUM_TX_BUFFER )
FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len ));
FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP );
- port->stats.tx_packets++;
- port->stats.tx_bytes += skb->len;
+ port->hdlc.stats.tx_packets++;
+ port->hdlc.stats.tx_bytes += skb->len;
dev_kfree_skb ( skb );
}
-static struct net_device_stats *
-fst_get_stats ( struct net_device *dev )
-{
- struct fst_port_info *port;
-
- if (( port = dev->priv ) != NULL )
- return &port->stats;
- else
- return NULL;
-}
-
-
/*
* Card setup having checked hardware resources.
* Should be pretty bizarre if we get an error here (kernel memory
*/
for ( i = 0 ; i < card->nports ; i++ )
{
- card->ports[i].if_ptr = &card->ports[i].pppdev;
card->ports[i].card = card;
card->ports[i].index = i;
- card->ports[i].proto = FST_HDLC;
card->ports[i].run = 0;
- dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL );
- if ( dev == NULL )
- {
- printk_err ("Cannot allocate net_device for port %d\n",
- i );
- /* No point going any further */
- card->nports = i;
- break;
- }
- memset ( dev, 0, sizeof ( struct net_device ));
- card->ports[i].dev = dev;
+ dev = hdlc_to_dev ( &card->ports[i].hdlc );
- if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 )
- {
- printk_err ("Cannot allocate i/f name for port %d\n",
- i );
- kfree ( dev );
- card->nports = i;
- break;
- }
-
- /* Fill in remainder of the net device info */
+ /* Fill in the net device info */
/* Since this is a PCI setup this is purely
* informational. Give them the buffer addresses
* and basic card I/O.
dev->base_addr = card->pci_conf;
dev->irq = card->irq;
- dev->get_stats = fst_get_stats;
- dev->mtu = FST_DEF_MTU;
- dev->change_mtu = fst_change_mtu;
- dev->priv = &card->ports[i];
- dev->tx_queue_len = FST_TX_QUEUE_LEN;
- dev->type = ARPHRD_MYTYPE;
- dev->addr_len = 0;
- dev->open = fst_open;
- dev->stop = fst_close;
- dev->hard_start_xmit = fst_start_xmit;
- dev->do_ioctl = fst_ioctl;
- dev->watchdog_timeo = FST_TX_TIMEOUT;
- dev->tx_timeout = fst_tx_timeout;
- dev->flags = IFF_POINTOPOINT|IFF_NOARP;
-
- if (( err = register_netdev ( dev )) < 0 )
+ dev->tx_queue_len = FST_TX_QUEUE_LEN;
+ dev->open = fst_open;
+ dev->stop = fst_close;
+ dev->do_ioctl = fst_ioctl;
+ dev->watchdog_timeo = FST_TX_TIMEOUT;
+ dev->tx_timeout = fst_tx_timeout;
+ card->ports[i].hdlc.attach = fst_attach;
+ card->ports[i].hdlc.xmit = fst_start_xmit;
+
+ if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 )
{
- printk_err ("Cannot register %s (errno %d)\n",
- dev->name, -err );
- kfree ( dev );
+ printk_err ("Cannot register HDLC device for port %d"
+ " (errno %d)\n", i, -err );
card->nports = i;
break;
}
spin_lock_init ( &card->card_lock );
printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n",
- card->ports[0].dev->name,
- card->ports[card->nports-1].dev->name,
+ hdlc_to_dev(&card->ports[0].hdlc)->name,
+ hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name,
type_strings[card->type], card->irq, card->nports );
}
for ( i = 0 ; i < card->nports ; i++ )
{
- unregister_netdev ( card->ports[i].dev );
- kfree ( card->ports[i].dev );
+ unregister_hdlc_device ( &card->ports[i].hdlc );
}
fst_disable_intr ( card );
module_init ( fst_init );
module_exit ( fst_cleanup_module );
-MODULE_LICENSE("GPL");
* A short common prefix is useful for routines within the driver to avoid
* conflict with other similar drivers and I chosen to use "fst_" for this
* purpose (FarSite T-series).
- *
- * Finally the device driver needs a short network interface name. Since
- * "hdlc" is already in use I've chosen the even less informative "sync"
- * for the present.
*/
#define FST_NAME "fst" /* In debug/info etc */
-#define FST_NDEV_NAME "sync" /* For net interface */
#define FST_DEV_NAME "farsync" /* For misc interfaces */
u32 bp; /* Buffer Pointer (24 bits) */
u16 len; /* Data Length */
u8 stat; /* Status */
- u8 unused2;
+ u8 unused; /* pads to 2-byte boundary */
}__attribute__ ((packed)) pkt_desc;
#define MD0_CRC_ITU_0 0x06
#define MD0_CRC_ITU 0x07
-#define MD2_NRZI 0x20 /* NRZI mode */
+#define MD2_NRZ 0x00
+#define MD2_NRZI 0x20
+#define MD2_MANCHESTER 0x80
+#define MD2_FM_MARK 0xA0
+#define MD2_FM_SPACE 0xC0
#define MD2_LOOPBACK 0x03 /* Local data Loopback */
#define CTL_NORTS 0x01
#error Either hd64570.h or hd64572.h must be included
#endif
-
-static card_t *first_card;
-static card_t **new_card = &first_card;
-
-
-/* Maximum events to handle at each interrupt - should I increase it? */
-#define INTR_WORK 4
+static char sca_version[]="1.09";
#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET)
#define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
#define sca_ina(reg, card) sca_inw(reg, card)
#define writea(value, ptr) writew(value, ptr)
+#else /* HD64572 */
+#define sca_outa(value, reg, card) sca_outl(value, reg, card)
+#define sca_ina(reg, card) sca_inl(reg, card)
+#define writea(value, ptr) writel(value, ptr)
+#endif
+
static inline int sca_intr_status(card_t *card)
{
+ u8 result = 0;
+
+#ifdef __HD64570_H /* HD64570 */
u8 isr0 = sca_in(ISR0, card);
u8 isr1 = sca_in(ISR1, card);
- u8 result = 0;
if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
- return result;
-}
-
#else /* HD64572 */
-#define sca_outa(value, reg, card) sca_outl(value, reg, card)
-#define sca_ina(reg, card) sca_inl(reg, card)
-#define writea(value, ptr) writel(value, ptr)
-
-
-static inline int sca_intr_status(card_t *card)
-{
u32 isr0 = sca_inl(ISR0, card);
- u8 result = 0;
if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0);
if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0);
if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0);
if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1);
- return result;
-}
-
#endif /* HD64570 vs HD64572 */
+ if (!(result & SCA_INTR_DMAC_TX(0)))
+ if (sca_in(DSR_TX(0), card) & DSR_EOM)
+ result |= SCA_INTR_DMAC_TX(0);
+ if (!(result & SCA_INTR_DMAC_TX(1)))
+ if (sca_in(DSR_TX(1), card) & DSR_EOM)
+ result |= SCA_INTR_DMAC_TX(1);
+
+ return result;
+}
-static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc,
- u8 rxin)
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin)
{
struct sk_buff *skb;
u16 len;
openwin(card, 0);
#endif
skb_put(skb, len);
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len);
debug_frame(skb);
#endif
port->hdlc.stats.rx_packets++;
port->hdlc.stats.rx_bytes += skb->len;
- hdlc_netif_rx(&port->hdlc, skb);
+ skb->mac.raw = skb->data;
+ skb->dev = hdlc_to_dev(&port->hdlc);
+ skb->protocol = htons(ETH_P_HDLC);
+ netif_rx(skb);
}
pkt_desc *desc;
u32 cda = sca_ina(dmac + CDAL, card);
- if (cda == desc_off)
+ if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
break; /* No frame received */
-#ifdef __HD64572_H
- if (cda == desc_off + 8)
- break; /* SCA-II updates CDA in 2 steps */
-#endif
-
desc = desc_address(port, port->rxin, 0);
stat = readb(&desc->stat);
if (!(stat & ST_RX_EOM))
DSR_TX(phy_node(port)), card);
while (1) {
- u32 desc_off = desc_offset(port, port->txlast, 1);
pkt_desc *desc;
- u16 len;
- if (sca_ina(dmac + CDAL, card) == desc_off)
+ u32 desc_off = desc_offset(port, port->txlast, 1);
+ u32 cda = sca_ina(dmac + CDAL, card);
+ if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
break; /* Transmitter is/will_be sending this frame */
desc = desc_address(port, port->txlast, 1);
- len = readw(&desc->len);
-
port->hdlc.stats.tx_packets++;
- port->hdlc.stats.tx_bytes += len;
+ port->hdlc.stats.tx_bytes += readw(&desc->len);
writeb(0, &desc->stat); /* Free descriptor */
-
port->txlast = (port->txlast + 1) %
port_to_card(port)->ring_buffers;
}
static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
{
card_t *card = dev_id;
- int boguscnt = INTR_WORK;
+/* Maximum events to handle at each interrupt - should I increase it? */
+ int boguscnt = 4;
int i;
u8 stat;
}
if (--boguscnt < 0) {
+#if 0
printk(KERN_ERR "%s: too much work at "
"interrupt\n",
hdlc_to_name(&port->hdlc));
+#endif
goto exit;
}
}
-static inline int sca_set_loopback(port_t *port, int line)
+static void sca_set_port(port_t *port)
{
card_t* card = port_to_card(port);
u8 msci = get_msci(port);
u8 md2 = sca_in(msci + MD2, card);
-
- switch(line) {
- case LINE_DEFAULT:
- md2 &= ~MD2_LOOPBACK;
- port->line &= ~LINE_LOOPBACK;
- break;
-
- case LINE_LOOPBACK:
- md2 |= MD2_LOOPBACK;
- port->line |= LINE_LOOPBACK;
- break;
-
- default:
- return -EINVAL;
- }
-
- sca_out(md2, msci + MD2, card);
- return 0;
-}
-
-
-
-static void sca_set_clock(port_t *port)
-{
- card_t *card = port_to_card(port);
- u8 msci = get_msci(port);
unsigned int tmc, br = 10, brv = 1024;
- if (port->clkrate > 0) {
+
+ if (port->settings.clock_rate > 0) {
/* Try lower br for better accuracy*/
do {
br--;
brv >>= 1; /* brv = 2^9 = 512 max in specs */
/* Baud Rate = CLOCK_BASE / TMC / 2^BR */
- tmc = CLOCK_BASE / (brv * port->clkrate);
+ tmc = CLOCK_BASE / (brv * port->settings.clock_rate);
}while(br > 1 && tmc <= 128);
if (tmc < 1) {
} else if (tmc > 255)
tmc = 256; /* tmc=0 means 256 - low baud rates */
- port->clkrate = CLOCK_BASE / (brv * tmc);
+ port->settings.clock_rate = CLOCK_BASE / (brv * tmc);
} else {
br = 9; /* Minimum clock rate */
tmc = 256; /* 8bit = 0 */
- port->clkrate = CLOCK_BASE / (256 * 512);
+ port->settings.clock_rate = CLOCK_BASE / (256 * 512);
}
port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
/* Set BRG bits */
sca_out(port->rxs, msci + RXS, card);
sca_out(port->txs, msci + TXS, card);
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+ else
+ md2 &= ~MD2_LOOPBACK;
+
+ sca_out(md2, msci + MD2, card);
+
}
-static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi)
+static void sca_open(hdlc_device *hdlc)
{
+ port_t *port = hdlc_to_port(hdlc);
card_t* card = port_to_card(port);
u8 msci = get_msci(port);
- u8 md2 = (nrzi ? MD2_NRZI : 0) |
- ((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0);
- u8 ctl = (idle ? CTL_IDLE : 0);
-#ifdef __HD64572_H
- ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */
-#endif
+ u8 md0, md2;
+
+ switch(port->encoding) {
+ case ENCODING_NRZ: md2 = MD2_NRZ; break;
+ case ENCODING_NRZI: md2 = MD2_NRZI; break;
+ case ENCODING_FM_MARK: md2 = MD2_FM_MARK; break;
+ case ENCODING_FM_SPACE: md2 = MD2_FM_SPACE; break;
+ default: md2 = MD2_MANCHESTER;
+ }
+
+ if (port->settings.loopback)
+ md2 |= MD2_LOOPBACK;
+
+ switch(port->parity) {
+ case PARITY_CRC16_PR0: md0 = MD0_HDLC | MD0_CRC_16_0; break;
+ case PARITY_CRC16_PR1: md0 = MD0_HDLC | MD0_CRC_16; break;
+#ifdef __HD64570_H
+ case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
+#else
+ case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
+#endif
+ case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
+ default: md0 = MD0_HDLC | MD0_CRC_NONE;
+ }
sca_out(CMD_RESET, msci + CMD, card);
- sca_out(MD0_HDLC | crc, msci + MD0, card);
+ sca_out(md0, msci + MD0, card);
sca_out(0x00, msci + MD1, card); /* no address field check */
sca_out(md2, msci + MD2, card);
sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
- sca_out(ctl, msci + CTL, card);
+#ifdef __HD64570_H
+ sca_out(CTL_IDLE, msci + CTL, card);
+#else
+ /* Skip the rest of underrun frame */
+ sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
+#endif
#ifdef __HD64570_H
/* Allow at least 8 bytes before requesting RX DMA operation */
sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
#else
sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
- /* Setting than to larger value may cause Illegal Access */
- sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */
- sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
- sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */
+ sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
+ sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
+ sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
+ sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
#endif
+/* We're using the following interrupts:
+ - TXINT (DMAC completed all transmisions, underflow or CTS change)
+ - all DMA interrupts
+*/
#ifdef __HD64570_H
/* MSCI TX INT IRQ enable */
sca_out(IE0_TXINT, msci + IE0, card);
- sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */
+ sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */
sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08),
IER0, card);
/* DMA IRQ enable */
sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
IER1, card);
#else
- /* MSCI TX INT and underrrun IRQ enable */
+ /* MSCI TX INT IRQ enable */
sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card);
/* DMA & MSCI IRQ enable */
sca_outl(sca_in(IER0, card) |
sca_out(port->txs, msci + TXS, card);
sca_out(CMD_TX_ENABLE, msci + CMD, card);
sca_out(CMD_RX_ENABLE, msci + CMD, card);
+
+ netif_start_queue(hdlc_to_dev(hdlc));
+}
+
+
+
+static void sca_close(hdlc_device *hdlc)
+{
+ port_t *port = hdlc_to_port(hdlc);
+
+ /* reset channel */
+ netif_stop_queue(hdlc_to_dev(hdlc));
+ sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
}
-#ifdef DEBUG_RINGS
+static int sca_attach(hdlc_device *hdlc, unsigned short encoding,
+ unsigned short parity)
+{
+ if (encoding != ENCODING_NRZ &&
+ encoding != ENCODING_NRZI &&
+ encoding != ENCODING_FM_MARK &&
+ encoding != ENCODING_FM_SPACE &&
+ encoding != ENCODING_MANCHESTER)
+ return -EINVAL;
+
+ if (parity != PARITY_NONE &&
+ parity != PARITY_CRC16_PR0 &&
+ parity != PARITY_CRC16_PR1 &&
+#ifdef __HD64570_H
+ parity != PARITY_CRC16_PR0_CCITT &&
+#else
+ parity != PARITY_CRC32_PR1_CCITT &&
+#endif
+ parity != PARITY_CRC16_PR1_CCITT)
+ return -EINVAL;
+
+ hdlc_to_port(hdlc)->encoding = encoding;
+ hdlc_to_port(hdlc)->parity = parity;
+ return 0;
+}
+
+
+
+#ifdef CONFIG_HDLC_DEBUG_RINGS
static void sca_dump_rings(hdlc_device *hdlc)
{
port_t *port = hdlc_to_port(hdlc);
openwin(card, page); /* Restore original page */
#endif
}
-#endif /* DEBUG_RINGS */
-
-
-
-static void sca_open(hdlc_device *hdlc)
-{
- port_t *port = hdlc_to_port(hdlc);
-
- sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0);
- netif_start_queue(hdlc_to_dev(hdlc));
-}
-
-
-static void sca_close(hdlc_device *hdlc)
-{
- port_t *port = hdlc_to_port(hdlc);
-
- /* reset channel */
- netif_stop_queue(hdlc_to_dev(hdlc));
- sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
-}
+#endif /* CONFIG_HDLC_DEBUG_RINGS */
-static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb)
+static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
- struct net_device *dev = hdlc_to_dev(hdlc);
card_t *card = port_to_card(port);
pkt_desc *desc;
u32 buff, len;
desc = desc_address(port, port->txin + 1, 1);
if (readb(&desc->stat)) { /* allow 1 packet gap */
/* should never happen - previous xmit should stop queue */
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
#endif
netif_stop_queue(dev);
return 1; /* request packet to be queued */
}
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len);
debug_frame(skb);
#endif
+++ /dev/null
-/*
- * Generic HDLC support routines for Linux
- *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
- *
- * 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.
- *
- * Current status:
- * - this is work in progress
- * - not heavily tested on SMP
- * - currently supported:
- * * raw IP-in-HDLC
- * * Cisco HDLC
- * * Frame Relay with ANSI or CCITT LMI (both user and network side)
- * * PPP (using syncppp.c)
- * * X.25
- *
- * Use sethdlc utility to set line parameters, protocol and PVCs
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/pkt_sched.h>
-#include <linux/inetdevice.h>
-#include <linux/lapb.h>
-#include <linux/rtnetlink.h>
-#include <linux/hdlc.h>
-
-/* #define DEBUG_PKT */
-/* #define DEBUG_HARD_HEADER */
-/* #define DEBUG_FECN */
-/* #define DEBUG_BECN */
-
-static const char* version = "HDLC support module revision 1.02 for Linux 2.4";
-
-
-#define CISCO_MULTICAST 0x8F /* Cisco multicast address */
-#define CISCO_UNICAST 0x0F /* Cisco unicast address */
-#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
-#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */
-#define CISCO_ADDR_REQ 0 /* Cisco address request */
-#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
-#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-
-/********************************************************
- *
- * Cisco HDLC support
- *
- *******************************************************/
-
-static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
- u16 type, void *daddr, void *saddr,
- unsigned int len)
-{
- hdlc_header *data;
-#ifdef DEBUG_HARD_HEADER
- printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
-#endif
-
- skb_push(skb, sizeof(hdlc_header));
- data = (hdlc_header*)skb->data;
- if (type == CISCO_KEEPALIVE)
- data->address = CISCO_MULTICAST;
- else
- data->address = CISCO_UNICAST;
- data->control = 0;
- data->protocol = htons(type);
-
- return sizeof(hdlc_header);
-}
-
-
-
-static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
- u32 par1, u32 par2)
-{
- struct sk_buff *skb;
- cisco_packet *data;
-
- skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet));
- if (!skb) {
- printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n",
- hdlc_to_name(hdlc));
- return;
- }
- skb_reserve(skb, 4);
- cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
- NULL, NULL, 0);
- data = (cisco_packet*)skb->tail;
-
- data->type = htonl(type);
- data->par1 = htonl(par1);
- data->par2 = htonl(par2);
- data->rel = 0xFFFF;
- data->time = htonl(jiffies * 1000 / HZ);
-
- skb_put(skb, sizeof(cisco_packet));
- skb->priority = TC_PRIO_CONTROL;
- skb->dev = hdlc_to_dev(hdlc);
-
- dev_queue_xmit(skb);
-}
-
-
-
-static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
- hdlc_header *data = (hdlc_header*)skb->data;
- cisco_packet *cisco_data;
- struct in_device *in_dev;
- u32 addr, mask;
-
- if (skb->len<sizeof(hdlc_header))
- goto rx_error;
-
- if (data->address != CISCO_MULTICAST &&
- data->address != CISCO_UNICAST)
- goto rx_error;
-
- skb_pull(skb, sizeof(hdlc_header));
-
- switch(ntohs(data->protocol)) {
- case ETH_P_IP:
- case ETH_P_IPX:
- case ETH_P_IPV6:
- skb->protocol = data->protocol;
- skb->dev = hdlc_to_dev(hdlc);
- netif_rx(skb);
- return;
-
- case CISCO_SYS_INFO:
- /* Packet is not needed, drop it. */
- dev_kfree_skb_any(skb);
- return;
-
- case CISCO_KEEPALIVE:
- if (skb->len != CISCO_PACKET_LEN &&
- skb->len != CISCO_BIG_PACKET_LEN) {
- printk(KERN_INFO "%s: Invalid length of Cisco "
- "control packet (%d bytes)\n",
- hdlc_to_name(hdlc), skb->len);
- goto rx_error;
- }
-
- cisco_data = (cisco_packet*)skb->data;
-
- switch(ntohl (cisco_data->type)) {
- case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
- in_dev = hdlc_to_dev(hdlc)->ip_ptr;
- addr = 0;
- mask = ~0; /* is the mask correct? */
-
- if (in_dev != NULL) {
- struct in_ifaddr **ifap = &in_dev->ifa_list;
-
- while (*ifap != NULL) {
- if (strcmp(hdlc_to_name(hdlc),
- (*ifap)->ifa_label) == 0) {
- addr = (*ifap)->ifa_local;
- mask = (*ifap)->ifa_mask;
- break;
- }
- ifap = &(*ifap)->ifa_next;
- }
-
- cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
- addr, mask);
- }
- dev_kfree_skb_any(skb);
- return;
-
- case CISCO_ADDR_REPLY:
- printk(KERN_INFO "%s: Unexpected Cisco IP address "
- "reply\n", hdlc_to_name(hdlc));
- goto rx_error;
-
- case CISCO_KEEPALIVE_REQ:
- hdlc->lmi.rxseq = ntohl(cisco_data->par1);
- if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) {
- hdlc->lmi.last_poll = jiffies;
- if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) {
- u32 sec, min, hrs, days;
- sec = ntohl(cisco_data->time) / 1000;
- min = sec / 60; sec -= min * 60;
- hrs = min / 60; min -= hrs * 60;
- days = hrs / 24; hrs -= days * 24;
- printk(KERN_INFO "%s: Link up (peer "
- "uptime %ud%uh%um%us)\n",
- hdlc_to_name(hdlc), days, hrs,
- min, sec);
- }
- hdlc->lmi.state |= LINK_STATE_RELIABLE;
- }
-
- dev_kfree_skb_any(skb);
- return;
- } /* switch(keepalive type) */
- } /* switch(protocol) */
-
- printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
- data->protocol);
- dev_kfree_skb_any(skb);
- return;
-
- rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
- dev_kfree_skb_any(skb);
-}
-
-
-
-static void cisco_timer(unsigned long arg)
-{
- hdlc_device *hdlc = (hdlc_device*)arg;
-
- if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
- (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) {
- hdlc->lmi.state &= ~LINK_STATE_RELIABLE;
- printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
- }
-
- cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq,
- hdlc->lmi.rxseq);
- hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
-
- hdlc->timer.function = cisco_timer;
- hdlc->timer.data = arg;
- add_timer(&hdlc->timer);
-}
-
-
-
-/******************************************************************
- *
- * generic Frame Relay routines
- *
- *****************************************************************/
-
-
-static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
- u16 type, void *daddr, void *saddr, unsigned int len)
-{
- u16 head_len;
-
- if (!daddr)
- daddr = dev->broadcast;
-
-#ifdef DEBUG_HARD_HEADER
- printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
-#endif
-
- switch(type) {
- case ETH_P_IP:
- head_len = 4;
- skb_push(skb, head_len);
- skb->data[3] = NLPID_IP;
- break;
-
- case ETH_P_IPV6:
- head_len = 4;
- skb_push(skb, head_len);
- skb->data[3] = NLPID_IPV6;
- break;
-
- case LMI_PROTO:
- head_len = 4;
- skb_push(skb, head_len);
- skb->data[3] = LMI_PROTO;
- break;
-
- default:
- head_len = 10;
- skb_push(skb, head_len);
- skb->data[3] = FR_PAD;
- skb->data[4] = NLPID_SNAP;
- skb->data[5] = FR_PAD;
- skb->data[6] = FR_PAD;
- skb->data[7] = FR_PAD;
- skb->data[8] = type>>8;
- skb->data[9] = (u8)type;
- }
-
- memcpy(skb->data, daddr, 2);
- skb->data[2] = FR_UI;
-
- return head_len;
-}
-
-
-
-static inline void fr_log_dlci_active(pvc_device *pvc)
-{
- printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
- pvc->state & PVC_STATE_ACTIVE ? "" : "in",
- pvc->state & PVC_STATE_NEW ? " new" : "");
-}
-
-
-
-static inline u8 fr_lmi_nextseq(u8 x)
-{
- x++;
- return x ? x : 1;
-}
-
-
-
-static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
-{
- struct sk_buff *skb;
- pvc_device *pvc = hdlc->first_pvc;
- int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH;
- int stat_len = 3;
- u8 *data;
- int i = 0;
-
- if (mode_is(hdlc, MODE_DCE) && fullrep) {
- len += hdlc->pvc_count * (2 + stat_len);
- if (len > HDLC_MAX_MTU) {
- printk(KERN_WARNING "%s: Too many PVCs while sending "
- "LMI full report\n", hdlc_to_name(hdlc));
- return;
- }
- }
-
- skb = dev_alloc_skb(len);
- if (!skb) {
- printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
- hdlc_to_name(hdlc));
- return;
- }
- memset(skb->data, 0, len);
- skb_reserve(skb, 4);
- fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
- data = skb->tail;
- data[i++] = LMI_CALLREF;
- data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY;
- if (mode_is(hdlc, MODE_FR_ANSI))
- data[i++] = LMI_ANSI_LOCKSHIFT;
- data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE :
- LMI_REPTYPE;
- data[i++] = LMI_REPT_LEN;
- data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
-
- data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE;
- data[i++] = LMI_INTEG_LEN;
- data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq);
- data[i++] = hdlc->lmi.rxseq;
-
- if (mode_is(hdlc, MODE_DCE) && fullrep) {
- while (pvc) {
- data[i++] = mode_is(hdlc, MODE_FR_CCITT) ?
- LMI_CCITT_PVCSTAT:LMI_PVCSTAT;
- data[i++] = stat_len;
-
- if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
- (pvc->netdev.flags & IFF_UP) &&
- !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) {
- pvc->state |= PVC_STATE_NEW;
- fr_log_dlci_active(pvc);
- }
-
- dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
- data+i, pvc->state);
- i += stat_len;
- pvc = pvc->next;
- }
- }
-
- skb_put(skb, i);
- skb->priority = TC_PRIO_CONTROL;
- skb->dev = hdlc_to_dev(hdlc);
-
- dev_queue_xmit(skb);
-}
-
-
-
-static void fr_timer(unsigned long arg)
-{
- hdlc_device *hdlc = (hdlc_device*)arg;
- int i, cnt = 0, reliable;
- u32 list;
-
- if (mode_is(hdlc, MODE_DCE))
- reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ);
- else {
- hdlc->lmi.last_errors <<= 1; /* Shift the list */
- if (hdlc->lmi.state & LINK_STATE_REQUEST) {
- printk(KERN_INFO "%s: No LMI status reply received\n",
- hdlc_to_name(hdlc));
- hdlc->lmi.last_errors |= 1;
- }
-
- for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393;
- i++, list >>= 1)
- cnt += (list & 1); /* errors count */
-
- reliable = (cnt < hdlc->lmi.N392);
- }
-
- if ((hdlc->lmi.state & LINK_STATE_RELIABLE) !=
- (reliable ? LINK_STATE_RELIABLE : 0)) {
- pvc_device *pvc = hdlc->first_pvc;
-
- while (pvc) {/* Deactivate all PVCs */
- pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE);
- pvc = pvc->next;
- }
-
- hdlc->lmi.state ^= LINK_STATE_RELIABLE;
- printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
- reliable ? "" : "un");
-
- if (reliable) {
- hdlc->lmi.N391cnt = 0; /* Request full status */
- hdlc->lmi.state |= LINK_STATE_CHANGED;
- }
- }
-
- if (mode_is(hdlc, MODE_DCE))
- hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ;
- else {
- if (hdlc->lmi.N391cnt)
- hdlc->lmi.N391cnt--;
-
- fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0);
-
- hdlc->lmi.state |= LINK_STATE_REQUEST;
- hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
- }
-
- hdlc->timer.function = fr_timer;
- hdlc->timer.data = arg;
- add_timer(&hdlc->timer);
-}
-
-
-
-static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
-{
- int stat_len;
- pvc_device *pvc;
- int reptype = -1, error;
- u8 rxseq, txseq;
- int i;
-
- if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ?
- LMI_ANSI_LENGTH : LMI_LENGTH)) {
- printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
- return 1;
- }
-
- if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ?
- LMI_STATUS : LMI_STATUS_ENQUIRY)) {
- printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
- hdlc_to_name(hdlc), skb->data[2],
- mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply");
- return 1;
- }
-
- i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6;
-
- if (skb->data[i] !=
- (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
- printk(KERN_INFO "%s: Not a report type=%x\n",
- hdlc_to_name(hdlc), skb->data[i]);
- return 1;
- }
- i++;
-
- i++; /* Skip length field */
-
- reptype = skb->data[i++];
-
- if (skb->data[i]!=
- (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
- printk(KERN_INFO "%s: Unsupported status element=%x\n",
- hdlc_to_name(hdlc), skb->data[i]);
- return 1;
- }
- i++;
-
- i++; /* Skip length field */
-
- hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */
- rxseq = skb->data[i++]; /* Should confirm our sequence */
-
- txseq = hdlc->lmi.txseq;
-
- if (mode_is(hdlc, MODE_DCE)) {
- if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
- printk(KERN_INFO "%s: Unsupported report type=%x\n",
- hdlc_to_name(hdlc), reptype);
- return 1;
- }
- }
-
- error = 0;
- if (!(hdlc->lmi.state & LINK_STATE_RELIABLE))
- error = 1;
-
- if (rxseq == 0 || rxseq != txseq) {
- hdlc->lmi.N391cnt = 0; /* Ask for full report next time */
- error = 1;
- }
-
- if (mode_is(hdlc, MODE_DCE)) {
- if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) {
-/* Stop sending full report - the last one has been confirmed by DTE */
- hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT;
- pvc = hdlc->first_pvc;
- while (pvc) {
- if (pvc->state & PVC_STATE_NEW) {
- pvc->state &= ~PVC_STATE_NEW;
- pvc->state |= PVC_STATE_ACTIVE;
- fr_log_dlci_active(pvc);
-
-/* Tell DTE that new PVC is now active */
- hdlc->lmi.state |= LINK_STATE_CHANGED;
- }
- pvc = pvc->next;
- }
- }
-
- if (hdlc->lmi.state & LINK_STATE_CHANGED) {
- reptype = LMI_FULLREP;
- hdlc->lmi.state |= LINK_STATE_FULLREP_SENT;
- hdlc->lmi.state &= ~LINK_STATE_CHANGED;
- }
-
- fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
- return 0;
- }
-
- /* DTE */
-
- if (reptype != LMI_FULLREP || error)
- return 0;
-
- stat_len = 3;
- pvc = hdlc->first_pvc;
-
- while (pvc) {
- pvc->newstate = 0;
- pvc = pvc->next;
- }
-
- while (skb->len >= i + 2 + stat_len) {
- u16 dlci;
- u8 state = 0;
-
- if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ?
- LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
- printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
- hdlc_to_name(hdlc), skb->data[i]);
- return 1;
- }
- i++;
-
- if (skb->data[i] != stat_len) {
- printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
- hdlc_to_name(hdlc), skb->data[i]);
- return 1;
- }
- i++;
-
- dlci = status_to_dlci(hdlc, skb->data+i, &state);
- pvc = find_pvc(hdlc, dlci);
-
- if (pvc)
- pvc->newstate = state;
- else if (state == PVC_STATE_NEW)
- printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
- hdlc_to_name(hdlc), dlci);
-
- i += stat_len;
- }
-
- pvc = hdlc->first_pvc;
-
- while (pvc) {
- if (pvc->newstate == PVC_STATE_NEW)
- pvc->newstate = PVC_STATE_ACTIVE;
-
- pvc->newstate |= (pvc->state &
- ~(PVC_STATE_NEW|PVC_STATE_ACTIVE));
- if (pvc->state != pvc->newstate) {
- pvc->state = pvc->newstate;
- fr_log_dlci_active(pvc);
- }
- pvc = pvc->next;
- }
-
- /* Next full report after N391 polls */
- hdlc->lmi.N391cnt = hdlc->lmi.N391;
-
- return 0;
-}
-
-
-
-static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
- fr_hdr *fh = (fr_hdr*)skb->data;
- u8 *data = skb->data;
- u16 dlci;
- pvc_device *pvc;
-
- if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
- goto rx_error;
-
- dlci = q922_to_dlci(skb->data);
-
- if (dlci == LMI_DLCI) {
- if (data[3] == LMI_PROTO) {
- if (fr_lmi_recv(hdlc, skb))
- goto rx_error;
- else {
- /* No request pending */
- hdlc->lmi.state &= ~LINK_STATE_REQUEST;
- hdlc->lmi.last_poll = jiffies;
- dev_kfree_skb_any(skb);
- return;
- }
- }
-
- printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
- hdlc_to_name(hdlc));
- goto rx_error;
- }
-
- pvc = find_pvc(hdlc, dlci);
- if (!pvc) {
-#ifdef DEBUG_PKT
- printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
- hdlc_to_name(hdlc), dlci);
-#endif
- goto rx_error;
- }
-
- if ((pvc->netdev.flags & IFF_UP) == 0) {
-#ifdef DEBUG_PKT
- printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
- hdlc_to_name(hdlc), dlci);
-#endif
- goto rx_error;
- }
-
- pvc->stats.rx_packets++; /* PVC traffic */
- pvc->stats.rx_bytes += skb->len;
-
- if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) {
-#ifdef DEBUG_FECN
- printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
- fh->fecn ? "N" : "FF");
-#endif
- pvc->state ^= PVC_STATE_FECN;
- }
-
- if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) {
-#ifdef DEBUG_FECN
- printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
- fh->becn ? "N" : "FF");
-#endif
- pvc->state ^= PVC_STATE_BECN;
- }
-
- if (pvc->state & PVC_STATE_BECN)
- pvc->stats.rx_compressed++;
-
- if (data[3] == NLPID_IP) {
- skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
- skb->protocol = htons(ETH_P_IP);
- skb->dev = &pvc->netdev;
- netif_rx(skb);
- return;
- }
-
-
- if (data[3] == NLPID_IPV6) {
- skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
- skb->protocol = htons(ETH_P_IPV6);
- skb->dev = &pvc->netdev;
- netif_rx(skb);
- return;
- }
-
- if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD &&
- data[6] == FR_PAD && data[7] == FR_PAD &&
- ((data[8]<<8) | data[9]) == ETH_P_ARP) {
- skb_pull(skb, 10);
- skb->protocol = htons(ETH_P_ARP);
- skb->dev = &pvc->netdev;
- netif_rx(skb);
- return;
- }
-
- printk(KERN_INFO "%s: Unusupported protocol %x\n",
- hdlc_to_name(hdlc), data[3]);
- dev_kfree_skb_any(skb);
- return;
-
- rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
- dev_kfree_skb_any(skb);
-}
-
-
-
-static void fr_cisco_open(hdlc_device *hdlc)
-{
- hdlc->lmi.state = LINK_STATE_CHANGED;
- hdlc->lmi.txseq = hdlc->lmi.rxseq = 0;
- hdlc->lmi.last_errors = 0xFFFFFFFF;
- hdlc->lmi.N391cnt = 0;
-
- init_timer(&hdlc->timer);
- hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */
- hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer;
- hdlc->timer.data = (unsigned long)hdlc;
- add_timer(&hdlc->timer);
-}
-
-
-
-static void fr_cisco_close(hdlc_device *hdlc)
-{
- pvc_device *pvc = hdlc->first_pvc;
-
- del_timer_sync(&hdlc->timer);
-
- while(pvc) { /* NULL in Cisco mode */
- dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
- pvc = pvc->next;
- }
-}
-
-
-
-/******************************************************************
- *
- * generic HDLC routines
- *
- *****************************************************************/
-
-
-
-static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-
-
-/********************************************************
- *
- * PVC device routines
- *
- *******************************************************/
-
-static int pvc_open(struct net_device *dev)
-{
- pvc_device *pvc = dev_to_pvc(dev);
- int result = 0;
-
- if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
- return -EIO; /* Master must be UP in order to activate PVC */
-
- memset(&(pvc->stats), 0, sizeof(struct net_device_stats));
- pvc->state = 0;
-
- if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc)
- result = pvc->master->open_pvc(pvc);
- if (result)
- return result;
-
- pvc->master->lmi.state |= LINK_STATE_CHANGED;
- return 0;
-}
-
-
-
-static int pvc_close(struct net_device *dev)
-{
- pvc_device *pvc = dev_to_pvc(dev);
- pvc->state = 0;
-
- if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc)
- pvc->master->close_pvc(pvc);
-
- pvc->master->lmi.state |= LINK_STATE_CHANGED;
- return 0;
-}
-
-
-
-static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- pvc_device *pvc = dev_to_pvc(dev);
-
- if (pvc->state & PVC_STATE_ACTIVE) {
- skb->dev = hdlc_to_dev(pvc->master);
- pvc->stats.tx_bytes += skb->len;
- pvc->stats.tx_packets++;
- if (pvc->state & PVC_STATE_FECN)
- pvc->stats.tx_compressed++; /* TX Congestion counter */
- dev_queue_xmit(skb);
- } else {
- pvc->stats.tx_dropped++;
- dev_kfree_skb(skb);
- }
-
- return 0;
-}
-
-
-
-static struct net_device_stats *pvc_get_stats(struct net_device *dev)
-{
- pvc_device *pvc = dev_to_pvc(dev);
- return &pvc->stats;
-}
-
-
-
-static int pvc_change_mtu(struct net_device *dev, int new_mtu)
-{
- if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
- return -EINVAL;
- dev->mtu = new_mtu;
- return 0;
-}
-
-
-
-static void destroy_pvc_list(hdlc_device *hdlc)
-{
- pvc_device *pvc = hdlc->first_pvc;
- while(pvc) {
- pvc_device *next = pvc->next;
- unregister_netdevice(&pvc->netdev);
- kfree(pvc);
- pvc = next;
- }
-
- hdlc->first_pvc = NULL; /* All PVCs destroyed */
- hdlc->pvc_count = 0;
- hdlc->lmi.state |= LINK_STATE_CHANGED;
-}
-
-
-
-/********************************************************
- *
- * X.25 protocol support routines
- *
- *******************************************************/
-
-#ifdef CONFIG_HDLC_X25
-/* These functions are callbacks called by LAPB layer */
-
-void x25_connect_disconnect(void *token, int reason, int code)
-{
- hdlc_device *hdlc = token;
- struct sk_buff *skb;
- unsigned char *ptr;
-
- if ((skb = dev_alloc_skb(1)) == NULL) {
- printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
- return;
- }
-
- ptr = skb_put(skb, 1);
- *ptr = code;
-
- skb->dev = hdlc_to_dev(hdlc);
- skb->protocol = htons(ETH_P_X25);
- skb->mac.raw = skb->data;
- skb->pkt_type = PACKET_HOST;
-
- netif_rx(skb);
-}
-
-void x25_connected(void *token, int reason)
-{
- x25_connect_disconnect(token, reason, 1);
-}
-
-void x25_disconnected(void *token, int reason)
-{
- x25_connect_disconnect(token, reason, 2);
-}
-
-
-int x25_data_indication(void *token, struct sk_buff *skb)
-{
- hdlc_device *hdlc = token;
- unsigned char *ptr;
-
- ptr = skb_push(skb, 1);
- *ptr = 0;
-
- skb->dev = hdlc_to_dev(hdlc);
- skb->protocol = htons(ETH_P_X25);
- skb->mac.raw = skb->data;
- skb->pkt_type = PACKET_HOST;
-
- return netif_rx(skb);
-}
-
-
-void x25_data_transmit(void *token, struct sk_buff *skb)
-{
- hdlc_device *hdlc = token;
- hdlc->xmit(hdlc, skb); /* Ignore return value :-( */
-}
-#endif /* CONFIG_HDLC_X25 */
-
-
-/********************************************************
- *
- * HDLC device routines
- *
- *******************************************************/
-
-static int hdlc_open(struct net_device *dev)
-{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- int result;
-
- if (hdlc->mode == MODE_NONE)
- return -ENOSYS;
-
- memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
- if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
- mode_is(hdlc, MODE_CISCO | MODE_SOFT))
- fr_cisco_open(hdlc);
-#ifdef CONFIG_HDLC_PPP
- else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
- sppp_attach(&hdlc->pppdev);
- /* sppp_attach nukes them. We don't need syncppp's ioctl */
- dev->do_ioctl = hdlc_ioctl;
- hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO;
- dev->type = ARPHRD_PPP;
- result = sppp_open(dev);
- if (result) {
- sppp_detach(dev);
- return result;
- }
- }
-#endif
-#ifdef CONFIG_HDLC_X25
- else if (mode_is(hdlc, MODE_X25)) {
- struct lapb_register_struct cb;
-
- cb.connect_confirmation = x25_connected;
- cb.connect_indication = x25_connected;
- cb.disconnect_confirmation = x25_disconnected;
- cb.disconnect_indication = x25_disconnected;
- cb.data_indication = x25_data_indication;
- cb.data_transmit = x25_data_transmit;
-
- result = lapb_register(hdlc, &cb);
- if (result != LAPB_OK)
- return result;
- }
-#endif
- result = hdlc->open(hdlc);
- if (result) {
- if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
- mode_is(hdlc, MODE_CISCO | MODE_SOFT))
- fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
- else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
- sppp_close(dev);
- sppp_detach(dev);
- dev->rebuild_header = NULL;
- dev->change_mtu = hdlc_change_mtu;
- dev->mtu = HDLC_MAX_MTU;
- dev->hard_header_len = 16;
- }
-#endif
-#ifdef CONFIG_HDLC_X25
- else if (mode_is(hdlc, MODE_X25))
- lapb_unregister(hdlc);
-#endif
- }
-
- return result;
-}
-
-
-
-static int hdlc_close(struct net_device *dev)
-{
- hdlc_device *hdlc = dev_to_hdlc(dev);
-
- hdlc->close(hdlc);
-
- if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
- mode_is(hdlc, MODE_CISCO | MODE_SOFT))
- fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
- else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
- sppp_close(dev);
- sppp_detach(dev);
- dev->rebuild_header = NULL;
- dev->change_mtu = hdlc_change_mtu;
- dev->mtu = HDLC_MAX_MTU;
- dev->hard_header_len = 16;
- }
-#endif
-#ifdef CONFIG_HDLC_X25
- else if (mode_is(hdlc, MODE_X25))
- lapb_unregister(hdlc);
-#endif
- return 0;
-}
-
-
-
-static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- hdlc_device *hdlc = dev_to_hdlc(dev);
-
-#ifdef CONFIG_HDLC_X25
- if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) {
- int result;
-
-
- /* X.25 to LAPB */
- switch (skb->data[0]) {
- case 0: /* Data to be transmitted */
- skb_pull(skb, 1);
- if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
- dev_kfree_skb(skb);
- return 0;
-
- case 1:
- if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
- if (result == LAPB_CONNECTED) {
- /* Send connect confirm. msg to level 3 */
- x25_connected(hdlc, 0);
- } else {
- printk(KERN_ERR "%s: LAPB connect "
- "request failed, error code = "
- "%i\n", hdlc_to_name(hdlc),
- result);
- }
- }
- break;
-
- case 2:
- if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) {
- if (result == LAPB_NOTCONNECTED) {
- /* Send disconnect confirm. msg to level 3 */
- x25_disconnected(hdlc, 0);
- } else {
- printk(KERN_ERR "%s: LAPB disconnect "
- "request failed, error code = "
- "%i\n", hdlc_to_name(hdlc),
- result);
- }
- }
- break;
-
- default:
- /* to be defined */
- break;
- }
-
- dev_kfree_skb(skb);
- return 0;
- } /* MODE_X25 */
-#endif /* CONFIG_HDLC_X25 */
-
- return hdlc->xmit(hdlc, skb);
-}
-
-
-
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb)
-{
-/* skb contains raw HDLC frame, in both hard- and software modes */
- skb->mac.raw = skb->data;
-
- switch(hdlc->mode & MODE_MASK) {
- case MODE_HDLC:
- skb->protocol = htons(ETH_P_IP);
- skb->dev = hdlc_to_dev(hdlc);
- netif_rx(skb);
- return;
-
- case MODE_FR:
- fr_netif(hdlc, skb);
- return;
-
- case MODE_CISCO:
- cisco_netif(hdlc, skb);
- return;
-
-#ifdef CONFIG_HDLC_PPP
- case MODE_PPP:
-#if 0
- sppp_input(hdlc_to_dev(hdlc), skb);
-#else
- skb->protocol = htons(ETH_P_WAN_PPP);
- skb->dev = hdlc_to_dev(hdlc);
- netif_rx(skb);
-#endif
- return;
-#endif
-#ifdef CONFIG_HDLC_X25
- case MODE_X25:
- skb->dev = hdlc_to_dev(hdlc);
- if (lapb_data_received(hdlc, skb) == LAPB_OK)
- return;
- break;
-#endif
- }
-
- hdlc->stats.rx_errors++;
- dev_kfree_skb_any(skb);
-}
-
-
-
-static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
-{
- return &dev_to_hdlc(dev)->stats;
-}
-
-
-
-static int hdlc_set_mode(hdlc_device *hdlc, int mode)
-{
- int result = -1; /* Default to soft modes */
- struct net_device *dev = hdlc_to_dev(hdlc);
-
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if(dev->flags & IFF_UP)
- return -EBUSY;
-
- dev->addr_len = 0;
- dev->hard_header = NULL;
- hdlc->mode = MODE_NONE;
-
- if (!(mode & MODE_SOFT))
- switch(mode & MODE_MASK) {
- case MODE_HDLC:
- result = hdlc->set_mode ?
- hdlc->set_mode(hdlc, MODE_HDLC) : 0;
- break;
-
- case MODE_CISCO: /* By card */
-#ifdef CONFIG_HDLC_PPP
- case MODE_PPP:
-#endif
-#ifdef CONFIG_HDLC_X25
- case MODE_X25:
-#endif
- case MODE_FR:
- result = hdlc->set_mode ?
- hdlc->set_mode(hdlc, mode) : -ENOSYS;
- break;
-
- default:
- return -EINVAL;
- }
-
- if (result) {
- mode |= MODE_SOFT; /* Try "host software" protocol */
-
- switch(mode & MODE_MASK) {
- case MODE_CISCO:
- dev->hard_header = cisco_hard_header;
- break;
-
-#ifdef CONFIG_HDLC_PPP
- case MODE_PPP:
- break;
-#endif
-#ifdef CONFIG_HDLC_X25
- case MODE_X25:
- break;
-#endif
-
- case MODE_FR:
- dev->hard_header = fr_hard_header;
- dev->addr_len = 2;
- *(u16*)dev->dev_addr = htons(LMI_DLCI);
- dlci_to_q922(dev->broadcast, LMI_DLCI);
- break;
-
- default:
- return -EINVAL;
- }
-
- result = hdlc->set_mode ?
- hdlc->set_mode(hdlc, MODE_HDLC) : 0;
- }
-
- if (result)
- return result;
-
- hdlc->mode = mode;
- switch(mode & MODE_MASK) {
-#ifdef CONFIG_HDLC_PPP
- case MODE_PPP: dev->type = ARPHRD_PPP; break;
-#endif
-#ifdef CONFIG_HDLC_X25
- case MODE_X25: dev->type = ARPHRD_X25; break;
-#endif
- case MODE_FR: dev->type = ARPHRD_FRAD; break;
- case MODE_CISCO: dev->type = ARPHRD_CISCO; break;
- default: dev->type = ARPHRD_RAWHDLC;
- }
-
- memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
- destroy_pvc_list(hdlc);
- return 0;
-}
-
-
-
-static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci)
-{
- pvc_device **pvc_p = &hdlc->first_pvc;
- pvc_device *pvc;
- int result, create = 1; /* Create or delete PVC */
-
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- if(dlci<0) {
- dlci = -dlci;
- create = 0;
- }
-
- if(dlci <= 0 || dlci >= 1024)
- return -EINVAL; /* Only 10 bits for DLCI, DLCI=0 is reserved */
-
- if(!mode_is(hdlc, MODE_FR))
- return -EINVAL; /* Only meaningfull on FR */
-
- while(*pvc_p) {
- if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
- break;
- pvc_p = &(*pvc_p)->next;
- }
-
- if (create) { /* Create PVC */
- if (*pvc_p != NULL)
- return -EEXIST;
-
- pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
- if (!pvc) {
- printk(KERN_WARNING "%s: Memory squeeze on "
- "hdlc_fr_pvc()\n", hdlc_to_name(hdlc));
- return -ENOBUFS;
- }
- memset(pvc, 0, sizeof(pvc_device));
-
- pvc->netdev.hard_start_xmit = pvc_xmit;
- pvc->netdev.get_stats = pvc_get_stats;
- pvc->netdev.open = pvc_open;
- pvc->netdev.stop = pvc_close;
- pvc->netdev.change_mtu = pvc_change_mtu;
- pvc->netdev.mtu = HDLC_MAX_MTU;
-
- pvc->netdev.type = ARPHRD_DLCI;
- pvc->netdev.hard_header_len = 16;
- pvc->netdev.hard_header = fr_hard_header;
- pvc->netdev.tx_queue_len = 0;
- pvc->netdev.flags = IFF_POINTOPOINT;
-
- pvc->master = hdlc;
- *(u16*)pvc->netdev.dev_addr = htons(dlci);
- dlci_to_q922(pvc->netdev.broadcast, dlci);
- pvc->netdev.addr_len = 2;
- pvc->netdev.irq = hdlc_to_dev(hdlc)->irq;
-
- result = dev_alloc_name(&pvc->netdev, "pvc%d");
- if (result < 0) {
- kfree(pvc);
- *pvc_p = NULL;
- return result;
- }
-
- if (register_netdevice(&pvc->netdev) != 0) {
- kfree(pvc);
- *pvc_p = NULL;
- return -EIO;
- }
-
- if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) {
- result = hdlc->create_pvc(pvc);
- if (result) {
- unregister_netdevice(&pvc->netdev);
- kfree(pvc);
- *pvc_p = NULL;
- return result;
- }
- }
-
- hdlc->lmi.state |= LINK_STATE_CHANGED;
- hdlc->pvc_count++;
- return 0;
- }
-
- if (*pvc_p == NULL) /* Delete PVC */
- return -ENOENT;
-
- pvc = *pvc_p;
-
- if (pvc->netdev.flags & IFF_UP)
- return -EBUSY; /* PVC in use */
-
- if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc)
- hdlc->destroy_pvc(pvc);
-
- hdlc->lmi.state |= LINK_STATE_CHANGED;
- hdlc->pvc_count--;
- *pvc_p = pvc->next;
- unregister_netdevice(&pvc->netdev);
- kfree(pvc);
- return 0;
-}
-
-
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- hdlc_device *hdlc = dev_to_hdlc(dev);
-
- switch(cmd) {
- case HDLCGMODE:
- ifr->ifr_ifru.ifru_ivalue = hdlc->mode;
- return 0;
-
- case HDLCSMODE:
- return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
- case HDLCPVC:
- return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
- default:
- if (hdlc->ioctl != NULL)
- return hdlc->ioctl(hdlc, ifr, cmd);
- }
-
- return -EINVAL;
-}
-
-
-
-static int hdlc_init(struct net_device *dev)
-{
- hdlc_device *hdlc = dev_to_hdlc(dev);
-
- memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
- dev->get_stats = hdlc_get_stats;
- dev->open = hdlc_open;
- dev->stop = hdlc_close;
- dev->hard_start_xmit = hdlc_xmit;
- dev->do_ioctl = hdlc_ioctl;
- dev->change_mtu = hdlc_change_mtu;
- dev->mtu = HDLC_MAX_MTU;
-
- dev->type = ARPHRD_RAWHDLC;
- dev->hard_header_len = 16;
-
- dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-
- return 0;
-}
-
-
-
-int register_hdlc_device(hdlc_device *hdlc)
-{
- int result;
- struct net_device *dev = hdlc_to_dev(hdlc);
-
- dev->init = hdlc_init;
- dev->priv = &hdlc->syncppp_ptr;
- hdlc->syncppp_ptr = &hdlc->pppdev;
- hdlc->pppdev.dev = dev;
- hdlc->mode = MODE_NONE;
- hdlc->lmi.T391 = 10; /* polling verification timer */
- hdlc->lmi.T392 = 15; /* link integrity verification polling timer */
- hdlc->lmi.N391 = 6; /* full status polling counter */
- hdlc->lmi.N392 = 3; /* error threshold */
- hdlc->lmi.N393 = 4; /* monitored events count */
-
- result = dev_alloc_name(dev, "hdlc%d");
- if (result<0)
- return result;
-
- result = register_netdev(dev);
- if (result != 0)
- return -EIO;
-
- MOD_INC_USE_COUNT;
- return 0;
-}
-
-
-
-void unregister_hdlc_device(hdlc_device *hdlc)
-{
- destroy_pvc_list(hdlc);
- unregister_netdev(hdlc_to_dev(hdlc));
- MOD_DEC_USE_COUNT;
-}
-
-MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
-MODULE_DESCRIPTION("HDLC support module");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(hdlc_netif_rx);
-EXPORT_SYMBOL(register_hdlc_device);
-EXPORT_SYMBOL(unregister_hdlc_device);
-
-static int __init hdlc_module_init(void)
-{
- printk(KERN_INFO "%s\n", version);
- return 0;
-}
-
-
-module_init(hdlc_module_init);
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ * Cisco HDLC support
+ *
+ * Copyright (C) 2000 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+#define CISCO_MULTICAST 0x8F /* Cisco multicast address */
+#define CISCO_UNICAST 0x0F /* Cisco unicast address */
+#define CISCO_KEEPALIVE 0x8035 /* Cisco keepalive protocol */
+#define CISCO_SYS_INFO 0x2000 /* Cisco interface/system info */
+#define CISCO_ADDR_REQ 0 /* Cisco address request */
+#define CISCO_ADDR_REPLY 1 /* Cisco address reply */
+#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
+
+
+static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
+ u16 type, void *daddr, void *saddr,
+ unsigned int len)
+{
+ hdlc_header *data;
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+ printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
+#endif
+
+ skb_push(skb, sizeof(hdlc_header));
+ data = (hdlc_header*)skb->data;
+ if (type == CISCO_KEEPALIVE)
+ data->address = CISCO_MULTICAST;
+ else
+ data->address = CISCO_UNICAST;
+ data->control = 0;
+ data->protocol = htons(type);
+
+ return sizeof(hdlc_header);
+}
+
+
+
+static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
+ u32 par1, u32 par2)
+{
+ struct sk_buff *skb;
+ cisco_packet *data;
+
+ skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
+ if (!skb) {
+ printk(KERN_WARNING
+ "%s: Memory squeeze on cisco_keepalive_send()\n",
+ hdlc_to_name(hdlc));
+ return;
+ }
+ skb_reserve(skb, 4);
+ cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
+ NULL, NULL, 0);
+ data = (cisco_packet*)skb->tail;
+
+ data->type = htonl(type);
+ data->par1 = htonl(par1);
+ data->par2 = htonl(par2);
+ data->rel = 0xFFFF;
+ data->time = htonl(jiffies * 1000 / HZ);
+
+ skb_put(skb, sizeof(cisco_packet));
+ skb->priority = TC_PRIO_CONTROL;
+ skb->dev = hdlc_to_dev(hdlc);
+
+ dev_queue_xmit(skb);
+}
+
+
+
+static void cisco_rx(struct sk_buff *skb)
+{
+ hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+ hdlc_header *data = (hdlc_header*)skb->data;
+ cisco_packet *cisco_data;
+ struct in_device *in_dev;
+ u32 addr, mask;
+
+ if (skb->len < sizeof(hdlc_header))
+ goto rx_error;
+
+ if (data->address != CISCO_MULTICAST &&
+ data->address != CISCO_UNICAST)
+ goto rx_error;
+
+ skb_pull(skb, sizeof(hdlc_header));
+
+ switch(ntohs(data->protocol)) {
+ case ETH_P_IP:
+ case ETH_P_IPX:
+ case ETH_P_IPV6:
+ skb->protocol = data->protocol;
+ skb->dev = hdlc_to_dev(hdlc);
+ netif_rx(skb);
+ return;
+
+ case CISCO_SYS_INFO:
+ /* Packet is not needed, drop it. */
+ dev_kfree_skb_any(skb);
+ return;
+
+ case CISCO_KEEPALIVE:
+ if (skb->len != CISCO_PACKET_LEN &&
+ skb->len != CISCO_BIG_PACKET_LEN) {
+ printk(KERN_INFO "%s: Invalid length of Cisco "
+ "control packet (%d bytes)\n",
+ hdlc_to_name(hdlc), skb->len);
+ goto rx_error;
+ }
+
+ cisco_data = (cisco_packet*)skb->data;
+
+ switch(ntohl (cisco_data->type)) {
+ case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
+ in_dev = hdlc_to_dev(hdlc)->ip_ptr;
+ addr = 0;
+ mask = ~0; /* is the mask correct? */
+
+ if (in_dev != NULL) {
+ struct in_ifaddr **ifap = &in_dev->ifa_list;
+
+ while (*ifap != NULL) {
+ if (strcmp(hdlc_to_name(hdlc),
+ (*ifap)->ifa_label) == 0) {
+ addr = (*ifap)->ifa_local;
+ mask = (*ifap)->ifa_mask;
+ break;
+ }
+ ifap = &(*ifap)->ifa_next;
+ }
+
+ cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
+ addr, mask);
+ }
+ dev_kfree_skb_any(skb);
+ return;
+
+ case CISCO_ADDR_REPLY:
+ printk(KERN_INFO "%s: Unexpected Cisco IP address "
+ "reply\n", hdlc_to_name(hdlc));
+ goto rx_error;
+
+ case CISCO_KEEPALIVE_REQ:
+ hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
+ if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) {
+ hdlc->state.cisco.last_poll = jiffies;
+ if (!hdlc->state.cisco.up) {
+ u32 sec, min, hrs, days;
+ sec = ntohl(cisco_data->time) / 1000;
+ min = sec / 60; sec -= min * 60;
+ hrs = min / 60; min -= hrs * 60;
+ days = hrs / 24; hrs -= days * 24;
+ printk(KERN_INFO "%s: Link up (peer "
+ "uptime %ud%uh%um%us)\n",
+ hdlc_to_name(hdlc), days, hrs,
+ min, sec);
+ }
+ hdlc->state.cisco.up = 1;
+ }
+
+ dev_kfree_skb_any(skb);
+ return;
+ } /* switch(keepalive type) */
+ } /* switch(protocol) */
+
+ printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
+ data->protocol);
+ dev_kfree_skb_any(skb);
+ return;
+
+ rx_error:
+ hdlc->stats.rx_errors++; /* Mark error */
+ dev_kfree_skb_any(skb);
+}
+
+
+
+static void cisco_timer(unsigned long arg)
+{
+ hdlc_device *hdlc = (hdlc_device*)arg;
+
+ if (hdlc->state.cisco.up &&
+ jiffies - hdlc->state.cisco.last_poll >=
+ hdlc->state.cisco.settings.timeout * HZ) {
+ hdlc->state.cisco.up = 0;
+ printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
+ }
+
+ cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ,
+ ++hdlc->state.cisco.txseq,
+ hdlc->state.cisco.rxseq);
+ hdlc->state.cisco.timer.expires = jiffies +
+ hdlc->state.cisco.settings.interval * HZ;
+ hdlc->state.cisco.timer.function = cisco_timer;
+ hdlc->state.cisco.timer.data = arg;
+ add_timer(&hdlc->state.cisco.timer);
+}
+
+
+
+static int cisco_open(hdlc_device *hdlc)
+{
+ hdlc->state.cisco.last_poll = 0;
+ hdlc->state.cisco.up = 0;
+ hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
+
+ init_timer(&hdlc->state.cisco.timer);
+ hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
+ hdlc->state.cisco.timer.function = cisco_timer;
+ hdlc->state.cisco.timer.data = (unsigned long)hdlc;
+ add_timer(&hdlc->state.cisco.timer);
+ return 0;
+}
+
+
+
+static void cisco_close(hdlc_device *hdlc)
+{
+ del_timer_sync(&hdlc->state.cisco.timer);
+}
+
+
+
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ const size_t size = sizeof(cisco_proto);
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ int result;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_CISCO;
+ if (ifr->ifr_settings.data_length == 0)
+ return 0; /* return protocol only */
+ if (ifr->ifr_settings.data_length < size)
+ return -ENOMEM; /* buffer too small */
+ if (copy_to_user(ifr->ifr_settings.data,
+ &hdlc->state.cisco.settings, size))
+ return -EFAULT;
+ ifr->ifr_settings.data_length = size;
+ return 0;
+
+ case IF_PROTO_CISCO:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (ifr->ifr_settings.data_length != size)
+ return -ENOMEM; /* incorrect data length */
+
+ if (copy_from_user(&hdlc->state.cisco.settings,
+ ifr->ifr_settings.data, size))
+ return -EFAULT;
+
+ /* FIXME - put sanity checks here */
+ hdlc_detach(hdlc);
+
+ result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+ if (result) {
+ hdlc->proto = -1;
+ return result;
+ }
+
+ hdlc->open = cisco_open;
+ hdlc->stop = cisco_close;
+ hdlc->netif_rx = cisco_rx;
+ hdlc->proto = IF_PROTO_CISCO;
+ dev->hard_start_xmit = hdlc->xmit;
+ dev->hard_header = cisco_hard_header;
+ dev->type = ARPHRD_CISCO;
+ dev->addr_len = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ * Frame Relay support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
+{
+ pvc_device *pvc=hdlc->state.fr.first_pvc;
+
+ while (pvc) {
+ if (netdev_dlci(&pvc->netdev) == dlci)
+ return pvc;
+ pvc = pvc->next;
+ }
+
+ return NULL;
+}
+
+
+
+__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status,
+ int *active, int *new)
+{
+ *new = (status[2] & 0x08);
+ *active = (!*new && (status[2] & 0x02));
+
+ return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
+}
+
+
+__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
+ int active, int new)
+{
+ status[0] = (dlci>>4) & 0x3F;
+ status[1] = ((dlci<<3) & 0x78) | 0x80;
+ status[2] = 0x80;
+
+ if (new)
+ status[2] |= 0x08;
+ else if (active)
+ status[2] |= 0x02;
+}
+
+
+
+static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
+ u16 type, void *daddr, void *saddr, unsigned int len)
+{
+ u16 head_len;
+
+ if (!daddr)
+ daddr = dev->broadcast;
+
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+ printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
+#endif
+
+ switch(type) {
+ case ETH_P_IP:
+ head_len = 4;
+ skb_push(skb, head_len);
+ skb->data[3] = NLPID_IP;
+ break;
+
+ case ETH_P_IPV6:
+ head_len = 4;
+ skb_push(skb, head_len);
+ skb->data[3] = NLPID_IPV6;
+ break;
+
+ case LMI_PROTO:
+ head_len = 4;
+ skb_push(skb, head_len);
+ skb->data[3] = LMI_PROTO;
+ break;
+
+ default:
+ head_len = 10;
+ skb_push(skb, head_len);
+ skb->data[3] = FR_PAD;
+ skb->data[4] = NLPID_SNAP;
+ skb->data[5] = FR_PAD;
+ skb->data[6] = FR_PAD;
+ skb->data[7] = FR_PAD;
+ skb->data[8] = type>>8;
+ skb->data[9] = (u8)type;
+ }
+
+ memcpy(skb->data, daddr, 2);
+ skb->data[2] = FR_UI;
+
+ return head_len;
+}
+
+
+
+static int pvc_open(struct net_device *dev)
+{
+ pvc_device *pvc = dev_to_pvc(dev);
+
+ if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
+ return -EIO; /* Master must be UP in order to activate PVC */
+
+ if (pvc->master->state.fr.settings.lmi != LMI_NONE)
+ pvc->state.active = 0;
+ else
+ pvc->state.active = 1;
+
+ pvc->state.new = 0;
+ pvc->master->state.fr.changed = 1;
+ return 0;
+}
+
+
+
+static int pvc_close(struct net_device *dev)
+{
+ pvc_device *pvc = dev_to_pvc(dev);
+ pvc->state.active = pvc->state.new = 0;
+ pvc->master->state.fr.changed = 1;
+ return 0;
+}
+
+
+
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ pvc_device *pvc = dev_to_pvc(dev);
+
+ if (pvc->state.active) {
+ skb->dev = hdlc_to_dev(pvc->master);
+ pvc->stats.tx_bytes += skb->len;
+ pvc->stats.tx_packets++;
+ if (pvc->state.fecn)
+ pvc->stats.tx_compressed++; /* TX Congestion counter */
+ dev_queue_xmit(skb);
+ } else {
+ pvc->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+
+
+static struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+ pvc_device *pvc = dev_to_pvc(dev);
+ return &pvc->stats;
+}
+
+
+
+static int pvc_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+
+
+static inline void fr_log_dlci_active(pvc_device *pvc)
+{
+ printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
+ pvc->state.active ? "" : "in",
+ pvc->state.new ? " new" : "");
+}
+
+
+
+static inline u8 fr_lmi_nextseq(u8 x)
+{
+ x++;
+ return x ? x : 1;
+}
+
+
+
+static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
+{
+ struct sk_buff *skb;
+ pvc_device *pvc = hdlc->state.fr.first_pvc;
+ int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH
+ : LMI_LENGTH;
+ int stat_len = 3;
+ u8 *data;
+ int i = 0;
+
+ if (hdlc->state.fr.settings.dce && fullrep) {
+ len += hdlc->state.fr.pvc_count * (2 + stat_len);
+ if (len > HDLC_MAX_MTU) {
+ printk(KERN_WARNING "%s: Too many PVCs while sending "
+ "LMI full report\n", hdlc_to_name(hdlc));
+ return;
+ }
+ }
+
+ skb = dev_alloc_skb(len);
+ if (!skb) {
+ printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
+ hdlc_to_name(hdlc));
+ return;
+ }
+ memset(skb->data, 0, len);
+ skb_reserve(skb, 4);
+ fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
+ data = skb->tail;
+ data[i++] = LMI_CALLREF;
+ data[i++] = hdlc->state.fr.settings.dce
+ ? LMI_STATUS : LMI_STATUS_ENQUIRY;
+ if (hdlc->state.fr.settings.lmi == LMI_ANSI)
+ data[i++] = LMI_ANSI_LOCKSHIFT;
+ data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_REPTYPE : LMI_REPTYPE;
+ data[i++] = LMI_REPT_LEN;
+ data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
+
+ data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_ALIVE : LMI_ALIVE;
+ data[i++] = LMI_INTEG_LEN;
+ data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
+ data[i++] = hdlc->state.fr.rxseq;
+
+ if (hdlc->state.fr.settings.dce && fullrep) {
+ while (pvc) {
+ data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
+ data[i++] = stat_len;
+
+ if (hdlc->state.fr.reliable &&
+ (pvc->netdev.flags & IFF_UP) &&
+ !pvc->state.active &&
+ !pvc->state.new) {
+ pvc->state.new = 1;
+ fr_log_dlci_active(pvc);
+ }
+
+ dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
+ data + i,
+ pvc->state.active, pvc->state.new);
+ i += stat_len;
+ pvc = pvc->next;
+ }
+ }
+
+ skb_put(skb, i);
+ skb->priority = TC_PRIO_CONTROL;
+ skb->dev = hdlc_to_dev(hdlc);
+
+ dev_queue_xmit(skb);
+}
+
+
+
+static void fr_timer(unsigned long arg)
+{
+ hdlc_device *hdlc = (hdlc_device*)arg;
+ int i, cnt = 0, reliable;
+ u32 list;
+
+ if (hdlc->state.fr.settings.dce)
+ reliable = (jiffies - hdlc->state.fr.last_poll <
+ hdlc->state.fr.settings.t392 * HZ);
+ else {
+ hdlc->state.fr.last_errors <<= 1; /* Shift the list */
+ if (hdlc->state.fr.request) {
+ if (hdlc->state.fr.reliable)
+ printk(KERN_INFO "%s: No LMI status reply "
+ "received\n", hdlc_to_name(hdlc));
+ hdlc->state.fr.last_errors |= 1;
+ }
+
+ list = hdlc->state.fr.last_errors;
+ for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
+ cnt += (list & 1); /* errors count */
+
+ reliable = (cnt < hdlc->state.fr.settings.n392);
+ }
+
+ if (hdlc->state.fr.reliable != reliable) {
+ pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+ hdlc->state.fr.reliable = reliable;
+ printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
+ reliable ? "" : "un");
+
+ if (reliable) {
+ hdlc->state.fr.n391cnt = 0; /* Request full status */
+ hdlc->state.fr.changed = 1;
+ } else {
+ while (pvc) { /* Deactivate all PVCs */
+ pvc->state.new = pvc->state.active = 0;
+ pvc = pvc->next;
+ }
+ }
+ }
+
+ if (hdlc->state.fr.settings.dce)
+ hdlc->state.fr.timer.expires = jiffies +
+ hdlc->state.fr.settings.t392 * HZ;
+ else {
+ if (hdlc->state.fr.n391cnt)
+ hdlc->state.fr.n391cnt--;
+
+ fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0);
+
+ hdlc->state.fr.request = 1;
+ hdlc->state.fr.timer.expires = jiffies +
+ hdlc->state.fr.settings.t391 * HZ;
+ }
+
+ hdlc->state.fr.timer.function = fr_timer;
+ hdlc->state.fr.timer.data = arg;
+ add_timer(&hdlc->state.fr.timer);
+}
+
+
+
+static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
+{
+ int stat_len;
+ pvc_device *pvc;
+ int reptype = -1, error;
+ u8 rxseq, txseq;
+ int i;
+
+ if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI)
+ ? LMI_ANSI_LENGTH : LMI_LENGTH)) {
+ printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
+ return 1;
+ }
+
+ if (skb->data[5] != (!hdlc->state.fr.settings.dce ?
+ LMI_STATUS : LMI_STATUS_ENQUIRY)) {
+ printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
+ hdlc_to_name(hdlc), skb->data[2],
+ hdlc->state.fr.settings.dce ? "enquiry" : "reply");
+ return 1;
+ }
+
+ i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6;
+
+ if (skb->data[i] !=
+ ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
+ printk(KERN_INFO "%s: Not a report type=%x\n",
+ hdlc_to_name(hdlc), skb->data[i]);
+ return 1;
+ }
+ i++;
+
+ i++; /* Skip length field */
+
+ reptype = skb->data[i++];
+
+ if (skb->data[i]!=
+ ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
+ printk(KERN_INFO "%s: Unsupported status element=%x\n",
+ hdlc_to_name(hdlc), skb->data[i]);
+ return 1;
+ }
+ i++;
+
+ i++; /* Skip length field */
+
+ hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
+ rxseq = skb->data[i++]; /* Should confirm our sequence */
+
+ txseq = hdlc->state.fr.txseq;
+
+ if (hdlc->state.fr.settings.dce) {
+ if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
+ printk(KERN_INFO "%s: Unsupported report type=%x\n",
+ hdlc_to_name(hdlc), reptype);
+ return 1;
+ }
+ }
+
+ error = 0;
+ if (!hdlc->state.fr.reliable)
+ error = 1;
+
+ if (rxseq == 0 || rxseq != txseq) {
+ hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
+ error = 1;
+ }
+
+ if (hdlc->state.fr.settings.dce) {
+ if (hdlc->state.fr.fullrep_sent && !error) {
+/* Stop sending full report - the last one has been confirmed by DTE */
+ hdlc->state.fr.fullrep_sent = 0;
+ pvc = hdlc->state.fr.first_pvc;
+ while (pvc) {
+ if (pvc->state.new) {
+ pvc->state.new = 0;
+ pvc->state.active = 1;
+ fr_log_dlci_active(pvc);
+
+/* Tell DTE that new PVC is now active */
+ hdlc->state.fr.changed = 1;
+ }
+ pvc = pvc->next;
+ }
+ }
+
+ if (hdlc->state.fr.changed) {
+ reptype = LMI_FULLREP;
+ hdlc->state.fr.fullrep_sent = 1;
+ hdlc->state.fr.changed = 0;
+ }
+
+ fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
+ return 0;
+ }
+
+ /* DTE */
+
+ if (reptype != LMI_FULLREP || error)
+ return 0;
+
+ stat_len = 3;
+ pvc = hdlc->state.fr.first_pvc;
+
+ while (pvc) {
+ pvc->state.deleted = pvc->state.active; /* mark active PVCs */
+ pvc = pvc->next;
+ }
+
+ while (skb->len >= i + 2 + stat_len) {
+ u16 dlci;
+ int active, new;
+
+ if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+ ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
+ printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
+ hdlc_to_name(hdlc), skb->data[i]);
+ return 1;
+ }
+ i++;
+
+ if (skb->data[i] != stat_len) {
+ printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
+ hdlc_to_name(hdlc), skb->data[i]);
+ return 1;
+ }
+ i++;
+
+ dlci = status_to_dlci(hdlc, skb->data + i, &active, &new);
+ pvc = find_pvc(hdlc, dlci);
+
+ active |= new;
+ if (pvc) {
+ if (active && !pvc->state.active &&
+ (pvc->netdev.flags & IFF_UP)) {
+ pvc->state.active = active;
+ fr_log_dlci_active(pvc);
+ }
+ pvc->state.deleted = 0;
+ }
+ else if (new)
+ printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
+ hdlc_to_name(hdlc), dlci);
+
+ i += stat_len;
+ }
+
+ pvc = hdlc->state.fr.first_pvc;
+
+ while (pvc) {
+ if (pvc->state.deleted) {
+ pvc->state.active = pvc->state.new = 0;
+ fr_log_dlci_active(pvc);
+ pvc->state.deleted = 0;
+ }
+ pvc = pvc->next;
+ }
+
+ /* Next full report after N391 polls */
+ hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
+
+ return 0;
+}
+
+
+
+static void fr_rx(struct sk_buff *skb)
+{
+ hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+ fr_hdr *fh = (fr_hdr*)skb->data;
+ u8 *data = skb->data;
+ u16 dlci;
+ pvc_device *pvc;
+
+ if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
+ goto rx_error;
+
+ dlci = q922_to_dlci(skb->data);
+
+ if (dlci == LMI_DLCI) {
+ if (hdlc->state.fr.settings.lmi == LMI_NONE)
+ goto rx_error; /* LMI packet with no LMI? */
+
+ if (data[3] == LMI_PROTO) {
+ if (fr_lmi_recv(hdlc, skb))
+ goto rx_error;
+ else {
+ /* No request pending */
+ hdlc->state.fr.request = 0;
+ hdlc->state.fr.last_poll = jiffies;
+ dev_kfree_skb_any(skb);
+ return;
+ }
+ }
+
+ printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
+ hdlc_to_name(hdlc));
+ goto rx_error;
+ }
+
+ pvc = find_pvc(hdlc, dlci);
+ if (!pvc) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+ printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
+ hdlc_to_name(hdlc), dlci);
+#endif
+ goto rx_error;
+ }
+
+ if ((pvc->netdev.flags & IFF_UP) == 0) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+ printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
+ hdlc_to_name(hdlc), dlci);
+#endif
+ goto rx_error;
+ }
+
+ pvc->stats.rx_packets++; /* PVC traffic */
+ pvc->stats.rx_bytes += skb->len;
+
+ if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+ printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
+ fh->fecn ? "N" : "FF");
+#endif
+ pvc->state.fecn ^= 1;
+ }
+
+ if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+ printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
+ fh->becn ? "N" : "FF");
+#endif
+ pvc->state.becn ^= 1;
+ }
+
+ if (pvc->state.becn)
+ pvc->stats.rx_compressed++;
+
+ skb->dev = &pvc->netdev;
+
+ if (data[3] == NLPID_IP) {
+ skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+ skb->protocol = htons(ETH_P_IP);
+ netif_rx(skb);
+ return;
+ }
+
+
+ if (data[3] == NLPID_IPV6) {
+ skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+ skb->protocol = htons(ETH_P_IPV6);
+ netif_rx(skb);
+ return;
+ }
+
+ if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+ u16 oui = ntohl(*(u16*)(data + 6));
+ u16 pid = ntohl(*(u16*)(data + 8));
+ skb_pull(skb, 10);
+
+ switch ((((u32)oui) << 16) | pid) {
+ case ETH_P_ARP: /* routed frame with SNAP */
+ case ETH_P_IPX:
+ skb->protocol = htons(pid);
+ break;
+
+ default:
+ printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
+ "PID=%x\n", hdlc_to_name(hdlc), oui, pid);
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
+ netif_rx(skb);
+ return;
+ }
+
+ printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n",
+ hdlc_to_name(hdlc), data[3]);
+ dev_kfree_skb_any(skb);
+ return;
+
+ rx_error:
+ hdlc->stats.rx_errors++; /* Mark error */
+ dev_kfree_skb_any(skb);
+}
+
+
+
+static int fr_open(hdlc_device *hdlc)
+{
+ if (hdlc->state.fr.settings.lmi != LMI_NONE) {
+ hdlc->state.fr.last_poll = 0;
+ hdlc->state.fr.reliable = 0;
+ hdlc->state.fr.changed = 1;
+ hdlc->state.fr.request = 0;
+ hdlc->state.fr.fullrep_sent = 0;
+ hdlc->state.fr.last_errors = 0xFFFFFFFF;
+ hdlc->state.fr.n391cnt = 0;
+ hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
+
+ init_timer(&hdlc->state.fr.timer);
+ /* First poll after 1 s */
+ hdlc->state.fr.timer.expires = jiffies + HZ;
+ hdlc->state.fr.timer.function = fr_timer;
+ hdlc->state.fr.timer.data = (unsigned long)hdlc;
+ add_timer(&hdlc->state.fr.timer);
+ } else
+ hdlc->state.fr.reliable = 1;
+
+ return 0;
+}
+
+
+
+static void fr_close(hdlc_device *hdlc)
+{
+ pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+ if (hdlc->state.fr.settings.lmi != LMI_NONE)
+ del_timer_sync(&hdlc->state.fr.timer);
+
+ while(pvc) {
+ dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
+ pvc = pvc->next;
+ }
+}
+
+
+
+static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
+{
+ pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+ pvc_device *pvc;
+ int result;
+
+ if(dlci <= 0 || dlci >= 1024)
+ return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */
+
+ while(*pvc_p) {
+ if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
+ break;
+ pvc_p = &(*pvc_p)->next;
+ }
+
+ if (create) { /* Create PVC */
+ if (*pvc_p != NULL)
+ return -EEXIST;
+
+ pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
+ if (!pvc) {
+ printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+ hdlc_to_name(hdlc));
+ return -ENOBUFS;
+ }
+ memset(pvc, 0, sizeof(pvc_device));
+
+ pvc->netdev.hard_start_xmit = pvc_xmit;
+ pvc->netdev.get_stats = pvc_get_stats;
+ pvc->netdev.open = pvc_open;
+ pvc->netdev.stop = pvc_close;
+ pvc->netdev.change_mtu = pvc_change_mtu;
+ pvc->netdev.mtu = HDLC_MAX_MTU;
+
+ pvc->netdev.type = ARPHRD_DLCI;
+ pvc->netdev.hard_header_len = 16;
+ pvc->netdev.hard_header = fr_hard_header;
+ pvc->netdev.tx_queue_len = 0;
+ pvc->netdev.flags = IFF_POINTOPOINT;
+
+ pvc->master = hdlc;
+ *(u16*)pvc->netdev.dev_addr = htons(dlci);
+ dlci_to_q922(pvc->netdev.broadcast, dlci);
+ pvc->netdev.addr_len = 2;
+
+ result = dev_alloc_name(&pvc->netdev, "pvc%d");
+ if (result < 0) {
+ kfree(pvc);
+ *pvc_p = NULL;
+ return result;
+ }
+
+ if (register_netdevice(&pvc->netdev) != 0) {
+ kfree(pvc);
+ *pvc_p = NULL;
+ return -EIO;
+ }
+
+ hdlc->state.fr.changed = 1;
+ hdlc->state.fr.pvc_count++;
+ return 0;
+ }
+
+ if (*pvc_p == NULL) /* Delete PVC */
+ return -ENOENT;
+
+ pvc = *pvc_p;
+
+ if (pvc->netdev.flags & IFF_UP)
+ return -EBUSY; /* PVC in use */
+
+ hdlc->state.fr.changed = 1;
+ hdlc->state.fr.pvc_count--;
+ *pvc_p = pvc->next;
+ unregister_netdevice(&pvc->netdev);
+ kfree(pvc);
+ return 0;
+}
+
+
+
+static void fr_destroy(hdlc_device *hdlc)
+{
+ pvc_device *pvc = hdlc->state.fr.first_pvc;
+ while(pvc) {
+ pvc_device *next = pvc->next;
+ unregister_netdevice(&pvc->netdev);
+ kfree(pvc);
+ pvc = next;
+ }
+
+ hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
+ hdlc->state.fr.pvc_count = 0;
+ hdlc->state.fr.changed = 1;
+}
+
+
+
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ const size_t size = sizeof(fr_proto);
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ fr_proto_pvc pvc;
+ int result;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_FR;
+ if (ifr->ifr_settings.data_length == 0)
+ return 0; /* return protocol only */
+ if (ifr->ifr_settings.data_length < size)
+ return -ENOMEM; /* buffer too small */
+ if (copy_to_user(ifr->ifr_settings.data,
+ &hdlc->state.fr.settings, size))
+ return -EFAULT;
+ ifr->ifr_settings.data_length = size;
+ return 0;
+
+ case IF_PROTO_FR:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (ifr->ifr_settings.data_length != size)
+ return -ENOMEM; /* incorrect data length */
+
+ if (copy_from_user(&hdlc->state.fr.settings,
+ ifr->ifr_settings.data, size))
+ return -EFAULT;
+
+ /* FIXME - put sanity checks here */
+ if (hdlc->proto != IF_PROTO_FR) {
+ hdlc_detach(hdlc);
+ hdlc->state.fr.first_pvc = NULL;
+ hdlc->state.fr.pvc_count = 0;
+ }
+
+ result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+ if (result) {
+ hdlc->proto = -1;
+ return result;
+ }
+
+ hdlc->open = fr_open;
+ hdlc->stop = fr_close;
+ hdlc->netif_rx = fr_rx;
+ hdlc->detach = fr_destroy;
+ hdlc->proto = IF_PROTO_FR;
+ dev->hard_start_xmit = hdlc->xmit;
+ dev->hard_header = fr_hard_header;
+ dev->type = ARPHRD_FRAD;
+ dev->addr_len = 2;
+ *(u16*)dev->dev_addr = htons(LMI_DLCI);
+ dlci_to_q922(dev->broadcast, LMI_DLCI);
+ return 0;
+
+ case IF_PROTO_FR_ADD_PVC:
+ case IF_PROTO_FR_DEL_PVC:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (copy_from_user(&pvc, ifr->ifr_settings.data,
+ sizeof(fr_proto_pvc)))
+ return -EFAULT;
+
+ return fr_pvc(hdlc, pvc.dlci,
+ ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC);
+ }
+
+ return -EINVAL;
+}
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ *
+ * Current status:
+ * - this is work in progress
+ * - not heavily tested on SMP
+ * - currently supported:
+ * * raw IP-in-HDLC
+ * * Cisco HDLC
+ * * Frame Relay with ANSI or CCITT LMI (both user and network side)
+ * * PPP
+ * * X.25
+ *
+ * Use sethdlc utility to set line parameters, protocol and PVCs
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static const char* version = "HDLC support module revision 1.08";
+
+
+static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+ return -EINVAL;
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+
+
+static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
+{
+ return &dev_to_hdlc(dev)->stats;
+}
+
+
+
+static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *p)
+{
+ dev_to_hdlc(dev)->netif_rx(skb);
+ return 0;
+}
+
+
+
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ unsigned int proto;
+
+ if (cmd != SIOCDEVICE)
+ return -EINVAL;
+
+ switch(ifr->ifr_settings.type) {
+ case IF_PROTO_HDLC:
+ case IF_PROTO_PPP:
+ case IF_PROTO_CISCO:
+ case IF_PROTO_FR:
+ case IF_PROTO_X25:
+ proto = ifr->ifr_settings.type;
+ break;
+
+ default:
+ proto = hdlc->proto;
+ }
+
+ switch(proto) {
+#ifdef CONFIG_HDLC_RAW
+ case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_PPP
+ case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_CISCO
+ case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_FR
+ case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr);
+#endif
+#ifdef CONFIG_HDLC_X25
+ case IF_PROTO_X25: return hdlc_x25_ioctl(hdlc, ifr);
+#endif
+ default: return -ENOSYS;
+ }
+}
+
+
+
+int register_hdlc_device(hdlc_device *hdlc)
+{
+ int result;
+ struct net_device *dev = hdlc_to_dev(hdlc);
+
+ dev->get_stats = hdlc_get_stats;
+ dev->change_mtu = hdlc_change_mtu;
+ dev->mtu = HDLC_MAX_MTU;
+
+ dev->type = ARPHRD_RAWHDLC;
+ dev->hard_header_len = 16;
+
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+
+ hdlc->proto = -1;
+ hdlc->detach = NULL;
+
+ result = dev_alloc_name(dev, "hdlc%d");
+ if (result<0)
+ return result;
+
+ result = register_netdev(dev);
+ if (result != 0)
+ return -EIO;
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+
+
+void unregister_hdlc_device(hdlc_device *hdlc)
+{
+ hdlc_detach(hdlc);
+
+ unregister_netdev(hdlc_to_dev(hdlc));
+ MOD_DEC_USE_COUNT;
+}
+
+
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("HDLC support module");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(hdlc_ioctl);
+EXPORT_SYMBOL(register_hdlc_device);
+EXPORT_SYMBOL(unregister_hdlc_device);
+
+struct packet_type hdlc_packet_type=
+{
+ __constant_htons(ETH_P_HDLC),
+ NULL,
+ hdlc_rcv,
+ NULL,
+ NULL
+};
+
+
+static int __init hdlc_module_init(void)
+{
+ printk(KERN_INFO "%s\n", version);
+ dev_add_pack(&hdlc_packet_type);
+ return 0;
+}
+
+
+
+static void __exit hdlc_module_exit(void)
+{
+ dev_remove_pack(&hdlc_packet_type);
+}
+
+
+module_init(hdlc_module_init);
+module_exit(hdlc_module_exit);
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ * Point-to-point protocol support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static int ppp_open(hdlc_device *hdlc)
+{
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ void *old_ioctl;
+ int result;
+
+ dev->priv = &hdlc->state.ppp.syncppp_ptr;
+ hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
+ hdlc->state.ppp.pppdev.dev = dev;
+
+ old_ioctl = dev->do_ioctl;
+ hdlc->state.ppp.old_change_mtu = dev->change_mtu;
+ sppp_attach(&hdlc->state.ppp.pppdev);
+ /* sppp_attach nukes them. We don't need syncppp's ioctl */
+ dev->do_ioctl = old_ioctl;
+ hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
+ dev->type = ARPHRD_PPP;
+ result = sppp_open(dev);
+ if (result) {
+ sppp_detach(dev);
+ return result;
+ }
+
+ return 0;
+}
+
+
+
+static void ppp_close(hdlc_device *hdlc)
+{
+ struct net_device *dev = hdlc_to_dev(hdlc);
+
+ sppp_close(dev);
+ sppp_detach(dev);
+ dev->rebuild_header = NULL;
+ dev->change_mtu = hdlc->state.ppp.old_change_mtu;
+ dev->mtu = HDLC_MAX_MTU;
+ dev->hard_header_len = 16;
+}
+
+
+
+static void ppp_rx(struct sk_buff *skb)
+{
+ skb->protocol = htons(ETH_P_WAN_PPP);
+ netif_rx(skb);
+}
+
+
+
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ int result;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_PPP;
+ ifr->ifr_settings.data_length = 0;
+ return 0; /* return protocol only, no settable parameters */
+
+ case IF_PROTO_PPP:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (ifr->ifr_settings.data_length != 0)
+ return -EINVAL; /* no settable parameters */
+
+ hdlc_detach(hdlc);
+
+ result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+ if (result) {
+ hdlc->proto = -1;
+ return result;
+ }
+
+ hdlc->open = ppp_open;
+ hdlc->stop = ppp_close;
+ hdlc->netif_rx = ppp_rx;
+ hdlc->proto = IF_PROTO_PPP;
+ dev->hard_start_xmit = hdlc->xmit;
+ dev->hard_header = NULL;
+ dev->type = ARPHRD_PPP;
+ dev->addr_len = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static void raw_rx(struct sk_buff *skb)
+{
+ skb->protocol = htons(ETH_P_IP);
+ netif_rx(skb);
+}
+
+
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ const size_t size = sizeof(hdlc_proto);
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ int result;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_HDLC;
+ if (ifr->ifr_settings.data_length == 0)
+ return 0; /* return protocol only */
+ if (ifr->ifr_settings.data_length < size)
+ return -ENOMEM; /* buffer too small */
+ if (copy_to_user(ifr->ifr_settings.data,
+ &hdlc->state.hdlc.settings, size))
+ return -EFAULT;
+ ifr->ifr_settings.data_length = size;
+ return 0;
+
+ case IF_PROTO_HDLC:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (ifr->ifr_settings.data_length != size)
+ return -ENOMEM; /* incorrect data length */
+
+ if (copy_from_user(&hdlc->state.hdlc.settings,
+ ifr->ifr_settings.data, size))
+ return -EFAULT;
+
+ /* FIXME - put sanity checks here */
+ hdlc_detach(hdlc);
+
+ result=hdlc->attach(hdlc, hdlc->state.hdlc.settings.encoding,
+ hdlc->state.hdlc.settings.parity);
+ if (result) {
+ hdlc->proto = -1;
+ return result;
+ }
+
+ hdlc->open = NULL;
+ hdlc->stop = NULL;
+ hdlc->netif_rx = raw_rx;
+ hdlc->proto = IF_PROTO_HDLC;
+ dev->hard_start_xmit = hdlc->xmit;
+ dev->hard_header = NULL;
+ dev->type = ARPHRD_RAWHDLC;
+ dev->addr_len = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
--- /dev/null
+/*
+ * Generic HDLC support routines for Linux
+ * X.25 support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+/* These functions are callbacks called by LAPB layer */
+
+static void x25_connect_disconnect(void *token, int reason, int code)
+{
+ hdlc_device *hdlc = token;
+ struct sk_buff *skb;
+ unsigned char *ptr;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
+ return;
+ }
+
+ ptr = skb_put(skb, 1);
+ *ptr = code;
+
+ skb->dev = hdlc_to_dev(hdlc);
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+}
+
+
+
+static void x25_connected(void *token, int reason)
+{
+ x25_connect_disconnect(token, reason, 1);
+}
+
+
+
+static void x25_disconnected(void *token, int reason)
+{
+ x25_connect_disconnect(token, reason, 2);
+}
+
+
+
+static int x25_data_indication(void *token, struct sk_buff *skb)
+{
+ hdlc_device *hdlc = token;
+ unsigned char *ptr;
+
+ ptr = skb_push(skb, 1);
+ *ptr = 0;
+
+ skb->dev = hdlc_to_dev(hdlc);
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ return netif_rx(skb);
+}
+
+
+
+static void x25_data_transmit(void *token, struct sk_buff *skb)
+{
+ hdlc_device *hdlc = token;
+ hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */
+}
+
+
+
+static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ int result;
+
+
+ /* X.25 to LAPB */
+ switch (skb->data[0]) {
+ case 0: /* Data to be transmitted */
+ skb_pull(skb, 1);
+ if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
+ dev_kfree_skb(skb);
+ return 0;
+
+ case 1:
+ if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
+ if (result == LAPB_CONNECTED)
+ /* Send connect confirm. msg to level 3 */
+ x25_connected(hdlc, 0);
+ else
+ printk(KERN_ERR "%s: LAPB connect request "
+ "failed, error code = %i\n",
+ hdlc_to_name(hdlc), result);
+ }
+ break;
+
+ case 2:
+ if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) {
+ if (result == LAPB_NOTCONNECTED)
+ /* Send disconnect confirm. msg to level 3 */
+ x25_disconnected(hdlc, 0);
+ else
+ printk(KERN_ERR "%s: LAPB disconnect request "
+ "failed, error code = %i\n",
+ hdlc_to_name(hdlc), result);
+ }
+ break;
+
+ default: /* to be defined */
+ break;
+ }
+
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+
+
+static int x25_open(hdlc_device *hdlc)
+{
+ struct lapb_register_struct cb;
+ int result;
+
+ cb.connect_confirmation = x25_connected;
+ cb.connect_indication = x25_connected;
+ cb.disconnect_confirmation = x25_disconnected;
+ cb.disconnect_indication = x25_disconnected;
+ cb.data_indication = x25_data_indication;
+ cb.data_transmit = x25_data_transmit;
+
+ result = lapb_register(hdlc, &cb);
+ if (result != LAPB_OK)
+ return result;
+ return 0;
+}
+
+
+
+static void x25_close(hdlc_device *hdlc)
+{
+ lapb_unregister(hdlc);
+}
+
+
+
+static void x25_rx(struct sk_buff *skb)
+{
+ hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+
+ if (lapb_data_received(hdlc, skb) == LAPB_OK)
+ return;
+ hdlc->stats.rx_errors++;
+ dev_kfree_skb_any(skb);
+}
+
+
+
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ int result;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_X25;
+ ifr->ifr_settings.data_length = 0;
+ return 0; /* return protocol only, no settable parameters */
+
+ case IF_PROTO_X25:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if(dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (ifr->ifr_settings.data_length != 0)
+ return -EINVAL; /* no settable parameters */
+
+ hdlc_detach(hdlc);
+
+ result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+ if (result) {
+ hdlc->proto = -1;
+ return result;
+ }
+
+ hdlc->open = x25_open;
+ hdlc->stop = x25_close;
+ hdlc->netif_rx = x25_rx;
+ hdlc->proto = IF_PROTO_X25;
+ dev->hard_start_xmit = x25_xmit;
+ dev->hard_header = NULL;
+ dev->type = ARPHRD_X25;
+ dev->addr_len = 0;
+ return 0;
+ }
+
+ return -EINVAL;
+}
/*
* SDL Inc. RISCom/N2 synchronous serial card driver for Linux
*
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2001 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
* SDL Inc. PPP/HDLC/CISCO driver
*/
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <asm/io.h>
#include "hd64570.h"
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
-static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4";
+static const char* version = "SDL RISCom/N2 driver version: 1.09";
static const char* devname = "RISCom/N2";
#define USE_WINDOWSIZE 16384
hdlc_device hdlc; /* HDLC device struct - must be first */
struct card_s *card;
spinlock_t lock; /* TX lock */
- int clkmode; /* clock mode */
- int clkrate; /* clock rate */
- int line; /* loopback only */
+ sync_serial_settings settings;
+ unsigned short encoding;
+ unsigned short parity;
u8 rxs, txs, tmc; /* SCA registers */
u8 valid; /* port enabled */
u8 phy_node; /* physical port # - 0 or 1 */
}card_t;
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
#define sca_reg(reg, card) (0x8000 | (card)->io | \
((reg) & 0x0F) | (((reg) & 0xF0) << 6))
-static int n2_set_clock(port_t *port, int value)
+static int n2_set_iface(port_t *port)
{
card_t *card = port->card;
int io = card->io;
u8 rxs = port->rxs & CLK_BRG_MASK;
u8 txs = port->txs & CLK_BRG_MASK;
- switch(value) {
+ switch(port->settings.clock_type) {
case CLOCK_EXT:
mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
rxs |= CLK_LINE_RX; /* RXC input */
port->txs = txs;
sca_out(rxs, msci + RXS, card);
sca_out(txs, msci + TXS, card);
- port->clkmode = value;
+ sca_set_port(port);
return 0;
}
-static int n2_open(hdlc_device *hdlc)
+static int n2_open(struct net_device *dev)
{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
int io = port->card->io;
- u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
+ u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
+
+ int result = hdlc_open(hdlc);
+ if (result)
+ return result;
MOD_INC_USE_COUNT;
mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
sca_open(hdlc);
- n2_set_clock(port, port->clkmode);
- return 0;
+ return n2_set_iface(port);
}
-static void n2_close(hdlc_device *hdlc)
+static int n2_close(struct net_device *dev)
{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
int io = port->card->io;
u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
sca_close(hdlc);
mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
outb(mcr, io + N2_MCR);
+ hdlc_close(hdlc);
MOD_DEC_USE_COUNT;
+ return 0;
}
-static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- int value = ifr->ifr_ifru.ifru_ivalue;
- int result = 0;
+ hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
+ const size_t size = sizeof(sync_serial_settings);
- if(!capable(CAP_NET_ADMIN))
- return -EPERM;
-
- switch(cmd) {
- case HDLCSCLOCK:
- result = n2_set_clock(port, value);
- case HDLCGCLOCK:
- value = port->clkmode;
- break;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+ if (cmd == SIOCDEVPRIVATE) {
+ sca_dump_rings(hdlc);
+ return 0;
+ }
+#endif
+ if (cmd != SIOCDEVICE)
+ return hdlc_ioctl(dev, ifr, cmd);
+
+ switch(ifr->ifr_settings.type) {
+ case IF_GET_IFACE:
+ ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+ if (ifr->ifr_settings.data_length == 0)
+ return 0; /* return interface type only */
+ if (ifr->ifr_settings.data_length < size)
+ return -ENOMEM; /* buffer too small */
+ if (copy_to_user(ifr->ifr_settings.data,
+ &port->settings, size))
+ return -EFAULT;
+ ifr->ifr_settings.data_length = size;
+ return 0;
- case HDLCSCLOCKRATE:
- port->clkrate = value;
- sca_set_clock(port);
- case HDLCGCLOCKRATE:
- value = port->clkrate;
- break;
+ case IF_IFACE_SYNC_SERIAL:
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
- case HDLCSLINE:
- result = sca_set_loopback(port, value);
- case HDLCGLINE:
- value = port->line;
- break;
+ if (ifr->ifr_settings.data_length != size)
+ return -ENOMEM; /* incorrect data length */
-#ifdef DEBUG_RINGS
- case HDLCRUN:
- sca_dump_rings(hdlc);
- return 0;
-#endif /* DEBUG_RINGS */
+ if (copy_from_user(&port->settings,
+ ifr->ifr_settings.data, size))
+ return -EFAULT;
+ /* FIXME - put sanity checks here */
+ return n2_set_iface(port);
default:
- return -EINVAL;
+ return hdlc_ioctl(dev, ifr, cmd);
}
-
- ifr->ifr_ifru.ifru_ivalue = value;
- return result;
}
sca_init(card, 0);
for (cnt = 0; cnt < 2; cnt++) {
port_t *port = &card->ports[cnt];
+ struct net_device *dev = hdlc_to_dev(&port->hdlc);
if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1))
continue;
port->log_node = 1;
spin_lock_init(&port->lock);
- hdlc_to_dev(&port->hdlc)->irq = irq;
- hdlc_to_dev(&port->hdlc)->mem_start = winbase;
- hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1;
- hdlc_to_dev(&port->hdlc)->tx_queue_len = 50;
- port->hdlc.ioctl = n2_ioctl;
- port->hdlc.open = n2_open;
- port->hdlc.close = n2_close;
+ dev->irq = irq;
+ dev->mem_start = winbase;
+ dev->mem_end = winbase + USE_WINDOWSIZE-1;
+ dev->tx_queue_len = 50;
+ dev->do_ioctl = n2_ioctl;
+ dev->open = n2_open;
+ dev->stop = n2_close;
+ port->hdlc.attach = sca_attach;
port->hdlc.xmit = sca_xmit;
+ port->settings.clock_type = CLOCK_EXT;
if (register_hdlc_device(&port->hdlc)) {
printk(KERN_WARNING "n2: unable to register hdlc "
return -ENOSYS; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s\n", version);
+ printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
do {
unsigned long io, irq, ram;
/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2002 Krzysztof Halasa <khc@pm.waw.pl>
*
* 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
#ifndef __HDLC_H
#define __HDLC_H
-/* Ioctls - to be changed */
-#define HDLCGSLOTMAP (0x89F4) /* E1/T1 slot bitmap */
-#define HDLCGCLOCK (0x89F5) /* clock sources */
-#define HDLCGCLOCKRATE (0x89F6) /* clock rate */
-#define HDLCGMODE (0x89F7) /* internal to hdlc.c - protocol used */
-#define HDLCGLINE (0x89F8) /* physical interface */
-#define HDLCSSLOTMAP (0x89F9)
-#define HDLCSCLOCK (0x89FA)
-#define HDLCSCLOCKRATE (0x89FB)
-#define HDLCSMODE (0x89FC) /* internal to hdlc.c - select protocol */
-#define HDLCPVC (0x89FD) /* internal to hdlc.c - create/delete PVC */
-#define HDLCSLINE (0x89FE)
-#define HDLCRUN (0x89FF) /* Download firmware and run board */
-
-/* Modes */
-#define MODE_NONE 0x00000000 /* Not initialized */
-#define MODE_DCE 0x00000080 /* DCE */
-#define MODE_HDLC 0x00000100 /* Raw HDLC frames */
-#define MODE_CISCO 0x00000200
-#define MODE_PPP 0x00000400
-#define MODE_FR 0x00000800 /* Any LMI */
-#define MODE_FR_ANSI 0x00000801
-#define MODE_FR_CCITT 0x00000802
-#define MODE_X25 0x00001000
-#define MODE_MASK 0x0000FF00
-#define MODE_SOFT 0x80000000 /* Driver modes, using hardware HDLC */
-
-/* Lines */
-#define LINE_DEFAULT 0x00000000
-#define LINE_V35 0x00000001
-#define LINE_RS232 0x00000002
-#define LINE_X21 0x00000003
-#define LINE_T1 0x00000004
-#define LINE_E1 0x00000005
-#define LINE_MASK 0x000000FF
-#define LINE_LOOPBACK 0x80000000 /* On-card loopback */
-
-#define CLOCK_EXT 0 /* External TX and RX clock - DTE */
-#define CLOCK_INT 1 /* Internal TX and RX clock - DCE */
-#define CLOCK_TXINT 2 /* Internal TX and external RX clock */
-#define CLOCK_TXFROMRX 3 /* TX clock derived from external RX clock */
+#define CLOCK_DEFAULT 0 /* Default (current) setting */
+#define CLOCK_EXT 1 /* External TX and RX clock - DTE */
+#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */
+#define CLOCK_TXINT 3 /* Internal TX and external RX clock */
+#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */
+typedef struct {
+ unsigned int clock_rate; /* bits per second */
+ unsigned int clock_type; /* internal, external, TX-internal etc. */
+ unsigned short loopback;
+}sync_serial_settings; /* V.35, V.24, X.21 */
+
+typedef struct {
+ unsigned int clock_rate; /* bits per second */
+ unsigned int clock_type; /* internal, external, TX-internal etc. */
+ unsigned short loopback;
+ unsigned int slot_map;
+}te1_settings; /* T1, E1 */
+
+
+
+#define ENCODING_DEFAULT 0 /* Default (current) setting */
+#define ENCODING_NRZ 1
+#define ENCODING_NRZI 2
+#define ENCODING_FM_MARK 3
+#define ENCODING_FM_SPACE 4
+#define ENCODING_MANCHESTER 5
+
+
+#define PARITY_DEFAULT 0 /* Default (current) setting */
+#define PARITY_NONE 1 /* No parity */
+#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */
+#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */
+#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
+#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
+#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
+#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
+
+typedef struct {
+ unsigned short encoding;
+ unsigned short parity;
+}hdlc_proto;
+
+
+#define LMI_DEFAULT 0 /* Default (current) setting */
+#define LMI_NONE 1 /* No LMI, all PVCs are static */
+#define LMI_ANSI 2 /* ANSI Annex D */
+#define LMI_CCITT 3 /* ITU-T Annex A */
+
+typedef struct {
+ unsigned int t391;
+ unsigned int t392;
+ unsigned int n391;
+ unsigned int n392;
+ unsigned int n393;
+ unsigned short lmi;
+ unsigned short dce; /* 1 for DCE (network side) operation */
+}fr_proto;
+
+typedef struct {
+ unsigned int dlci;
+}fr_proto_pvc; /* for creating/deleting FR PVCs */
+
+
+typedef struct {
+ unsigned int interval;
+ unsigned int timeout;
+}cisco_proto;
+
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
-#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */
-#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
#ifdef __KERNEL__
#include <linux/netdevice.h>
#include <net/syncppp.h>
-#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */
+#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */
+#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
-#define LINK_STATE_RELIABLE 0x01
-#define LINK_STATE_REQUEST 0x02 /* full stat sent (DCE) / req pending (DTE) */
-#define LINK_STATE_CHANGED 0x04 /* change in PVCs state, send full report */
-#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */
+#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */
#define PVC_STATE_NEW 0x01
#define PVC_STATE_ACTIVE 0x02
typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
unsigned ea1 : 1;
unsigned cr : 1;
unsigned dlcih: 6;
unsigned becn : 1;
unsigned fecn : 1;
unsigned dlcil: 4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ unsigned dlcih: 6;
+ unsigned cr : 1;
+ unsigned ea1 : 1;
+
+ unsigned dlcil: 4;
+ unsigned fecn : 1;
+ unsigned becn : 1;
+ unsigned de : 1;
+ unsigned ea2 : 1;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
}__attribute__ ((packed)) fr_hdr;
struct hdlc_device_struct *master;
struct pvc_device_struct *next;
- u8 state;
- u8 newstate;
+ struct {
+ int active;
+ int new;
+ int deleted;
+ int fecn;
+ int becn;
+ }state;
}pvc_device;
-typedef struct {
- u32 last_errors; /* last errors bit list */
- int last_poll; /* ! */
- u8 T391; /* ! link integrity verification polling timer */
- u8 T392; /* ! polling verification timer */
- u8 N391; /* full status polling counter */
- u8 N392; /* error threshold */
- u8 N393; /* monitored events count */
- u8 N391cnt;
-
- u8 state; /* ! */
- u32 txseq; /* ! TX sequence number - Cisco uses 4 bytes */
- u32 rxseq; /* ! RX sequence number */
-}fr_lmi; /* ! means used in Cisco HDLC as well */
-
-
typedef struct hdlc_device_struct {
- /* to be initialized by hardware driver: */
+ /* To be initialized by hardware driver */
struct net_device netdev; /* master net device - must be first */
struct net_device_stats stats;
- struct ppp_device pppdev;
- struct ppp_device *syncppp_ptr;
+ /* used by HDLC layer to take control over HDLC device from hw driver*/
+ int (*attach)(struct hdlc_device_struct *hdlc,
+ unsigned short encoding, unsigned short parity);
+
+ /* hardware driver must handle this instead of dev->hard_start_xmit */
+ int (*xmit)(struct sk_buff *skb, struct net_device *dev);
+
- /* set_mode may be NULL if HDLC-only board */
- int (*set_mode)(struct hdlc_device_struct *hdlc, int mode);
+ /* Things below are for HDLC layer internal use only */
+ int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
int (*open)(struct hdlc_device_struct *hdlc);
- void (*close)(struct hdlc_device_struct *hdlc);
- int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb);
- int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr,
- int cmd);
-
- /* Only in "hardware" FR modes etc. - may be NULL */
- int (*create_pvc)(pvc_device *pvc);
- void (*destroy_pvc)(pvc_device *pvc);
- int (*open_pvc)(pvc_device *pvc);
- void (*close_pvc)(pvc_device *pvc);
-
- /* for hdlc.c internal use only */
- pvc_device *first_pvc;
- u16 pvc_count;
- int mode;
-
- struct timer_list timer;
- fr_lmi lmi;
+ void (*stop)(struct hdlc_device_struct *hdlc);
+ void (*detach)(struct hdlc_device_struct *hdlc);
+ void (*netif_rx)(struct sk_buff *skb);
+ int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */
+
+ union {
+ struct {
+ fr_proto settings;
+ pvc_device *first_pvc;
+ int pvc_count;
+
+ struct timer_list timer;
+ int last_poll;
+ int reliable;
+ int changed;
+ int request;
+ int fullrep_sent;
+ u32 last_errors; /* last errors bit list */
+ u8 n391cnt;
+ u8 txseq; /* TX sequence number */
+ u8 rxseq; /* RX sequence number */
+ }fr;
+
+ struct {
+ cisco_proto settings;
+
+ struct timer_list timer;
+ int last_poll;
+ int up;
+ u32 txseq; /* TX sequence number */
+ u32 rxseq; /* RX sequence number */
+ }cisco;
+
+ struct {
+ hdlc_proto settings;
+ }hdlc;
+
+ struct {
+ struct ppp_device pppdev;
+ struct ppp_device *syncppp_ptr;
+ int (*old_change_mtu)(struct net_device *dev,
+ int new_mtu);
+ }ppp;
+ }state;
}hdlc_device;
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+
+
+/* Exported from hdlc.o */
+
+/* Called by hardware driver when a user requests HDLC service */
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+/* Must be used by hardware driver on module startup/exit */
int register_hdlc_device(hdlc_device *hdlc);
void unregister_hdlc_device(hdlc_device *hdlc);
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb);
static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc)
}
-static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state)
-{
- *state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW);
- if (status[2] & 0x08)
- *state |= PVC_STATE_NEW;
- else if (status[2] & 0x02)
- *state |= PVC_STATE_ACTIVE;
-
- return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
-}
-
-
-static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
- u8 state)
-{
- status[0] = (dlci>>4) & 0x3F;
- status[1] = ((dlci<<3) & 0x78) | 0x80;
- status[2] = 0x80;
-
- if (state & PVC_STATE_NEW)
- status[2] |= 0x08;
- else if (state & PVC_STATE_ACTIVE)
- status[2] |= 0x02;
-}
-
-
-
static __inline__ u16 netdev_dlci(struct net_device *dev)
{
return ntohs(*(u16*)dev->dev_addr);
static __inline__ u16 q922_to_dlci(u8 *hdr)
{
- return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4);
+ return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
}
static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci)
{
- hdr[0] = (dlci>>2) & 0xFC;
- hdr[1] = ((dlci<<4) & 0xF0) | 0x01;
-}
-
-
-
-static __inline__ int mode_is(hdlc_device *hdlc, int mask)
-{
- return (hdlc->mode & mask) == mask;
-}
-
-
-
-static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
-{
- pvc_device *pvc=hdlc->first_pvc;
-
- while (pvc) {
- if (netdev_dlci(&pvc->netdev) == dlci)
- return pvc;
- pvc=pvc->next;
- }
-
- return NULL;
+ hdr[0] = (dlci >> 2) & 0xFC;
+ hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
}
}
+
+/* Must be called by hardware driver when HDLC device is being opened */
+static __inline__ int hdlc_open(hdlc_device *hdlc)
+{
+ if (hdlc->proto == -1)
+ return -ENOSYS; /* no protocol attached */
+
+ if (hdlc->open)
+ return hdlc->open(hdlc);
+ return 0;
+}
+
+
+/* Must be called by hardware driver when HDLC device is being closed */
+static __inline__ void hdlc_close(hdlc_device *hdlc)
+{
+ if (hdlc->stop)
+ hdlc->stop(hdlc);
+}
+
+
+/* May be used by hardware driver to gain control over HDLC device */
+static __inline__ void hdlc_detach(hdlc_device *hdlc)
+{
+ if (hdlc->detach)
+ hdlc->detach(hdlc);
+ hdlc->detach = NULL;
+}
+
+
#endif /* __KERNEL */
#endif /* __HDLC_H */
/* Private (from user) interface flags (netdevice->priv_flags). */
#define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */
+
+#define IF_GET_IFACE 0x0001 /* for querying only */
+#define IF_GET_PROTO 0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35 0x1000 /* V.35 serial interface */
+#define IF_IFACE_V24 0x1001 /* V.24 serial interface */
+#define IF_IFACE_X21 0x1002 /* X.21 serial interface */
+#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */
+#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */
+#define IF_IFACE_SYNC_SERIAL 0x1005 /* cant'b be set by software */
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */
+#define IF_PROTO_PPP 0x2001 /* PPP protocol */
+#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */
+#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */
+#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */
+#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */
+#define IF_PROTO_X25 0x2006 /* X.25 */
+
+
+
/*
* Device mapping structure. I'd just gone off and designed a
* beautiful scheme using only loadable modules with arguments
/* 3 bytes spare */
};
+struct if_settings
+{
+ unsigned int type; /* Type of physical device or protocol */
+ unsigned int data_length; /* device/protocol data length */
+ void * data; /* pointer to data, ignored if length = 0 */
+};
+
+
/*
* Interface request structure used for socket
* ioctl's. All interface ioctl's must have parameter
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
char * ifru_data;
+ struct if_settings ifru_settings;
} ifr_ifru;
};
#define ifr_bandwidth ifr_ifru.ifru_ivalue /* link bandwidth */
#define ifr_qlen ifr_ifru.ifru_ivalue /* Queue length */
#define ifr_newname ifr_ifru.ifru_newname /* New name */
+#define ifr_settings ifr_ifru.ifru_settings /* Device/proto settings*/
/*
* Structure used in SIOCGIFCONF request.
#define ETH_P_CONTROL 0x0016 /* Card specific control frames */
#define ETH_P_IRDA 0x0017 /* Linux-IrDA */
#define ETH_P_ECONET 0x0018 /* Acorn Econet */
+#define ETH_P_HDLC 0x0019 /* HDLC frames */
/*
* This is an Ethernet frame header.
#define SIOCGMIIREG 0x8948 /* Read MII PHY register. */
#define SIOCSMIIREG 0x8949 /* Write MII PHY register. */
+#define SIOCDEVICE 0x894A /* get/set netdev parameters */
+
/* ARP cache control calls. */
/* 0x8950 - 0x8952 * obsolete calls, don't re-use */
#define SIOCDARP 0x8953 /* delete ARP table entry */
cmd == SIOCETHTOOL ||
cmd == SIOCGMIIPHY ||
cmd == SIOCGMIIREG ||
- cmd == SIOCSMIIREG) {
+ cmd == SIOCSMIIREG ||
+ cmd == SIOCDEVICE) {
if (dev->do_ioctl) {
if (!netif_device_present(dev))
return -ENODEV;
*/
default:
- if (cmd >= SIOCDEVPRIVATE &&
- cmd <= SIOCDEVPRIVATE + 15) {
+ if (cmd == SIOCDEVICE ||
+ (cmd >= SIOCDEVPRIVATE &&
+ cmd <= SIOCDEVPRIVATE + 15)) {
dev_load(ifr.ifr_name);
dev_probe_lock();
rtnl_lock();