Merge branch 'x86-platform-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / staging / batman-adv / soft-interface.c
index 2ea97de..3904db9 100644 (file)
 #include "hard-interface.h"
 #include "routing.h"
 #include "send.h"
+#include "bat_debugfs.h"
 #include "translation-table.h"
 #include "types.h"
 #include "hash.h"
+#include "send.h"
+#include "bat_sysfs.h"
 #include <linux/slab.h>
 #include <linux/ethtool.h>
 #include <linux/etherdevice.h>
+#include "unicast.h"
 
-static uint32_t bcast_seqno = 1; /* give own bcast messages seq numbers to avoid
-                                 * broadcast storms */
-static int32_t skb_packets;
-static int32_t skb_bad_packets;
 
-unsigned char main_if_addr[ETH_ALEN];
 static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd);
 static void bat_get_drvinfo(struct net_device *dev,
                            struct ethtool_drvinfo *info);
@@ -56,23 +55,21 @@ static const struct ethtool_ops bat_ethtool_ops = {
        .set_rx_csum = bat_set_rx_csum
 };
 
-void set_main_if_addr(uint8_t *addr)
-{
-       memcpy(main_if_addr, addr, ETH_ALEN);
-}
-
-int my_skb_push(struct sk_buff *skb, unsigned int len)
+int my_skb_head_push(struct sk_buff *skb, unsigned int len)
 {
-       int result = 0;
-
-       skb_packets++;
-       if (skb_headroom(skb) < len) {
-               skb_bad_packets++;
-               result = pskb_expand_head(skb, len, 0, GFP_ATOMIC);
-
-               if (result < 0)
-                       return result;
-       }
+       int result;
+
+       /**
+        * TODO: We must check if we can release all references to non-payload
+        * data using skb_header_release in our skbs to allow skb_cow_header to
+        * work optimally. This means that those skbs are not allowed to read
+        * or write any data which is before the current position of skb->data
+        * after that call and thus allow other skbs with the same data buffer
+        * to write freely in that area.
+        */
+       result = skb_cow_head(skb, len);
+       if (result < 0)
+               return result;
 
        skb_push(skb, len);
        return 0;
@@ -92,21 +89,23 @@ static int interface_release(struct net_device *dev)
 
 static struct net_device_stats *interface_stats(struct net_device *dev)
 {
-       struct bat_priv *priv = netdev_priv(dev);
-       return &priv->stats;
+       struct bat_priv *bat_priv = netdev_priv(dev);
+       return &bat_priv->stats;
 }
 
 static int interface_set_mac_addr(struct net_device *dev, void *p)
 {
+       struct bat_priv *bat_priv = netdev_priv(dev);
        struct sockaddr *addr = p;
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
        /* only modify hna-table if it has been initialised before */
-       if (atomic_read(&module_state) == MODULE_ACTIVE) {
-               hna_local_remove(dev->dev_addr, "mac address changed");
-               hna_local_add(addr->sa_data);
+       if (atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) {
+               hna_local_remove(bat_priv, dev->dev_addr,
+                                "mac address changed");
+               hna_local_add(dev, addr->sa_data);
        }
 
        memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
@@ -117,7 +116,7 @@ static int interface_set_mac_addr(struct net_device *dev, void *p)
 static int interface_change_mtu(struct net_device *dev, int new_mtu)
 {
        /* check ranges */
-       if ((new_mtu < 68) || (new_mtu > hardif_min_mtu()))
+       if ((new_mtu < 68) || (new_mtu > hardif_min_mtu(dev)))
                return -EINVAL;
 
        dev->mtu = new_mtu;
@@ -125,34 +124,27 @@ static int interface_change_mtu(struct net_device *dev, int new_mtu)
        return 0;
 }
 
-int interface_tx(struct sk_buff *skb, struct net_device *dev)
+int interface_tx(struct sk_buff *skb, struct net_device *soft_iface)
 {
-       struct unicast_packet *unicast_packet;
-       struct bcast_packet *bcast_packet;
-       struct orig_node *orig_node;
-       struct neigh_node *router;
        struct ethhdr *ethhdr = (struct ethhdr *)skb->data;
-       struct bat_priv *priv = netdev_priv(dev);
-       struct batman_if *batman_if;
-       struct bat_priv *bat_priv;
-       uint8_t dstaddr[6];
-       int data_len = skb->len;
-       unsigned long flags;
+       struct bat_priv *bat_priv = netdev_priv(soft_iface);
+       struct bcast_packet *bcast_packet;
+       int data_len = skb->len, ret;
 
-       if (atomic_read(&module_state) != MODULE_ACTIVE)
+       if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE)
                goto dropped;
 
-       /* FIXME: each batman_if will be attached to a softif */
-       bat_priv = netdev_priv(soft_device);
+       soft_iface->trans_start = jiffies;
 
-       dev->trans_start = jiffies;
        /* TODO: check this for locks */
-       hna_local_add(ethhdr->h_source);
+       hna_local_add(soft_iface, ethhdr->h_source);
 
        /* ethernet packet should be broadcasted */
        if (is_bcast(ethhdr->h_dest) || is_mcast(ethhdr->h_dest)) {
+               if (!bat_priv->primary_if)
+                       goto dropped;
 
-               if (my_skb_push(skb, sizeof(struct bcast_packet)) < 0)
+               if (my_skb_head_push(skb, sizeof(struct bcast_packet)) < 0)
                        goto dropped;
 
                bcast_packet = (struct bcast_packet *)skb->data;
@@ -164,14 +156,14 @@ int interface_tx(struct sk_buff *skb, struct net_device *dev)
 
                /* hw address of first interface is the orig mac because only
                 * this mac is known throughout the mesh */
-               memcpy(bcast_packet->orig, main_if_addr, ETH_ALEN);
+               memcpy(bcast_packet->orig,
+                      bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN);
 
                /* set broadcast sequence number */
-               bcast_packet->seqno = htonl(bcast_seqno);
+               bcast_packet->seqno =
+                       htonl(atomic_inc_return(&bat_priv->bcast_seqno));
 
-               /* broadcast packet. on success, increase seqno. */
-               if (add_bcast_packet_to_list(skb) == NETDEV_TX_OK)
-                       bcast_seqno++;
+               add_bcast_packet_to_list(bat_priv, skb);
 
                /* a copy is stored in the bcast list, therefore removing
                 * the original skb. */
@@ -179,64 +171,27 @@ int interface_tx(struct sk_buff *skb, struct net_device *dev)
 
        /* unicast packet */
        } else {
-               spin_lock_irqsave(&orig_hash_lock, flags);
-               /* get routing information */
-               orig_node = ((struct orig_node *)hash_find(orig_hash,
-                                                          ethhdr->h_dest));
-
-               /* check for hna host */
-               if (!orig_node)
-                       orig_node = transtable_search(ethhdr->h_dest);
-
-               router = find_router(orig_node, NULL);
-
-               if (!router)
-                       goto unlock;
-
-               /* don't lock while sending the packets ... we therefore
-                * copy the required data before sending */
-
-               batman_if = router->if_incoming;
-               memcpy(dstaddr, router->addr, ETH_ALEN);
-
-               spin_unlock_irqrestore(&orig_hash_lock, flags);
-
-               if (batman_if->if_status != IF_ACTIVE)
-                       goto dropped;
-
-               if (my_skb_push(skb, sizeof(struct unicast_packet)) < 0)
-                       goto dropped;
-
-               unicast_packet = (struct unicast_packet *)skb->data;
-
-               unicast_packet->version = COMPAT_VERSION;
-               /* batman packet type: unicast */
-               unicast_packet->packet_type = BAT_UNICAST;
-               /* set unicast ttl */
-               unicast_packet->ttl = TTL;
-               /* copy the destination for faster routing */
-               memcpy(unicast_packet->dest, orig_node->orig, ETH_ALEN);
-
-               send_skb_packet(skb, batman_if, dstaddr);
+               ret = unicast_send_skb(skb, bat_priv);
+               if (ret != 0)
+                       goto dropped_freed;
        }
 
-       priv->stats.tx_packets++;
-       priv->stats.tx_bytes += data_len;
+       bat_priv->stats.tx_packets++;
+       bat_priv->stats.tx_bytes += data_len;
        goto end;
 
-unlock:
-       spin_unlock_irqrestore(&orig_hash_lock, flags);
 dropped:
-       priv->stats.tx_dropped++;
        kfree_skb(skb);
+dropped_freed:
+       bat_priv->stats.tx_dropped++;
 end:
        return NETDEV_TX_OK;
 }
 
-void interface_rx(struct sk_buff *skb, int hdr_size)
+void interface_rx(struct net_device *soft_iface,
+                 struct sk_buff *skb, int hdr_size)
 {
-       struct net_device *dev = soft_device;
-       struct bat_priv *priv = netdev_priv(dev);
+       struct bat_priv *priv = netdev_priv(soft_iface);
 
        /* check if enough space is available for pulling, and pull */
        if (!pskb_may_pull(skb, hdr_size)) {
@@ -246,8 +201,8 @@ void interface_rx(struct sk_buff *skb, int hdr_size)
        skb_pull_rcsum(skb, hdr_size);
 /*     skb_set_mac_header(skb, -sizeof(struct ethhdr));*/
 
-       skb->dev = dev;
-       skb->protocol = eth_type_trans(skb, dev);
+       /* skb->dev & skb->pkt_type are set here */
+       skb->protocol = eth_type_trans(skb, soft_iface);
 
        /* should not be neccesary anymore as we use skb_pull_rcsum()
         * TODO: please verify this and remove this TODO
@@ -255,13 +210,10 @@ void interface_rx(struct sk_buff *skb, int hdr_size)
 
 /*     skb->ip_summed = CHECKSUM_UNNECESSARY;*/
 
-       /* TODO: set skb->pkt_type to PACKET_BROADCAST, PACKET_MULTICAST,
-        * PACKET_OTHERHOST or PACKET_HOST */
-
        priv->stats.rx_packets++;
-       priv->stats.rx_bytes += skb->len;
+       priv->stats.rx_bytes += skb->len + sizeof(struct ethhdr);
 
-       dev->last_rx = jiffies;
+       soft_iface->last_rx = jiffies;
 
        netif_rx(skb);
 }
@@ -278,7 +230,7 @@ static const struct net_device_ops bat_netdev_ops = {
 };
 #endif
 
-void interface_setup(struct net_device *dev)
+static void interface_setup(struct net_device *dev)
 {
        struct bat_priv *priv = netdev_priv(dev);
        char dev_addr[ETH_ALEN];
@@ -297,7 +249,11 @@ void interface_setup(struct net_device *dev)
 #endif
        dev->destructor = free_netdev;
 
-       dev->mtu = hardif_min_mtu();
+       /**
+        * can't call min_mtu, because the needed variables
+        * have not been initialized yet
+        */
+       dev->mtu = ETH_DATA_LEN;
        dev->hard_header_len = BAT_HEADER_LEN; /* reserve more space in the
                                                * skbuff for our header */
 
@@ -310,6 +266,81 @@ void interface_setup(struct net_device *dev)
        memset(priv, 0, sizeof(struct bat_priv));
 }
 
+struct net_device *softif_create(char *name)
+{
+       struct net_device *soft_iface;
+       struct bat_priv *bat_priv;
+       int ret;
+
+       soft_iface = alloc_netdev(sizeof(struct bat_priv) , name,
+                                  interface_setup);
+
+       if (!soft_iface) {
+               pr_err("Unable to allocate the batman interface: %s\n", name);
+               goto out;
+       }
+
+       ret = register_netdev(soft_iface);
+       if (ret < 0) {
+               pr_err("Unable to register the batman interface '%s': %i\n",
+                      name, ret);
+               goto free_soft_iface;
+       }
+
+       bat_priv = netdev_priv(soft_iface);
+
+       atomic_set(&bat_priv->aggregation_enabled, 1);
+       atomic_set(&bat_priv->bonding_enabled, 0);
+       atomic_set(&bat_priv->vis_mode, VIS_TYPE_CLIENT_UPDATE);
+       atomic_set(&bat_priv->orig_interval, 1000);
+       atomic_set(&bat_priv->log_level, 0);
+       atomic_set(&bat_priv->frag_enabled, 1);
+       atomic_set(&bat_priv->bcast_queue_left, BCAST_QUEUE_LEN);
+       atomic_set(&bat_priv->batman_queue_left, BATMAN_QUEUE_LEN);
+
+       atomic_set(&bat_priv->mesh_state, MESH_INACTIVE);
+       atomic_set(&bat_priv->bcast_seqno, 1);
+       atomic_set(&bat_priv->hna_local_changed, 0);
+
+       bat_priv->primary_if = NULL;
+       bat_priv->num_ifaces = 0;
+
+       ret = sysfs_add_meshif(soft_iface);
+       if (ret < 0)
+               goto unreg_soft_iface;
+
+       ret = debugfs_add_meshif(soft_iface);
+       if (ret < 0)
+               goto unreg_sysfs;
+
+       ret = mesh_init(soft_iface);
+       if (ret < 0)
+               goto unreg_debugfs;
+
+       return soft_iface;
+
+unreg_debugfs:
+       debugfs_del_meshif(soft_iface);
+unreg_sysfs:
+       sysfs_del_meshif(soft_iface);
+unreg_soft_iface:
+       unregister_netdev(soft_iface);
+       return NULL;
+
+free_soft_iface:
+       free_netdev(soft_iface);
+out:
+       return NULL;
+}
+
+void softif_destroy(struct net_device *soft_iface)
+{
+       debugfs_del_meshif(soft_iface);
+       sysfs_del_meshif(soft_iface);
+       mesh_free(soft_iface);
+       unregister_netdevice(soft_iface);
+}
+
 /* ethtool */
 static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
 {