Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrzysztof Halasa <khc@pm.waw.pl>2003-04-06 19:22:40 -0700
committerLinus Torvalds <torvalds@home.transmeta.com>2003-04-06 19:22:40 -0700
commit80d316b743aa85e2a3a0e449efc6feada77041a3 (patch)
tree684015eaac17b9979ff7854aede0708a1048e3f7
parent00c6dc1cafc25e1979543eb4c62f51821f06b556 (diff)
[PATCH] generic HDLC update
This version fixes: - missing rtnl_lock()/rtnl_unload() bug on unregister_hdlc_device - N2, C101: interrupt handler now works under high IRQ load from other devices (with previous versions, the IRQ processing for the card could sometimes stop after reaching "work limit") This is production-tested on devices I have access to (N2, C101, PC300, PCI200SYN).
-rw-r--r--Documentation/networking/generic-hdlc.txt42
-rw-r--r--drivers/net/wan/Kconfig41
-rw-r--r--drivers/net/wan/Makefile1
-rw-r--r--drivers/net/wan/c101.c76
-rw-r--r--drivers/net/wan/dscc4.c2
-rw-r--r--drivers/net/wan/farsync.c2
-rw-r--r--drivers/net/wan/hd6457x.c144
-rw-r--r--drivers/net/wan/hdlc_cisco.c44
-rw-r--r--drivers/net/wan/hdlc_fr.c603
-rw-r--r--drivers/net/wan/hdlc_generic.c37
-rw-r--r--drivers/net/wan/hdlc_ppp.c16
-rw-r--r--drivers/net/wan/hdlc_raw.c19
-rw-r--r--drivers/net/wan/hdlc_raw_eth.c110
-rw-r--r--drivers/net/wan/hdlc_x25.c8
-rw-r--r--drivers/net/wan/n2.c159
-rw-r--r--include/linux/hdlc.h66
-rw-r--r--include/linux/hdlc/ioctl.h17
-rw-r--r--include/linux/if.h10
18 files changed, 895 insertions, 502 deletions
diff --git a/Documentation/networking/generic-hdlc.txt b/Documentation/networking/generic-hdlc.txt
index 37631c95b90a..25dd993e0d3f 100644
--- a/Documentation/networking/generic-hdlc.txt
+++ b/Documentation/networking/generic-hdlc.txt
@@ -1,11 +1,13 @@
-Generic HDLC layer for Linux kernel 2.4/2.5
+Generic HDLC layer
Krzysztof Halasa <khc@pm.waw.pl>
-May, 2001
+January, 2003
Generic HDLC layer currently supports:
-- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP),
-- raw HDLC (IPv4 only),
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP).
+ Normal (routed) and Ethernet-bridged (Ethernet device emulation)
+ interfaces can share a single PVC.
+- raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
- Cisco HDLC,
- PPP (uses syncppp.c),
- X.25 (uses X.25 routines).
@@ -15,6 +17,10 @@ There are hardware drivers for the following cards:
- RISCom/N2 by SDL Communications Inc.
- and others, some not in the official kernel.
+Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
+with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
+
+
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:
@@ -58,6 +64,9 @@ Setting protocol:
no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
+* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding
+ as above.
+
* 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
@@ -77,7 +86,12 @@ Setting protocol:
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.
+Frame-Relay only:
+* create n | delete n - adds / deletes PVC interface with DLCI #n.
+ Newly created interface will be named pvc0, pvc1 etc.
+
+* create ether n | delete ether n - adds a device for Ethernet-bridged
+ frames. The device will be named pvceth0, pvceth1 etc.
@@ -85,30 +99,30 @@ Setting protocol:
Board-specific issues
---------------------
-n2.o and c101.o need parameters to work (note double quotes):
+n2.o and c101.o need parameters to work:
- insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"'
+ insmod n2 hw=io,irq,ram,ports[:io,irq,...]
example:
- insmod n2 hw='"0x300,10,0xD0000,01"'
+ insmod n2 hw=0x300,10,0xD0000,01
or
- insmod c101 hw='"irq,ram[:irq,...]"
+ insmod c101 hw=irq,ram[:irq,...]
example:
- insmod c101 hw='"9,0xdc000"'
+ insmod c101 hw=9,0xdc000
If built into the kernel, these drivers need kernel (command line) parameters:
- n2=io,irq,ram,ports:...
+ n2.hw=io,irq,ram,ports:...
or
- c101=irq,ram:...
+ c101.hw=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:
+command to see port's packet descriptor rings (in kernel logs):
sethdlc hdlc0 private
-The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS.
+The hardware driver has 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.
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index dded2729c36c..8a6e03628073 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -253,6 +253,11 @@ config HDLC
Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame
Relay, synchronous Point-to-Point Protocol (PPP) and X.25.
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read Documentation/modules.txt. The module
+ will be called hdlc.o.
+
If unsure, say N here.
config HDLC_RAW
@@ -264,6 +269,17 @@ config HDLC_RAW
If unsure, say N here.
+config HDLC_RAW_ETH
+ bool "Raw HDLC Ethernet device support"
+ depends on HDLC
+ help
+ Say Y to this option if you want generic HDLC driver to support
+ raw HDLC Ethernet device emulation over WAN (Wide Area Network)
+ connections.
+ You will need it for Ethernet over HDLC bridges.
+
+ If unsure, say N here.
+
config HDLC_CISCO
bool "Cisco HDLC support"
depends on HDLC
@@ -344,6 +360,11 @@ config N2
Note that N2csu and N2dds cards are not supported by this driver.
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called n2.o.
+
If unsure, say N here.
config C101
@@ -354,6 +375,11 @@ config C101
Technologies Co., Ltd. If you have such a card,
say Y here and see <http://hq.pm.waw.pl/pub/hdlc/>
+ If you want to compile the driver as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want),
+ say M here and read <file:Documentation/modules.txt>. The module
+ will be called c101.o.
+
If unsure, say N here.
config FARSYNC
@@ -377,18 +403,33 @@ config FARSYNC
config HDLC_DEBUG_PKT
bool "Debug received/transmitted packets"
depends on HDLC
+ help
+ This option is for developers only - do NOT use on production
+ systems.
config HDLC_DEBUG_HARD_HEADER
bool "Debug hard_header routines"
depends on HDLC
+ help
+ This option is for developers only - do NOT use on production
+ systems.
config HDLC_DEBUG_ECN
bool "Debug FECN/BECN conditions"
depends on HDLC
+ help
+ This option is for developers only - do NOT use on production
+ systems.
config HDLC_DEBUG_RINGS
bool "Debug RX/TX packet rings"
depends on HDLC
+ help
+ If you answer Y here you will be able to get a diagnostic dump of
+ port's TX and RX packet rings, using "sethdlc hdlcX private"
+ command. It does not affect normal operations.
+
+ If unsure, say Y here.
config DLCI
tristate "Frame relay DLCI support"
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 5eef9b7d7c75..4d540014c93f 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -19,6 +19,7 @@ cyclomx-objs := $(cyclomx-y)
hdlc-y := hdlc_generic.o
hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o
+hdlc-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o
hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c
index bf879f910abc..5995756d1016 100644
--- a/drivers/net/wan/c101.c
+++ b/drivers/net/wan/c101.c
@@ -1,12 +1,11 @@
/*
* Moxa C101 synchronous serial card driver for Linux
*
- * Copyright (C) 2000-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*
* For information see http://hq.pm.waw.pl/hdlc/
*
@@ -23,6 +22,7 @@
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/init.h>
+#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/hdlc.h>
#include <linux/delay.h>
@@ -31,7 +31,7 @@
#include "hd64570.h"
-static const char* version = "Moxa C101 driver version: 1.10";
+static const char* version = "Moxa C101 driver version: 1.14";
static const char* devname = "C101";
#define C101_PAGE 0x1D00
@@ -41,6 +41,10 @@ static const char* devname = "C101";
#define C101_MAPPED_RAM_SIZE 0x4000
#define RAM_SIZE (256 * 1024)
+#define TX_RING_BUFFERS 10
+#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) / \
+ (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS)
+
#define CLOCK_BASE 9830400 /* 9.8304 MHz */
#define PAGE0_ALWAYS_MAPPED
@@ -52,20 +56,20 @@ typedef struct card_s {
spinlock_t lock; /* TX lock */
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;
+ int rxpart; /* partial frame received, next frame invalid*/
unsigned short encoding;
unsigned short parity;
+ u16 rx_ring_buffers; /* number of buffers in a ring */
+ u16 tx_ring_buffers;
+ u16 buff_offset; /* offset of first buffer of first channel */
+ u16 rxin; /* rx ring buffer 'in' pointer */
+ u16 txin; /* tx ring buffer 'in' and 'last' pointers */
+ u16 txlast;
u8 rxs, txs, tmc; /* SCA registers */
u8 irq; /* IRQ (3-15) */
- u8 ring_buffers; /* number of buffers in a ring */
u8 page;
- u8 rxin; /* rx ring buffer 'in' pointer */
- u8 txin; /* tx ring buffer 'in' and 'last' pointers */
- u8 txlast;
- u8 rxpart; /* partial frame received, next frame invalid*/
-
struct card_s *next_card;
}card_t;
@@ -78,7 +82,12 @@ 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))
#define sca_inw(reg, card) readw((card)->win0base + C101_SCA + (reg))
-#define sca_outw(value, reg, card) writew(value, (card)->win0base + C101_SCA + (reg))
+
+/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
+#define sca_outw(value, reg, card) do { \
+ writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \
+ writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\
+} while(0)
#define port_to_card(port) (port)
#define log_node(port) (0)
@@ -146,11 +155,16 @@ static int c101_open(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc);
+
+ if (!try_module_get(THIS_MODULE))
+ return -EFAULT; /* rmmod in progress */
+
int result = hdlc_open(hdlc);
- if (result)
+ if (result) {
return result;
+ module_put(THIS_MODULE);
+ }
- 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);
@@ -168,7 +182,7 @@ static int c101_close(struct net_device *dev)
writeb(0, port->win0base + C101_DTR);
sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
hdlc_close(hdlc);
- MOD_DEC_USE_COUNT;
+ module_put(THIS_MODULE);
return 0;
}
@@ -229,6 +243,8 @@ static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static void c101_destroy_card(card_t *card)
{
+ readb(card->win0base + C101_PAGE); /* Resets SCA? */
+
if (card->irq)
free_irq(card->irq, card);
@@ -242,7 +258,7 @@ static void c101_destroy_card(card_t *card)
-static int c101_run(unsigned long irq, unsigned long winbase)
+static int __init c101_run(unsigned long irq, unsigned long winbase)
{
struct net_device *dev;
card_t *card;
@@ -285,9 +301,10 @@ static int c101_run(unsigned long irq, unsigned long winbase)
return -EBUSY;
}
- /* 2 rings required for 1 port */
- card->ring_buffers = (RAM_SIZE -C101_WINDOW_SIZE) / (2 * HDLC_MAX_MRU);
- printk(KERN_DEBUG "c101: using %u packets rings\n",card->ring_buffers);
+ card->tx_ring_buffers = TX_RING_BUFFERS;
+ card->rx_ring_buffers = RX_RING_BUFFERS;
+ printk(KERN_DEBUG "c101: using %u TX + %u RX packets rings\n",
+ card->tx_ring_buffers, card->rx_ring_buffers);
card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */
@@ -337,7 +354,7 @@ static int __init c101_init(void)
return -ENOSYS; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
+ printk(KERN_INFO "%s\n", version);
do {
unsigned long irq, ram;
@@ -352,7 +369,7 @@ static int __init c101_init(void)
c101_run(irq, ram);
if (*hw == '\x0')
- return 0;
+ return first_card ? 0 : -ENOSYS;
}while(*hw++ == ':');
printk(KERN_ERR "c101: invalid hardware parameters\n");
@@ -360,17 +377,6 @@ static int __init c101_init(void)
}
-#ifndef MODULE
-static int __init c101_setup(char *str)
-{
- hw = str;
- return 1;
-}
-
-__setup("c101=", c101_setup);
-#endif
-
-
static void __exit c101_cleanup(void)
{
card_t *card = first_card;
@@ -389,5 +395,5 @@ module_exit(c101_cleanup);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Moxa C101 serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_PARM(hw, "s"); /* hw=irq,ram:irq,... */
+MODULE_LICENSE("GPL v2");
+module_param(hw, charp, 0444); /* hw=irq,ram:irq,... */
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index c5561f75cefb..21ab4da5b88c 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -620,7 +620,7 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv,
skb->tail += pkt_len;
skb->len = pkt_len;
if (netif_running(dev))
- skb->protocol = htons(ETH_P_HDLC);
+ skb->protocol = hdlc_type_trans(skb, dev);
skb->dev->last_rx = jiffies;
netif_rx(skb);
} else {
diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c
index 01c96aea4eee..e583a6a4082e 100644
--- a/drivers/net/wan/farsync.c
+++ b/drivers/net/wan/farsync.c
@@ -761,7 +761,7 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
/* Push upstream */
skb->mac.raw = skb->data;
skb->dev = hdlc_to_dev ( &port->hdlc );
- skb->protocol = htons ( ETH_P_HDLC );
+ skb->protocol = hdlc_type_trans(skb, skb->dev);
netif_rx ( skb );
port_to_dev ( port )->last_rx = jiffies;
diff --git a/drivers/net/wan/hd6457x.c b/drivers/net/wan/hd6457x.c
index fb1d60d05a46..110ae6c03a3b 100644
--- a/drivers/net/wan/hd6457x.c
+++ b/drivers/net/wan/hd6457x.c
@@ -1,16 +1,29 @@
/*
* Hitachi SCA HD64570 and HD64572 common driver for Linux
*
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*
* Sources of information:
* Hitachi HD64570 SCA User's Manual
* Hitachi HD64572 SCA-II User's Manual
+ *
+ * We use the following SCA memory map:
+ *
+ * Packet buffer descriptor rings - starting from winbase or win0base:
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
+ *
+ * Packet data buffers - starting from winbase + buff_offset:
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers
+ * rx_ring_buffers * HDLC_MAX_MRU = logical channel #0 RX buffers (if used)
+ * tx_ring_buffers * HDLC_MAX_MRU = logical channel #0 TX buffers (if used)
*/
#include <linux/module.h>
@@ -42,8 +55,6 @@
#error Either hd64570.h or hd64572.h must be included
#endif
-static char sca_version[]="1.09";
-
#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET)
#define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
#define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
@@ -116,24 +127,35 @@ static inline port_t* dev_to_port(struct net_device *dev)
-static inline u8 next_desc(port_t *port, u8 desc)
+static inline u16 next_desc(port_t *port, u16 desc, int transmit)
+{
+ return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
+ : port_to_card(port)->rx_ring_buffers);
+}
+
+
+
+static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
{
- return (desc + 1) % port_to_card(port)->ring_buffers;
+ u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
+ u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
+
+ desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
+ return log_node(port) * (rx_buffs + tx_buffs) +
+ transmit * rx_buffs + desc;
}
-static inline u16 desc_offset(port_t *port, u8 desc, u8 transmit)
+static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
{
/* Descriptor offset always fits in 16 bytes */
- u8 buffs = port_to_card(port)->ring_buffers;
- return ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
- sizeof(pkt_desc);
+ return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
}
-static inline pkt_desc* desc_address(port_t *port, u8 desc, u8 transmit)
+static inline pkt_desc* desc_address(port_t *port, u16 desc, int transmit)
{
#ifdef PAGE0_ALWAYS_MAPPED
return (pkt_desc*)(win0base(port_to_card(port))
@@ -146,12 +168,10 @@ static inline pkt_desc* desc_address(port_t *port, u8 desc, u8 transmit)
-static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit)
+static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
{
- u8 buffs = port_to_card(port)->ring_buffers;
return port_to_card(port)->buff_offset +
- ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
- (u32)HDLC_MAX_MRU;
+ desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
}
@@ -159,8 +179,7 @@ static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit)
static void sca_init_sync_port(port_t *port)
{
card_t *card = port_to_card(port);
- u8 transmit, i;
- u16 dmac, buffs = card->ring_buffers;
+ int transmit, i;
port->rxin = 0;
port->txin = 0;
@@ -171,6 +190,10 @@ static void sca_init_sync_port(port_t *port)
#endif
for (transmit = 0; transmit < 2; transmit++) {
+ u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
+ u16 buffs = transmit ? card->tx_ring_buffers
+ : card->rx_ring_buffers;
+
for (i = 0; i < buffs; i++) {
pkt_desc* desc = desc_address(port, i, transmit);
u16 chain_off = desc_offset(port, i + 1, transmit);
@@ -182,7 +205,6 @@ static void sca_init_sync_port(port_t *port)
writeb(0, &desc->stat);
}
- dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
/* DMA disable - to halt state */
sca_out(0, transmit ? DSR_TX(phy_node(port)) :
DSR_RX(phy_node(port)), card);
@@ -247,7 +269,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, u16 rxin)
{
struct sk_buff *skb;
u16 len;
@@ -294,7 +316,7 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin)
skb->mac.raw = skb->data;
skb->dev = hdlc_to_dev(&port->hdlc);
skb->dev->last_rx = jiffies;
- skb->protocol = htons(ETH_P_HDLC);
+ skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc));
netif_rx(skb);
}
@@ -341,7 +363,7 @@ static inline void sca_rx_intr(port_t *port)
/* Set new error descriptor address */
sca_outa(desc_off, dmac + EDAL, card);
- port->rxin = next_desc(port, port->rxin);
+ port->rxin = next_desc(port, port->rxin, 0);
}
/* make sure RX DMA is enabled */
@@ -377,8 +399,7 @@ static inline void sca_tx_intr(port_t *port)
port->hdlc.stats.tx_packets++;
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;
+ port->txlast = next_desc(port, port->txlast, 1);
}
netif_wake_queue(hdlc_to_dev(&port->hdlc));
@@ -390,8 +411,6 @@ 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;
-/* Maximum events to handle at each interrupt - should I increase it? */
- int boguscnt = 4;
int i;
u8 stat;
@@ -412,19 +431,9 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
if (stat & SCA_INTR_DMAC_TX(i))
sca_tx_intr(port);
}
-
- if (--boguscnt < 0) {
-#if 0
- printk(KERN_ERR "%s: too much work at "
- "interrupt\n",
- hdlc_to_name(&port->hdlc));
-#endif
- goto exit;
- }
}
}
- exit:
#ifndef ALL_PAGES_ALWAYS_MAPPED
openwin(card, page); /* Restore original page */
#endif
@@ -435,7 +444,7 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
static void sca_set_port(port_t *port)
{
card_t* card = port_to_card(port);
- u8 msci = get_msci(port);
+ u16 msci = get_msci(port);
u8 md2 = sca_in(msci + MD2, card);
unsigned int tmc, br = 10, brv = 1024;
@@ -495,7 +504,7 @@ 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);
+ u16 msci = get_msci(port);
u8 md0, md2;
switch(port->encoding) {
@@ -516,7 +525,7 @@ static void sca_open(hdlc_device *hdlc)
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
+#endif
case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
default: md0 = MD0_HDLC | MD0_CRC_NONE;
}
@@ -613,7 +622,7 @@ static int sca_attach(hdlc_device *hdlc, unsigned short encoding,
parity != PARITY_CRC16_PR0_CCITT &&
#else
parity != PARITY_CRC32_PR1_CCITT &&
-#endif
+#endif
parity != PARITY_CRC16_PR1_CCITT)
return -EINVAL;
@@ -639,14 +648,13 @@ static void sca_dump_rings(hdlc_device *hdlc)
openwin(card, 0);
#endif
- printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u "
- "%sactive",
+ printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
sca_ina(get_dmac_rx(port) + CDAL, card),
sca_ina(get_dmac_rx(port) + EDAL, card),
sca_in(DSR_RX(phy_node(port)), card),
port->rxin,
sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in");
- for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+ for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
printk(" %02X",
readb(&(desc_address(port, cnt, 0)->stat)));
@@ -658,7 +666,7 @@ static void sca_dump_rings(hdlc_device *hdlc)
port->txlast,
sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
- for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+ for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
printk(" %02X",
readb(&(desc_address(port, cnt, 1)->stat)));
printk("\n");
@@ -750,7 +758,7 @@ static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
writeb(ST_TX_EOM, &desc->stat);
dev->trans_start = jiffies;
- port->txin = next_desc(port, port->txin);
+ port->txin = next_desc(port, port->txin, 1);
sca_outa(desc_offset(port, port->txin, 1),
get_dmac_tx(port) + EDAL, card);
@@ -767,7 +775,49 @@ static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
}
-static void sca_init(card_t *card, int wait_states)
+
+#ifdef NEED_DETECT_RAM
+static u32 __devinit sca_detect_ram(card_t *card, u8 *rambase, u32 ramsize)
+{
+ /* Round RAM size to 32 bits, fill from end to start */
+ u32 i = ramsize &= ~3;
+
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+ u32 size = winsize(card);
+
+ openwin(card, (i - 4) / size); /* select last window */
+#endif
+ do {
+ i -= 4;
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+ if ((i + 4) % size == 0)
+ openwin(card, i / size);
+ writel(i ^ 0x12345678, rambase + i % size);
+#else
+ writel(i ^ 0x12345678, rambase + i);
+#endif
+ }while (i > 0);
+
+ for (i = 0; i < ramsize ; i += 4) {
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+ if (i % size == 0)
+ openwin(card, i / size);
+
+ if (readl(rambase + i % size) != (i ^ 0x12345678))
+ break;
+#else
+ if (readl(rambase + i) != (i ^ 0x12345678))
+ break;
+#endif
+ }
+
+ return i;
+}
+#endif /* NEED_DETECT_RAM */
+
+
+
+static void __devinit sca_init(card_t *card, int wait_states)
{
sca_out(wait_states, WCRL, card); /* Wait Control */
sca_out(wait_states, WCRM, card);
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index 4d837aea20b1..f6234c5a64cc 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux
* Cisco HDLC support
*
- * Copyright (C) 2000 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*/
#include <linux/config.h>
@@ -80,17 +79,43 @@ static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
data->par1 = htonl(par1);
data->par2 = htonl(par2);
data->rel = 0xFFFF;
- data->time = htonl(jiffies * 1000 / HZ);
+ /* we will need do_div here if 1000 % HZ != 0 */
+ data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
skb_put(skb, sizeof(cisco_packet));
skb->priority = TC_PRIO_CONTROL;
skb->dev = hdlc_to_dev(hdlc);
+ skb->nh.raw = skb->data;
dev_queue_xmit(skb);
}
+static unsigned short cisco_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ hdlc_header *data = (hdlc_header*)skb->data;
+
+ if (skb->len < sizeof(hdlc_header))
+ return __constant_htons(ETH_P_HDLC);
+
+ if (data->address != CISCO_MULTICAST &&
+ data->address != CISCO_UNICAST)
+ return __constant_htons(ETH_P_HDLC);
+
+ switch(data->protocol) {
+ case __constant_htons(ETH_P_IP):
+ case __constant_htons(ETH_P_IPX):
+ case __constant_htons(ETH_P_IPV6):
+ skb_pull(skb, sizeof(hdlc_header));
+ return data->protocol;
+ default:
+ return __constant_htons(ETH_P_HDLC);
+ }
+}
+
+
static void cisco_rx(struct sk_buff *skb)
{
hdlc_device *hdlc = dev_to_hdlc(skb->dev);
@@ -109,14 +134,6 @@ static void cisco_rx(struct sk_buff *skb)
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);
@@ -288,6 +305,7 @@ int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = cisco_open;
hdlc->stop = cisco_close;
hdlc->netif_rx = cisco_rx;
+ hdlc->type_trans = cisco_type_trans;
hdlc->proto = IF_PROTO_CISCO;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = cisco_hard_header;
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 20a67ed26811..c0b5f1821043 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -2,13 +2,22 @@
* Generic HDLC support routines for Linux
* Frame Relay support
*
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+
+ Theory of PVC state in DCE mode:
+
+ (exist,new) -> 0,0 when "PVC create" or if "link unreliable"
+ 0,x -> 1,1 if "link reliable" when sending FULL STATUS
+ 1,1 -> 1,0 if received FULL STATUS ACK
+
+ (active) -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create"
+ -> 1 when "PVC up" and (exist,new) = 1,0
+*/
#include <linux/config.h>
#include <linux/module.h>
@@ -20,19 +29,23 @@
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/pkt_sched.h>
+#include <linux/random.h>
#include <linux/inetdevice.h>
#include <linux/lapb.h>
#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
#include <linux/hdlc.h>
__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
{
- pvc_device *pvc=hdlc->state.fr.first_pvc;
-
- while (pvc) {
- if (netdev_dlci(&pvc->netdev) == dlci)
+ pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+ while(pvc) {
+ if (pvc->dlci == dlci)
return pvc;
+ if (pvc->dlci > dlci)
+ return NULL; /* the listed is sorted */
pvc = pvc->next;
}
@@ -40,18 +53,72 @@ __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
}
+__inline__ pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci)
+{
+ pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
+
+ while(*pvc_p) {
+ if ((*pvc_p)->dlci == dlci)
+ return *pvc_p;
+ if ((*pvc_p)->dlci > dlci)
+ break; /* the listed is sorted */
+ pvc_p = &(*pvc_p)->next;
+ }
+
+ pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
+ if (!pvc)
+ return NULL;
+
+ memset(pvc, 0, sizeof(pvc_device));
+ pvc->dlci = dlci;
+ pvc->master = hdlc;
+ pvc->next = *pvc_p; /* Put it in the chain */
+ *pvc_p = pvc;
+ return pvc;
+}
+
+
+__inline__ int pvc_is_used(pvc_device *pvc)
+{
+ return pvc->main != NULL || pvc->ether != NULL;
+}
+
+
+__inline__ void delete_unused_pvcs(hdlc_device *hdlc)
+{
+ pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+
+ while(*pvc_p) {
+ if (!pvc_is_used(*pvc_p)) {
+ pvc_device *pvc = *pvc_p;
+ *pvc_p = pvc->next;
+ kfree(pvc);
+ continue;
+ }
+ pvc_p = &(*pvc_p)->next;
+ }
+}
+
+
+__inline__ struct net_device** get_dev_p(pvc_device *pvc, int type)
+{
+ if (type == ARPHRD_ETHER)
+ return &pvc->ether;
+ else
+ return &pvc->main;
+}
+
-__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status,
- int *active, int *new)
+__inline__ u16 status_to_dlci(u8 *status, int *active, int *new)
{
- *new = (status[2] & 0x08);
- *active = (!*new && (status[2] & 0x02));
+ *new = (status[2] & 0x08) ? 1 : 0;
+ *active = (status[2] & 0x02) ? 1 : 0;
return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
}
-__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
+__inline__ void dlci_to_status(u16 dlci, u8 *status,
int active, int new)
{
status[0] = (dlci>>4) & 0x3F;
@@ -66,37 +133,50 @@ __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
-static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
- u16 type, void *daddr, void *saddr, unsigned int len)
+static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
{
u16 head_len;
+ struct sk_buff *skb = *skb_p;
- 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:
+ switch(skb->protocol) {
+ case __constant_ntohs(ETH_P_IP):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = NLPID_IP;
break;
- case ETH_P_IPV6:
+ case __constant_ntohs(ETH_P_IPV6):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = NLPID_IPV6;
break;
- case LMI_PROTO:
+ case __constant_ntohs(LMI_PROTO):
head_len = 4;
skb_push(skb, head_len);
skb->data[3] = LMI_PROTO;
break;
+ case __constant_ntohs(ETH_P_802_3):
+ head_len = 10;
+ if (skb_headroom(skb) < head_len) {
+ struct sk_buff *skb2 = skb_realloc_headroom(skb,
+ head_len);
+ if (!skb2)
+ return -ENOBUFS;
+ dev_kfree_skb(skb);
+ skb = *skb_p = skb2;
+ }
+ skb_push(skb, head_len);
+ skb->data[3] = FR_PAD;
+ skb->data[4] = NLPID_SNAP;
+ skb->data[5] = FR_PAD;
+ skb->data[6] = 0x80;
+ skb->data[7] = 0xC2;
+ skb->data[8] = 0x00;
+ skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */
+ break;
+
default:
head_len = 10;
skb_push(skb, head_len);
@@ -105,14 +185,12 @@ static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
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;
+ *(u16*)(skb->data + 8) = skb->protocol;
}
- memcpy(skb->data, daddr, 2);
+ dlci_to_q922(skb->data, dlci);
skb->data[2] = FR_UI;
-
- return head_len;
+ return 0;
}
@@ -124,13 +202,12 @@ static int pvc_open(struct net_device *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;
+ if (pvc->open_count++ == 0) {
+ if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+ pvc->state.active = 1;
- pvc->state.new = 0;
- pvc->master->state.fr.changed = 1;
+ pvc->master->state.fr.dce_changed = 1;
+ }
return 0;
}
@@ -139,38 +216,94 @@ static int pvc_open(struct net_device *dev)
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;
+
+ if (--pvc->open_count == 0) {
+ if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+ pvc->state.active = 0;
+
+ if (pvc->master->state.fr.settings.dce) {
+ pvc->master->state.fr.dce_changed = 1;
+ pvc->state.active = 0;
+ }
+ }
return 0;
}
-static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
pvc_device *pvc = dev_to_pvc(dev);
+ fr_proto_pvc_info info;
- 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);
+ if (ifr->ifr_settings.type == IF_GET_PROTO) {
+ if (dev->type == ARPHRD_ETHER)
+ ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC;
+ else
+ ifr->ifr_settings.type = IF_PROTO_FR_PVC;
+
+ if (ifr->ifr_settings.size < sizeof(info)) {
+ /* data size wanted */
+ ifr->ifr_settings.size = sizeof(info);
+ return -ENOBUFS;
+ }
+
+ info.dlci = pvc->dlci;
+ memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ);
+ if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
+ &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
}
- return 0;
+ return -EINVAL;
+}
+
+
+__inline__ struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+ return (struct net_device_stats *)
+ ((char *)dev + sizeof(struct net_device));
}
-static struct net_device_stats *pvc_get_stats(struct net_device *dev)
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
{
pvc_device *pvc = dev_to_pvc(dev);
- return &pvc->stats;
+ struct net_device_stats *stats = pvc_get_stats(dev);
+
+ if (pvc->state.active) {
+ if (dev->type == ARPHRD_ETHER) {
+ int pad = ETH_ZLEN - skb->len;
+ if (pad > 0) { /* Pad the frame with zeros */
+ int len = skb->len;
+ if (skb_tailroom(skb) < pad)
+ if (pskb_expand_head(skb, 0, pad,
+ GFP_ATOMIC)) {
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ skb_put(skb, pad);
+ memset(skb->data + len, 0, pad);
+ }
+ skb->protocol = __constant_htons(ETH_P_802_3);
+ }
+ if (!fr_hard_header(&skb, pvc->dlci)) {
+ stats->tx_bytes += skb->len;
+ stats->tx_packets++;
+ if (pvc->state.fecn) /* TX Congestion counter */
+ stats->tx_compressed++;
+ skb->dev = hdlc_to_dev(pvc->master);
+ dev_queue_xmit(skb);
+ return 0;
+ }
+ }
+
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0;
}
@@ -187,9 +320,15 @@ static int pvc_change_mtu(struct net_device *dev, int new_mtu)
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" : "");
+ printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
+ hdlc_to_name(pvc->master),
+ pvc->dlci,
+ pvc->main ? pvc->main->name : "",
+ pvc->main && pvc->ether ? " " : "",
+ pvc->ether ? pvc->ether->name : "",
+ pvc->state.new ? " new" : "",
+ !pvc->state.exist ? "deleted" :
+ pvc->state.active ? "active" : "inactive");
}
@@ -213,8 +352,8 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
int i = 0;
if (hdlc->state.fr.settings.dce && fullrep) {
- len += hdlc->state.fr.pvc_count * (2 + stat_len);
- if (len > HDLC_MAX_MTU) {
+ len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
+ if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending "
"LMI full report\n", hdlc_to_name(hdlc));
return;
@@ -224,12 +363,13 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
skb = dev_alloc_skb(len);
if (!skb) {
printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
- hdlc_to_name(hdlc));
+ 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);
+ skb->protocol = __constant_htons(LMI_PROTO);
+ fr_hard_header(&skb, LMI_DLCI);
data = skb->tail;
data[i++] = LMI_CALLREF;
data[i++] = hdlc->state.fr.settings.dce
@@ -253,16 +393,20 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
? 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;
+ /* LMI start/restart */
+ if (hdlc->state.fr.reliable && !pvc->state.exist) {
+ pvc->state.exist = pvc->state.new = 1;
+ fr_log_dlci_active(pvc);
+ }
+
+ /* ifconfig PVC up */
+ if (pvc->open_count && !pvc->state.active &&
+ pvc->state.exist && !pvc->state.new) {
+ pvc->state.active = 1;
fr_log_dlci_active(pvc);
}
- dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
- data + i,
+ dlci_to_status(pvc->dlci, data + i,
pvc->state.active, pvc->state.new);
i += stat_len;
pvc = pvc->next;
@@ -272,6 +416,7 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
skb_put(skb, i);
skb->priority = TC_PRIO_CONTROL;
skb->dev = hdlc_to_dev(hdlc);
+ skb->nh.raw = skb->data;
dev_queue_xmit(skb);
}
@@ -312,10 +457,11 @@ static void fr_timer(unsigned long arg)
if (reliable) {
hdlc->state.fr.n391cnt = 0; /* Request full status */
- hdlc->state.fr.changed = 1;
+ hdlc->state.fr.dce_changed = 1;
} else {
while (pvc) { /* Deactivate all PVCs */
- pvc->state.new = pvc->state.active = 0;
+ pvc->state.exist = 0;
+ pvc->state.active = pvc->state.new = 0;
pvc = pvc->next;
}
}
@@ -346,7 +492,7 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
{
int stat_len;
pvc_device *pvc;
- int reptype = -1, error;
+ int reptype = -1, error, no_ram;
u8 rxseq, txseq;
int i;
@@ -420,20 +566,18 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
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;
+ hdlc->state.fr.dce_changed = 1;
}
pvc = pvc->next;
}
}
- if (hdlc->state.fr.changed) {
+ if (hdlc->state.fr.dce_changed) {
reptype = LMI_FULLREP;
hdlc->state.fr.fullrep_sent = 1;
- hdlc->state.fr.changed = 0;
+ hdlc->state.fr.dce_changed = 0;
}
fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
@@ -449,13 +593,14 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
pvc = hdlc->state.fr.first_pvc;
while (pvc) {
- pvc->state.deleted = pvc->state.active; /* mark active PVCs */
+ pvc->state.deleted = 1;
pvc = pvc->next;
}
+ no_ram = 0;
while (skb->len >= i + 2 + stat_len) {
u16 dlci;
- int active, new;
+ unsigned int active, new;
if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
@@ -472,21 +617,28 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
}
i++;
- dlci = status_to_dlci(hdlc, skb->data + i, &active, &new);
- pvc = find_pvc(hdlc, dlci);
+ dlci = status_to_dlci(skb->data + i, &active, &new);
+
+ pvc = add_pvc(hdlc, dlci);
+
+ if (!pvc && !no_ram) {
+ printk(KERN_WARNING
+ "%s: Memory squeeze on fr_lmi_recv()\n",
+ hdlc_to_name(hdlc));
+ no_ram = 1;
+ }
- active |= new;
if (pvc) {
- if (active && !pvc->state.active &&
- (pvc->netdev.flags & IFF_UP)) {
+ pvc->state.exist = 1;
+ pvc->state.deleted = 0;
+ if (active != pvc->state.active ||
+ new != pvc->state.new ||
+ !pvc->state.exist) {
+ pvc->state.new = new;
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;
}
@@ -494,10 +646,10 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
pvc = hdlc->state.fr.first_pvc;
while (pvc) {
- if (pvc->state.deleted) {
+ if (pvc->state.deleted && pvc->state.exist) {
pvc->state.active = pvc->state.new = 0;
+ pvc->state.exist = 0;
fr_log_dlci_active(pvc);
- pvc->state.deleted = 0;
}
pvc = pvc->next;
}
@@ -517,8 +669,9 @@ static void fr_rx(struct sk_buff *skb)
u8 *data = skb->data;
u16 dlci;
pvc_device *pvc;
+ struct net_device *dev = NULL;
- if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
+ if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI)
goto rx_error;
dlci = q922_to_dlci(skb->data);
@@ -550,57 +703,39 @@ static void fr_rx(struct sk_buff *skb)
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;
+ dev_kfree_skb_any(skb);
+ return;
}
- pvc->stats.rx_packets++; /* PVC traffic */
- pvc->stats.rx_bytes += skb->len;
-
- if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
+ if (pvc->state.fecn != fh->fecn) {
#ifdef CONFIG_HDLC_DEBUG_ECN
- printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
- fh->fecn ? "N" : "FF");
+ printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc),
+ dlci, fh->fecn ? "N" : "FF");
#endif
pvc->state.fecn ^= 1;
}
- if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {
+ if (pvc->state.becn != fh->becn) {
#ifdef CONFIG_HDLC_DEBUG_ECN
- printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
- fh->becn ? "N" : "FF");
+ printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc),
+ dlci, 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) */
+ dev = pvc->main;
skb->protocol = htons(ETH_P_IP);
- netif_rx(skb);
- return;
- }
-
- if (data[3] == NLPID_IPV6) {
+ } else if (data[3] == NLPID_IPV6) {
skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+ dev = pvc->main;
skb->protocol = htons(ETH_P_IPV6);
- netif_rx(skb);
- return;
- }
- if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+ } else if (skb->len > 10 && data[3] == FR_PAD &&
+ data[4] == NLPID_SNAP && data[5] == FR_PAD) {
u16 oui = ntohs(*(u16*)(data + 6));
u16 pid = ntohs(*(u16*)(data + 8));
skb_pull(skb, 10);
@@ -610,23 +745,39 @@ static void fr_rx(struct sk_buff *skb)
case ETH_P_IPX:
case ETH_P_IP: /* a long variant */
case ETH_P_IPV6:
+ dev = pvc->main;
skb->protocol = htons(pid);
break;
+ case 0x80C20007: /* bridged Ethernet frame */
+ if ((dev = pvc->ether) != NULL)
+ skb->protocol = eth_type_trans(skb, dev);
+ 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);
+ } else {
+ printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
+ "length = %i\n", hdlc_to_name(hdlc), data[3], skb->len);
+ dev_kfree_skb_any(skb);
return;
}
- printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n",
- hdlc_to_name(hdlc), data[3]);
- dev_kfree_skb_any(skb);
+ if (dev) {
+ struct net_device_stats *stats = pvc_get_stats(dev);
+ stats->rx_packets++; /* PVC traffic */
+ stats->rx_bytes += skb->len;
+ if (pvc->state.becn)
+ stats->rx_compressed++;
+ skb->dev = dev;
+ netif_rx(skb);
+ } else
+ dev_kfree_skb_any(skb);
+
return;
rx_error:
@@ -641,7 +792,7 @@ 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.dce_changed = 1;
hdlc->state.fr.request = 0;
hdlc->state.fr.fullrep_sent = 0;
hdlc->state.fr.last_errors = 0xFFFFFFFF;
@@ -669,90 +820,119 @@ static void fr_close(hdlc_device *hdlc)
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 */
+ while(pvc) { /* Shutdown all PVCs for this FRAD */
+ if (pvc->main)
+ dev_close(pvc->main);
+ if (pvc->ether)
+ dev_close(pvc->ether);
+ pvc->state.active = pvc->state.new = pvc->state.fecn =
+ pvc->state.becn = 0;
+ pvc->state.exist = 0;
pvc = pvc->next;
}
}
-
-static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
+static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
{
- pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
- pvc_device *pvc;
- int result;
+ pvc_device *pvc = NULL;
+ struct net_device *dev;
+ int result, used;
+ char * prefix = "pvc%d";
- if(dlci <= 0 || dlci >= 1024)
- return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */
+ if (type == ARPHRD_ETHER)
+ prefix = "pvceth%d";
- while(*pvc_p) {
- if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
- break;
- pvc_p = &(*pvc_p)->next;
+ if ((pvc = add_pvc(hdlc, dlci)) == NULL) {
+ printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
+ hdlc_to_name(hdlc));
+ return -ENOBUFS;
}
- if (create) { /* Create PVC */
- if (*pvc_p != NULL)
- return -EEXIST;
+ if (*get_dev_p(pvc, type))
+ 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;
- }
+ used = pvc_is_used(pvc);
- if (register_netdevice(&pvc->netdev) != 0) {
- kfree(pvc);
- *pvc_p = NULL;
- return -EIO;
- }
+ dev = kmalloc(sizeof(struct net_device) +
+ sizeof(struct net_device_stats), GFP_KERNEL);
+ if (!dev) {
+ printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+ hdlc_to_name(hdlc));
+ delete_unused_pvcs(hdlc);
+ return -ENOBUFS;
+ }
+ memset(dev, 0, sizeof(struct net_device) +
+ sizeof(struct net_device_stats));
- hdlc->state.fr.changed = 1;
- hdlc->state.fr.pvc_count++;
- return 0;
+ if (type == ARPHRD_ETHER) {
+ ether_setup(dev);
+ memcpy(dev->dev_addr, "\x00\x01", 2);
+ get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+ } else {
+ dev->type = ARPHRD_DLCI;
+ dev->flags = IFF_POINTOPOINT;
+ dev->hard_header_len = 10;
+ dev->addr_len = 2;
+ *(u16*)dev->dev_addr = htons(dlci);
+ dlci_to_q922(dev->broadcast, dlci);
}
+ dev->hard_start_xmit = pvc_xmit;
+ dev->get_stats = pvc_get_stats;
+ dev->open = pvc_open;
+ dev->stop = pvc_close;
+ dev->do_ioctl = pvc_ioctl;
+ dev->change_mtu = pvc_change_mtu;
+ dev->mtu = HDLC_MAX_MTU;
+ dev->tx_queue_len = 0;
+ dev->priv = pvc;
+
+ result = dev_alloc_name(dev, prefix);
+ if (result < 0) {
+ kfree(dev);
+ delete_unused_pvcs(hdlc);
+ return result;
+ }
+
+ if (register_netdevice(dev) != 0) {
+ kfree(dev);
+ delete_unused_pvcs(hdlc);
+ return -EIO;
+ }
+
+ *get_dev_p(pvc, type) = dev;
+ if (!used) {
+ hdlc->state.fr.dce_changed = 1;
+ hdlc->state.fr.dce_pvc_count++;
+ }
+ return 0;
+}
+
+
+
+static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
+{
+ pvc_device *pvc;
+ struct net_device *dev;
- if (*pvc_p == NULL) /* Delete PVC */
+ if ((pvc = find_pvc(hdlc, dlci)) == NULL)
return -ENOENT;
- pvc = *pvc_p;
+ if ((dev = *get_dev_p(pvc, type)) == NULL)
+ return -ENOENT;
- if (pvc->netdev.flags & IFF_UP)
+ if (dev->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);
+ unregister_netdevice(dev);
+ kfree(dev);
+ *get_dev_p(pvc, type) = NULL;
+
+ if (!pvc_is_used(pvc)) {
+ hdlc->state.fr.dce_pvc_count--;
+ hdlc->state.fr.dce_changed = 1;
+ }
+ delete_unused_pvcs(hdlc);
return 0;
}
@@ -763,14 +943,21 @@ static void fr_destroy(hdlc_device *hdlc)
pvc_device *pvc = hdlc->state.fr.first_pvc;
while(pvc) {
pvc_device *next = pvc->next;
- unregister_netdev(&pvc->netdev);
+ if (pvc->main) {
+ unregister_netdevice(pvc->main);
+ kfree(pvc->main);
+ }
+ if (pvc->ether) {
+ unregister_netdevice(pvc->ether);
+ kfree(pvc->ether);
+ }
kfree(pvc);
pvc = next;
}
hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
- hdlc->state.fr.pvc_count = 0;
- hdlc->state.fr.changed = 1;
+ hdlc->state.fr.dce_pvc_count = 0;
+ hdlc->state.fr.dce_changed = 1;
}
@@ -828,25 +1015,27 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
if (hdlc->proto != IF_PROTO_FR) {
hdlc_proto_detach(hdlc);
hdlc->state.fr.first_pvc = NULL;
- hdlc->state.fr.pvc_count = 0;
+ hdlc->state.fr.dce_pvc_count = 0;
}
memcpy(&hdlc->state.fr.settings, &new_settings, size);
hdlc->open = fr_open;
hdlc->stop = fr_close;
hdlc->netif_rx = fr_rx;
+ hdlc->type_trans = NULL;
hdlc->proto_detach = fr_destroy;
hdlc->proto = IF_PROTO_FR;
dev->hard_start_xmit = hdlc->xmit;
- dev->hard_header = fr_hard_header;
+ dev->hard_header = NULL;
dev->type = ARPHRD_FRAD;
- dev->addr_len = 2;
- *(u16*)dev->dev_addr = htons(LMI_DLCI);
- dlci_to_q922(dev->broadcast, LMI_DLCI);
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->addr_len = 0;
return 0;
case IF_PROTO_FR_ADD_PVC:
case IF_PROTO_FR_DEL_PVC:
+ case IF_PROTO_FR_ADD_ETH_PVC:
+ case IF_PROTO_FR_DEL_ETH_PVC:
if(!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -854,8 +1043,20 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
sizeof(fr_proto_pvc)))
return -EFAULT;
- return fr_pvc(hdlc, pvc.dlci,
- ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC);
+ if (pvc.dlci <= 0 || pvc.dlci >= 1024)
+ return -EINVAL; /* Only 10 bits, DLCI 0 reserved */
+
+ if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC ||
+ ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC)
+ result = ARPHRD_ETHER; /* bridged Ethernet device */
+ else
+ result = ARPHRD_DLCI;
+
+ if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC ||
+ ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC)
+ return fr_add_pvc(hdlc, pvc.dlci, result);
+ else
+ return fr_del_pvc(hdlc, pvc.dlci, result);
}
return -EINVAL;
diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc_generic.c
index 09d2b24b109c..f003d03844d3 100644
--- a/drivers/net/wan/hdlc_generic.c
+++ b/drivers/net/wan/hdlc_generic.c
@@ -1,17 +1,13 @@
/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*
- * Current status:
- * - this is work in progress
- * - not heavily tested on SMP
- * - currently supported:
+ * Currently supported:
* * raw IP-in-HDLC
* * Cisco HDLC
* * Frame Relay with ANSI or CCITT LMI (both user and network side)
@@ -37,7 +33,7 @@
#include <linux/hdlc.h>
-static const char* version = "HDLC support module revision 1.11";
+static const char* version = "HDLC support module revision 1.14";
static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
@@ -60,7 +56,13 @@ static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *p)
{
- dev_to_hdlc(dev)->netif_rx(skb);
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+ if (hdlc->netif_rx)
+ hdlc->netif_rx(skb);
+ else {
+ hdlc->stats.rx_dropped++; /* Shouldn't happen */
+ dev_kfree_skb(skb);
+ }
return 0;
}
@@ -69,6 +71,10 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
#define hdlc_raw_ioctl(hdlc, ifr) -ENOSYS
#endif
+#ifndef CONFIG_HDLC_RAW_ETH
+#define hdlc_raw_eth_ioctl(hdlc, ifr) -ENOSYS
+#endif
+
#ifndef CONFIG_HDLC_PPP
#define hdlc_ppp_ioctl(hdlc, ifr) -ENOSYS
#endif
@@ -96,6 +102,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch(ifr->ifr_settings.type) {
case IF_PROTO_HDLC:
+ case IF_PROTO_HDLC_ETH:
case IF_PROTO_PPP:
case IF_PROTO_CISCO:
case IF_PROTO_FR:
@@ -109,6 +116,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch(proto) {
case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr);
+ case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(hdlc, ifr);
case IF_PROTO_PPP: return hdlc_ppp_ioctl(hdlc, ifr);
case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr);
case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr);
@@ -144,7 +152,6 @@ int register_hdlc_device(hdlc_device *hdlc)
if (result != 0)
return -EIO;
- MOD_INC_USE_COUNT;
return 0;
}
@@ -152,17 +159,17 @@ int register_hdlc_device(hdlc_device *hdlc)
void unregister_hdlc_device(hdlc_device *hdlc)
{
+ rtnl_lock();
hdlc_proto_detach(hdlc);
-
- unregister_netdev(hdlc_to_dev(hdlc));
- MOD_DEC_USE_COUNT;
+ unregister_netdevice(hdlc_to_dev(hdlc));
+ rtnl_unlock();
}
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("HDLC support module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
EXPORT_SYMBOL(hdlc_ioctl);
EXPORT_SYMBOL(register_hdlc_device);
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index 2e9d40cf9e4d..1d93f3ac14ed 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*/
#include <linux/config.h>
@@ -68,10 +67,10 @@ static void ppp_close(hdlc_device *hdlc)
-static void ppp_rx(struct sk_buff *skb)
+static unsigned short ppp_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
{
- skb->protocol = htons(ETH_P_WAN_PPP);
- netif_rx(skb);
+ return __constant_htons(ETH_P_WAN_PPP);
}
@@ -103,7 +102,8 @@ int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = ppp_open;
hdlc->stop = ppp_close;
- hdlc->netif_rx = ppp_rx;
+ hdlc->netif_rx = NULL;
+ hdlc->type_trans = ppp_type_trans;
hdlc->proto = IF_PROTO_PPP;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
index 66d32beabb5a..7bc89d695ecd 100644
--- a/drivers/net/wan/hdlc_raw.c
+++ b/drivers/net/wan/hdlc_raw.c
@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux
* HDLC support
*
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*/
#include <linux/config.h>
@@ -26,10 +25,10 @@
#include <linux/hdlc.h>
-static void raw_rx(struct sk_buff *skb)
+static unsigned short raw_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
{
- skb->protocol = htons(ETH_P_IP);
- netif_rx(skb);
+ return __constant_htons(ETH_P_IP);
}
@@ -67,7 +66,7 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
new_settings.encoding = ENCODING_NRZ;
if (new_settings.parity == PARITY_DEFAULT)
- new_settings.parity = PARITY_NONE;
+ new_settings.parity = PARITY_CRC16_PR1_CCITT;
result = hdlc->attach(hdlc, new_settings.encoding,
new_settings.parity);
@@ -79,11 +78,13 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = NULL;
hdlc->stop = NULL;
- hdlc->netif_rx = raw_rx;
+ hdlc->netif_rx = NULL;
+ hdlc->type_trans = raw_type_trans;
hdlc->proto = IF_PROTO_HDLC;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_RAWHDLC;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->addr_len = 0;
return 0;
}
diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c
new file mode 100644
index 000000000000..90c69addb6d5
--- /dev/null
+++ b/drivers/net/wan/hdlc_raw_eth.c
@@ -0,0 +1,110 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC Ethernet emulation support
+ *
+ * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/random.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/hdlc.h>
+
+
+static int eth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+ int pad = ETH_ZLEN - skb->len;
+ if (pad > 0) { /* Pad the frame with zeros */
+ int len = skb->len;
+ if (skb_tailroom(skb) < pad)
+ if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) {
+ dev_to_hdlc(dev)->stats.tx_dropped++;
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ skb_put(skb, pad);
+ memset(skb->data + len, 0, pad);
+ }
+ return dev_to_hdlc(dev)->xmit(skb, dev);
+}
+
+
+int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+ raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
+ const size_t size = sizeof(raw_hdlc_proto);
+ raw_hdlc_proto new_settings;
+ struct net_device *dev = hdlc_to_dev(hdlc);
+ int result;
+ void *old_ch_mtu;
+ int old_qlen;
+
+ switch (ifr->ifr_settings.type) {
+ case IF_GET_PROTO:
+ ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
+ if (ifr->ifr_settings.size < size) {
+ ifr->ifr_settings.size = size; /* data size wanted */
+ return -ENOBUFS;
+ }
+ if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+ return -EFAULT;
+ return 0;
+
+ case IF_PROTO_HDLC_ETH:
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (dev->flags & IFF_UP)
+ return -EBUSY;
+
+ if (copy_from_user(&new_settings, raw_s, size))
+ return -EFAULT;
+
+ if (new_settings.encoding == ENCODING_DEFAULT)
+ new_settings.encoding = ENCODING_NRZ;
+
+ if (new_settings.parity == PARITY_DEFAULT)
+ new_settings.parity = PARITY_CRC16_PR1_CCITT;
+
+ result = hdlc->attach(hdlc, new_settings.encoding,
+ new_settings.parity);
+ if (result)
+ return result;
+
+ hdlc_proto_detach(hdlc);
+ memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
+
+ hdlc->open = NULL;
+ hdlc->stop = NULL;
+ hdlc->netif_rx = NULL;
+ hdlc->type_trans = eth_type_trans;
+ hdlc->proto = IF_PROTO_HDLC_ETH;
+ dev->hard_start_xmit = eth_tx;
+ old_ch_mtu = dev->change_mtu;
+ old_qlen = dev->tx_queue_len;
+ ether_setup(dev);
+ dev->change_mtu = old_ch_mtu;
+ dev->tx_queue_len = old_qlen;
+ memcpy(dev->dev_addr, "\x00\x01", 2);
+ get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+ return 0;
+ }
+
+ return -EINVAL;
+}
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index ff70ac6f1f07..53b118c5f8d5 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux
* X.25 support
*
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*/
#include <linux/config.h>
@@ -204,6 +203,7 @@ int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = x25_open;
hdlc->stop = x25_close;
hdlc->netif_rx = x25_rx;
+ hdlc->type_trans = NULL;
hdlc->proto = IF_PROTO_X25;
dev->hard_start_xmit = x25_xmit;
dev->hard_header = NULL;
diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c
index ccd5ae7bc62f..939ee3544523 100644
--- a/drivers/net/wan/n2.c
+++ b/drivers/net/wan/n2.c
@@ -1,12 +1,11 @@
/*
* SDL Inc. RISCom/N2 synchronous serial card driver for Linux
*
- * Copyright (C) 1998-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*
* For information see http://hq.pm.waw.pl/hdlc/
*
@@ -28,20 +27,28 @@
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/ioport.h>
+#include <linux/moduleparam.h>
#include <linux/netdevice.h>
#include <linux/hdlc.h>
#include <asm/io.h>
#include "hd64570.h"
-static const char* version = "SDL RISCom/N2 driver version: 1.10";
+static const char* version = "SDL RISCom/N2 driver version: 1.14";
static const char* devname = "RISCom/N2";
#define USE_WINDOWSIZE 16384
#define USE_BUS16BITS 1
#define CLOCK_BASE 9830400 /* 9.8304 MHz */
-
+#define MAX_PAGES 16 /* 16 RAM pages at max */
+#define MAX_RAM_SIZE 0x80000 /* 512 KB */
+#if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE
+#undef MAX_RAM_SIZE
+#define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE)
+#endif
#define N2_IOPORTS 0x10
+#define NEED_DETECT_RAM
+#define MAX_TX_BUFFERS 10
static char *hw = NULL; /* pointer to hw=xxx command line string */
@@ -86,16 +93,16 @@ typedef struct port_s {
struct card_s *card;
spinlock_t lock; /* TX lock */
sync_serial_settings settings;
+ int valid; /* port enabled */
+ int rxpart; /* partial frame received, next frame invalid*/
unsigned short encoding;
unsigned short parity;
+ u16 rxin; /* rx ring buffer 'in' pointer */
+ u16 txin; /* tx ring buffer 'in' and 'last' pointers */
+ u16 txlast;
u8 rxs, txs, tmc; /* SCA registers */
- u8 valid; /* port enabled */
u8 phy_node; /* physical port # - 0 or 1 */
u8 log_node; /* logical port # */
- u8 rxin; /* rx ring buffer 'in' pointer */
- u8 txin; /* tx ring buffer 'in' and 'last' pointers */
- u8 txlast;
- u8 rxpart; /* partial frame received, next frame invalid*/
}port_t;
@@ -106,8 +113,9 @@ typedef struct card_s {
u32 ram_size; /* number of bytes */
u16 io; /* IO Base address */
u16 buff_offset; /* offset of first buffer of first channel */
+ u16 rx_ring_buffers; /* number of buffers in a ring */
+ u16 tx_ring_buffers;
u8 irq; /* IRQ (3-15) */
- u8 ring_buffers; /* number of buffers in a ring */
port_t ports[2];
struct card_s *next_card;
@@ -209,14 +217,19 @@ static int n2_open(struct net_device *dev)
int io = port->card->io;
u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
+
+ if (!try_module_get(THIS_MODULE))
+ return -EFAULT; /* rmmod in progress */
+
int result = hdlc_open(hdlc);
- if (result)
+ if (result) {
return result;
+ module_put(THIS_MODULE);
+ }
- MOD_INC_USE_COUNT;
mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
outb(mcr, io + N2_MCR);
-
+
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);
@@ -237,7 +250,7 @@ static int n2_close(struct net_device *dev)
mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
outb(mcr, io + N2_MCR);
hdlc_close(hdlc);
- MOD_DEC_USE_COUNT;
+ module_put(THIS_MODULE);
return 0;
}
@@ -297,62 +310,6 @@ static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-static u8 n2_count_page(card_t *card)
-{
- u8 page;
- int i, bcount = USE_WINDOWSIZE, wcount = USE_WINDOWSIZE/2;
- u16 *dp = (u16*)card->winbase;
- u8 *bp = (u8*)card->winbase;
- u8 psr = inb(card->io + N2_PSR) & PSR_WINBITS;
-
-
- for (page = 0; page < 16; page++) {
- outb(psr | page, card->io + N2_PSR); /* select a page */
- writeb(page, dp);
- if (readb(dp) != page)
- break; /* If can't read back, no good memory */
-
- outb(psr, card->io + N2_PSR); /* goto page 0 */
- if (readb(dp))
- break; /* If page 0 changed, then wrapped around */
-
- outb(psr | page, card->io + N2_PSR); /* select page again */
-
- /* first do byte tests */
- for (i = 0; i < bcount; i++)
- writeb(i, bp + i);
- for (i = 0; i < bcount; i++)
- if (readb(bp + i) != (i & 0xff))
- return 0;
-
- for (i = 0; i < bcount; i++)
- writeb(~i, bp + i);
- for (i = 0; i < bcount; i++)
- if (readb(bp + i) != (~i & 0xff))
- return 0;
-
- /* next do 16-bit tests */
- for (i = 0; i < wcount; i++)
- writew(0x55AA, dp + i);
- for (i = 0; i < wcount; i++)
- if (readw(dp + i) != 0x55AA)
- return 0;
-
- for (i = 0; i < wcount; i++)
- writew(0xAA55, dp + i);
- for (i = 0; i < wcount; i++)
- if (readw(dp + i) != 0xAA55)
- return 0;
-
- for (i = 0; i < wcount; i++)
- writew(page, dp + i);
- }
-
- return page;
-}
-
-
-
static void n2_destroy_card(card_t *card)
{
int cnt;
@@ -376,11 +333,12 @@ static void n2_destroy_card(card_t *card)
-static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
- long valid0, long valid1)
+static int __init n2_run(unsigned long io, unsigned long irq,
+ unsigned long winbase, long valid0, long valid1)
{
card_t *card;
u8 cnt, pcr;
+ int i;
if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
printk(KERN_ERR "n2: invalid I/O port value\n");
@@ -391,7 +349,7 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
printk(KERN_ERR "n2: invalid IRQ value\n");
return -ENODEV;
}
-
+
if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
printk(KERN_ERR "n2: invalid RAM value\n");
return -ENODEV;
@@ -451,25 +409,27 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
outb(pcr, io + N2_PCR);
- cnt = n2_count_page(card);
- if (!cnt) {
- printk(KERN_ERR "n2: memory test failed.\n");
- n2_destroy_card(card);
- return -EIO;
- }
+ card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
- card->ram_size = cnt * USE_WINDOWSIZE;
+ /* number of TX + RX buffers for one port */
+ i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) +
+ HDLC_MAX_MRU));
- /* 4 rings required for 2 ports, 2 rings for one port */
- card->ring_buffers = card->ram_size /
- ((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
+ card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
+ card->rx_ring_buffers = i - card->tx_ring_buffers;
- card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc))
- * card->ring_buffers;
+ card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
+ (card->tx_ring_buffers + card->rx_ring_buffers);
printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, "
- "using %u packets rings\n", card->ram_size / 1024, card->irq,
- card->ring_buffers);
+ "using %u TX + %u RX packets rings\n", card->ram_size / 1024,
+ card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+
+ if (card->tx_ring_buffers < 1) {
+ printk(KERN_ERR "n2: RAM test failed\n");
+ n2_destroy_card(card);
+ return -EIO;
+ }
pcr |= PCR_RUNSCA; /* run SCA */
outb(pcr, io + N2_PCR);
@@ -531,7 +491,7 @@ static int __init n2_init(void)
return -ENOSYS; /* no parameters specified, abort */
}
- printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
+ printk(KERN_INFO "%s\n", version);
do {
unsigned long io, irq, ram;
@@ -558,7 +518,7 @@ static int __init n2_init(void)
break;
hw++;
}
-
+
if (!valid[0] && !valid[1])
break; /* at least one port must be used */
@@ -566,7 +526,7 @@ static int __init n2_init(void)
n2_run(io, irq, ram, valid[0], valid[1]);
if (*hw == '\x0')
- return 0;
+ return first_card ? 0 : -ENOSYS;
}while(*hw++ == ':');
printk(KERN_ERR "n2: invalid hardware parameters\n");
@@ -574,17 +534,6 @@ static int __init n2_init(void)
}
-#ifndef MODULE
-static int __init n2_setup(char *str)
-{
- hw = str;
- return 1;
-}
-
-__setup("n2=", n2_setup);
-#endif
-
-
static void __exit n2_cleanup(void)
{
card_t *card = first_card;
@@ -602,5 +551,5 @@ module_exit(n2_cleanup);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("RISCom/N2 serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_PARM(hw, "s"); /* hw=io,irq,ram,ports:io,irq,... */
+MODULE_LICENSE("GPL v2");
+module_param(hw, charp, 0444); /* hw=io,irq,ram,ports:io,irq,... */
diff --git a/include/linux/hdlc.h b/include/linux/hdlc.h
index aeceb6f1a7be..6b89e9c85f9b 100644
--- a/include/linux/hdlc.h
+++ b/include/linux/hdlc.h
@@ -1,12 +1,11 @@
/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2003 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
*/
#ifndef __HDLC_H
@@ -52,7 +51,7 @@
#include <linux/hdlc/ioctl.h>
#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */
-#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
+#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
#define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */
@@ -145,17 +144,20 @@ typedef struct {
typedef struct pvc_device_struct {
- struct net_device netdev; /* PVC net device - must be first */
- struct net_device_stats stats;
struct hdlc_device_struct *master;
- struct pvc_device_struct *next;
+ struct net_device *main;
+ struct net_device *ether; /* bridged Ethernet interface */
+ struct pvc_device_struct *next; /* Sorted in ascending DLCI order */
+ int dlci;
+ int open_count;
struct {
- int active;
- int new;
- int deleted;
- int fecn;
- int becn;
+ unsigned int new: 1;
+ unsigned int active: 1;
+ unsigned int exist: 1;
+ unsigned int deleted: 1;
+ unsigned int fecn: 1;
+ unsigned int becn: 1;
}state;
}pvc_device;
@@ -180,18 +182,20 @@ typedef struct hdlc_device_struct {
void (*stop)(struct hdlc_device_struct *hdlc);
void (*proto_detach)(struct hdlc_device_struct *hdlc);
void (*netif_rx)(struct sk_buff *skb);
+ unsigned short (*type_trans)(struct sk_buff *skb,
+ struct net_device *dev);
int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */
union {
struct {
fr_proto settings;
pvc_device *first_pvc;
- int pvc_count;
+ int dce_pvc_count;
struct timer_list timer;
int last_poll;
int reliable;
- int changed;
+ int dce_changed;
int request;
int fullrep_sent;
u32 last_errors; /* last errors bit list */
@@ -226,6 +230,7 @@ typedef struct hdlc_device_struct {
int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_raw_eth_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);
@@ -254,15 +259,9 @@ static __inline__ hdlc_device* dev_to_hdlc(struct net_device *dev)
}
-static __inline__ struct net_device* pvc_to_dev(pvc_device *pvc)
-{
- return &pvc->netdev;
-}
-
-
static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
{
- return (pvc_device*)dev;
+ return (pvc_device*)dev->priv;
}
@@ -272,19 +271,6 @@ static __inline__ const char *hdlc_to_name(hdlc_device *hdlc)
}
-static __inline__ const char *pvc_to_name(pvc_device *pvc)
-{
- return pvc_to_dev(pvc)->name;
-}
-
-
-static __inline__ u16 netdev_dlci(struct net_device *dev)
-{
- return ntohs(*(u16*)dev->dev_addr);
-}
-
-
-
static __inline__ u16 q922_to_dlci(u8 *hdr)
{
return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
@@ -345,5 +331,15 @@ static __inline__ void hdlc_proto_detach(hdlc_device *hdlc)
}
+static __inline__ unsigned short hdlc_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+ if (hdlc->type_trans)
+ return hdlc->type_trans(skb, dev);
+ else
+ return __constant_htons(ETH_P_HDLC);
+}
+
#endif /* __KERNEL */
#endif /* __HDLC_H */
diff --git a/include/linux/hdlc/ioctl.h b/include/linux/hdlc/ioctl.h
index c35e1a35d847..78430ba3ea69 100644
--- a/include/linux/hdlc/ioctl.h
+++ b/include/linux/hdlc/ioctl.h
@@ -34,22 +34,15 @@ typedef struct {
} fr_proto_pvc; /* for creating/deleting FR PVCs */
typedef struct {
+ unsigned int dlci;
+ char master[IFNAMSIZ]; /* Name of master FRAD device */
+}fr_proto_pvc_info; /* for returning PVC information only */
+
+typedef struct {
unsigned int interval;
unsigned int timeout;
} cisco_proto;
/* PPP doesn't need any info now - supply length = 0 to ioctl */
-union hdlc_settings {
- raw_hdlc_proto raw_hdlc;
- cisco_proto cisco;
- fr_proto fr;
- fr_proto_pvc fr_pvc;
-};
-
-union line_settings {
- sync_serial_settings sync;
- te1_settings te1;
-};
-
#endif /* __HDLC_IOCTL_H__ */
diff --git a/include/linux/if.h b/include/linux/if.h
index 898cfdf1bb14..c4c72e790700 100644
--- a/include/linux/if.h
+++ b/include/linux/if.h
@@ -21,6 +21,8 @@
#include <linux/types.h> /* for "__kernel_caddr_t" et al */
#include <linux/socket.h> /* for "struct sockaddr" et al */
+
+#define IFNAMSIZ 16
#include <linux/hdlc/ioctl.h>
/* Standard interface flags (netdevice->flags). */
@@ -69,7 +71,11 @@
#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 */
-
+#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */
+#define IF_PROTO_FR_ETH_PVC 0x200B
/*
@@ -103,6 +109,7 @@ struct if_settings
cisco_proto *cisco;
fr_proto *fr;
fr_proto_pvc *fr_pvc;
+ fr_proto_pvc_info *fr_pvc_info;
/* interface settings */
sync_serial_settings *sync;
@@ -120,7 +127,6 @@ struct if_settings
struct ifreq
{
#define IFHWADDRLEN 6
-#define IFNAMSIZ 16
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */