[PATCH] Modularize generic HDLC

This patch enables building of individual WAN protocol support
routines (parts of generic HDLC) as separate modules.
All protocol-private definitions are moved from hdlc.h file
to protocol drivers. User-space interface and interface
between generic HDLC and underlying low-level HDLC drivers
are unchanged.

Signed-off-by: Krzysztof Halasa <khc@pm.waw.pl>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
Krzysztof Halasa 2006-09-26 23:23:45 +02:00 committed by Jeff Garzik
parent c226951b93
commit eb2a2fd91f
11 changed files with 739 additions and 504 deletions

View File

@ -154,7 +154,7 @@ config HDLC
If unsure, say N.
config HDLC_RAW
bool "Raw HDLC support"
tristate "Raw HDLC support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC over WAN connections.
@ -162,7 +162,7 @@ config HDLC_RAW
If unsure, say N.
config HDLC_RAW_ETH
bool "Raw HDLC Ethernet device support"
tristate "Raw HDLC Ethernet device support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC Ethernet device emulation
@ -173,7 +173,7 @@ config HDLC_RAW_ETH
If unsure, say N.
config HDLC_CISCO
bool "Cisco HDLC support"
tristate "Cisco HDLC support"
depends on HDLC
help
Generic HDLC driver supporting Cisco HDLC over WAN connections.
@ -181,7 +181,7 @@ config HDLC_CISCO
If unsure, say N.
config HDLC_FR
bool "Frame Relay support"
tristate "Frame Relay support"
depends on HDLC
help
Generic HDLC driver supporting Frame Relay over WAN connections.
@ -189,7 +189,7 @@ config HDLC_FR
If unsure, say N.
config HDLC_PPP
bool "Synchronous Point-to-Point Protocol (PPP) support"
tristate "Synchronous Point-to-Point Protocol (PPP) support"
depends on HDLC
help
Generic HDLC driver supporting PPP over WAN connections.
@ -197,7 +197,7 @@ config HDLC_PPP
If unsure, say N.
config HDLC_X25
bool "X.25 protocol support"
tristate "X.25 protocol support"
depends on HDLC && (LAPB=m && HDLC=m || LAPB=y)
help
Generic HDLC driver supporting X.25 over WAN connections.

View File

@ -9,14 +9,13 @@ cyclomx-y := cycx_main.o
cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o
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
hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o
hdlc-objs := $(hdlc-y)
obj-$(CONFIG_HDLC) += hdlc.o
obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
obj-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
obj-$(CONFIG_HDLC_FR) += hdlc_fr.o
obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o syncppp.o
obj-$(CONFIG_HDLC_X25) += hdlc_x25.o
pc300-y := pc300_drv.o
pc300-$(CONFIG_PC300_MLPPP) += pc300_tty.o
@ -38,10 +37,6 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o
obj-$(CONFIG_LAPBETHER) += lapbether.o
obj-$(CONFIG_SBNI) += sbni.o
obj-$(CONFIG_PC300) += pc300.o
obj-$(CONFIG_HDLC) += hdlc.o
ifeq ($(CONFIG_HDLC_PPP),y)
obj-$(CONFIG_HDLC) += syncppp.o
endif
obj-$(CONFIG_N2) += n2.o
obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o

View File

@ -1,7 +1,7 @@
/*
* Generic HDLC support routines for Linux
*
* Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 2006 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
@ -17,9 +17,9 @@
* Use sethdlc utility to set line parameters, protocol and PVCs
*
* How does it work:
* - proto.open(), close(), start(), stop() calls are serialized.
* - proto->open(), close(), start(), stop() calls are serialized.
* The order is: open, [ start, stop ... ] close ...
* - proto.start() and stop() are called with spin_lock_irq held.
* - proto->start() and stop() are called with spin_lock_irq held.
*/
#include <linux/module.h>
@ -38,10 +38,12 @@
#include <linux/hdlc.h>
static const char* version = "HDLC support module revision 1.19";
static const char* version = "HDLC support module revision 1.20";
#undef DEBUG_LINK
static struct hdlc_proto *first_proto = NULL;
static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
{
@ -63,11 +65,11 @@ 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, struct net_device *orig_dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto.netif_rx)
return hdlc->proto.netif_rx(skb);
struct hdlc_device_desc *desc = dev_to_desc(dev);
if (desc->netif_rx)
return desc->netif_rx(skb);
hdlc->stats.rx_dropped++; /* Shouldn't happen */
desc->stats.rx_dropped++; /* Shouldn't happen */
dev_kfree_skb(skb);
return NET_RX_DROP;
}
@ -77,8 +79,8 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
static inline void hdlc_proto_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto.start)
return hdlc->proto.start(dev);
if (hdlc->proto->start)
return hdlc->proto->start(dev);
}
@ -86,8 +88,8 @@ static inline void hdlc_proto_start(struct net_device *dev)
static inline void hdlc_proto_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto.stop)
return hdlc->proto.stop(dev);
if (hdlc->proto->stop)
return hdlc->proto->stop(dev);
}
@ -144,15 +146,15 @@ int hdlc_open(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
printk(KERN_DEBUG "hdlc_open() carrier %i open %i\n",
printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
if (hdlc->proto.id == -1)
if (hdlc->proto == NULL)
return -ENOSYS; /* no protocol attached */
if (hdlc->proto.open) {
int result = hdlc->proto.open(dev);
if (hdlc->proto->open) {
int result = hdlc->proto->open(dev);
if (result)
return result;
}
@ -178,7 +180,7 @@ void hdlc_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
printk(KERN_DEBUG "hdlc_close() carrier %i open %i\n",
printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
@ -190,68 +192,34 @@ void hdlc_close(struct net_device *dev)
spin_unlock_irq(&hdlc->state_lock);
if (hdlc->proto.close)
hdlc->proto.close(dev);
if (hdlc->proto->close)
hdlc->proto->close(dev);
}
#ifndef CONFIG_HDLC_RAW
#define hdlc_raw_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_RAW_ETH
#define hdlc_raw_eth_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_PPP
#define hdlc_ppp_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_CISCO
#define hdlc_cisco_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_FR
#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
#endif
#ifndef CONFIG_HDLC_X25
#define hdlc_x25_ioctl(dev, ifr) -ENOSYS
#endif
int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
unsigned int proto;
struct hdlc_proto *proto = first_proto;
int result;
if (cmd != SIOCWANDEV)
return -EINVAL;
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:
case IF_PROTO_X25:
proto = ifr->ifr_settings.type;
break;
default:
proto = hdlc->proto.id;
if (dev_to_hdlc(dev)->proto) {
result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr);
if (result != -EINVAL)
return result;
}
switch(proto) {
case IF_PROTO_HDLC: return hdlc_raw_ioctl(dev, ifr);
case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(dev, ifr);
case IF_PROTO_PPP: return hdlc_ppp_ioctl(dev, ifr);
case IF_PROTO_CISCO: return hdlc_cisco_ioctl(dev, ifr);
case IF_PROTO_FR: return hdlc_fr_ioctl(dev, ifr);
case IF_PROTO_X25: return hdlc_x25_ioctl(dev, ifr);
default: return -EINVAL;
/* Not handled by currently attached protocol (if any) */
while (proto) {
if ((result = proto->ioctl(dev, ifr)) != -EINVAL)
return result;
proto = proto->next;
}
return -EINVAL;
}
void hdlc_setup(struct net_device *dev)
@ -267,8 +235,6 @@ void hdlc_setup(struct net_device *dev)
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
hdlc->proto.id = -1;
hdlc->proto.detach = NULL;
hdlc->carrier = 1;
hdlc->open = 0;
spin_lock_init(&hdlc->state_lock);
@ -277,7 +243,8 @@ void hdlc_setup(struct net_device *dev)
struct net_device *alloc_hdlcdev(void *priv)
{
struct net_device *dev;
dev = alloc_netdev(sizeof(hdlc_device), "hdlc%d", hdlc_setup);
dev = alloc_netdev(sizeof(struct hdlc_device_desc) +
sizeof(hdlc_device), "hdlc%d", hdlc_setup);
if (dev)
dev_to_hdlc(dev)->priv = priv;
return dev;
@ -286,13 +253,71 @@ struct net_device *alloc_hdlcdev(void *priv)
void unregister_hdlc_device(struct net_device *dev)
{
rtnl_lock();
hdlc_proto_detach(dev_to_hdlc(dev));
unregister_netdevice(dev);
detach_hdlc_protocol(dev);
rtnl_unlock();
}
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
int (*rx)(struct sk_buff *skb), size_t size)
{
detach_hdlc_protocol(dev);
if (!try_module_get(proto->module))
return -ENOSYS;
if (size)
if ((dev_to_hdlc(dev)->state = kmalloc(size,
GFP_KERNEL)) == NULL) {
printk(KERN_WARNING "Memory squeeze on"
" hdlc_proto_attach()\n");
module_put(proto->module);
return -ENOBUFS;
}
dev_to_hdlc(dev)->proto = proto;
dev_to_desc(dev)->netif_rx = rx;
return 0;
}
void detach_hdlc_protocol(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->proto) {
if (hdlc->proto->detach)
hdlc->proto->detach(dev);
module_put(hdlc->proto->module);
hdlc->proto = NULL;
}
kfree(hdlc->state);
hdlc->state = NULL;
}
void register_hdlc_protocol(struct hdlc_proto *proto)
{
proto->next = first_proto;
first_proto = proto;
}
void unregister_hdlc_protocol(struct hdlc_proto *proto)
{
struct hdlc_proto **p = &first_proto;
while (*p) {
if (*p == proto) {
*p = proto->next;
return;
}
p = &((*p)->next);
}
}
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("HDLC support module");
MODULE_LICENSE("GPL v2");
@ -303,6 +328,10 @@ EXPORT_SYMBOL(hdlc_ioctl);
EXPORT_SYMBOL(hdlc_setup);
EXPORT_SYMBOL(alloc_hdlcdev);
EXPORT_SYMBOL(unregister_hdlc_device);
EXPORT_SYMBOL(register_hdlc_protocol);
EXPORT_SYMBOL(unregister_hdlc_protocol);
EXPORT_SYMBOL(attach_hdlc_protocol);
EXPORT_SYMBOL(detach_hdlc_protocol);
static struct packet_type hdlc_packet_type = {
.type = __constant_htons(ETH_P_HDLC),

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Cisco HDLC support
*
* Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 2000 - 2006 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
@ -34,17 +34,56 @@
#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
struct hdlc_header {
u8 address;
u8 control;
u16 protocol;
}__attribute__ ((packed));
struct cisco_packet {
u32 type; /* code */
u32 par1;
u32 par2;
u16 rel; /* reliability */
u32 time;
}__attribute__ ((packed));
#define CISCO_PACKET_LEN 18
#define CISCO_BIG_PACKET_LEN 20
struct cisco_state {
cisco_proto settings;
struct timer_list timer;
unsigned long last_poll;
int up;
int request_sent;
u32 txseq; /* TX sequence number */
u32 rxseq; /* RX sequence number */
};
static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr);
static inline struct cisco_state * state(hdlc_device *hdlc)
{
return(struct cisco_state *)(hdlc->state);
}
static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
u16 type, void *daddr, void *saddr,
unsigned int len)
{
hdlc_header *data;
struct hdlc_header *data;
#ifdef DEBUG_HARD_HEADER
printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
#endif
skb_push(skb, sizeof(hdlc_header));
data = (hdlc_header*)skb->data;
skb_push(skb, sizeof(struct hdlc_header));
data = (struct hdlc_header*)skb->data;
if (type == CISCO_KEEPALIVE)
data->address = CISCO_MULTICAST;
else
@ -52,7 +91,7 @@ static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
data->control = 0;
data->protocol = htons(type);
return sizeof(hdlc_header);
return sizeof(struct hdlc_header);
}
@ -61,9 +100,10 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
u32 par1, u32 par2)
{
struct sk_buff *skb;
cisco_packet *data;
struct cisco_packet *data;
skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
skb = dev_alloc_skb(sizeof(struct hdlc_header) +
sizeof(struct cisco_packet));
if (!skb) {
printk(KERN_WARNING
"%s: Memory squeeze on cisco_keepalive_send()\n",
@ -72,7 +112,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
}
skb_reserve(skb, 4);
cisco_hard_header(skb, dev, CISCO_KEEPALIVE, NULL, NULL, 0);
data = (cisco_packet*)(skb->data + 4);
data = (struct cisco_packet*)(skb->data + 4);
data->type = htonl(type);
data->par1 = htonl(par1);
@ -81,7 +121,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
/* we will need do_div here if 1000 % HZ != 0 */
data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
skb_put(skb, sizeof(cisco_packet));
skb_put(skb, sizeof(struct cisco_packet));
skb->priority = TC_PRIO_CONTROL;
skb->dev = dev;
skb->nh.raw = skb->data;
@ -93,9 +133,9 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
{
hdlc_header *data = (hdlc_header*)skb->data;
struct hdlc_header *data = (struct hdlc_header*)skb->data;
if (skb->len < sizeof(hdlc_header))
if (skb->len < sizeof(struct hdlc_header))
return __constant_htons(ETH_P_HDLC);
if (data->address != CISCO_MULTICAST &&
@ -106,7 +146,7 @@ static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
case __constant_htons(ETH_P_IP):
case __constant_htons(ETH_P_IPX):
case __constant_htons(ETH_P_IPV6):
skb_pull(skb, sizeof(hdlc_header));
skb_pull(skb, sizeof(struct hdlc_header));
return data->protocol;
default:
return __constant_htons(ETH_P_HDLC);
@ -118,12 +158,12 @@ static int cisco_rx(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
hdlc_device *hdlc = dev_to_hdlc(dev);
hdlc_header *data = (hdlc_header*)skb->data;
cisco_packet *cisco_data;
struct hdlc_header *data = (struct hdlc_header*)skb->data;
struct cisco_packet *cisco_data;
struct in_device *in_dev;
u32 addr, mask;
if (skb->len < sizeof(hdlc_header))
if (skb->len < sizeof(struct hdlc_header))
goto rx_error;
if (data->address != CISCO_MULTICAST &&
@ -137,15 +177,17 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_SUCCESS;
case CISCO_KEEPALIVE:
if (skb->len != sizeof(hdlc_header) + CISCO_PACKET_LEN &&
skb->len != sizeof(hdlc_header) + CISCO_BIG_PACKET_LEN) {
printk(KERN_INFO "%s: Invalid length of Cisco "
"control packet (%d bytes)\n",
dev->name, skb->len);
if ((skb->len != sizeof(struct hdlc_header) +
CISCO_PACKET_LEN) &&
(skb->len != sizeof(struct hdlc_header) +
CISCO_BIG_PACKET_LEN)) {
printk(KERN_INFO "%s: Invalid length of Cisco control"
" packet (%d bytes)\n", dev->name, skb->len);
goto rx_error;
}
cisco_data = (cisco_packet*)(skb->data + sizeof(hdlc_header));
cisco_data = (struct cisco_packet*)(skb->data + sizeof
(struct hdlc_header));
switch(ntohl (cisco_data->type)) {
case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
@ -178,11 +220,11 @@ static int cisco_rx(struct sk_buff *skb)
goto rx_error;
case CISCO_KEEPALIVE_REQ:
hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
if (hdlc->state.cisco.request_sent &&
ntohl(cisco_data->par2)==hdlc->state.cisco.txseq) {
hdlc->state.cisco.last_poll = jiffies;
if (!hdlc->state.cisco.up) {
state(hdlc)->rxseq = ntohl(cisco_data->par1);
if (state(hdlc)->request_sent &&
ntohl(cisco_data->par2) == state(hdlc)->txseq) {
state(hdlc)->last_poll = jiffies;
if (!state(hdlc)->up) {
u32 sec, min, hrs, days;
sec = ntohl(cisco_data->time) / 1000;
min = sec / 60; sec -= min * 60;
@ -193,7 +235,7 @@ static int cisco_rx(struct sk_buff *skb)
dev->name, days, hrs,
min, sec);
netif_dormant_off(dev);
hdlc->state.cisco.up = 1;
state(hdlc)->up = 1;
}
}
@ -208,7 +250,7 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_DROP;
rx_error:
hdlc->stats.rx_errors++; /* Mark error */
dev_to_desc(dev)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@ -220,23 +262,22 @@ static void cisco_timer(unsigned long arg)
struct net_device *dev = (struct net_device *)arg;
hdlc_device *hdlc = dev_to_hdlc(dev);
if (hdlc->state.cisco.up &&
time_after(jiffies, hdlc->state.cisco.last_poll +
hdlc->state.cisco.settings.timeout * HZ)) {
hdlc->state.cisco.up = 0;
if (state(hdlc)->up &&
time_after(jiffies, state(hdlc)->last_poll +
state(hdlc)->settings.timeout * HZ)) {
state(hdlc)->up = 0;
printk(KERN_INFO "%s: Link down\n", dev->name);
netif_dormant_on(dev);
}
cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ,
++hdlc->state.cisco.txseq,
hdlc->state.cisco.rxseq);
hdlc->state.cisco.request_sent = 1;
hdlc->state.cisco.timer.expires = jiffies +
hdlc->state.cisco.settings.interval * HZ;
hdlc->state.cisco.timer.function = cisco_timer;
hdlc->state.cisco.timer.data = arg;
add_timer(&hdlc->state.cisco.timer);
cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq,
state(hdlc)->rxseq);
state(hdlc)->request_sent = 1;
state(hdlc)->timer.expires = jiffies +
state(hdlc)->settings.interval * HZ;
state(hdlc)->timer.function = cisco_timer;
state(hdlc)->timer.data = arg;
add_timer(&state(hdlc)->timer);
}
@ -244,15 +285,15 @@ static void cisco_timer(unsigned long arg)
static void cisco_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
hdlc->state.cisco.up = 0;
hdlc->state.cisco.request_sent = 0;
hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
state(hdlc)->up = 0;
state(hdlc)->request_sent = 0;
state(hdlc)->txseq = state(hdlc)->rxseq = 0;
init_timer(&hdlc->state.cisco.timer);
hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
hdlc->state.cisco.timer.function = cisco_timer;
hdlc->state.cisco.timer.data = (unsigned long)dev;
add_timer(&hdlc->state.cisco.timer);
init_timer(&state(hdlc)->timer);
state(hdlc)->timer.expires = jiffies + HZ; /*First poll after 1s*/
state(hdlc)->timer.function = cisco_timer;
state(hdlc)->timer.data = (unsigned long)dev;
add_timer(&state(hdlc)->timer);
}
@ -260,15 +301,24 @@ static void cisco_start(struct net_device *dev)
static void cisco_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
del_timer_sync(&hdlc->state.cisco.timer);
del_timer_sync(&state(hdlc)->timer);
netif_dormant_on(dev);
hdlc->state.cisco.up = 0;
hdlc->state.cisco.request_sent = 0;
state(hdlc)->up = 0;
state(hdlc)->request_sent = 0;
}
int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
static struct hdlc_proto proto = {
.start = cisco_start,
.stop = cisco_stop,
.type_trans = cisco_type_trans,
.ioctl = cisco_ioctl,
.module = THIS_MODULE,
};
static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
{
cisco_proto __user *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
const size_t size = sizeof(cisco_proto);
@ -278,12 +328,14 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto)
return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_CISCO;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size))
if (copy_to_user(cisco_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@ -302,19 +354,15 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
if (result)
return result;
hdlc_proto_detach(hdlc);
memcpy(&hdlc->state.cisco.settings, &new_settings, size);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
result = attach_hdlc_protocol(dev, &proto, cisco_rx,
sizeof(struct cisco_state));
if (result)
return result;
hdlc->proto.start = cisco_start;
hdlc->proto.stop = cisco_stop;
hdlc->proto.netif_rx = cisco_rx;
hdlc->proto.type_trans = cisco_type_trans;
hdlc->proto.id = IF_PROTO_CISCO;
memcpy(&state(hdlc)->settings, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = cisco_hard_header;
dev->hard_header_cache = NULL;
@ -327,3 +375,25 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Cisco HDLC protocol support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Frame Relay support
*
* Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 2006 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
@ -52,6 +52,8 @@
#undef DEBUG_PKT
#undef DEBUG_ECN
#undef DEBUG_LINK
#undef DEBUG_PROTO
#undef DEBUG_PVC
#define FR_UI 0x03
#define FR_PAD 0x00
@ -115,13 +117,53 @@ typedef struct {
}__attribute__ ((packed)) fr_hdr;
typedef struct pvc_device_struct {
struct net_device *frad;
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 {
unsigned int new: 1;
unsigned int active: 1;
unsigned int exist: 1;
unsigned int deleted: 1;
unsigned int fecn: 1;
unsigned int becn: 1;
unsigned int bandwidth; /* Cisco LMI reporting only */
}state;
}pvc_device;
struct frad_state {
fr_proto settings;
pvc_device *first_pvc;
int dce_pvc_count;
struct timer_list timer;
unsigned long last_poll;
int reliable;
int dce_changed;
int request;
int fullrep_sent;
u32 last_errors; /* last errors bit list */
u8 n391cnt;
u8 txseq; /* TX sequence number */
u8 rxseq; /* RX sequence number */
};
static int fr_ioctl(struct net_device *dev, struct ifreq *ifr);
static inline u16 q922_to_dlci(u8 *hdr)
{
return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
}
static inline void dlci_to_q922(u8 *hdr, u16 dlci)
{
hdr[0] = (dlci >> 2) & 0xFC;
@ -129,10 +171,21 @@ static inline void dlci_to_q922(u8 *hdr, u16 dlci)
}
static inline struct frad_state * state(hdlc_device *hdlc)
{
return(struct frad_state *)(hdlc->state);
}
static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
{
return dev->priv;
}
static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
{
pvc_device *pvc = hdlc->state.fr.first_pvc;
pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->dlci == dlci)
@ -146,10 +199,10 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
}
static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
static pvc_device* add_pvc(struct net_device *dev, u16 dlci)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
pvc_device *pvc, **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if ((*pvc_p)->dlci == dlci)
@ -160,12 +213,15 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
}
pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
#ifdef DEBUG_PVC
printk(KERN_DEBUG "add_pvc: allocated pvc %p, frad %p\n", pvc, dev);
#endif
if (!pvc)
return NULL;
memset(pvc, 0, sizeof(pvc_device));
pvc->dlci = dlci;
pvc->master = dev;
pvc->frad = dev;
pvc->next = *pvc_p; /* Put it in the chain */
*pvc_p = pvc;
return pvc;
@ -174,7 +230,7 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
static inline int pvc_is_used(pvc_device *pvc)
{
return pvc->main != NULL || pvc->ether != NULL;
return pvc->main || pvc->ether;
}
@ -200,11 +256,14 @@ static inline void pvc_carrier(int on, pvc_device *pvc)
static inline void delete_unused_pvcs(hdlc_device *hdlc)
{
pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
pvc_device **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if (!pvc_is_used(*pvc_p)) {
pvc_device *pvc = *pvc_p;
#ifdef DEBUG_PVC
printk(KERN_DEBUG "freeing unused pvc: %p\n", pvc);
#endif
*pvc_p = pvc->next;
kfree(pvc);
continue;
@ -295,16 +354,16 @@ static int pvc_open(struct net_device *dev)
{
pvc_device *pvc = dev_to_pvc(dev);
if ((pvc->master->flags & IFF_UP) == 0)
return -EIO; /* Master must be UP in order to activate PVC */
if ((pvc->frad->flags & IFF_UP) == 0)
return -EIO; /* Frad must be UP in order to activate PVC */
if (pvc->open_count++ == 0) {
hdlc_device *hdlc = dev_to_hdlc(pvc->master);
if (hdlc->state.fr.settings.lmi == LMI_NONE)
pvc->state.active = netif_carrier_ok(pvc->master);
hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
if (state(hdlc)->settings.lmi == LMI_NONE)
pvc->state.active = netif_carrier_ok(pvc->frad);
pvc_carrier(pvc->state.active, pvc);
hdlc->state.fr.dce_changed = 1;
state(hdlc)->dce_changed = 1;
}
return 0;
}
@ -316,12 +375,12 @@ static int pvc_close(struct net_device *dev)
pvc_device *pvc = dev_to_pvc(dev);
if (--pvc->open_count == 0) {
hdlc_device *hdlc = dev_to_hdlc(pvc->master);
if (hdlc->state.fr.settings.lmi == LMI_NONE)
hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
if (state(hdlc)->settings.lmi == LMI_NONE)
pvc->state.active = 0;
if (hdlc->state.fr.settings.dce) {
hdlc->state.fr.dce_changed = 1;
if (state(hdlc)->settings.dce) {
state(hdlc)->dce_changed = 1;
pvc->state.active = 0;
}
}
@ -348,7 +407,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
info.dlci = pvc->dlci;
memcpy(info.master, pvc->master->name, IFNAMSIZ);
memcpy(info.master, pvc->frad->name, IFNAMSIZ);
if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
&info, sizeof(info)))
return -EFAULT;
@ -361,7 +420,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static inline struct net_device_stats *pvc_get_stats(struct net_device *dev)
{
return netdev_priv(dev);
return &dev_to_desc(dev)->stats;
}
@ -393,7 +452,7 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_packets++;
if (pvc->state.fecn) /* TX Congestion counter */
stats->tx_compressed++;
skb->dev = pvc->master;
skb->dev = pvc->frad;
dev_queue_xmit(skb);
return 0;
}
@ -419,7 +478,7 @@ 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: DLCI %d [%s%s%s]%s %s\n",
pvc->master->name,
pvc->frad->name,
pvc->dlci,
pvc->main ? pvc->main->name : "",
pvc->main && pvc->ether ? " " : "",
@ -438,21 +497,20 @@ static inline u8 fr_lmi_nextseq(u8 x)
}
static void fr_lmi_send(struct net_device *dev, int fullrep)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
struct sk_buff *skb;
pvc_device *pvc = hdlc->state.fr.first_pvc;
int lmi = hdlc->state.fr.settings.lmi;
int dce = hdlc->state.fr.settings.dce;
pvc_device *pvc = state(hdlc)->first_pvc;
int lmi = state(hdlc)->settings.lmi;
int dce = state(hdlc)->settings.dce;
int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3;
u8 *data;
int i = 0;
if (dce && fullrep) {
len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
len += state(hdlc)->dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending "
"LMI full report\n", dev->name);
@ -486,8 +544,9 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE;
data[i++] = LMI_INTEG_LEN;
data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
data[i++] = hdlc->state.fr.rxseq;
data[i++] = state(hdlc)->txseq =
fr_lmi_nextseq(state(hdlc)->txseq);
data[i++] = state(hdlc)->rxseq;
if (dce && fullrep) {
while (pvc) {
@ -496,7 +555,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = stat_len;
/* LMI start/restart */
if (hdlc->state.fr.reliable && !pvc->state.exist) {
if (state(hdlc)->reliable && !pvc->state.exist) {
pvc->state.exist = pvc->state.new = 1;
fr_log_dlci_active(pvc);
}
@ -541,15 +600,15 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
static void fr_set_link_state(int reliable, struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc = hdlc->state.fr.first_pvc;
pvc_device *pvc = state(hdlc)->first_pvc;
hdlc->state.fr.reliable = reliable;
state(hdlc)->reliable = reliable;
if (reliable) {
netif_dormant_off(dev);
hdlc->state.fr.n391cnt = 0; /* Request full status */
hdlc->state.fr.dce_changed = 1;
state(hdlc)->n391cnt = 0; /* Request full status */
state(hdlc)->dce_changed = 1;
if (hdlc->state.fr.settings.lmi == LMI_NONE) {
if (state(hdlc)->settings.lmi == LMI_NONE) {
while (pvc) { /* Activate all PVCs */
pvc_carrier(1, pvc);
pvc->state.exist = pvc->state.active = 1;
@ -563,7 +622,7 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
pvc_carrier(0, pvc);
pvc->state.exist = pvc->state.active = 0;
pvc->state.new = 0;
if (!hdlc->state.fr.settings.dce)
if (!state(hdlc)->settings.dce)
pvc->state.bandwidth = 0;
pvc = pvc->next;
}
@ -571,7 +630,6 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
}
static void fr_timer(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
@ -579,62 +637,61 @@ static void fr_timer(unsigned long arg)
int i, cnt = 0, reliable;
u32 list;
if (hdlc->state.fr.settings.dce) {
reliable = hdlc->state.fr.request &&
time_before(jiffies, hdlc->state.fr.last_poll +
hdlc->state.fr.settings.t392 * HZ);
hdlc->state.fr.request = 0;
if (state(hdlc)->settings.dce) {
reliable = state(hdlc)->request &&
time_before(jiffies, state(hdlc)->last_poll +
state(hdlc)->settings.t392 * HZ);
state(hdlc)->request = 0;
} else {
hdlc->state.fr.last_errors <<= 1; /* Shift the list */
if (hdlc->state.fr.request) {
if (hdlc->state.fr.reliable)
state(hdlc)->last_errors <<= 1; /* Shift the list */
if (state(hdlc)->request) {
if (state(hdlc)->reliable)
printk(KERN_INFO "%s: No LMI status reply "
"received\n", dev->name);
hdlc->state.fr.last_errors |= 1;
state(hdlc)->last_errors |= 1;
}
list = hdlc->state.fr.last_errors;
for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
list = state(hdlc)->last_errors;
for (i = 0; i < state(hdlc)->settings.n393; i++, list >>= 1)
cnt += (list & 1); /* errors count */
reliable = (cnt < hdlc->state.fr.settings.n392);
reliable = (cnt < state(hdlc)->settings.n392);
}
if (hdlc->state.fr.reliable != reliable) {
if (state(hdlc)->reliable != reliable) {
printk(KERN_INFO "%s: Link %sreliable\n", dev->name,
reliable ? "" : "un");
fr_set_link_state(reliable, dev);
}
if (hdlc->state.fr.settings.dce)
hdlc->state.fr.timer.expires = jiffies +
hdlc->state.fr.settings.t392 * HZ;
if (state(hdlc)->settings.dce)
state(hdlc)->timer.expires = jiffies +
state(hdlc)->settings.t392 * HZ;
else {
if (hdlc->state.fr.n391cnt)
hdlc->state.fr.n391cnt--;
if (state(hdlc)->n391cnt)
state(hdlc)->n391cnt--;
fr_lmi_send(dev, hdlc->state.fr.n391cnt == 0);
fr_lmi_send(dev, state(hdlc)->n391cnt == 0);
hdlc->state.fr.last_poll = jiffies;
hdlc->state.fr.request = 1;
hdlc->state.fr.timer.expires = jiffies +
hdlc->state.fr.settings.t391 * HZ;
state(hdlc)->last_poll = jiffies;
state(hdlc)->request = 1;
state(hdlc)->timer.expires = jiffies +
state(hdlc)->settings.t391 * HZ;
}
hdlc->state.fr.timer.function = fr_timer;
hdlc->state.fr.timer.data = arg;
add_timer(&hdlc->state.fr.timer);
state(hdlc)->timer.function = fr_timer;
state(hdlc)->timer.data = arg;
add_timer(&state(hdlc)->timer);
}
static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc;
u8 rxseq, txseq;
int lmi = hdlc->state.fr.settings.lmi;
int dce = hdlc->state.fr.settings.dce;
int lmi = state(hdlc)->settings.lmi;
int dce = state(hdlc)->settings.dce;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;
if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
@ -645,8 +702,8 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
NLPID_CCITT_ANSI_LMI)) {
printk(KERN_INFO "%s: Received non-LMI frame with LMI"
" DLCI\n", dev->name);
printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
dev->name);
return 1;
}
@ -706,53 +763,53 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
i++;
hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
state(hdlc)->rxseq = skb->data[i++]; /* TX sequence from peer */
rxseq = skb->data[i++]; /* Should confirm our sequence */
txseq = hdlc->state.fr.txseq;
txseq = state(hdlc)->txseq;
if (dce)
hdlc->state.fr.last_poll = jiffies;
state(hdlc)->last_poll = jiffies;
error = 0;
if (!hdlc->state.fr.reliable)
if (!state(hdlc)->reliable)
error = 1;
if (rxseq == 0 || rxseq != txseq) {
hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
if (rxseq == 0 || rxseq != txseq) { /* Ask for full report next time */
state(hdlc)->n391cnt = 0;
error = 1;
}
if (dce) {
if (hdlc->state.fr.fullrep_sent && !error) {
if (state(hdlc)->fullrep_sent && !error) {
/* Stop sending full report - the last one has been confirmed by DTE */
hdlc->state.fr.fullrep_sent = 0;
pvc = hdlc->state.fr.first_pvc;
state(hdlc)->fullrep_sent = 0;
pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.new) {
pvc->state.new = 0;
/* Tell DTE that new PVC is now active */
hdlc->state.fr.dce_changed = 1;
state(hdlc)->dce_changed = 1;
}
pvc = pvc->next;
}
}
if (hdlc->state.fr.dce_changed) {
if (state(hdlc)->dce_changed) {
reptype = LMI_FULLREP;
hdlc->state.fr.fullrep_sent = 1;
hdlc->state.fr.dce_changed = 0;
state(hdlc)->fullrep_sent = 1;
state(hdlc)->dce_changed = 0;
}
hdlc->state.fr.request = 1; /* got request */
state(hdlc)->request = 1; /* got request */
fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
return 0;
}
/* DTE */
hdlc->state.fr.request = 0; /* got response, no request pending */
state(hdlc)->request = 0; /* got response, no request pending */
if (error)
return 0;
@ -760,7 +817,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (reptype != LMI_FULLREP)
return 0;
pvc = hdlc->state.fr.first_pvc;
pvc = state(hdlc)->first_pvc;
while (pvc) {
pvc->state.deleted = 1;
@ -827,7 +884,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
i += stat_len;
}
pvc = hdlc->state.fr.first_pvc;
pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.deleted && pvc->state.exist) {
@ -841,17 +898,16 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
/* Next full report after N391 polls */
hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
state(hdlc)->n391cnt = state(hdlc)->settings.n391;
return 0;
}
static int fr_rx(struct sk_buff *skb)
{
struct net_device *ndev = skb->dev;
hdlc_device *hdlc = dev_to_hdlc(ndev);
struct net_device *frad = skb->dev;
hdlc_device *hdlc = dev_to_hdlc(frad);
fr_hdr *fh = (fr_hdr*)skb->data;
u8 *data = skb->data;
u16 dlci;
@ -864,11 +920,11 @@ static int fr_rx(struct sk_buff *skb)
dlci = q922_to_dlci(skb->data);
if ((dlci == LMI_CCITT_ANSI_DLCI &&
(hdlc->state.fr.settings.lmi == LMI_ANSI ||
hdlc->state.fr.settings.lmi == LMI_CCITT)) ||
(state(hdlc)->settings.lmi == LMI_ANSI ||
state(hdlc)->settings.lmi == LMI_CCITT)) ||
(dlci == LMI_CISCO_DLCI &&
hdlc->state.fr.settings.lmi == LMI_CISCO)) {
if (fr_lmi_recv(ndev, skb))
state(hdlc)->settings.lmi == LMI_CISCO)) {
if (fr_lmi_recv(frad, skb))
goto rx_error;
dev_kfree_skb_any(skb);
return NET_RX_SUCCESS;
@ -878,7 +934,7 @@ static int fr_rx(struct sk_buff *skb)
if (!pvc) {
#ifdef DEBUG_PKT
printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
ndev->name, dlci);
frad->name, dlci);
#endif
dev_kfree_skb_any(skb);
return NET_RX_DROP;
@ -886,7 +942,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.fecn != fh->fecn) {
#ifdef DEBUG_ECN
printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", ndev->name,
printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", frad->name,
dlci, fh->fecn ? "N" : "FF");
#endif
pvc->state.fecn ^= 1;
@ -894,7 +950,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.becn != fh->becn) {
#ifdef DEBUG_ECN
printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", ndev->name,
printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", frad->name,
dlci, fh->becn ? "N" : "FF");
#endif
pvc->state.becn ^= 1;
@ -902,7 +958,7 @@ static int fr_rx(struct sk_buff *skb)
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
hdlc->stats.rx_dropped++;
dev_to_desc(frad)->stats.rx_dropped++;
return NET_RX_DROP;
}
@ -938,13 +994,13 @@ static int fr_rx(struct sk_buff *skb)
default:
printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
"PID=%x\n", ndev->name, oui, pid);
"PID=%x\n", frad->name, oui, pid);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
} else {
printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
"length = %i\n", ndev->name, data[3], skb->len);
"length = %i\n", frad->name, data[3], skb->len);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@ -964,7 +1020,7 @@ static int fr_rx(struct sk_buff *skb)
}
rx_error:
hdlc->stats.rx_errors++; /* Mark error */
dev_to_desc(frad)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@ -977,44 +1033,42 @@ static void fr_start(struct net_device *dev)
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_start\n");
#endif
if (hdlc->state.fr.settings.lmi != LMI_NONE) {
hdlc->state.fr.reliable = 0;
hdlc->state.fr.dce_changed = 1;
hdlc->state.fr.request = 0;
hdlc->state.fr.fullrep_sent = 0;
hdlc->state.fr.last_errors = 0xFFFFFFFF;
hdlc->state.fr.n391cnt = 0;
hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
if (state(hdlc)->settings.lmi != LMI_NONE) {
state(hdlc)->reliable = 0;
state(hdlc)->dce_changed = 1;
state(hdlc)->request = 0;
state(hdlc)->fullrep_sent = 0;
state(hdlc)->last_errors = 0xFFFFFFFF;
state(hdlc)->n391cnt = 0;
state(hdlc)->txseq = state(hdlc)->rxseq = 0;
init_timer(&hdlc->state.fr.timer);
init_timer(&state(hdlc)->timer);
/* First poll after 1 s */
hdlc->state.fr.timer.expires = jiffies + HZ;
hdlc->state.fr.timer.function = fr_timer;
hdlc->state.fr.timer.data = (unsigned long)dev;
add_timer(&hdlc->state.fr.timer);
state(hdlc)->timer.expires = jiffies + HZ;
state(hdlc)->timer.function = fr_timer;
state(hdlc)->timer.data = (unsigned long)dev;
add_timer(&state(hdlc)->timer);
} else
fr_set_link_state(1, dev);
}
static void fr_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_stop\n");
#endif
if (hdlc->state.fr.settings.lmi != LMI_NONE)
del_timer_sync(&hdlc->state.fr.timer);
if (state(hdlc)->settings.lmi != LMI_NONE)
del_timer_sync(&state(hdlc)->timer);
fr_set_link_state(0, dev);
}
static void fr_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc = hdlc->state.fr.first_pvc;
pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) { /* Shutdown all PVCs for this FRAD */
if (pvc->main)
@ -1025,7 +1079,8 @@ static void fr_close(struct net_device *dev)
}
}
static void dlci_setup(struct net_device *dev)
static void pvc_setup(struct net_device *dev)
{
dev->type = ARPHRD_DLCI;
dev->flags = IFF_POINTOPOINT;
@ -1033,9 +1088,9 @@ static void dlci_setup(struct net_device *dev)
dev->addr_len = 2;
}
static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
{
hdlc_device *hdlc = dev_to_hdlc(master);
hdlc_device *hdlc = dev_to_hdlc(frad);
pvc_device *pvc = NULL;
struct net_device *dev;
int result, used;
@ -1044,9 +1099,9 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
if (type == ARPHRD_ETHER)
prefix = "pvceth%d";
if ((pvc = add_pvc(master, dlci)) == NULL) {
if ((pvc = add_pvc(frad, dlci)) == NULL) {
printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
master->name);
frad->name);
return -ENOBUFS;
}
@ -1060,11 +1115,11 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
"pvceth%d", ether_setup);
else
dev = alloc_netdev(sizeof(struct net_device_stats),
"pvc%d", dlci_setup);
"pvc%d", pvc_setup);
if (!dev) {
printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
master->name);
frad->name);
delete_unused_pvcs(hdlc);
return -ENOBUFS;
}
@ -1102,8 +1157,8 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
dev->destructor = free_netdev;
*get_dev_p(pvc, type) = dev;
if (!used) {
hdlc->state.fr.dce_changed = 1;
hdlc->state.fr.dce_pvc_count++;
state(hdlc)->dce_changed = 1;
state(hdlc)->dce_pvc_count++;
}
return 0;
}
@ -1128,8 +1183,8 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
*get_dev_p(pvc, type) = NULL;
if (!pvc_is_used(pvc)) {
hdlc->state.fr.dce_pvc_count--;
hdlc->state.fr.dce_changed = 1;
state(hdlc)->dce_pvc_count--;
state(hdlc)->dce_changed = 1;
}
delete_unused_pvcs(hdlc);
return 0;
@ -1137,14 +1192,13 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
static void fr_destroy(hdlc_device *hdlc)
static void fr_destroy(struct net_device *frad)
{
pvc_device *pvc;
pvc = hdlc->state.fr.first_pvc;
hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
hdlc->state.fr.dce_pvc_count = 0;
hdlc->state.fr.dce_changed = 1;
hdlc_device *hdlc = dev_to_hdlc(frad);
pvc_device *pvc = state(hdlc)->first_pvc;
state(hdlc)->first_pvc = NULL; /* All PVCs destroyed */
state(hdlc)->dce_pvc_count = 0;
state(hdlc)->dce_changed = 1;
while (pvc) {
pvc_device *next = pvc->next;
@ -1161,8 +1215,17 @@ static void fr_destroy(hdlc_device *hdlc)
}
static struct hdlc_proto proto = {
.close = fr_close,
.start = fr_start,
.stop = fr_stop,
.detach = fr_destroy,
.ioctl = fr_ioctl,
.module = THIS_MODULE,
};
int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
{
fr_proto __user *fr_s = ifr->ifr_settings.ifs_ifsu.fr;
const size_t size = sizeof(fr_proto);
@ -1173,12 +1236,14 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_FR;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))
if (copy_to_user(fr_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@ -1213,20 +1278,16 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
if (hdlc->proto.id != IF_PROTO_FR) {
hdlc_proto_detach(hdlc);
hdlc->state.fr.first_pvc = NULL;
hdlc->state.fr.dce_pvc_count = 0;
if (dev_to_hdlc(dev)->proto != &proto) { /* Different proto */
result = attach_hdlc_protocol(dev, &proto, fr_rx,
sizeof(struct frad_state));
if (result)
return result;
state(hdlc)->first_pvc = NULL;
state(hdlc)->dce_pvc_count = 0;
}
memcpy(&hdlc->state.fr.settings, &new_settings, size);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
memcpy(&state(hdlc)->settings, &new_settings, size);
hdlc->proto.close = fr_close;
hdlc->proto.start = fr_start;
hdlc->proto.stop = fr_stop;
hdlc->proto.detach = fr_destroy;
hdlc->proto.netif_rx = fr_rx;
hdlc->proto.id = IF_PROTO_FR;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_FRAD;
@ -1238,6 +1299,9 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
case IF_PROTO_FR_DEL_PVC:
case IF_PROTO_FR_ADD_ETH_PVC:
case IF_PROTO_FR_DEL_ETH_PVC:
if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
return -EINVAL;
if(!capable(CAP_NET_ADMIN))
return -EPERM;
@ -1263,3 +1327,24 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Frame-Relay protocol support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
* Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 2006 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
@ -22,6 +22,21 @@
#include <linux/lapb.h>
#include <linux/rtnetlink.h>
#include <linux/hdlc.h>
#include <net/syncppp.h>
struct ppp_state {
struct ppp_device pppdev;
struct ppp_device *syncppp_ptr;
int (*old_change_mtu)(struct net_device *dev, int new_mtu);
};
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
static inline struct ppp_state* state(hdlc_device *hdlc)
{
return(struct ppp_state *)(hdlc->state);
}
static int ppp_open(struct net_device *dev)
@ -30,16 +45,16 @@ static int ppp_open(struct net_device *dev)
void *old_ioctl;
int result;
dev->priv = &hdlc->state.ppp.syncppp_ptr;
hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
hdlc->state.ppp.pppdev.dev = dev;
dev->priv = &state(hdlc)->syncppp_ptr;
state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
state(hdlc)->pppdev.dev = dev;
old_ioctl = dev->do_ioctl;
hdlc->state.ppp.old_change_mtu = dev->change_mtu;
sppp_attach(&hdlc->state.ppp.pppdev);
state(hdlc)->old_change_mtu = dev->change_mtu;
sppp_attach(&state(hdlc)->pppdev);
/* sppp_attach nukes them. We don't need syncppp's ioctl */
dev->do_ioctl = old_ioctl;
hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
state(hdlc)->pppdev.sppp.pp_flags &= ~PP_CISCO;
dev->type = ARPHRD_PPP;
result = sppp_open(dev);
if (result) {
@ -59,7 +74,7 @@ static void ppp_close(struct net_device *dev)
sppp_close(dev);
sppp_detach(dev);
dev->rebuild_header = NULL;
dev->change_mtu = hdlc->state.ppp.old_change_mtu;
dev->change_mtu = state(hdlc)->old_change_mtu;
dev->mtu = HDLC_MAX_MTU;
dev->hard_header_len = 16;
}
@ -73,13 +88,24 @@ static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
static struct hdlc_proto proto = {
.open = ppp_open,
.close = ppp_close,
.type_trans = ppp_type_trans,
.ioctl = ppp_ioctl,
.module = THIS_MODULE,
};
static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto)
return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_PPP;
return 0; /* return protocol only, no settable parameters */
@ -96,13 +122,10 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
hdlc_proto_detach(hdlc);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
hdlc->proto.open = ppp_open;
hdlc->proto.close = ppp_close;
hdlc->proto.type_trans = ppp_type_trans;
hdlc->proto.id = IF_PROTO_PPP;
result = attach_hdlc_protocol(dev, &proto, NULL,
sizeof(struct ppp_state));
if (result)
return result;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_PPP;
@ -113,3 +136,25 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("PPP protocol support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC support
*
* Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 2006 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
@ -24,6 +24,8 @@
#include <linux/hdlc.h>
static int raw_ioctl(struct net_device *dev, struct ifreq *ifr);
static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
{
return __constant_htons(ETH_P_IP);
@ -31,7 +33,14 @@ static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
static struct hdlc_proto proto = {
.type_trans = raw_type_trans,
.ioctl = raw_ioctl,
.module = THIS_MODULE,
};
static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@ -41,12 +50,14 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto)
return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_HDLC;
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))
if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@ -71,12 +82,11 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
hdlc_proto_detach(hdlc);
memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
hdlc->proto.type_trans = raw_type_trans;
hdlc->proto.id = IF_PROTO_HDLC;
result = attach_hdlc_protocol(dev, &proto, NULL,
sizeof(raw_hdlc_proto));
if (result)
return result;
memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_RAWHDLC;
@ -88,3 +98,25 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Raw HDLC protocol support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC Ethernet emulation support
*
* Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 2002-2006 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
@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/hdlc.h>
static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
static int eth_tx(struct sk_buff *skb, struct net_device *dev)
{
@ -44,7 +45,14 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev)
}
int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
static struct hdlc_proto proto = {
.type_trans = eth_type_trans,
.ioctl = raw_eth_ioctl,
.module = THIS_MODULE,
};
static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@ -56,12 +64,14 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto)
return -EINVAL;
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))
if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@ -86,12 +96,11 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
hdlc_proto_detach(hdlc);
memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
hdlc->proto.type_trans = eth_type_trans;
hdlc->proto.id = IF_PROTO_HDLC_ETH;
result = attach_hdlc_protocol(dev, &proto, NULL,
sizeof(raw_hdlc_proto));
if (result)
return result;
memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = eth_tx;
old_ch_mtu = dev->change_mtu;
old_qlen = dev->tx_queue_len;
@ -106,3 +115,25 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("Ethernet encapsulation support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* X.25 support
*
* Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
* Copyright (C) 1999 - 2006 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
@ -25,6 +25,8 @@
#include <net/x25device.h>
static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
/* These functions are callbacks called by LAPB layer */
static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
@ -162,30 +164,39 @@ static void x25_close(struct net_device *dev)
static int x25_rx(struct sk_buff *skb)
{
hdlc_device *hdlc = dev_to_hdlc(skb->dev);
struct hdlc_device_desc *desc = dev_to_desc(skb->dev);
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
hdlc->stats.rx_dropped++;
desc->stats.rx_dropped++;
return NET_RX_DROP;
}
if (lapb_data_received(skb->dev, skb) == LAPB_OK)
return NET_RX_SUCCESS;
hdlc->stats.rx_errors++;
desc->stats.rx_errors++;
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
static struct hdlc_proto proto = {
.open = x25_open,
.close = x25_close,
.ioctl = x25_ioctl,
.module = THIS_MODULE,
};
int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
if (dev_to_hdlc(dev)->proto != &proto)
return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_X25;
return 0; /* return protocol only, no settable parameters */
@ -200,14 +211,9 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
hdlc_proto_detach(hdlc);
memset(&hdlc->proto, 0, sizeof(hdlc->proto));
hdlc->proto.open = x25_open;
hdlc->proto.close = x25_close;
hdlc->proto.netif_rx = x25_rx;
hdlc->proto.type_trans = NULL;
hdlc->proto.id = IF_PROTO_X25;
if ((result = attach_hdlc_protocol(dev, &proto,
x25_rx, 0)) != 0)
return result;
dev->hard_start_xmit = x25_xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_X25;
@ -218,3 +224,25 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
static int __init mod_init(void)
{
register_hdlc_protocol(&proto);
return 0;
}
static void __exit mod_exit(void)
{
unregister_hdlc_protocol(&proto);
}
module_init(mod_init);
module_exit(mod_exit);
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
MODULE_LICENSE("GPL v2");

View File

@ -11,95 +11,46 @@
#ifndef __HDLC_H
#define __HDLC_H
#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */
#define CLOCK_DEFAULT 0 /* Default setting */
#define CLOCK_EXT 1 /* External TX and RX clock - DTE */
#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */
#define CLOCK_TXINT 3 /* Internal TX and external RX clock */
#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */
#define ENCODING_DEFAULT 0 /* Default setting */
#define ENCODING_NRZ 1
#define ENCODING_NRZI 2
#define ENCODING_FM_MARK 3
#define ENCODING_FM_SPACE 4
#define ENCODING_MANCHESTER 5
#define PARITY_DEFAULT 0 /* Default setting */
#define PARITY_NONE 1 /* No parity */
#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */
#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */
#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
#define LMI_DEFAULT 0 /* Default setting */
#define LMI_NONE 1 /* No LMI, all PVCs are static */
#define LMI_ANSI 2 /* ANSI Annex D */
#define LMI_CCITT 3 /* ITU-T Annex A */
#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */
#define HDLC_MAX_MTU 1500 /* Ethernet 1500 bytes */
#if 0
#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
#else
#define HDLC_MAX_MRU 1600 /* as required for FR network */
#endif
#ifdef __KERNEL__
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <net/syncppp.h>
#include <linux/hdlc/ioctl.h>
typedef struct { /* Used in Cisco and PPP mode */
u8 address;
u8 control;
u16 protocol;
}__attribute__ ((packed)) hdlc_header;
typedef struct {
u32 type; /* code */
u32 par1;
u32 par2;
u16 rel; /* reliability */
u32 time;
}__attribute__ ((packed)) cisco_packet;
#define CISCO_PACKET_LEN 18
#define CISCO_BIG_PACKET_LEN 20
typedef struct pvc_device_struct {
struct net_device *master;
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 {
unsigned int new: 1;
unsigned int active: 1;
unsigned int exist: 1;
unsigned int deleted: 1;
unsigned int fecn: 1;
unsigned int becn: 1;
unsigned int bandwidth; /* Cisco LMI reporting only */
}state;
}pvc_device;
typedef struct hdlc_device_struct {
/* To be initialized by hardware driver */
/* Used by all network devices here, pointed to by netdev_priv(dev) */
struct hdlc_device_desc {
int (*netif_rx)(struct sk_buff *skb);
struct net_device_stats stats;
};
/* This structure is a private property of HDLC protocols.
Hardware drivers have no interest here */
struct hdlc_proto {
int (*open)(struct net_device *dev);
void (*close)(struct net_device *dev);
void (*start)(struct net_device *dev); /* if open & DCD */
void (*stop)(struct net_device *dev); /* if open & !DCD */
void (*detach)(struct net_device *dev);
int (*ioctl)(struct net_device *dev, struct ifreq *ifr);
unsigned short (*type_trans)(struct sk_buff *skb,
struct net_device *dev);
struct module *module;
struct hdlc_proto *next; /* next protocol in the list */
};
typedef struct hdlc_device {
/* used by HDLC layer to take control over HDLC device from hw driver*/
int (*attach)(struct net_device *dev,
unsigned short encoding, unsigned short parity);
@ -107,82 +58,18 @@ typedef struct hdlc_device_struct {
/* hardware driver must handle this instead of dev->hard_start_xmit */
int (*xmit)(struct sk_buff *skb, struct net_device *dev);
/* Things below are for HDLC layer internal use only */
struct {
int (*open)(struct net_device *dev);
void (*close)(struct net_device *dev);
/* if open & DCD */
void (*start)(struct net_device *dev);
/* if open & !DCD */
void (*stop)(struct net_device *dev);
void (*detach)(struct hdlc_device_struct *hdlc);
int (*netif_rx)(struct sk_buff *skb);
unsigned short (*type_trans)(struct sk_buff *skb,
struct net_device *dev);
int id; /* IF_PROTO_HDLC/CISCO/FR/etc. */
}proto;
const struct hdlc_proto *proto;
int carrier;
int open;
spinlock_t state_lock;
union {
struct {
fr_proto settings;
pvc_device *first_pvc;
int dce_pvc_count;
struct timer_list timer;
unsigned long last_poll;
int reliable;
int dce_changed;
int request;
int fullrep_sent;
u32 last_errors; /* last errors bit list */
u8 n391cnt;
u8 txseq; /* TX sequence number */
u8 rxseq; /* RX sequence number */
}fr;
struct {
cisco_proto settings;
struct timer_list timer;
unsigned long last_poll;
int up;
int request_sent;
u32 txseq; /* TX sequence number */
u32 rxseq; /* RX sequence number */
}cisco;
struct {
raw_hdlc_proto settings;
}raw_hdlc;
struct {
struct ppp_device pppdev;
struct ppp_device *syncppp_ptr;
int (*old_change_mtu)(struct net_device *dev,
int new_mtu);
}ppp;
}state;
void *state;
void *priv;
}hdlc_device;
int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr);
int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr);
int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr);
int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr);
/* Exported from hdlc.o */
/* Exported from hdlc module */
/* Called by hardware driver when a user requests HDLC service */
int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
@ -191,17 +78,21 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
#define register_hdlc_device(dev) register_netdev(dev)
void unregister_hdlc_device(struct net_device *dev);
void register_hdlc_protocol(struct hdlc_proto *proto);
void unregister_hdlc_protocol(struct hdlc_proto *proto);
struct net_device *alloc_hdlcdev(void *priv);
static __inline__ hdlc_device* dev_to_hdlc(struct net_device *dev)
static __inline__ struct hdlc_device_desc* dev_to_desc(struct net_device *dev)
{
return netdev_priv(dev);
}
static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
static __inline__ hdlc_device* dev_to_hdlc(struct net_device *dev)
{
return (pvc_device*)dev->priv;
return netdev_priv(dev) + sizeof(struct hdlc_device_desc);
}
@ -225,18 +116,14 @@ int hdlc_open(struct net_device *dev);
/* Must be called by hardware driver when HDLC device is being closed */
void hdlc_close(struct net_device *dev);
int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
int (*rx)(struct sk_buff *skb), size_t size);
/* May be used by hardware driver to gain control over HDLC device */
static __inline__ void hdlc_proto_detach(hdlc_device *hdlc)
{
if (hdlc->proto.detach)
hdlc->proto.detach(hdlc);
hdlc->proto.detach = NULL;
}
void detach_hdlc_protocol(struct net_device *dev);
static __inline__ struct net_device_stats *hdlc_stats(struct net_device *dev)
{
return &dev_to_hdlc(dev)->stats;
return &dev_to_desc(dev)->stats;
}
@ -248,8 +135,8 @@ static __inline__ __be16 hdlc_type_trans(struct sk_buff *skb,
skb->mac.raw = skb->data;
skb->dev = dev;
if (hdlc->proto.type_trans)
return hdlc->proto.type_trans(skb, dev);
if (hdlc->proto->type_trans)
return hdlc->proto->type_trans(skb, dev);
else
return htons(ETH_P_HDLC);
}

View File

@ -1,6 +1,39 @@
#ifndef __HDLC_IOCTL_H__
#define __HDLC_IOCTL_H__
#define GENERIC_HDLC_VERSION 4 /* For synchronization with sethdlc utility */
#define CLOCK_DEFAULT 0 /* Default setting */
#define CLOCK_EXT 1 /* External TX and RX clock - DTE */
#define CLOCK_INT 2 /* Internal TX and RX clock - DCE */
#define CLOCK_TXINT 3 /* Internal TX and external RX clock */
#define CLOCK_TXFROMRX 4 /* TX clock derived from external RX clock */
#define ENCODING_DEFAULT 0 /* Default setting */
#define ENCODING_NRZ 1
#define ENCODING_NRZI 2
#define ENCODING_FM_MARK 3
#define ENCODING_FM_SPACE 4
#define ENCODING_MANCHESTER 5
#define PARITY_DEFAULT 0 /* Default setting */
#define PARITY_NONE 1 /* No parity */
#define PARITY_CRC16_PR0 2 /* CRC16, initial value 0x0000 */
#define PARITY_CRC16_PR1 3 /* CRC16, initial value 0xFFFF */
#define PARITY_CRC16_PR0_CCITT 4 /* CRC16, initial 0x0000, ITU-T version */
#define PARITY_CRC16_PR1_CCITT 5 /* CRC16, initial 0xFFFF, ITU-T version */
#define PARITY_CRC32_PR0_CCITT 6 /* CRC32, initial value 0x00000000 */
#define PARITY_CRC32_PR1_CCITT 7 /* CRC32, initial value 0xFFFFFFFF */
#define LMI_DEFAULT 0 /* Default setting */
#define LMI_NONE 1 /* No LMI, all PVCs are static */
#define LMI_ANSI 2 /* ANSI Annex D */
#define LMI_CCITT 3 /* ITU-T Annex A */
#define LMI_CISCO 4 /* The "original" LMI, aka Gang of Four */
typedef struct {
unsigned int clock_rate; /* bits per second */
unsigned int clock_type; /* internal, external, TX-internal etc. */