Commit a36167db authored by François Romieu's avatar François Romieu Committed by Jeff Garzik

WAN drivers update 1/5:

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