#include "linux/inetdevice.h"
#include "linux/ctype.h"
#include "linux/bootmem.h"
+#include "linux/ethtool.h"
+#include "asm/uaccess.h"
#include "user_util.h"
#include "kern_util.h"
#include "net_kern.h"
#include "mconsole_kern.h"
#include "init.h"
#include "irq_user.h"
+#include "irq_kern.h"
static spinlock_t opened_lock = SPIN_LOCK_UNLOCKED;
LIST_HEAD(opened);
struct sk_buff *skb;
/* If we can't allocate memory, try again next round. */
- if ((skb = dev_alloc_skb(dev->mtu)) == NULL) {
+ skb = dev_alloc_skb(dev->mtu);
+ if (skb == NULL) {
lp->stats.rx_dropped++;
return 0;
}
return pkt_len;
}
-void uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t uml_net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = dev_id;
struct uml_net_private *lp = dev->priv;
int err;
if(!netif_running(dev))
- return;
+ return(IRQ_NONE);
spin_lock(&lp->lock);
while((err = uml_net_rx(dev)) > 0) ;
out:
spin_unlock(&lp->lock);
+ return(IRQ_HANDLED);
}
static int uml_net_open(struct net_device *dev)
spin_lock(&opened_lock);
list_add(&lp->list, &opened);
spin_unlock(&opened_lock);
+
+ /* clear buffer - it can happen that the host side of the interface
+ * is full when we get here. In this case, new data is never queued,
+ * SIGIOs never arrive, and the net never works.
+ */
+ while((err = uml_net_rx(dev)) > 0) ;
+
out:
spin_unlock(&lp->lock);
return(err);
static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- return(-EINVAL);
+ static const struct ethtool_drvinfo info = {
+ .cmd = ETHTOOL_GDRVINFO,
+ .driver = "uml virtual ethernet",
+ .version = "42",
+ };
+ void *useraddr;
+ u32 ethcmd;
+
+ switch (cmd) {
+ case SIOCETHTOOL:
+ useraddr = ifr->ifr_data;
+ if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd)))
+ return -EFAULT;
+ switch (ethcmd) {
+ case ETHTOOL_GDRVINFO:
+ if (copy_to_user(useraddr, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ default:
+ return -EINVAL;
+ }
}
void uml_net_user_timer_expire(unsigned long _conn)
#endif
}
-/*
- * default do nothing hard header packet routines for struct net_device init.
- * real ethernet transports will overwrite with real routines.
- */
-static int uml_net_hard_header(struct sk_buff *skb, struct net_device *dev,
- unsigned short type, void *daddr, void *saddr, unsigned len)
-{
- return(0); /* no change */
-}
-
-static int uml_net_rebuild_header(struct sk_buff *skb)
-{
- return(0); /* ignore */
-}
-
-static int uml_net_header_cache(struct neighbour *neigh, struct hh_cache *hh)
-{
- return(-1); /* fail */
-}
-
-static void uml_net_header_cache_update(struct hh_cache *hh,
- struct net_device *dev, unsigned char * haddr)
-{
- /* ignore */
-}
-
-static int uml_net_header_parse(struct sk_buff *skb, unsigned char *haddr)
-{
- return(0); /* nothing */
-}
-
static spinlock_t devices_lock = SPIN_LOCK_UNLOCKED;
static struct list_head devices = LIST_HEAD_INIT(devices);
struct uml_net *device;
struct net_device *dev;
struct uml_net_private *lp;
- int err, size;
+ int save, err, size;
size = transport->private_size + sizeof(struct uml_net_private) +
sizeof(((struct uml_net_private *) 0)->user);
snprintf(dev->name, sizeof(dev->name), "eth%d", n);
device->dev = dev;
- dev->hard_header = uml_net_hard_header;
- dev->rebuild_header = uml_net_rebuild_header;
- dev->hard_header_cache = uml_net_header_cache;
- dev->header_cache_update= uml_net_header_cache_update;
- dev->hard_header_parse = uml_net_header_parse;
-
(*transport->kern->init)(dev, init);
dev->mtu = transport->user->max_packet;
}
lp = dev->priv;
- INIT_LIST_HEAD(&lp->list);
- spin_lock_init(&lp->lock);
- lp->dev = dev;
- lp->fd = -1;
- lp->mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0 };
- lp->have_mac = device->have_mac;
- lp->protocol = transport->kern->protocol;
- lp->open = transport->user->open;
- lp->close = transport->user->close;
- lp->remove = transport->user->remove;
- lp->read = transport->kern->read;
- lp->write = transport->kern->write;
- lp->add_address = transport->user->add_address;
- lp->delete_address = transport->user->delete_address;
- lp->set_mtu = transport->user->set_mtu;
+ /* lp.user is the first four bytes of the transport data, which
+ * has already been initialized. This structure assignment will
+ * overwrite that, so we make sure that .user gets overwritten with
+ * what it already has.
+ */
+ save = lp->user[0];
+ *lp = ((struct uml_net_private)
+ { .list = LIST_HEAD_INIT(lp->list),
+ .lock = SPIN_LOCK_UNLOCKED,
+ .dev = dev,
+ .fd = -1,
+ .mac = { 0xfe, 0xfd, 0x0, 0x0, 0x0, 0x0},
+ .have_mac = device->have_mac,
+ .protocol = transport->kern->protocol,
+ .open = transport->user->open,
+ .close = transport->user->close,
+ .remove = transport->user->remove,
+ .read = transport->kern->read,
+ .write = transport->kern->write,
+ .add_address = transport->user->add_address,
+ .delete_address = transport->user->delete_address,
+ .set_mtu = transport->user->set_mtu,
+ .user = { save } });
init_timer(&lp->tl);
lp->tl.function = uml_net_user_timer_expire;
unregister_netdev(dev);
list_del(&device->list);
- free_netdev(device);
+ kfree(device);
+ free_netdev(dev);
return(0);
}