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;
......
This diff is collapsed.
......@@ -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;
......
This diff is collapsed.
/*
* 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,14 +217,19 @@ static int n2_open(struct net_device *dev)
int io = port->card->io;
u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
if (!try_module_get(THIS_MODULE))
return -EFAULT; /* rmmod in progress */
int result = hdlc_open(hdlc);
if (result)
if (result) {
return result;
module_put(THIS_MODULE);
}
MOD_INC_USE_COUNT;
mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
outb(mcr, io + N2_MCR);
outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
sca_open(hdlc);
......@@ -237,7 +250,7 @@ static int n2_close(struct net_device *dev)
mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
outb(mcr, io + N2_MCR);
hdlc_close(hdlc);
MOD_DEC_USE_COUNT;
module_put(THIS_MODULE);
return 0;
}
......@@ -297,62 +310,6 @@ static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static u8 n2_count_page(card_t *card)
{
u8 page;
int i, bcount = USE_WINDOWSIZE, wcount = USE_WINDOWSIZE/2;
u16 *dp = (u16*)card->winbase;
u8 *bp = (u8*)card->winbase;
u8 psr = inb(card->io + N2_PSR) & PSR_WINBITS;
for (page = 0; page < 16; page++) {
outb(psr | page, card->io + N2_PSR); /* select a page */
writeb(page, dp);
if (readb(dp) != page)
break; /* If can't read back, no good memory */
outb(psr, card->io + N2_PSR); /* goto page 0 */
if (readb(dp))
break; /* If page 0 changed, then wrapped around */
outb(psr | page, card->io + N2_PSR); /* select page again */
/* first do byte tests */
for (i = 0; i < bcount; i++)
writeb(i, bp + i);
for (i = 0; i < bcount; i++)
if (readb(bp + i) != (i & 0xff))
return 0;
for (i = 0; i < bcount; i++)
writeb(~i, bp + i);
for (i = 0; i < bcount; i++)
if (readb(bp + i) != (~i & 0xff))
return 0;
/* next do 16-bit tests */
for (i = 0; i < wcount; i++)
writew(0x55AA, dp + i);
for (i = 0; i < wcount; i++)
if (readw(dp + i) != 0x55AA)
return 0;
for (i = 0; i < wcount; i++)
writew(0xAA55, dp + i);
for (i = 0; i < wcount; i++)
if (readw(dp + i) != 0xAA55)
return 0;
for (i = 0; i < wcount; i++)
writew(page, dp + i);
}
return page;
}
static void n2_destroy_card(card_t *card)
{
int cnt;
......@@ -376,11 +333,12 @@ static void n2_destroy_card(card_t *card)
static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
long valid0, long valid1)
static int __init n2_run(unsigned long io, unsigned long irq,
unsigned long winbase, long valid0, long valid1)
{
card_t *card;
u8 cnt, pcr;
int i;
if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
printk(KERN_ERR "n2: invalid I/O port value\n");
......@@ -391,7 +349,7 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
printk(KERN_ERR "n2: invalid IRQ value\n");
return -ENODEV;
}
if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
printk(KERN_ERR "n2: invalid RAM value\n");
return -ENODEV;
......@@ -451,25 +409,27 @@ static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
outb(pcr, io + N2_PCR);
cnt = n2_count_page(card);
if (!cnt) {
printk(KERN_ERR "n2: memory test failed.\n");
n2_destroy_card(card);
return -EIO;
}
card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
card->ram_size = cnt * USE_WINDOWSIZE;
/* number of TX + RX buffers for one port */
i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) +
HDLC_MAX_MRU));
/* 4 rings required for 2 ports, 2 rings for one port */
card->ring_buffers = card->ram_size /
((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
card->rx_ring_buffers = i - card->tx_ring_buffers;
card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc))
* card->ring_buffers;
card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
(card->tx_ring_buffers + card->rx_ring_buffers);
printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, "
"using %u packets rings\n", card->ram_size / 1024, card->irq,
card->ring_buffers);
"using %u TX + %u RX packets rings\n", card->ram_size / 1024,
card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
if (card->tx_ring_buffers < 1) {
printk(KERN_ERR "n2: RAM test failed\n");
n2_destroy_card(card);
return -EIO;
}
pcr |= PCR_RUNSCA; /* run SCA */
outb(pcr, io + N2_PCR);
......@@ -531,7 +491,7 @@ static int __init n2_init(void)
return -ENOSYS; /* no parameters specified, abort */
}
printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
printk(KERN_INFO "%s\n", version);
do {
unsigned long io, irq, ram;
......@@ -558,7 +518,7 @@ static int __init n2_init(void)
break;
hw++;
}
if (!valid[0] && !valid[1])
break; /* at least one port must be used */
......@@ -566,7 +526,7 @@ static int __init n2_init(void)
n2_run(io, irq, ram, valid[0], valid[1]);
if (*hw == '\x0')
return 0;
return first_card ? 0 : -ENOSYS;
}while(*hw++ == ':');
printk(KERN_ERR "n2: invalid hardware parameters\n");
......@@ -574,17 +534,6 @@ static int __init n2_init(void)
}
#ifndef MODULE
static int __init n2_setup(char *str)
{
hw = str;
return 1;
}
__setup("n2=", n2_setup);
#endif
static void __exit n2_cleanup(void)
{
card_t *card = first_card;
......@@ -602,5 +551,5 @@ module_exit(n2_cleanup);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("RISCom/N2 serial port driver");
MODULE_LICENSE("GPL");
MODULE_PARM(hw, "s"); /* hw=io,irq,ram,ports:io,irq,... */
MODULE_LICENSE("GPL v2");
module_param(hw, charp, 0444); /* hw=io,irq,ram,ports:io,irq,... */
/*
* 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