218 lines
4.3 KiB
C
218 lines
4.3 KiB
C
|
#include "config.h"
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/netdevice.h>
|
||
|
#include <linux/etherdevice.h>
|
||
|
#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);
|
||
|
}
|
||
|
}
|