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);
if(!capable(CAP_NET_ADMIN)) #ifdef CONFIG_HDLC_DEBUG_RINGS
return -EPERM; if (cmd == SIOCDEVPRIVATE) {
sca_dump_rings(hdlc);
switch(cmd) { return 0;
case HDLCSCLOCK: }
result = c101_set_clock(port, value); #endif
case HDLCGCLOCK: if (cmd != SIOCDEVICE)
value = port->clkmode; return hdlc_ioctl(dev, ifr, cmd);
break;
switch(ifr->ifr_settings.type) {
case IF_GET_IFACE:
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
if (ifr->ifr_settings.data_length == 0)
return 0; /* return interface type only */
if (ifr->ifr_settings.data_length < size)
return -ENOMEM; /* buffer too small */
if (copy_to_user(ifr->ifr_settings.data,
&port->settings, size))
return -EFAULT;
ifr->ifr_settings.data_length = size;
return 0;
case HDLCSCLOCKRATE: case IF_IFACE_SYNC_SERIAL:
port->clkrate = value; if(!capable(CAP_NET_ADMIN))
sca_set_clock(port); return -EPERM;
case HDLCGCLOCKRATE:
value = port->clkrate;
break;
case HDLCSLINE: if (ifr->ifr_settings.data_length != size)
result = sca_set_loopback(port, value); return -ENOMEM; /* incorrect data length */
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 c101_set_iface(port);
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;
......
This diff is collapsed.
This diff is collapsed.
...@@ -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
......
This diff is collapsed.
/*
* 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
*
* 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);
if(!capable(CAP_NET_ADMIN)) #ifdef CONFIG_HDLC_DEBUG_RINGS
return -EPERM; if (cmd == SIOCDEVPRIVATE) {
sca_dump_rings(hdlc);
switch(cmd) { return 0;
case HDLCSCLOCK: }
result = n2_set_clock(port, value); #endif
case HDLCGCLOCK: if (cmd != SIOCDEVICE)
value = port->clkmode; return hdlc_ioctl(dev, ifr, cmd);
break;
switch(ifr->ifr_settings.type) {
case IF_GET_IFACE:
ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
if (ifr->ifr_settings.data_length == 0)
return 0; /* return interface type only */
if (ifr->ifr_settings.data_length < size)
return -ENOMEM; /* buffer too small */
if (copy_to_user(ifr->ifr_settings.data,
&port->settings, size))
return -EFAULT;
ifr->ifr_settings.data_length = size;
return 0;
case HDLCSCLOCKRATE: case IF_IFACE_SYNC_SERIAL:
port->clkrate = value; if(!capable(CAP_NET_ADMIN))
sca_set_clock(port); return -EPERM;
case HDLCGCLOCKRATE:
value = port->clkrate;
break;
case HDLCSLINE: if (ifr->ifr_settings.data_length != size)
result = sca_set_loopback(port, value); return -ENOMEM; /* incorrect data length */
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;
......
This diff is collapsed.
...@@ -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