Commit 80d316b7 authored by Krzysztof Halasa's avatar Krzysztof Halasa Committed by Linus Torvalds

[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).
parent 00c6dc1c
Generic HDLC layer for Linux kernel 2.4/2.5 Generic HDLC layer
Krzysztof Halasa <khc@pm.waw.pl> Krzysztof Halasa <khc@pm.waw.pl>
May, 2001 January, 2003
Generic HDLC layer currently supports: Generic HDLC layer currently supports:
- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP), - Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP).
- raw HDLC (IPv4 only), 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, - Cisco HDLC,
- PPP (uses syncppp.c), - PPP (uses syncppp.c),
- X.25 (uses X.25 routines). - X.25 (uses X.25 routines).
...@@ -15,6 +17,10 @@ There are hardware drivers for the following cards: ...@@ -15,6 +17,10 @@ There are hardware drivers for the following cards:
- RISCom/N2 by SDL Communications Inc. - RISCom/N2 by SDL Communications Inc.
- and others, some not in the official kernel. - 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 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 create a number of "hdlc" (hdlc0 etc) network devices, one for each
WAN port. You'll need the "sethdlc" utility, get it from: WAN port. You'll need the "sethdlc" utility, get it from:
...@@ -58,6 +64,9 @@ Setting protocol: ...@@ -58,6 +64,9 @@ Setting protocol:
no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity 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) * cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
interval - time in seconds between keepalive packets interval - time in seconds between keepalive packets
timeout - time in seconds after last received keepalive packet before timeout - time in seconds after last received keepalive packet before
...@@ -77,7 +86,12 @@ Setting protocol: ...@@ -77,7 +86,12 @@ Setting protocol:
n392 - error threshold - both user and network n392 - error threshold - both user and network
n393 - monitored events count - 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: ...@@ -85,30 +99,30 @@ Setting protocol:
Board-specific issues 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: example:
insmod n2 hw='"0x300,10,0xD0000,01"' insmod n2 hw=0x300,10,0xD0000,01
or or
insmod c101 hw='"irq,ram[:irq,...]" insmod c101 hw=irq,ram[:irq,...]
example: example:
insmod c101 hw='"9,0xdc000"' insmod c101 hw=9,0xdc000
If built into the kernel, these drivers need kernel (command line) parameters: If built into the kernel, these drivers need kernel (command line) parameters:
n2=io,irq,ram,ports:... n2.hw=io,irq,ram,ports:...
or or
c101=irq,ram:... c101.hw=irq,ram:...
If you have a problem with N2 or C101 card, you can issue the "private" 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 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 Attaching this info to bug reports would be helpful. Anyway, let me know
if you have problems using this. if you have problems using this.
......
...@@ -253,6 +253,11 @@ config HDLC ...@@ -253,6 +253,11 @@ config HDLC
Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame
Relay, synchronous Point-to-Point Protocol (PPP) and X.25. 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. If unsure, say N here.
config HDLC_RAW config HDLC_RAW
...@@ -264,6 +269,17 @@ config HDLC_RAW ...@@ -264,6 +269,17 @@ config HDLC_RAW
If unsure, say N here. 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 config HDLC_CISCO
bool "Cisco HDLC support" bool "Cisco HDLC support"
depends on HDLC depends on HDLC
...@@ -344,6 +360,11 @@ config N2 ...@@ -344,6 +360,11 @@ config N2
Note that N2csu and N2dds cards are not supported by this driver. 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. If unsure, say N here.
config C101 config C101
...@@ -354,6 +375,11 @@ config C101 ...@@ -354,6 +375,11 @@ config C101
Technologies Co., Ltd. If you have such a card, Technologies Co., Ltd. If you have such a card,
say Y here and see <http://hq.pm.waw.pl/pub/hdlc/> 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. If unsure, say N here.
config FARSYNC config FARSYNC
...@@ -377,18 +403,33 @@ config FARSYNC ...@@ -377,18 +403,33 @@ config FARSYNC
config HDLC_DEBUG_PKT config HDLC_DEBUG_PKT
bool "Debug received/transmitted packets" bool "Debug received/transmitted packets"
depends on HDLC depends on HDLC
help
This option is for developers only - do NOT use on production
systems.
config HDLC_DEBUG_HARD_HEADER config HDLC_DEBUG_HARD_HEADER
bool "Debug hard_header routines" bool "Debug hard_header routines"
depends on HDLC depends on HDLC
help
This option is for developers only - do NOT use on production
systems.
config HDLC_DEBUG_ECN config HDLC_DEBUG_ECN
bool "Debug FECN/BECN conditions" bool "Debug FECN/BECN conditions"
depends on HDLC depends on HDLC
help
This option is for developers only - do NOT use on production
systems.
config HDLC_DEBUG_RINGS config HDLC_DEBUG_RINGS
bool "Debug RX/TX packet rings" bool "Debug RX/TX packet rings"
depends on HDLC 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 config DLCI
tristate "Frame relay DLCI support" tristate "Frame relay DLCI support"
......
...@@ -19,6 +19,7 @@ cyclomx-objs := $(cyclomx-y) ...@@ -19,6 +19,7 @@ cyclomx-objs := $(cyclomx-y)
hdlc-y := hdlc_generic.o hdlc-y := hdlc_generic.o
hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.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_CISCO) += hdlc_cisco.o
hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o
hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
......
/* /*
* Moxa C101 synchronous serial card driver for Linux * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
* *
* For information see http://hq.pm.waw.pl/hdlc/ * For information see http://hq.pm.waw.pl/hdlc/
* *
...@@ -23,6 +22,7 @@ ...@@ -23,6 +22,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/hdlc.h> #include <linux/hdlc.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include "hd64570.h" #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"; static const char* devname = "C101";
#define C101_PAGE 0x1D00 #define C101_PAGE 0x1D00
...@@ -41,6 +41,10 @@ static const char* devname = "C101"; ...@@ -41,6 +41,10 @@ static const char* devname = "C101";
#define C101_MAPPED_RAM_SIZE 0x4000 #define C101_MAPPED_RAM_SIZE 0x4000
#define RAM_SIZE (256 * 1024) #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 CLOCK_BASE 9830400 /* 9.8304 MHz */
#define PAGE0_ALWAYS_MAPPED #define PAGE0_ALWAYS_MAPPED
...@@ -52,20 +56,20 @@ typedef struct card_s { ...@@ -52,20 +56,20 @@ typedef struct card_s {
spinlock_t lock; /* TX lock */ spinlock_t lock; /* TX lock */
u8 *win0base; /* ISA window base address */ u8 *win0base; /* ISA window base address */
u32 phy_winbase; /* ISA physical base address */ u32 phy_winbase; /* ISA physical base address */
u16 buff_offset; /* offset of first buffer of first channel */
sync_serial_settings settings; sync_serial_settings settings;
int rxpart; /* partial frame received, next frame invalid*/
unsigned short encoding; unsigned short encoding;
unsigned short parity; 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 rxs, txs, tmc; /* SCA registers */
u8 irq; /* IRQ (3-15) */ u8 irq; /* IRQ (3-15) */
u8 ring_buffers; /* number of buffers in a ring */
u8 page; 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; struct card_s *next_card;
}card_t; }card_t;
...@@ -78,7 +82,12 @@ static card_t **new_card = &first_card; ...@@ -78,7 +82,12 @@ static card_t **new_card = &first_card;
#define sca_in(reg, card) readb((card)->win0base + C101_SCA + (reg)) #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_out(value, reg, card) writeb(value, (card)->win0base + C101_SCA + (reg))
#define sca_inw(reg, card) readw((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 port_to_card(port) (port)
#define log_node(port) (0) #define log_node(port) (0)
...@@ -146,11 +155,16 @@ static int c101_open(struct net_device *dev) ...@@ -146,11 +155,16 @@ static int c101_open(struct net_device *dev)
{ {
hdlc_device *hdlc = dev_to_hdlc(dev); hdlc_device *hdlc = dev_to_hdlc(dev);
port_t *port = hdlc_to_port(hdlc); port_t *port = hdlc_to_port(hdlc);
if (!try_module_get(THIS_MODULE))
return -EFAULT; /* rmmod in progress */
int result = hdlc_open(hdlc); int result = hdlc_open(hdlc);
if (result) if (result) {
return result; return result;
module_put(THIS_MODULE);
}
MOD_INC_USE_COUNT;
writeb(1, port->win0base + C101_DTR); writeb(1, port->win0base + C101_DTR);
sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */ sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
sca_open(hdlc); sca_open(hdlc);
...@@ -168,7 +182,7 @@ static int c101_close(struct net_device *dev) ...@@ -168,7 +182,7 @@ static int c101_close(struct net_device *dev)
writeb(0, port->win0base + C101_DTR); writeb(0, port->win0base + C101_DTR);
sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port); sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
hdlc_close(hdlc); hdlc_close(hdlc);
MOD_DEC_USE_COUNT; module_put(THIS_MODULE);
return 0; return 0;
} }
...@@ -229,6 +243,8 @@ static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -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) static void c101_destroy_card(card_t *card)
{ {
readb(card->win0base + C101_PAGE); /* Resets SCA? */
if (card->irq) if (card->irq)
free_irq(card->irq, card); free_irq(card->irq, card);
...@@ -242,7 +258,7 @@ static void c101_destroy_card(card_t *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; struct net_device *dev;
card_t *card; card_t *card;
...@@ -285,9 +301,10 @@ static int c101_run(unsigned long irq, unsigned long winbase) ...@@ -285,9 +301,10 @@ static int c101_run(unsigned long irq, unsigned long winbase)
return -EBUSY; return -EBUSY;
} }
/* 2 rings required for 1 port */ card->tx_ring_buffers = TX_RING_BUFFERS;
card->ring_buffers = (RAM_SIZE -C101_WINDOW_SIZE) / (2 * HDLC_MAX_MRU); card->rx_ring_buffers = RX_RING_BUFFERS;
printk(KERN_DEBUG "c101: using %u packets rings\n",card->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 */ card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */
...@@ -337,7 +354,7 @@ static int __init c101_init(void) ...@@ -337,7 +354,7 @@ static int __init c101_init(void)
return -ENOSYS; /* no parameters specified, abort */ return -ENOSYS; /* no parameters specified, abort */
} }
printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); printk(KERN_INFO "%s\n", version);
do { do {
unsigned long irq, ram; unsigned long irq, ram;
...@@ -352,7 +369,7 @@ static int __init c101_init(void) ...@@ -352,7 +369,7 @@ static int __init c101_init(void)
c101_run(irq, ram); c101_run(irq, ram);
if (*hw == '\x0') if (*hw == '\x0')
return 0; return first_card ? 0 : -ENOSYS;
}while(*hw++ == ':'); }while(*hw++ == ':');
printk(KERN_ERR "c101: invalid hardware parameters\n"); printk(KERN_ERR "c101: invalid hardware parameters\n");
...@@ -360,17 +377,6 @@ static int __init c101_init(void) ...@@ -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) static void __exit c101_cleanup(void)
{ {
card_t *card = first_card; card_t *card = first_card;
...@@ -389,5 +395,5 @@ module_exit(c101_cleanup); ...@@ -389,5 +395,5 @@ module_exit(c101_cleanup);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Moxa C101 serial port driver"); MODULE_DESCRIPTION("Moxa C101 serial port driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_PARM(hw, "s"); /* hw=irq,ram:irq,... */ module_param(hw, charp, 0444); /* hw=irq,ram:irq,... */
...@@ -620,7 +620,7 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, ...@@ -620,7 +620,7 @@ static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv,
skb->tail += pkt_len; skb->tail += pkt_len;
skb->len = pkt_len; skb->len = pkt_len;
if (netif_running(dev)) if (netif_running(dev))
skb->protocol = htons(ETH_P_HDLC); skb->protocol = hdlc_type_trans(skb, dev);
skb->dev->last_rx = jiffies; skb->dev->last_rx = jiffies;
netif_rx(skb); netif_rx(skb);
} else { } else {
......
...@@ -761,7 +761,7 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port ) ...@@ -761,7 +761,7 @@ fst_intr_rx ( struct fst_card_info *card, struct fst_port_info *port )
/* Push upstream */ /* Push upstream */
skb->mac.raw = skb->data; skb->mac.raw = skb->data;
skb->dev = hdlc_to_dev ( &port->hdlc ); skb->dev = hdlc_to_dev ( &port->hdlc );
skb->protocol = htons ( ETH_P_HDLC ); skb->protocol = hdlc_type_trans(skb, skb->dev);
netif_rx ( skb ); netif_rx ( skb );
port_to_dev ( port )->last_rx = jiffies; port_to_dev ( port )->last_rx = jiffies;
......
/* /*
* Hitachi SCA HD64570 and HD64572 common driver for Linux * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
* *
* Sources of information: * Sources of information:
* Hitachi HD64570 SCA User's Manual * Hitachi HD64570 SCA User's Manual
* Hitachi HD64572 SCA-II 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> #include <linux/module.h>
...@@ -42,8 +55,6 @@ ...@@ -42,8 +55,6 @@
#error Either hd64570.h or hd64572.h must be included #error Either hd64570.h or hd64572.h must be included
#endif #endif
static char sca_version[]="1.09";
#define get_msci(port) (phy_node(port) ? MSCI1_OFFSET : MSCI0_OFFSET) #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_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
#define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_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) ...@@ -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 */ /* Descriptor offset always fits in 16 bytes */
u8 buffs = port_to_card(port)->ring_buffers; return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
return ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
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 #ifdef PAGE0_ALWAYS_MAPPED
return (pkt_desc*)(win0base(port_to_card(port)) 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) ...@@ -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 + return port_to_card(port)->buff_offset +
((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) * desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
(u32)HDLC_MAX_MRU;
} }
...@@ -159,8 +179,7 @@ static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit) ...@@ -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) static void sca_init_sync_port(port_t *port)
{ {
card_t *card = port_to_card(port); card_t *card = port_to_card(port);
u8 transmit, i; int transmit, i;
u16 dmac, buffs = card->ring_buffers;
port->rxin = 0; port->rxin = 0;
port->txin = 0; port->txin = 0;
...@@ -171,6 +190,10 @@ static void sca_init_sync_port(port_t *port) ...@@ -171,6 +190,10 @@ static void sca_init_sync_port(port_t *port)
#endif #endif
for (transmit = 0; transmit < 2; transmit++) { 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++) { for (i = 0; i < buffs; i++) {
pkt_desc* desc = desc_address(port, i, transmit); pkt_desc* desc = desc_address(port, i, transmit);
u16 chain_off = desc_offset(port, i + 1, transmit); u16 chain_off = desc_offset(port, i + 1, transmit);
...@@ -182,7 +205,6 @@ static void sca_init_sync_port(port_t *port) ...@@ -182,7 +205,6 @@ static void sca_init_sync_port(port_t *port)
writeb(0, &desc->stat); writeb(0, &desc->stat);
} }
dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
/* DMA disable - to halt state */ /* DMA disable - to halt state */
sca_out(0, transmit ? DSR_TX(phy_node(port)) : sca_out(0, transmit ? DSR_TX(phy_node(port)) :
DSR_RX(phy_node(port)), card); DSR_RX(phy_node(port)), card);
...@@ -247,7 +269,7 @@ static inline void sca_msci_intr(port_t *port) ...@@ -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; struct sk_buff *skb;
u16 len; u16 len;
...@@ -294,7 +316,7 @@ static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin) ...@@ -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->mac.raw = skb->data;
skb->dev = hdlc_to_dev(&port->hdlc); skb->dev = hdlc_to_dev(&port->hdlc);
skb->dev->last_rx = jiffies; 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); netif_rx(skb);
} }
...@@ -341,7 +363,7 @@ static inline void sca_rx_intr(port_t *port) ...@@ -341,7 +363,7 @@ static inline void sca_rx_intr(port_t *port)
/* Set new error descriptor address */ /* Set new error descriptor address */
sca_outa(desc_off, dmac + EDAL, card); 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 */ /* make sure RX DMA is enabled */
...@@ -377,8 +399,7 @@ static inline void sca_tx_intr(port_t *port) ...@@ -377,8 +399,7 @@ static inline void sca_tx_intr(port_t *port)
port->hdlc.stats.tx_packets++; port->hdlc.stats.tx_packets++;
port->hdlc.stats.tx_bytes += readw(&desc->len); port->hdlc.stats.tx_bytes += readw(&desc->len);
writeb(0, &desc->stat); /* Free descriptor */ writeb(0, &desc->stat); /* Free descriptor */
port->txlast = (port->txlast + 1) % port->txlast = next_desc(port, port->txlast, 1);
port_to_card(port)->ring_buffers;
} }
netif_wake_queue(hdlc_to_dev(&port->hdlc)); netif_wake_queue(hdlc_to_dev(&port->hdlc));
...@@ -390,8 +411,6 @@ static inline void sca_tx_intr(port_t *port) ...@@ -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) static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
{ {
card_t *card = dev_id; card_t *card = dev_id;
/* Maximum events to handle at each interrupt - should I increase it? */
int boguscnt = 4;
int i; int i;
u8 stat; u8 stat;
...@@ -412,19 +431,9 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) ...@@ -412,19 +431,9 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
if (stat & SCA_INTR_DMAC_TX(i)) if (stat & SCA_INTR_DMAC_TX(i))
sca_tx_intr(port); 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 #ifndef ALL_PAGES_ALWAYS_MAPPED
openwin(card, page); /* Restore original page */ openwin(card, page); /* Restore original page */
#endif #endif
...@@ -435,7 +444,7 @@ static void sca_intr(int irq, void* dev_id, struct pt_regs *regs) ...@@ -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) static void sca_set_port(port_t *port)
{ {
card_t* card = port_to_card(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); u8 md2 = sca_in(msci + MD2, card);
unsigned int tmc, br = 10, brv = 1024; unsigned int tmc, br = 10, brv = 1024;
...@@ -495,7 +504,7 @@ static void sca_open(hdlc_device *hdlc) ...@@ -495,7 +504,7 @@ static void sca_open(hdlc_device *hdlc)
{ {
port_t *port = hdlc_to_port(hdlc); port_t *port = hdlc_to_port(hdlc);
card_t* card = port_to_card(port); card_t* card = port_to_card(port);
u8 msci = get_msci(port); u16 msci = get_msci(port);
u8 md0, md2; u8 md0, md2;
switch(port->encoding) { switch(port->encoding) {
...@@ -516,7 +525,7 @@ static void sca_open(hdlc_device *hdlc) ...@@ -516,7 +525,7 @@ static void sca_open(hdlc_device *hdlc)
case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break; case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
#else #else
case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break; 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; case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU; break;
default: md0 = MD0_HDLC | MD0_CRC_NONE; default: md0 = MD0_HDLC | MD0_CRC_NONE;
} }
...@@ -613,7 +622,7 @@ static int sca_attach(hdlc_device *hdlc, unsigned short encoding, ...@@ -613,7 +622,7 @@ static int sca_attach(hdlc_device *hdlc, unsigned short encoding,
parity != PARITY_CRC16_PR0_CCITT && parity != PARITY_CRC16_PR0_CCITT &&
#else #else
parity != PARITY_CRC32_PR1_CCITT && parity != PARITY_CRC32_PR1_CCITT &&
#endif #endif
parity != PARITY_CRC16_PR1_CCITT) parity != PARITY_CRC16_PR1_CCITT)
return -EINVAL; return -EINVAL;
...@@ -639,14 +648,13 @@ static void sca_dump_rings(hdlc_device *hdlc) ...@@ -639,14 +648,13 @@ static void sca_dump_rings(hdlc_device *hdlc)
openwin(card, 0); openwin(card, 0);
#endif #endif
printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u " printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
"%sactive",
sca_ina(get_dmac_rx(port) + CDAL, card), sca_ina(get_dmac_rx(port) + CDAL, card),
sca_ina(get_dmac_rx(port) + EDAL, card), sca_ina(get_dmac_rx(port) + EDAL, card),
sca_in(DSR_RX(phy_node(port)), card), sca_in(DSR_RX(phy_node(port)), card),
port->rxin, port->rxin,
sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in"); 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", printk(" %02X",
readb(&(desc_address(port, cnt, 0)->stat))); readb(&(desc_address(port, cnt, 0)->stat)));
...@@ -658,7 +666,7 @@ static void sca_dump_rings(hdlc_device *hdlc) ...@@ -658,7 +666,7 @@ static void sca_dump_rings(hdlc_device *hdlc)
port->txlast, port->txlast,
sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in"); 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", printk(" %02X",
readb(&(desc_address(port, cnt, 1)->stat))); readb(&(desc_address(port, cnt, 1)->stat)));
printk("\n"); printk("\n");
...@@ -750,7 +758,7 @@ static int sca_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -750,7 +758,7 @@ static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
writeb(ST_TX_EOM, &desc->stat); writeb(ST_TX_EOM, &desc->stat);
dev->trans_start = jiffies; 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), sca_outa(desc_offset(port, port->txin, 1),
get_dmac_tx(port) + EDAL, card); get_dmac_tx(port) + EDAL, card);
...@@ -767,7 +775,49 @@ static int sca_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -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, WCRL, card); /* Wait Control */
sca_out(wait_states, WCRM, card); sca_out(wait_states, WCRM, card);
......
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* Cisco HDLC support * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -80,17 +79,43 @@ static void cisco_keepalive_send(hdlc_device *hdlc, u32 type, ...@@ -80,17 +79,43 @@ static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
data->par1 = htonl(par1); data->par1 = htonl(par1);
data->par2 = htonl(par2); data->par2 = htonl(par2);
data->rel = 0xFFFF; 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_put(skb, sizeof(cisco_packet));
skb->priority = TC_PRIO_CONTROL; skb->priority = TC_PRIO_CONTROL;
skb->dev = hdlc_to_dev(hdlc); skb->dev = hdlc_to_dev(hdlc);
skb->nh.raw = skb->data;
dev_queue_xmit(skb); 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) static void cisco_rx(struct sk_buff *skb)
{ {
hdlc_device *hdlc = dev_to_hdlc(skb->dev); hdlc_device *hdlc = dev_to_hdlc(skb->dev);
...@@ -109,14 +134,6 @@ static void cisco_rx(struct sk_buff *skb) ...@@ -109,14 +134,6 @@ static void cisco_rx(struct sk_buff *skb)
skb_pull(skb, sizeof(hdlc_header)); skb_pull(skb, sizeof(hdlc_header));
switch(ntohs(data->protocol)) { 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: case CISCO_SYS_INFO:
/* Packet is not needed, drop it. */ /* Packet is not needed, drop it. */
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
...@@ -288,6 +305,7 @@ int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -288,6 +305,7 @@ int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = cisco_open; hdlc->open = cisco_open;
hdlc->stop = cisco_close; hdlc->stop = cisco_close;
hdlc->netif_rx = cisco_rx; hdlc->netif_rx = cisco_rx;
hdlc->type_trans = cisco_type_trans;
hdlc->proto = IF_PROTO_CISCO; hdlc->proto = IF_PROTO_CISCO;
dev->hard_start_xmit = hdlc->xmit; dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = cisco_hard_header; dev->hard_header = cisco_hard_header;
......
...@@ -2,13 +2,22 @@ ...@@ -2,13 +2,22 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* Frame Relay support * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version. *
*/
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/config.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -20,19 +29,23 @@ ...@@ -20,19 +29,23 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/pkt_sched.h> #include <linux/pkt_sched.h>
#include <linux/random.h>
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/lapb.h> #include <linux/lapb.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/hdlc.h> #include <linux/hdlc.h>
__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
{ {
pvc_device *pvc=hdlc->state.fr.first_pvc; pvc_device *pvc = hdlc->state.fr.first_pvc;
while (pvc) { while(pvc) {
if (netdev_dlci(&pvc->netdev) == dlci) if (pvc->dlci == dlci)
return pvc; return pvc;
if (pvc->dlci > dlci)
return NULL; /* the listed is sorted */
pvc = pvc->next; pvc = pvc->next;
} }
...@@ -40,18 +53,72 @@ __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci) ...@@ -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, __inline__ u16 status_to_dlci(u8 *status, int *active, int *new)
int *active, int *new)
{ {
*new = (status[2] & 0x08); *new = (status[2] & 0x08) ? 1 : 0;
*active = (!*new && (status[2] & 0x02)); *active = (status[2] & 0x02) ? 1 : 0;
return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3); 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) int active, int new)
{ {
status[0] = (dlci>>4) & 0x3F; status[0] = (dlci>>4) & 0x3F;
...@@ -66,37 +133,50 @@ __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status, ...@@ -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, static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
u16 type, void *daddr, void *saddr, unsigned int len)
{ {
u16 head_len; u16 head_len;
struct sk_buff *skb = *skb_p;
if (!daddr) switch(skb->protocol) {
daddr = dev->broadcast; case __constant_ntohs(ETH_P_IP):
#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; head_len = 4;
skb_push(skb, head_len); skb_push(skb, head_len);
skb->data[3] = NLPID_IP; skb->data[3] = NLPID_IP;
break; break;
case ETH_P_IPV6: case __constant_ntohs(ETH_P_IPV6):
head_len = 4; head_len = 4;
skb_push(skb, head_len); skb_push(skb, head_len);
skb->data[3] = NLPID_IPV6; skb->data[3] = NLPID_IPV6;
break; break;
case LMI_PROTO: case __constant_ntohs(LMI_PROTO):
head_len = 4; head_len = 4;
skb_push(skb, head_len); skb_push(skb, head_len);
skb->data[3] = LMI_PROTO; skb->data[3] = LMI_PROTO;
break; 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: default:
head_len = 10; head_len = 10;
skb_push(skb, head_len); skb_push(skb, head_len);
...@@ -105,14 +185,12 @@ static int fr_hard_header(struct sk_buff *skb, struct net_device *dev, ...@@ -105,14 +185,12 @@ static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
skb->data[5] = FR_PAD; skb->data[5] = FR_PAD;
skb->data[6] = FR_PAD; skb->data[6] = FR_PAD;
skb->data[7] = FR_PAD; skb->data[7] = FR_PAD;
skb->data[8] = type>>8; *(u16*)(skb->data + 8) = skb->protocol;
skb->data[9] = (u8)type;
} }
memcpy(skb->data, daddr, 2); dlci_to_q922(skb->data, dlci);
skb->data[2] = FR_UI; skb->data[2] = FR_UI;
return 0;
return head_len;
} }
...@@ -124,13 +202,12 @@ static int pvc_open(struct net_device *dev) ...@@ -124,13 +202,12 @@ static int pvc_open(struct net_device *dev)
if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0) if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
return -EIO; /* Master must be UP in order to activate PVC */ return -EIO; /* Master must be UP in order to activate PVC */
if (pvc->master->state.fr.settings.lmi != LMI_NONE) if (pvc->open_count++ == 0) {
pvc->state.active = 0; if (pvc->master->state.fr.settings.lmi == LMI_NONE)
else pvc->state.active = 1;
pvc->state.active = 1;
pvc->state.new = 0; pvc->master->state.fr.dce_changed = 1;
pvc->master->state.fr.changed = 1; }
return 0; return 0;
} }
...@@ -139,38 +216,94 @@ static int pvc_open(struct net_device *dev) ...@@ -139,38 +216,94 @@ static int pvc_open(struct net_device *dev)
static int pvc_close(struct net_device *dev) static int pvc_close(struct net_device *dev)
{ {
pvc_device *pvc = dev_to_pvc(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; 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); pvc_device *pvc = dev_to_pvc(dev);
fr_proto_pvc_info info;
if (pvc->state.active) { if (ifr->ifr_settings.type == IF_GET_PROTO) {
skb->dev = hdlc_to_dev(pvc->master); if (dev->type == ARPHRD_ETHER)
pvc->stats.tx_bytes += skb->len; ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC;
pvc->stats.tx_packets++; else
if (pvc->state.fecn) ifr->ifr_settings.type = IF_PROTO_FR_PVC;
pvc->stats.tx_compressed++; /* TX Congestion counter */
dev_queue_xmit(skb); if (ifr->ifr_settings.size < sizeof(info)) {
} else { /* data size wanted */
pvc->stats.tx_dropped++; ifr->ifr_settings.size = sizeof(info);
dev_kfree_skb(skb); 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); 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) ...@@ -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) static inline void fr_log_dlci_active(pvc_device *pvc)
{ {
printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc), printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
pvc->state.active ? "" : "in", hdlc_to_name(pvc->master),
pvc->state.new ? " new" : ""); 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) ...@@ -213,8 +352,8 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
int i = 0; int i = 0;
if (hdlc->state.fr.settings.dce && fullrep) { if (hdlc->state.fr.settings.dce && fullrep) {
len += hdlc->state.fr.pvc_count * (2 + stat_len); len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MTU) { if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending " printk(KERN_WARNING "%s: Too many PVCs while sending "
"LMI full report\n", hdlc_to_name(hdlc)); "LMI full report\n", hdlc_to_name(hdlc));
return; return;
...@@ -224,12 +363,13 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep) ...@@ -224,12 +363,13 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
skb = dev_alloc_skb(len); skb = dev_alloc_skb(len);
if (!skb) { if (!skb) {
printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n", printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
hdlc_to_name(hdlc)); hdlc_to_name(hdlc));
return; return;
} }
memset(skb->data, 0, len); memset(skb->data, 0, len);
skb_reserve(skb, 4); 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 = skb->tail;
data[i++] = LMI_CALLREF; data[i++] = LMI_CALLREF;
data[i++] = hdlc->state.fr.settings.dce data[i++] = hdlc->state.fr.settings.dce
...@@ -253,16 +393,20 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep) ...@@ -253,16 +393,20 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
? LMI_CCITT_PVCSTAT : LMI_PVCSTAT; ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
data[i++] = stat_len; data[i++] = stat_len;
if (hdlc->state.fr.reliable && /* LMI start/restart */
(pvc->netdev.flags & IFF_UP) && if (hdlc->state.fr.reliable && !pvc->state.exist) {
!pvc->state.active && pvc->state.exist = pvc->state.new = 1;
!pvc->state.new) { fr_log_dlci_active(pvc);
pvc->state.new = 1; }
/* 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); fr_log_dlci_active(pvc);
} }
dlci_to_status(hdlc, netdev_dlci(&pvc->netdev), dlci_to_status(pvc->dlci, data + i,
data + i,
pvc->state.active, pvc->state.new); pvc->state.active, pvc->state.new);
i += stat_len; i += stat_len;
pvc = pvc->next; pvc = pvc->next;
...@@ -272,6 +416,7 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep) ...@@ -272,6 +416,7 @@ static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
skb_put(skb, i); skb_put(skb, i);
skb->priority = TC_PRIO_CONTROL; skb->priority = TC_PRIO_CONTROL;
skb->dev = hdlc_to_dev(hdlc); skb->dev = hdlc_to_dev(hdlc);
skb->nh.raw = skb->data;
dev_queue_xmit(skb); dev_queue_xmit(skb);
} }
...@@ -312,10 +457,11 @@ static void fr_timer(unsigned long arg) ...@@ -312,10 +457,11 @@ static void fr_timer(unsigned long arg)
if (reliable) { if (reliable) {
hdlc->state.fr.n391cnt = 0; /* Request full status */ hdlc->state.fr.n391cnt = 0; /* Request full status */
hdlc->state.fr.changed = 1; hdlc->state.fr.dce_changed = 1;
} else { } else {
while (pvc) { /* Deactivate all PVCs */ 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; pvc = pvc->next;
} }
} }
...@@ -346,7 +492,7 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) ...@@ -346,7 +492,7 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
{ {
int stat_len; int stat_len;
pvc_device *pvc; pvc_device *pvc;
int reptype = -1, error; int reptype = -1, error, no_ram;
u8 rxseq, txseq; u8 rxseq, txseq;
int i; int i;
...@@ -420,20 +566,18 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) ...@@ -420,20 +566,18 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
while (pvc) { while (pvc) {
if (pvc->state.new) { if (pvc->state.new) {
pvc->state.new = 0; pvc->state.new = 0;
pvc->state.active = 1;
fr_log_dlci_active(pvc);
/* Tell DTE that new PVC is now active */ /* Tell DTE that new PVC is now active */
hdlc->state.fr.changed = 1; hdlc->state.fr.dce_changed = 1;
} }
pvc = pvc->next; pvc = pvc->next;
} }
} }
if (hdlc->state.fr.changed) { if (hdlc->state.fr.dce_changed) {
reptype = LMI_FULLREP; reptype = LMI_FULLREP;
hdlc->state.fr.fullrep_sent = 1; 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); 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) ...@@ -449,13 +593,14 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
pvc = hdlc->state.fr.first_pvc; pvc = hdlc->state.fr.first_pvc;
while (pvc) { while (pvc) {
pvc->state.deleted = pvc->state.active; /* mark active PVCs */ pvc->state.deleted = 1;
pvc = pvc->next; pvc = pvc->next;
} }
no_ram = 0;
while (skb->len >= i + 2 + stat_len) { while (skb->len >= i + 2 + stat_len) {
u16 dlci; u16 dlci;
int active, new; unsigned int active, new;
if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT) if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) { ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
...@@ -472,21 +617,28 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) ...@@ -472,21 +617,28 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
} }
i++; i++;
dlci = status_to_dlci(hdlc, skb->data + i, &active, &new); dlci = status_to_dlci(skb->data + i, &active, &new);
pvc = find_pvc(hdlc, dlci);
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 (pvc) {
if (active && !pvc->state.active && pvc->state.exist = 1;
(pvc->netdev.flags & IFF_UP)) { pvc->state.deleted = 0;
if (active != pvc->state.active ||
new != pvc->state.new ||
!pvc->state.exist) {
pvc->state.new = new;
pvc->state.active = active; pvc->state.active = active;
fr_log_dlci_active(pvc); 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; i += stat_len;
} }
...@@ -494,10 +646,10 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb) ...@@ -494,10 +646,10 @@ static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
pvc = hdlc->state.fr.first_pvc; pvc = hdlc->state.fr.first_pvc;
while (pvc) { while (pvc) {
if (pvc->state.deleted) { if (pvc->state.deleted && pvc->state.exist) {
pvc->state.active = pvc->state.new = 0; pvc->state.active = pvc->state.new = 0;
pvc->state.exist = 0;
fr_log_dlci_active(pvc); fr_log_dlci_active(pvc);
pvc->state.deleted = 0;
} }
pvc = pvc->next; pvc = pvc->next;
} }
...@@ -517,8 +669,9 @@ static void fr_rx(struct sk_buff *skb) ...@@ -517,8 +669,9 @@ static void fr_rx(struct sk_buff *skb)
u8 *data = skb->data; u8 *data = skb->data;
u16 dlci; u16 dlci;
pvc_device *pvc; 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; goto rx_error;
dlci = q922_to_dlci(skb->data); dlci = q922_to_dlci(skb->data);
...@@ -550,57 +703,39 @@ static void fr_rx(struct sk_buff *skb) ...@@ -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", printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
hdlc_to_name(hdlc), dlci); hdlc_to_name(hdlc), dlci);
#endif #endif
goto rx_error; dev_kfree_skb_any(skb);
} return;
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 */ if (pvc->state.fecn != fh->fecn) {
pvc->stats.rx_bytes += skb->len;
if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
#ifdef CONFIG_HDLC_DEBUG_ECN #ifdef CONFIG_HDLC_DEBUG_ECN
printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc), printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc),
fh->fecn ? "N" : "FF"); dlci, fh->fecn ? "N" : "FF");
#endif #endif
pvc->state.fecn ^= 1; 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 #ifdef CONFIG_HDLC_DEBUG_ECN
printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc), printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc),
fh->becn ? "N" : "FF"); dlci, fh->becn ? "N" : "FF");
#endif #endif
pvc->state.becn ^= 1; pvc->state.becn ^= 1;
} }
if (pvc->state.becn)
pvc->stats.rx_compressed++;
skb->dev = &pvc->netdev;
if (data[3] == NLPID_IP) { if (data[3] == NLPID_IP) {
skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
dev = pvc->main;
skb->protocol = htons(ETH_P_IP); skb->protocol = htons(ETH_P_IP);
netif_rx(skb);
return;
}
} else if (data[3] == NLPID_IPV6) {
if (data[3] == NLPID_IPV6) {
skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */ skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
dev = pvc->main;
skb->protocol = htons(ETH_P_IPV6); 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 oui = ntohs(*(u16*)(data + 6));
u16 pid = ntohs(*(u16*)(data + 8)); u16 pid = ntohs(*(u16*)(data + 8));
skb_pull(skb, 10); skb_pull(skb, 10);
...@@ -610,23 +745,39 @@ static void fr_rx(struct sk_buff *skb) ...@@ -610,23 +745,39 @@ static void fr_rx(struct sk_buff *skb)
case ETH_P_IPX: case ETH_P_IPX:
case ETH_P_IP: /* a long variant */ case ETH_P_IP: /* a long variant */
case ETH_P_IPV6: case ETH_P_IPV6:
dev = pvc->main;
skb->protocol = htons(pid); skb->protocol = htons(pid);
break; break;
case 0x80C20007: /* bridged Ethernet frame */
if ((dev = pvc->ether) != NULL)
skb->protocol = eth_type_trans(skb, dev);
break;
default: default:
printk(KERN_INFO "%s: Unsupported protocol, OUI=%x " printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
"PID=%x\n", hdlc_to_name(hdlc), oui, pid); "PID=%x\n", hdlc_to_name(hdlc), oui, pid);
dev_kfree_skb_any(skb); dev_kfree_skb_any(skb);
return; return;
} }
} else {
netif_rx(skb); 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; return;
} }
printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n", if (dev) {
hdlc_to_name(hdlc), data[3]); struct net_device_stats *stats = pvc_get_stats(dev);
dev_kfree_skb_any(skb); 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; return;
rx_error: rx_error:
...@@ -641,7 +792,7 @@ static int fr_open(hdlc_device *hdlc) ...@@ -641,7 +792,7 @@ static int fr_open(hdlc_device *hdlc)
if (hdlc->state.fr.settings.lmi != LMI_NONE) { if (hdlc->state.fr.settings.lmi != LMI_NONE) {
hdlc->state.fr.last_poll = 0; hdlc->state.fr.last_poll = 0;
hdlc->state.fr.reliable = 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.request = 0;
hdlc->state.fr.fullrep_sent = 0; hdlc->state.fr.fullrep_sent = 0;
hdlc->state.fr.last_errors = 0xFFFFFFFF; hdlc->state.fr.last_errors = 0xFFFFFFFF;
...@@ -669,90 +820,119 @@ static void fr_close(hdlc_device *hdlc) ...@@ -669,90 +820,119 @@ static void fr_close(hdlc_device *hdlc)
if (hdlc->state.fr.settings.lmi != LMI_NONE) if (hdlc->state.fr.settings.lmi != LMI_NONE)
del_timer_sync(&hdlc->state.fr.timer); del_timer_sync(&hdlc->state.fr.timer);
while(pvc) { while(pvc) { /* Shutdown all PVCs for this FRAD */
dev_close(&pvc->netdev); /* 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; pvc = pvc->next;
} }
} }
static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
{ {
pvc_device **pvc_p = &hdlc->state.fr.first_pvc; pvc_device *pvc = NULL;
pvc_device *pvc; struct net_device *dev;
int result; int result, used;
char * prefix = "pvc%d";
if(dlci <= 0 || dlci >= 1024) if (type == ARPHRD_ETHER)
return -EINVAL; /* Only 10 bits for DLCI, DLCI 0 reserved */ prefix = "pvceth%d";
while(*pvc_p) { if ((pvc = add_pvc(hdlc, dlci)) == NULL) {
if (netdev_dlci(&(*pvc_p)->netdev) == dlci) printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
break; hdlc_to_name(hdlc));
pvc_p = &(*pvc_p)->next; return -ENOBUFS;
} }
if (create) { /* Create PVC */ if (*get_dev_p(pvc, type))
if (*pvc_p != NULL) return -EEXIST;
return -EEXIST;
pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL); used = pvc_is_used(pvc);
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) { dev = kmalloc(sizeof(struct net_device) +
kfree(pvc); sizeof(struct net_device_stats), GFP_KERNEL);
*pvc_p = NULL; if (!dev) {
return -EIO; 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; if (type == ARPHRD_ETHER) {
hdlc->state.fr.pvc_count++; ether_setup(dev);
return 0; 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; 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 */ return -EBUSY; /* PVC in use */
hdlc->state.fr.changed = 1; unregister_netdevice(dev);
hdlc->state.fr.pvc_count--; kfree(dev);
*pvc_p = pvc->next; *get_dev_p(pvc, type) = NULL;
unregister_netdevice(&pvc->netdev);
kfree(pvc); if (!pvc_is_used(pvc)) {
hdlc->state.fr.dce_pvc_count--;
hdlc->state.fr.dce_changed = 1;
}
delete_unused_pvcs(hdlc);
return 0; return 0;
} }
...@@ -763,14 +943,21 @@ static void fr_destroy(hdlc_device *hdlc) ...@@ -763,14 +943,21 @@ static void fr_destroy(hdlc_device *hdlc)
pvc_device *pvc = hdlc->state.fr.first_pvc; pvc_device *pvc = hdlc->state.fr.first_pvc;
while(pvc) { while(pvc) {
pvc_device *next = pvc->next; 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); kfree(pvc);
pvc = next; pvc = next;
} }
hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */ hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
hdlc->state.fr.pvc_count = 0; hdlc->state.fr.dce_pvc_count = 0;
hdlc->state.fr.changed = 1; hdlc->state.fr.dce_changed = 1;
} }
...@@ -828,25 +1015,27 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -828,25 +1015,27 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
if (hdlc->proto != IF_PROTO_FR) { if (hdlc->proto != IF_PROTO_FR) {
hdlc_proto_detach(hdlc); hdlc_proto_detach(hdlc);
hdlc->state.fr.first_pvc = NULL; 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); memcpy(&hdlc->state.fr.settings, &new_settings, size);
hdlc->open = fr_open; hdlc->open = fr_open;
hdlc->stop = fr_close; hdlc->stop = fr_close;
hdlc->netif_rx = fr_rx; hdlc->netif_rx = fr_rx;
hdlc->type_trans = NULL;
hdlc->proto_detach = fr_destroy; hdlc->proto_detach = fr_destroy;
hdlc->proto = IF_PROTO_FR; hdlc->proto = IF_PROTO_FR;
dev->hard_start_xmit = hdlc->xmit; dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = fr_hard_header; dev->hard_header = NULL;
dev->type = ARPHRD_FRAD; dev->type = ARPHRD_FRAD;
dev->addr_len = 2; dev->flags = IFF_POINTOPOINT | IFF_NOARP;
*(u16*)dev->dev_addr = htons(LMI_DLCI); dev->addr_len = 0;
dlci_to_q922(dev->broadcast, LMI_DLCI);
return 0; return 0;
case IF_PROTO_FR_ADD_PVC: case IF_PROTO_FR_ADD_PVC:
case IF_PROTO_FR_DEL_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)) if(!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
...@@ -854,8 +1043,20 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -854,8 +1043,20 @@ int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
sizeof(fr_proto_pvc))) sizeof(fr_proto_pvc)))
return -EFAULT; return -EFAULT;
return fr_pvc(hdlc, pvc.dlci, if (pvc.dlci <= 0 || pvc.dlci >= 1024)
ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC); 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; return -EINVAL;
......
/* /*
* Generic HDLC support routines for Linux * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
* *
* Current status: * Currently supported:
* - this is work in progress
* - not heavily tested on SMP
* - currently supported:
* * raw IP-in-HDLC * * raw IP-in-HDLC
* * Cisco HDLC * * Cisco HDLC
* * Frame Relay with ANSI or CCITT LMI (both user and network side) * * Frame Relay with ANSI or CCITT LMI (both user and network side)
...@@ -37,7 +33,7 @@ ...@@ -37,7 +33,7 @@
#include <linux/hdlc.h> #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) 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) ...@@ -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, static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *p) 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; return 0;
} }
...@@ -69,6 +71,10 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -69,6 +71,10 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
#define hdlc_raw_ioctl(hdlc, ifr) -ENOSYS #define hdlc_raw_ioctl(hdlc, ifr) -ENOSYS
#endif #endif
#ifndef CONFIG_HDLC_RAW_ETH
#define hdlc_raw_eth_ioctl(hdlc, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_PPP #ifndef CONFIG_HDLC_PPP
#define hdlc_ppp_ioctl(hdlc, ifr) -ENOSYS #define hdlc_ppp_ioctl(hdlc, ifr) -ENOSYS
#endif #endif
...@@ -96,6 +102,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -96,6 +102,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch(ifr->ifr_settings.type) { switch(ifr->ifr_settings.type) {
case IF_PROTO_HDLC: case IF_PROTO_HDLC:
case IF_PROTO_HDLC_ETH:
case IF_PROTO_PPP: case IF_PROTO_PPP:
case IF_PROTO_CISCO: case IF_PROTO_CISCO:
case IF_PROTO_FR: case IF_PROTO_FR:
...@@ -109,6 +116,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -109,6 +116,7 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
switch(proto) { switch(proto) {
case IF_PROTO_HDLC: return hdlc_raw_ioctl(hdlc, ifr); 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_PPP: return hdlc_ppp_ioctl(hdlc, ifr);
case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr); case IF_PROTO_CISCO: return hdlc_cisco_ioctl(hdlc, ifr);
case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr); case IF_PROTO_FR: return hdlc_fr_ioctl(hdlc, ifr);
...@@ -144,7 +152,6 @@ int register_hdlc_device(hdlc_device *hdlc) ...@@ -144,7 +152,6 @@ int register_hdlc_device(hdlc_device *hdlc)
if (result != 0) if (result != 0)
return -EIO; return -EIO;
MOD_INC_USE_COUNT;
return 0; return 0;
} }
...@@ -152,17 +159,17 @@ int register_hdlc_device(hdlc_device *hdlc) ...@@ -152,17 +159,17 @@ int register_hdlc_device(hdlc_device *hdlc)
void unregister_hdlc_device(hdlc_device *hdlc) void unregister_hdlc_device(hdlc_device *hdlc)
{ {
rtnl_lock();
hdlc_proto_detach(hdlc); hdlc_proto_detach(hdlc);
unregister_netdevice(hdlc_to_dev(hdlc));
unregister_netdev(hdlc_to_dev(hdlc)); rtnl_unlock();
MOD_DEC_USE_COUNT;
} }
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("HDLC support module"); MODULE_DESCRIPTION("HDLC support module");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
EXPORT_SYMBOL(hdlc_ioctl); EXPORT_SYMBOL(hdlc_ioctl);
EXPORT_SYMBOL(register_hdlc_device); EXPORT_SYMBOL(register_hdlc_device);
......
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* Point-to-point protocol support * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -68,10 +67,10 @@ static void ppp_close(hdlc_device *hdlc) ...@@ -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); return __constant_htons(ETH_P_WAN_PPP);
netif_rx(skb);
} }
...@@ -103,7 +102,8 @@ int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -103,7 +102,8 @@ int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = ppp_open; hdlc->open = ppp_open;
hdlc->stop = ppp_close; hdlc->stop = ppp_close;
hdlc->netif_rx = ppp_rx; hdlc->netif_rx = NULL;
hdlc->type_trans = ppp_type_trans;
hdlc->proto = IF_PROTO_PPP; hdlc->proto = IF_PROTO_PPP;
dev->hard_start_xmit = hdlc->xmit; dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL; dev->hard_header = NULL;
......
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* HDLC support * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -26,10 +25,10 @@ ...@@ -26,10 +25,10 @@
#include <linux/hdlc.h> #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); return __constant_htons(ETH_P_IP);
netif_rx(skb);
} }
...@@ -67,7 +66,7 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -67,7 +66,7 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
new_settings.encoding = ENCODING_NRZ; new_settings.encoding = ENCODING_NRZ;
if (new_settings.parity == PARITY_DEFAULT) 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, result = hdlc->attach(hdlc, new_settings.encoding,
new_settings.parity); new_settings.parity);
...@@ -79,11 +78,13 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -79,11 +78,13 @@ int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = NULL; hdlc->open = NULL;
hdlc->stop = NULL; hdlc->stop = NULL;
hdlc->netif_rx = raw_rx; hdlc->netif_rx = NULL;
hdlc->type_trans = raw_type_trans;
hdlc->proto = IF_PROTO_HDLC; hdlc->proto = IF_PROTO_HDLC;
dev->hard_start_xmit = hdlc->xmit; dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL; dev->hard_header = NULL;
dev->type = ARPHRD_RAWHDLC; dev->type = ARPHRD_RAWHDLC;
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
dev->addr_len = 0; dev->addr_len = 0;
return 0; return 0;
} }
......
/*
* 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;
}
...@@ -2,12 +2,11 @@ ...@@ -2,12 +2,11 @@
* Generic HDLC support routines for Linux * Generic HDLC support routines for Linux
* X.25 support * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -204,6 +203,7 @@ int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr) ...@@ -204,6 +203,7 @@ int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
hdlc->open = x25_open; hdlc->open = x25_open;
hdlc->stop = x25_close; hdlc->stop = x25_close;
hdlc->netif_rx = x25_rx; hdlc->netif_rx = x25_rx;
hdlc->type_trans = NULL;
hdlc->proto = IF_PROTO_X25; hdlc->proto = IF_PROTO_X25;
dev->hard_start_xmit = x25_xmit; dev->hard_start_xmit = x25_xmit;
dev->hard_header = NULL; dev->hard_header = NULL;
......
/* /*
* SDL Inc. RISCom/N2 synchronous serial card driver for Linux * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
* *
* For information see http://hq.pm.waw.pl/hdlc/ * For information see http://hq.pm.waw.pl/hdlc/
* *
...@@ -28,20 +27,28 @@ ...@@ -28,20 +27,28 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/moduleparam.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/hdlc.h> #include <linux/hdlc.h>
#include <asm/io.h> #include <asm/io.h>
#include "hd64570.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"; static const char* devname = "RISCom/N2";
#define USE_WINDOWSIZE 16384 #define USE_WINDOWSIZE 16384
#define USE_BUS16BITS 1 #define USE_BUS16BITS 1
#define CLOCK_BASE 9830400 /* 9.8304 MHz */ #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 N2_IOPORTS 0x10
#define NEED_DETECT_RAM
#define MAX_TX_BUFFERS 10
static char *hw = NULL; /* pointer to hw=xxx command line string */ static char *hw = NULL; /* pointer to hw=xxx command line string */
...@@ -86,16 +93,16 @@ typedef struct port_s { ...@@ -86,16 +93,16 @@ typedef struct port_s {
struct card_s *card; struct card_s *card;
spinlock_t lock; /* TX lock */ spinlock_t lock; /* TX lock */
sync_serial_settings settings; sync_serial_settings settings;
int valid; /* port enabled */
int rxpart; /* partial frame received, next frame invalid*/
unsigned short encoding; unsigned short encoding;
unsigned short parity; 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 rxs, txs, tmc; /* SCA registers */
u8 valid; /* port enabled */
u8 phy_node; /* physical port # - 0 or 1 */ u8 phy_node; /* physical port # - 0 or 1 */
u8 log_node; /* logical port # */ 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; }port_t;
...@@ -106,8 +113,9 @@ typedef struct card_s { ...@@ -106,8 +113,9 @@ typedef struct card_s {
u32 ram_size; /* number of bytes */ u32 ram_size; /* number of bytes */
u16 io; /* IO Base address */ u16 io; /* IO Base address */
u16 buff_offset; /* offset of first buffer of first channel */ 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 irq; /* IRQ (3-15) */
u8 ring_buffers; /* number of buffers in a ring */
port_t ports[2]; port_t ports[2];
struct card_s *next_card; struct card_s *next_card;
...@@ -209,14 +217,19 @@ static int n2_open(struct net_device *dev) ...@@ -209,14 +217,19 @@ static int n2_open(struct net_device *dev)
int io = port->card->io; 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);
if (!try_module_get(THIS_MODULE))
return -EFAULT; /* rmmod in progress */
int result = hdlc_open(hdlc); int result = hdlc_open(hdlc);
if (result) if (result) {
return result; return result;
module_put(THIS_MODULE);
}
MOD_INC_USE_COUNT;
mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */ mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
outb(mcr, io + N2_MCR); outb(mcr, io + N2_MCR);
outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */ outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */ outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
sca_open(hdlc); sca_open(hdlc);
...@@ -237,7 +250,7 @@ static int n2_close(struct net_device *dev) ...@@ -237,7 +250,7 @@ static int n2_close(struct net_device *dev)
mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */ mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
outb(mcr, io + N2_MCR); outb(mcr, io + N2_MCR);
hdlc_close(hdlc); hdlc_close(hdlc);
MOD_DEC_USE_COUNT; module_put(THIS_MODULE);
return 0; return 0;
} }
...@@ -297,62 +310,6 @@ static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) ...@@ -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) static void n2_destroy_card(card_t *card)
{ {
int cnt; int cnt;
...@@ -376,11 +333,12 @@ static void n2_destroy_card(card_t *card) ...@@ -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, static int __init n2_run(unsigned long io, unsigned long irq,
long valid0, long valid1) unsigned long winbase, long valid0, long valid1)
{ {
card_t *card; card_t *card;
u8 cnt, pcr; u8 cnt, pcr;
int i;
if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) { if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
printk(KERN_ERR "n2: invalid I/O port value\n"); 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, ...@@ -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"); printk(KERN_ERR "n2: invalid IRQ value\n");
return -ENODEV; return -ENODEV;
} }
if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) { if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
printk(KERN_ERR "n2: invalid RAM value\n"); printk(KERN_ERR "n2: invalid RAM value\n");
return -ENODEV; return -ENODEV;
...@@ -451,25 +409,27 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase, ...@@ -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); pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
outb(pcr, io + N2_PCR); outb(pcr, io + N2_PCR);
cnt = n2_count_page(card); card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
if (!cnt) {
printk(KERN_ERR "n2: memory test failed.\n");
n2_destroy_card(card);
return -EIO;
}
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->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
card->ring_buffers = card->ram_size / card->rx_ring_buffers = i - card->tx_ring_buffers;
((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc)) card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
* card->ring_buffers; (card->tx_ring_buffers + card->rx_ring_buffers);
printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, " printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, "
"using %u packets rings\n", card->ram_size / 1024, card->irq, "using %u TX + %u RX packets rings\n", card->ram_size / 1024,
card->ring_buffers); 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 */ pcr |= PCR_RUNSCA; /* run SCA */
outb(pcr, io + N2_PCR); outb(pcr, io + N2_PCR);
...@@ -531,7 +491,7 @@ static int __init n2_init(void) ...@@ -531,7 +491,7 @@ static int __init n2_init(void)
return -ENOSYS; /* no parameters specified, abort */ return -ENOSYS; /* no parameters specified, abort */
} }
printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version); printk(KERN_INFO "%s\n", version);
do { do {
unsigned long io, irq, ram; unsigned long io, irq, ram;
...@@ -558,7 +518,7 @@ static int __init n2_init(void) ...@@ -558,7 +518,7 @@ static int __init n2_init(void)
break; break;
hw++; hw++;
} }
if (!valid[0] && !valid[1]) if (!valid[0] && !valid[1])
break; /* at least one port must be used */ break; /* at least one port must be used */
...@@ -566,7 +526,7 @@ static int __init n2_init(void) ...@@ -566,7 +526,7 @@ static int __init n2_init(void)
n2_run(io, irq, ram, valid[0], valid[1]); n2_run(io, irq, ram, valid[0], valid[1]);
if (*hw == '\x0') if (*hw == '\x0')
return 0; return first_card ? 0 : -ENOSYS;
}while(*hw++ == ':'); }while(*hw++ == ':');
printk(KERN_ERR "n2: invalid hardware parameters\n"); printk(KERN_ERR "n2: invalid hardware parameters\n");
...@@ -574,17 +534,6 @@ static int __init n2_init(void) ...@@ -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) static void __exit n2_cleanup(void)
{ {
card_t *card = first_card; card_t *card = first_card;
...@@ -602,5 +551,5 @@ module_exit(n2_cleanup); ...@@ -602,5 +551,5 @@ module_exit(n2_cleanup);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>"); MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("RISCom/N2 serial port driver"); MODULE_DESCRIPTION("RISCom/N2 serial port driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_PARM(hw, "s"); /* hw=io,irq,ram,ports:io,irq,... */ module_param(hw, charp, 0444); /* hw=io,irq,ram,ports:io,irq,... */
/* /*
* Generic HDLC support routines for Linux * 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 * 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 * under the terms of version 2 of the GNU General Public License
* the Free Software Foundation; either version 2 of the License, or * as published by the Free Software Foundation.
* (at your option) any later version.
*/ */
#ifndef __HDLC_H #ifndef __HDLC_H
...@@ -52,7 +51,7 @@ ...@@ -52,7 +51,7 @@
#include <linux/hdlc/ioctl.h> #include <linux/hdlc/ioctl.h>
#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */ #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 */ #define MAXLEN_LMISTAT 20 /* max size of status enquiry frame */
...@@ -145,17 +144,20 @@ typedef struct { ...@@ -145,17 +144,20 @@ typedef struct {
typedef struct pvc_device_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 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 { struct {
int active; unsigned int new: 1;
int new; unsigned int active: 1;
int deleted; unsigned int exist: 1;
int fecn; unsigned int deleted: 1;
int becn; unsigned int fecn: 1;
unsigned int becn: 1;
}state; }state;
}pvc_device; }pvc_device;
...@@ -180,18 +182,20 @@ typedef struct hdlc_device_struct { ...@@ -180,18 +182,20 @@ typedef struct hdlc_device_struct {
void (*stop)(struct hdlc_device_struct *hdlc); void (*stop)(struct hdlc_device_struct *hdlc);
void (*proto_detach)(struct hdlc_device_struct *hdlc); void (*proto_detach)(struct hdlc_device_struct *hdlc);
void (*netif_rx)(struct sk_buff *skb); 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. */ int proto; /* IF_PROTO_HDLC/CISCO/FR/etc. */
union { union {
struct { struct {
fr_proto settings; fr_proto settings;
pvc_device *first_pvc; pvc_device *first_pvc;
int pvc_count; int dce_pvc_count;
struct timer_list timer; struct timer_list timer;
int last_poll; int last_poll;
int reliable; int reliable;
int changed; int dce_changed;
int request; int request;
int fullrep_sent; int fullrep_sent;
u32 last_errors; /* last errors bit list */ u32 last_errors; /* last errors bit list */
...@@ -226,6 +230,7 @@ typedef struct hdlc_device_struct { ...@@ -226,6 +230,7 @@ typedef struct hdlc_device_struct {
int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr); 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_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
int hdlc_ppp_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_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
...@@ -254,15 +259,9 @@ static __inline__ hdlc_device* dev_to_hdlc(struct net_device *dev) ...@@ -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) 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) ...@@ -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) 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);
...@@ -345,5 +331,15 @@ static __inline__ void hdlc_proto_detach(hdlc_device *hdlc) ...@@ -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 /* __KERNEL */
#endif /* __HDLC_H */ #endif /* __HDLC_H */
...@@ -33,6 +33,11 @@ typedef struct { ...@@ -33,6 +33,11 @@ typedef struct {
unsigned int dlci; unsigned int dlci;
} fr_proto_pvc; /* for creating/deleting FR PVCs */ } 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 { typedef struct {
unsigned int interval; unsigned int interval;
unsigned int timeout; unsigned int timeout;
...@@ -40,16 +45,4 @@ typedef struct { ...@@ -40,16 +45,4 @@ typedef struct {
/* PPP doesn't need any info now - supply length = 0 to ioctl */ /* 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__ */ #endif /* __HDLC_IOCTL_H__ */
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <linux/types.h> /* for "__kernel_caddr_t" et al */ #include <linux/types.h> /* for "__kernel_caddr_t" et al */
#include <linux/socket.h> /* for "struct sockaddr" et al */ #include <linux/socket.h> /* for "struct sockaddr" et al */
#define IFNAMSIZ 16
#include <linux/hdlc/ioctl.h> #include <linux/hdlc/ioctl.h>
/* Standard interface flags (netdevice->flags). */ /* Standard interface flags (netdevice->flags). */
...@@ -69,7 +71,11 @@ ...@@ -69,7 +71,11 @@
#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ #define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */
#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ #define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */
#define IF_PROTO_X25 0x2006 /* X.25 */ #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 ...@@ -103,6 +109,7 @@ struct if_settings
cisco_proto *cisco; cisco_proto *cisco;
fr_proto *fr; fr_proto *fr;
fr_proto_pvc *fr_pvc; fr_proto_pvc *fr_pvc;
fr_proto_pvc_info *fr_pvc_info;
/* interface settings */ /* interface settings */
sync_serial_settings *sync; sync_serial_settings *sync;
...@@ -120,7 +127,6 @@ struct if_settings ...@@ -120,7 +127,6 @@ struct if_settings
struct ifreq struct ifreq
{ {
#define IFHWADDRLEN 6 #define IFHWADDRLEN 6
#define IFNAMSIZ 16
union union
{ {
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment