#include "config.h" #include #include #include #include #include "iface.h" /* */ struct sc_netdev_priv { struct net_device* dev; struct sc_netdev_priv* next; }; /* */ #define CAPWAP_IFACE_COUNT 8 #define CAPWAP_IFACE_HASH(x) ((x) % CAPWAP_IFACE_COUNT) static uint32_t sc_iface_count; static DEFINE_SPINLOCK(sc_iface_lock); static struct sc_netdev_priv* sc_iface_hash[CAPWAP_IFACE_COUNT]; /* */ static void sc_iface_netdev_uninit(struct net_device* dev) { unsigned long flags; struct sc_netdev_priv* search; int hash = CAPWAP_IFACE_HASH(dev->ifindex); struct sc_netdev_priv* priv = (struct sc_netdev_priv*)netdev_priv(dev); /* Remove interface from hash */ spin_lock_irqsave(&sc_iface_lock, flags); search = sc_iface_hash[hash]; if (search) { if (priv == search) { netif_tx_lock_bh(dev); netif_carrier_off(dev); netif_tx_unlock_bh(dev); sc_iface_hash[hash] = priv->next; dev_put(dev); sc_iface_count--; } else { while (search->next && (search->next != priv)) { search = search->next; } if (search->next) { netif_tx_lock_bh(dev); netif_carrier_off(dev); netif_tx_unlock_bh(dev); search->next = priv->next; dev_put(dev); sc_iface_count--; } } } spin_unlock_irqrestore(&sc_iface_lock, flags); /* Close stations with link to this device */ /* TODO */ } /* */ static int sc_iface_netdev_open(struct net_device* dev) { netif_start_queue(dev); return 0; } /* */ static int sc_iface_netdev_stop(struct net_device* dev) { netif_stop_queue(dev); return 0; } /* */ static int sc_iface_netdev_tx(struct sk_buff* skb, struct net_device* dev) { /* TODO */ return 0; } /* */ static int sc_iface_netdev_change_mtu(struct net_device* dev, int new_mtu) { /* TODO */ return 0; } /* */ static void sc_iface_netdev_setup(struct net_device* dev) { struct sc_netdev_priv* priv = (struct sc_netdev_priv*)netdev_priv(dev); /* */ memset(priv, 0, sizeof(struct sc_netdev_priv)); priv->dev = dev; } /* */ static const struct net_device_ops capwap_netdev_ops = { .ndo_uninit = sc_iface_netdev_uninit, .ndo_open = sc_iface_netdev_open, .ndo_stop = sc_iface_netdev_stop, .ndo_start_xmit = sc_iface_netdev_tx, .ndo_change_mtu = sc_iface_netdev_change_mtu, }; /* */ int sc_iface_create(const char* ifname, uint16_t mtu) { int err; int hash; unsigned long flags; struct net_device* dev; struct sc_netdev_priv* priv; /* Create interface */ dev = alloc_netdev(sizeof(struct sc_netdev_priv), ifname, sc_iface_netdev_setup); if (!dev) { return -ENOMEM; } /* */ priv = (struct sc_netdev_priv*)netdev_priv(dev); dev->netdev_ops = &capwap_netdev_ops; ether_setup(dev); eth_hw_addr_random(dev); dev->mtu = mtu; dev->hw_features = NETIF_F_HW_CSUM; dev->features = dev->hw_features; /* */ err = register_netdev(dev); if (err) { free_netdev(dev); return err; } /* */ hash = CAPWAP_IFACE_HASH(dev->ifindex); spin_lock_irqsave(&sc_iface_lock, flags); sc_iface_count++; priv->next = sc_iface_hash[hash]; sc_iface_hash[hash] = priv; dev_hold(dev); spin_unlock_irqrestore(&sc_iface_lock, flags); /* Enable carrier */ netif_tx_lock_bh(dev); netif_carrier_on(dev); netif_tx_unlock_bh(dev); return dev->ifindex; } /* */ int sc_iface_delete(uint32_t ifindex) { unsigned long flags; struct sc_netdev_priv* priv; /* */ spin_lock_irqsave(&sc_iface_lock, flags); priv = sc_iface_hash[CAPWAP_IFACE_HASH(ifindex)]; while (priv) { if (priv->dev->ifindex == ifindex) { break; } priv = priv->next; } spin_unlock_irqrestore(&sc_iface_lock, flags); /* */ if (!priv) { return -ENOENT; } /* */ unregister_netdev(priv->dev); free_netdev(priv->dev); return 0; } /* */ void sc_iface_closeall(void) { int i; unsigned long flags; while (sc_iface_count) { struct net_device* dev = NULL; spin_lock_irqsave(&sc_iface_lock, flags); for (i = 0; i < CAPWAP_IFACE_COUNT; i++) { if (sc_iface_hash[i]) { dev = sc_iface_hash[i]->dev; break; } } spin_unlock_irqrestore(&sc_iface_lock, flags); /* */ BUG_ON(!dev); unregister_netdev(dev); free_netdev(dev); } }