fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / net / wan / hdlc_cisco.c
index 1fd0466..b0bc5dd 100644 (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
 #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;
+       __be32 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;
@@ -192,10 +234,8 @@ static int cisco_rx(struct sk_buff *skb)
                                               "uptime %ud%uh%um%us)\n",
                                               dev->name, days, hrs,
                                               min, sec);
-#if 0
-                                       netif_carrier_on(dev);
-#endif
-                                       hdlc->state.cisco.up = 1;
+                                       netif_dormant_off(dev);
+                                       state(hdlc)->up = 1;
                                }
                        }
 
@@ -210,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;
 }
@@ -222,25 +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);
-#if 0
-               netif_carrier_off(dev);
-#endif
+               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);
 }
 
 
@@ -248,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);
 }
 
 
@@ -264,18 +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);
-#if 0
-       if (netif_carrier_ok(dev))
-               netif_carrier_off(dev);
-#endif
-       hdlc->state.cisco.up = 0;
-       hdlc->state.cisco.request_sent = 0;
+       del_timer_sync(&state(hdlc)->timer);
+       netif_dormant_on(dev);
+       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);
@@ -285,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;
 
@@ -309,27 +354,46 @@ 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;
                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");