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

[PATCH] generic HDLC update

This version fixes:
- missing rtnl_lock()/rtnl_unload() bug on unregister_hdlc_device
- N2, C101: interrupt handler now works under high IRQ load from other
  devices (with previous versions, the IRQ processing for the card could
  sometimes stop after reaching "work limit")

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