From a36167db9ca1bb9df30fbac7961bc910cb85ad0e Mon Sep 17 00:00:00 2001 From: François Romieu Date: Wed, 6 Mar 2002 16:37:33 -0500 Subject: WAN drivers update 1/5: Add new HDLC interface, split up huge hdlc.c driver into multiple files based on hardware type. Convert WAN drivers to new interface. --- Documentation/networking/generic-hdlc.txt | 115 +++ drivers/net/wan/Config.help | 5 +- drivers/net/wan/Config.in | 15 +- drivers/net/wan/Makefile | 14 +- drivers/net/wan/c101.c | 124 +-- drivers/net/wan/dscc4.c | 107 +-- drivers/net/wan/farsync.c | 565 +++++------ drivers/net/wan/farsync.h | 5 - drivers/net/wan/hd64570.h | 8 +- drivers/net/wan/hd6457x.c | 250 ++--- drivers/net/wan/hdlc.c | 1453 ----------------------------- drivers/net/wan/hdlc_cisco.c | 302 ++++++ drivers/net/wan/hdlc_fr.c | 851 +++++++++++++++++ drivers/net/wan/hdlc_generic.c | 188 ++++ drivers/net/wan/hdlc_ppp.c | 121 +++ drivers/net/wan/hdlc_raw.c | 93 ++ drivers/net/wan/hdlc_x25.c | 223 +++++ drivers/net/wan/n2.c | 125 +-- include/linux/hdlc.h | 339 ++++--- include/linux/if.h | 33 + include/linux/if_ether.h | 1 + include/linux/sockios.h | 2 + net/core/dev.c | 8 +- 23 files changed, 2715 insertions(+), 2232 deletions(-) create mode 100644 Documentation/networking/generic-hdlc.txt delete mode 100644 drivers/net/wan/hdlc.c create mode 100644 drivers/net/wan/hdlc_cisco.c create mode 100644 drivers/net/wan/hdlc_fr.c create mode 100644 drivers/net/wan/hdlc_generic.c create mode 100644 drivers/net/wan/hdlc_ppp.c create mode 100644 drivers/net/wan/hdlc_raw.c create mode 100644 drivers/net/wan/hdlc_x25.c diff --git a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt new file mode 100644 index 000000000000..37631c95b90a --- /dev/null +++ b/Documentation/networking/generic-hdlc.txt @@ -0,0 +1,115 @@ +Generic HDLC layer for Linux kernel 2.4/2.5 +Krzysztof Halasa +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/ diff --git a/drivers/net/wan/Config.help b/drivers/net/wan/Config.help index d9ecd0636727..eebc3287fa86 100644 --- a/drivers/net/wan/Config.help +++ b/drivers/net/wan/Config.help @@ -41,14 +41,15 @@ CONFIG_FARSYNC 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 . 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 diff --git a/drivers/net/wan/Config.in b/drivers/net/wan/Config.in index 0a910e1611d9..9f0e4c3c5c1c 100644 --- a/drivers/net/wan/Config.in +++ b/drivers/net/wan/Config.in @@ -39,9 +39,6 @@ if [ "$CONFIG_WAN" = "y" ]; then 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 @@ -55,8 +52,13 @@ if [ "$CONFIG_WAN" = "y" ]; then 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 @@ -65,6 +67,11 @@ if [ "$CONFIG_WAN" = "y" ]; then 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 diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index 988990ef0848..04e5538586bc 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -9,7 +9,7 @@ SUB_DIRS := 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) @@ -22,6 +22,11 @@ wanpipe-$(CONFIG_WANPIPE_MULTPPP) += wanpipe_multppp.o 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 @@ -56,12 +61,17 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.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) diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c index e8f413ef9489..4fe768a46521 100644 --- a/drivers/net/wan/c101.c +++ b/drivers/net/wan/c101.c @@ -1,7 +1,7 @@ /* * Moxa C101 synchronous serial card driver for Linux * - * Copyright (C) 2000 Krzysztof Halasa + * Copyright (C) 2000-2001 Krzysztof Halasa * * 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 @@ -15,6 +15,7 @@ * Moxa C101 User's Manual */ +#include #include #include #include @@ -29,10 +30,8 @@ #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 @@ -51,12 +50,12 @@ static char *hw; /* pointer to hw=xxx command line string */ 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 */ @@ -72,6 +71,9 @@ typedef struct card_s { 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)) @@ -105,13 +107,13 @@ static inline void openwin(card_t *card, u8 page) #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 */ @@ -140,76 +142,85 @@ static int c101_set_clock(port_t *port, int value) 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; } @@ -231,6 +242,7 @@ static void c101_destroy_card(card_t *card) static int c101_run(unsigned long irq, unsigned long winbase) { + struct net_device *dev; card_t *card; int result; @@ -284,15 +296,19 @@ static int c101_run(unsigned long irq, unsigned long winbase) 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) { @@ -319,7 +335,7 @@ static int __init c101_init(void) 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; diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index cc48edb4c809..0d55b479ec00 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -72,7 +72,7 @@ * 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. @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -114,7 +115,7 @@ #include /* 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; @@ -168,9 +169,9 @@ struct RxFD { #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 @@ -343,6 +344,11 @@ static inline int dscc4_set_quartz(struct dscc4_dev_priv *, int); 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; @@ -479,52 +485,49 @@ static int dscc4_do_action(struct net_device *dev, char *msg) 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; @@ -612,7 +615,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev, * 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); @@ -620,7 +623,7 @@ static int __init dscc4_init_one (struct pci_dev *pdev, 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); @@ -740,7 +743,6 @@ static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr) 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; @@ -778,7 +780,7 @@ static void dscc4_timer(unsigned long data) 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; @@ -844,7 +846,7 @@ static int dscc4_loopback_check(struct dscc4_dev_priv *dpriv) 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; @@ -893,8 +895,10 @@ static int dscc4_open(struct net_device *dev) * 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); @@ -925,7 +929,7 @@ static int dscc4_tx_poll(struct dscc4_dev_priv *dpriv, struct net_device *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; @@ -935,7 +939,6 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) 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); @@ -972,7 +975,7 @@ static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev) 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); @@ -1011,7 +1014,7 @@ static inline int dscc4_check_clock_ability(int port) 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; @@ -1062,7 +1065,7 @@ static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state) 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; @@ -1133,7 +1136,7 @@ static int dscc4_match(struct thingie *p, int value) 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; @@ -1160,7 +1163,7 @@ static int dscc4_clock_setting(struct net_device *dev) 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 }, @@ -1184,7 +1187,7 @@ static int dscc4_encoding_setting(struct net_device *dev) 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; @@ -1203,7 +1206,7 @@ static int dscc4_loopback_setting(struct net_device *dev) 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 }, @@ -1516,7 +1519,6 @@ try: } } else { /* ! SccEvt */ #ifdef DEBUG_PARANOIA - int i; static struct { u32 mask; const char *irq_name; @@ -1528,21 +1530,21 @@ try: { 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; @@ -1633,7 +1635,7 @@ try: 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; @@ -1654,7 +1656,7 @@ static int dscc4_init_ring(struct net_device *dev) 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; @@ -1689,7 +1691,7 @@ static int dscc4_init_ring(struct net_device *dev) 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; @@ -1753,7 +1755,8 @@ static void __exit dscc4_remove_one(struct pci_dev *pdev) 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 && @@ -1782,7 +1785,7 @@ static struct pci_device_id dscc4_pci_tbl[] __devinitdata = { 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, diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 64258f647ce4..79241627cf3c 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -1,5 +1,5 @@ /* - * 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 * @@ -18,11 +18,10 @@ #include #include #include -#include #include -#include #include -#include +#include +#include #include "farsync.h" @@ -32,6 +31,7 @@ */ MODULE_AUTHOR("R.J.Dunlop "); MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd."); +MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; @@ -331,26 +331,15 @@ struct buf_window { /* 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 @@ -370,6 +359,11 @@ struct fst_card_info { 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 @@ -632,25 +626,18 @@ fst_intr_ctlchg ( struct fst_card_info *card, struct fst_port_info *port ) 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 )); } } } @@ -693,24 +680,24 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *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 @@ -747,7 +734,7 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port ) { 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 ); @@ -771,29 +758,16 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port ) 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; } @@ -844,7 +818,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs ) case CTLB_CHG: case CTLC_CHG: case CTLD_CHG: - if ( port->run && port->dev != NULL ) + if ( port->run ) fst_intr_ctlchg ( card, port ); break; @@ -863,9 +837,8 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs ) * 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: @@ -890,7 +863,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs ) 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 */ @@ -907,7 +880,7 @@ fst_intr ( int irq, void *dev_id, struct pt_regs *regs ) --port->txcnt; if ( ++port->txipos >= NUM_TX_BUFFER ) port->txipos = 0; - netif_wake_queue ( port->dev ); + netif_wake_queue ( port_to_dev ( port )); } } @@ -968,146 +941,29 @@ check_started_ok ( struct fst_card_info *card ) } -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; @@ -1125,7 +981,7 @@ gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port, 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; @@ -1153,6 +1009,119 @@ gather_conf_info ( struct fst_card_info *card, struct fst_port_info *port, } +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 ) { @@ -1164,7 +1133,7 @@ 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 )) @@ -1201,7 +1170,7 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) * 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; } @@ -1261,6 +1230,9 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) 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; @@ -1268,12 +1240,25 @@ fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd ) 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 ); } } @@ -1306,9 +1291,9 @@ fst_openport ( struct fst_port_info *port ) 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 )); } } @@ -1335,64 +1320,15 @@ fst_closeport ( struct fst_port_info *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; } @@ -1400,34 +1336,22 @@ fst_open ( struct net_device *dev ) 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 ) @@ -1436,10 +1360,10 @@ 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 ); @@ -1459,22 +1383,15 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) 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; } @@ -1484,7 +1401,7 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) 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; } @@ -1498,7 +1415,7 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) 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 ) @@ -1518,8 +1435,8 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) 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 ); @@ -1528,18 +1445,6 @@ fst_start_xmit ( struct sk_buff *skb, struct net_device *dev ) } -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 @@ -1566,34 +1471,13 @@ fst_init_card ( struct fst_card_info *card ) */ 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. @@ -1609,26 +1493,19 @@ fst_init_card ( struct fst_card_info *card ) 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; } @@ -1637,8 +1514,8 @@ fst_init_card ( struct fst_card_info *card ) 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 ); } @@ -1789,8 +1666,7 @@ fst_remove_one ( struct pci_dev *pdev ) 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 ); @@ -1830,4 +1706,3 @@ fst_cleanup_module(void) module_init ( fst_init ); module_exit ( fst_cleanup_module ); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/wan/farsync.h b/drivers/net/wan/farsync.h index bbc08a84ca07..f9e32e2f79b4 100644 --- a/drivers/net/wan/farsync.h +++ b/drivers/net/wan/farsync.h @@ -32,13 +32,8 @@ * 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 */ diff --git a/drivers/net/wan/hd64570.h b/drivers/net/wan/hd64570.h index 9d22122dad11..cd3fcf86d6cf 100644 --- a/drivers/net/wan/hd64570.h +++ b/drivers/net/wan/hd64570.h @@ -152,7 +152,7 @@ typedef struct { 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; @@ -202,7 +202,11 @@ typedef struct { #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 diff --git a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c index ff976438932d..7f8677c62a04 100644 --- a/drivers/net/wan/hd6457x.c +++ b/drivers/net/wan/hd6457x.c @@ -42,13 +42,7 @@ #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) @@ -63,11 +57,19 @@ static card_t **new_card = &first_card; #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); @@ -76,19 +78,8 @@ static inline int sca_intr_status(card_t *card) 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); @@ -97,11 +88,17 @@ static inline int sca_intr_status(card_t *card) 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; +} @@ -250,8 +247,7 @@ static inline void sca_msci_intr(port_t *port) -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; @@ -289,13 +285,16 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, 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); } @@ -320,14 +319,9 @@ static inline void sca_rx_intr(port_t *port) 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)) @@ -371,20 +365,17 @@ static inline void sca_tx_intr(port_t *port) 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; } @@ -398,7 +389,8 @@ static inline void sca_tx_intr(port_t *port) 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; @@ -421,9 +413,11 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) } if (--boguscnt < 0) { +#if 0 printk(KERN_ERR "%s: too much work at " "interrupt\n", hdlc_to_name(&port->hdlc)); +#endif goto exit; } } @@ -437,47 +431,22 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) -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) { @@ -487,11 +456,11 @@ static void sca_set_clock(port_t *port) } 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; @@ -509,27 +478,59 @@ static void sca_set_clock(port_t *port) /* 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 */ @@ -539,24 +540,28 @@ static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi) 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) | @@ -573,11 +578,52 @@ static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi) 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); @@ -644,34 +690,14 @@ static void sca_dump_rings(hdlc_device *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; @@ -685,7 +711,7 @@ static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb) 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); @@ -693,7 +719,7 @@ static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb) 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 diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c deleted file mode 100644 index 34da9c73b4d9..000000000000 --- a/drivers/net/wan/hdlc.c +++ /dev/null @@ -1,1453 +0,0 @@ -/* - * Generic HDLC support routines for Linux - * - * Copyright (C) 1999, 2000 Krzysztof Halasa - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* #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->lenaddress != 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 "); -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); diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c new file mode 100644 index 000000000000..143ef3dfa7bf --- /dev/null +++ b/drivers/net/wan/hdlc_cisco.c @@ -0,0 +1,302 @@ +/* + * Generic HDLC support routines for Linux + * Cisco HDLC support + * + * Copyright (C) 2000 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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; +} diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c new file mode 100644 index 000000000000..89f2aa84ebda --- /dev/null +++ b/drivers/net/wan/hdlc_fr.c @@ -0,0 +1,851 @@ +/* + * Generic HDLC support routines for Linux + * Frame Relay support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +__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; +} diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c new file mode 100644 index 000000000000..f1454c617db7 --- /dev/null +++ b/drivers/net/wan/hdlc_generic.c @@ -0,0 +1,188 @@ +/* + * Generic HDLC support routines for Linux + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 "); +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); diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c new file mode 100644 index 000000000000..0f95449a6cdc --- /dev/null +++ b/drivers/net/wan/hdlc_ppp.c @@ -0,0 +1,121 @@ +/* + * Generic HDLC support routines for Linux + * Point-to-point protocol support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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; +} diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c new file mode 100644 index 000000000000..f61e5460067f --- /dev/null +++ b/drivers/net/wan/hdlc_raw.c @@ -0,0 +1,93 @@ +/* + * Generic HDLC support routines for Linux + * HDLC support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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; +} diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c new file mode 100644 index 000000000000..b0bbce528a9f --- /dev/null +++ b/drivers/net/wan/hdlc_x25.c @@ -0,0 +1,223 @@ +/* + * Generic HDLC support routines for Linux + * X.25 support + * + * Copyright (C) 1999 - 2001 Krzysztof Halasa + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c index 4c0ae1d3a7fb..d5d0d1c2e872 100644 --- a/drivers/net/wan/n2.c +++ b/drivers/net/wan/n2.c @@ -1,7 +1,7 @@ /* * SDL Inc. RISCom/N2 synchronous serial card driver for Linux * - * Copyright (C) 1998-2000 Krzysztof Halasa + * Copyright (C) 1998-2001 Krzysztof Halasa * * 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 @@ -17,6 +17,7 @@ * SDL Inc. PPP/HDLC/CISCO driver */ +#include #include #include #include @@ -33,10 +34,8 @@ #include #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 @@ -87,9 +86,9 @@ typedef struct port_s { 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 */ @@ -116,6 +115,9 @@ typedef struct card_s { }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)) @@ -157,7 +159,7 @@ static __inline__ void close_windows(card_t *card) -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; @@ -166,7 +168,7 @@ static int n2_set_clock(port_t *port, int value) 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 */ @@ -200,17 +202,22 @@ static int n2_set_clock(port_t *port, int value) 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 */ @@ -219,14 +226,14 @@ static int n2_open(hdlc_device *hdlc) 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); @@ -234,52 +241,57 @@ static void n2_close(hdlc_device *hdlc) 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; } @@ -465,6 +477,7 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase, 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; @@ -476,14 +489,16 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase, 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 " @@ -515,7 +530,7 @@ static int __init n2_init(void) 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; diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h index 3ac12961d802..eea06e00db22 100644 --- a/include/linux/hdlc.h +++ b/include/linux/hdlc.h @@ -1,7 +1,7 @@ /* * Generic HDLC support routines for Linux * - * Copyright (C) 1999, 2000 Krzysztof Halasa + * Copyright (C) 1999-2002 Krzysztof Halasa * * 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 @@ -12,51 +12,78 @@ #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__ @@ -64,12 +91,10 @@ #include #include -#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 @@ -112,6 +137,7 @@ typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) unsigned ea1 : 1; unsigned cr : 1; unsigned dlcih: 6; @@ -121,6 +147,19 @@ typedef struct { 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 " +#endif }__attribute__ ((packed)) fr_hdr; @@ -151,63 +190,96 @@ typedef struct pvc_device_struct { 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) @@ -246,33 +318,6 @@ static __inline__ const char *pvc_to_name(pvc_device *pvc) } -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); @@ -282,37 +327,15 @@ static __inline__ u16 netdev_dlci(struct net_device *dev) 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; } @@ -332,5 +355,35 @@ static __inline__ void debug_frame(const struct sk_buff *skb) } + +/* 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 */ diff --git a/include/linux/if.h b/include/linux/if.h index 9a38b2e2ab33..2bba24a313a4 100644 --- a/include/linux/if.h +++ b/include/linux/if.h @@ -48,6 +48,29 @@ /* 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 @@ -69,6 +92,14 @@ struct ifmap /* 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 @@ -98,6 +129,7 @@ struct ifreq char ifru_slave[IFNAMSIZ]; /* Just fits the size */ char ifru_newname[IFNAMSIZ]; char * ifru_data; + struct if_settings ifru_settings; } ifr_ifru; }; @@ -117,6 +149,7 @@ struct ifreq #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. diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h index b64559d713f9..dbeb3728ef24 100644 --- a/include/linux/if_ether.h +++ b/include/linux/if_ether.h @@ -85,6 +85,7 @@ #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. diff --git a/include/linux/sockios.h b/include/linux/sockios.h index 0c5c4e1f803a..cc9e0d7186d0 100644 --- a/include/linux/sockios.h +++ b/include/linux/sockios.h @@ -81,6 +81,8 @@ #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 */ diff --git a/net/core/dev.c b/net/core/dev.c index 3afa0c27a4a1..8cfc4d14aad7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2110,7 +2110,8 @@ static int dev_ifsioc(struct ifreq *ifr, unsigned int cmd) cmd == SIOCETHTOOL || cmd == SIOCGMIIPHY || cmd == SIOCGMIIREG || - cmd == SIOCSMIIREG) { + cmd == SIOCSMIIREG || + cmd == SIOCDEVICE) { if (dev->do_ioctl) { if (!netif_device_present(dev)) return -ENODEV; @@ -2276,8 +2277,9 @@ int dev_ioctl(unsigned int cmd, void *arg) */ 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(); -- cgit v1.2.3