diff options
author | Krzysztof Halasa <khc@pm.waw.pl> | 2006-09-26 23:23:45 +0200 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-09-26 17:40:24 -0400 |
commit | eb2a2fd91f7c8a53b15063d6f08cf22b9a56cbfb (patch) | |
tree | 1d910a9460b76fd85ed02e8b9131270e4977f6f7 /drivers/net | |
parent | c226951b93f7cd7c3a10b17384535b617bd43fd0 (diff) |
[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>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wan/Kconfig | 12 | ||||
-rw-r--r-- | drivers/net/wan/Makefile | 19 | ||||
-rw-r--r-- | drivers/net/wan/hdlc.c (renamed from drivers/net/wan/hdlc_generic.c) | 169 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_cisco.c | 198 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_fr.c | 389 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_ppp.c | 77 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_raw.c | 50 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_raw_eth.c | 49 | ||||
-rw-r--r-- | drivers/net/wan/hdlc_x25.c | 54 |
9 files changed, 666 insertions, 351 deletions
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 54b8e492ef9..58b7efbb075 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -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. diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile index 316ca6869d5..83ec2c87ba3 100644 --- a/drivers/net/wan/Makefile +++ b/drivers/net/wan/Makefile @@ -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 diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc.c index 04ca1f7b642..db354e0edbe 100644 --- a/drivers/net/wan/hdlc_generic.c +++ b/drivers/net/wan/hdlc.c @@ -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), diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index f289daba0c7..7ec2b2f9b7e 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -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; - - 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); + state(hdlc)->up = 0; + state(hdlc)->request_sent = 0; + state(hdlc)->txseq = state(hdlc)->rxseq = 0; + + 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"); diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index 7bb737bbdeb..b45ab680d2d 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -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); |