Merge branch 'master' into sh/clkfwk
[safe/jmp/linux-2.6] / drivers / net / wan / hdlc_cisco.c
index f289dab..cf5fd17 100644 (file)
@@ -2,26 +2,25 @@
  * 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
  * as published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
 #include <linux/errno.h>
+#include <linux/hdlc.h>
 #include <linux/if_arp.h>
+#include <linux/inetdevice.h>
 #include <linux/init.h>
-#include <linux/skbuff.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/pkt_sched.h>
-#include <linux/inetdevice.h>
-#include <linux/lapb.h>
+#include <linux/poll.h>
 #include <linux/rtnetlink.h>
-#include <linux/hdlc.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
 
 #undef DEBUG_HARD_HEADER
 
 #define CISCO_KEEPALIVE_REQ    2       /* Cisco keepalive request */
 
 
+struct hdlc_header {
+       u8 address;
+       u8 control;
+       __be16 protocol;
+}__attribute__ ((packed));
+
+
+struct cisco_packet {
+       __be32 type;            /* code */
+       __be32 par1;
+       __be32 par2;
+       __be16 rel;             /* reliability */
+       __be32 time;
+}__attribute__ ((packed));
+#define        CISCO_PACKET_LEN        18
+#define        CISCO_BIG_PACKET_LEN    20
+
+
+struct cisco_state {
+       cisco_proto settings;
+
+       struct timer_list timer;
+       spinlock_t lock;
+       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,
+                            u16 type, const void *daddr, const 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,18 +91,19 @@ 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);
 }
 
 
 
 static void cisco_keepalive_send(struct net_device *dev, u32 type,
-                                u32 par1, u32 par2)
+                                __be32 par1, __be32 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,19 +112,19 @@ 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);
-       data->par2 = htonl(par2);
-       data->rel = 0xFFFF;
+       data->par1 = par1;
+       data->par2 = par2;
+       data->rel = cpu_to_be16(0xFFFF);
        /* 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;
+       skb_reset_network_header(skb);
 
        dev_queue_xmit(skb);
 }
@@ -93,23 +133,23 @@ 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))
-               return __constant_htons(ETH_P_HDLC);
+       if (skb->len < sizeof(struct hdlc_header))
+               return cpu_to_be16(ETH_P_HDLC);
 
        if (data->address != CISCO_MULTICAST &&
            data->address != CISCO_UNICAST)
-               return __constant_htons(ETH_P_HDLC);
+               return cpu_to_be16(ETH_P_HDLC);
 
        switch(data->protocol) {
-       case __constant_htons(ETH_P_IP):
-       case __constant_htons(ETH_P_IPX):
-       case __constant_htons(ETH_P_IPV6):
-               skb_pull(skb, sizeof(hdlc_header));
+       case cpu_to_be16(ETH_P_IP):
+       case cpu_to_be16(ETH_P_IPX):
+       case cpu_to_be16(ETH_P_IPV6):
+               skb_pull(skb, sizeof(struct hdlc_header));
                return data->protocol;
        default:
-               return __constant_htons(ETH_P_HDLC);
+               return cpu_to_be16(ETH_P_HDLC);
        }
 }
 
@@ -118,40 +158,43 @@ 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 cisco_state *st = state(hdlc);
+       struct hdlc_header *data = (struct hdlc_header*)skb->data;
+       struct cisco_packet *cisco_data;
        struct in_device *in_dev;
-       u32 addr, mask;
+       __be32 addr, mask;
 
-       if (skb->len < sizeof(hdlc_header))
+       if (skb->len < sizeof(struct hdlc_header))
                goto rx_error;
 
        if (data->address != CISCO_MULTICAST &&
            data->address != CISCO_UNICAST)
                goto rx_error;
 
-       switch(ntohs(data->protocol)) {
+       switch (ntohs(data->protocol)) {
        case CISCO_SYS_INFO:
                /* Packet is not needed, drop it. */
                dev_kfree_skb_any(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 :-) */
                        in_dev = dev->ip_ptr;
                        addr = 0;
-                       mask = ~0; /* is the mask correct? */
+                       mask = ~cpu_to_be32(0); /* is the mask correct? */
 
                        if (in_dev != NULL) {
                                struct in_ifaddr **ifap = &in_dev->ifa_list;
@@ -178,11 +221,12 @@ 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) {
+                       spin_lock(&st->lock);
+                       st->rxseq = ntohl(cisco_data->par1);
+                       if (st->request_sent &&
+                           ntohl(cisco_data->par2) == st->txseq) {
+                               st->last_poll = jiffies;
+                               if (!st->up) {
                                        u32 sec, min, hrs, days;
                                        sec = ntohl(cisco_data->time) / 1000;
                                        min = sec / 60; sec -= min * 60;
@@ -190,12 +234,12 @@ static int cisco_rx(struct sk_buff *skb)
                                        days = hrs / 24; hrs -= days * 24;
                                        printk(KERN_INFO "%s: Link up (peer "
                                               "uptime %ud%uh%um%us)\n",
-                                              dev->name, days, hrs,
-                                              min, sec);
+                                              dev->name, days, hrs, min, sec);
                                        netif_dormant_off(dev);
-                                       hdlc->state.cisco.up = 1;
+                                       st->up = 1;
                                }
                        }
+                       spin_unlock(&st->lock);
 
                        dev_kfree_skb_any(skb);
                        return NET_RX_SUCCESS;
@@ -203,12 +247,12 @@ static int cisco_rx(struct sk_buff *skb)
        } /* switch(protocol) */
 
        printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name,
-              data->protocol);
+              ntohs(data->protocol));
        dev_kfree_skb_any(skb);
        return NET_RX_DROP;
 
- rx_error:
-       hdlc->stats.rx_errors++; /* Mark error */
+rx_error:
+       dev->stats.rx_errors++; /* Mark error */
        dev_kfree_skb_any(skb);
        return NET_RX_DROP;
 }
@@ -219,24 +263,25 @@ static void cisco_timer(unsigned long arg)
 {
        struct net_device *dev = (struct net_device *)arg;
        hdlc_device *hdlc = dev_to_hdlc(dev);
+       struct cisco_state *st = state(hdlc);
 
-       if (hdlc->state.cisco.up &&
-           time_after(jiffies, hdlc->state.cisco.last_poll +
-                      hdlc->state.cisco.settings.timeout * HZ)) {
-               hdlc->state.cisco.up = 0;
+       spin_lock(&st->lock);
+       if (st->up &&
+           time_after(jiffies, st->last_poll + st->settings.timeout * HZ)) {
+               st->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, htonl(++st->txseq),
+                            htonl(st->rxseq));
+       st->request_sent = 1;
+       spin_unlock(&st->lock);
+
+       st->timer.expires = jiffies + st->settings.interval * HZ;
+       st->timer.function = cisco_timer;
+       st->timer.data = arg;
+       add_timer(&st->timer);
 }
 
 
@@ -244,15 +289,20 @@ 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);
+       struct cisco_state *st = state(hdlc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&st->lock, flags);
+       st->up = 0;
+       st->request_sent = 0;
+       st->txseq = st->rxseq = 0;
+       spin_unlock_irqrestore(&st->lock, flags);
+
+       init_timer(&st->timer);
+       st->timer.expires = jiffies + HZ; /* First poll after 1 s */
+       st->timer.function = cisco_timer;
+       st->timer.data = (unsigned long)dev;
+       add_timer(&st->timer);
 }
 
 
@@ -260,15 +310,33 @@ 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);
+       struct cisco_state *st = state(hdlc);
+       unsigned long flags;
+
+       del_timer_sync(&st->timer);
+
+       spin_lock_irqsave(&st->lock, flags);
        netif_dormant_on(dev);
-       hdlc->state.cisco.up = 0;
-       hdlc->state.cisco.request_sent = 0;
+       st->up = 0;
+       st->request_sent = 0;
+       spin_unlock_irqrestore(&st->lock, flags);
 }
 
 
+static struct hdlc_proto proto = {
+       .start          = cisco_start,
+       .stop           = cisco_stop,
+       .type_trans     = cisco_type_trans,
+       .ioctl          = cisco_ioctl,
+       .netif_rx       = cisco_rx,
+       .module         = THIS_MODULE,
+};
+
+static const struct header_ops cisco_header_ops = {
+       .create = cisco_hard_header,
+};
 
-int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
+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,20 +346,22 @@ 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;
 
        case IF_PROTO_CISCO:
-               if(!capable(CAP_NET_ADMIN))
+               if (!capable(CAP_NET_ADMIN))
                        return -EPERM;
 
-               if(dev->flags & IFF_UP)
+               if (dev->flags & IFF_UP)
                        return -EBUSY;
 
                if (copy_from_user(&new_settings, cisco_s, size))
@@ -301,29 +371,44 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
                    new_settings.timeout < 2)
                        return -EINVAL;
 
-               result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               result = hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+               if (result)
+                       return result;
 
+               result = attach_hdlc_protocol(dev, &proto,
+                                             sizeof(struct cisco_state));
                if (result)
                        return result;
 
-               hdlc_proto_detach(hdlc);
-               memcpy(&hdlc->state.cisco.settings, &new_settings, size);
-               memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
-               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;
-               dev->hard_start_xmit = hdlc->xmit;
-               dev->hard_header = cisco_hard_header;
-               dev->hard_header_cache = NULL;
+               memcpy(&state(hdlc)->settings, &new_settings, size);
+               spin_lock_init(&state(hdlc)->lock);
+               dev->header_ops = &cisco_header_ops;
                dev->type = ARPHRD_CISCO;
-               dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-               dev->addr_len = 0;
                netif_dormant_on(dev);
                return 0;
        }
 
        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");