#include "linux/inetdevice.h"
#include "linux/ctype.h"
#include "linux/bootmem.h"
+#include "linux/ethtool.h"
+#include "linux/platform_device.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);
+#define DRIVER_NAME "uml-netdev"
+
+static DEFINE_SPINLOCK(opened_lock);
+static LIST_HEAD(opened);
static int uml_net_rx(struct net_device *dev)
{
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)
+static void uml_dev_close(void* dev)
+{
+ dev_close( (struct net_device *) dev);
+}
+
+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) ;
if(err < 0) {
+ DECLARE_WORK(close_work, uml_dev_close, dev);
printk(KERN_ERR
"Device '%s' read returned %d, shutting it down\n",
dev->name, err);
- dev_close(dev);
+ /* dev_close can't be called in interrupt context, and takes
+ * again lp->lock.
+ * And dev_close() can be safely called multiple times on the
+ * same device, since it tests for (dev->flags & IFF_UP). So
+ * there's no harm in delaying the device shutdown. */
+ schedule_work(&close_work);
goto out;
}
reactivate_fd(lp->fd, UM_ETH_IRQ);
- out:
+out:
spin_unlock(&lp->lock);
+ return(IRQ_HANDLED);
}
static int uml_net_open(struct net_device *dev)
{
struct uml_net_private *lp = dev->priv;
- char addr[sizeof("255.255.255.255\0")];
int err;
spin_lock(&lp->lock);
}
if(!lp->have_mac){
- dev_ip_addr(dev, addr, &lp->mac[2]);
+ dev_ip_addr(dev, &lp->mac[2]);
set_ether_mac(dev, lp->mac);
}
SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
if(err != 0){
printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
- if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
- lp->fd = -1;
err = -ENETUNREACH;
+ goto out_close;
}
lp->tl.data = (unsigned long) &lp->user;
netif_start_queue(dev);
+ /* 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) ;
+
+ spin_unlock(&lp->lock);
+
spin_lock(&opened_lock);
list_add(&lp->list, &opened);
spin_unlock(&opened_lock);
- out:
+
+ return 0;
+out_close:
+ if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+ lp->fd = -1;
+out:
spin_unlock(&lp->lock);
- return(err);
+ return err;
}
static int uml_net_close(struct net_device *dev)
spin_lock(&lp->lock);
free_irq(dev->irq, dev);
- if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+ if(lp->close != NULL)
+ (*lp->close)(lp->fd, &lp->user);
lp->fd = -1;
+
+ spin_unlock(&lp->lock);
+
spin_lock(&opened_lock);
list_del(&lp->list);
spin_unlock(&opened_lock);
- spin_unlock(&lp->lock);
return 0;
}
return err;
}
-static int uml_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+static void uml_net_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
{
- return(-EINVAL);
+ strcpy(info->driver, DRIVER_NAME);
+ strcpy(info->version, "42");
}
+static struct ethtool_ops uml_net_ethtool_ops = {
+ .get_drvinfo = uml_net_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
void uml_net_user_timer_expire(unsigned long _conn)
{
#ifdef undef
#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 DEFINE_SPINLOCK(devices_lock);
+static LIST_HEAD(devices);
-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);
+static struct platform_driver uml_net_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+static int driver_registered;
static int eth_configure(int n, void *init, char *mac,
struct transport *transport)
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);
return 1;
}
+ lp = dev->priv;
+ /* This points to the transport private data. It's still clear, but we
+ * must memset it to 0 *now*. Let's help the drivers. */
+ memset(lp, 0, size);
+
+ /* sysfs register */
+ if (!driver_registered) {
+ platform_driver_register(¨_net_driver);
+ driver_registered = 1;
+ }
+ device->pdev.id = n;
+ device->pdev.name = DRIVER_NAME;
+ platform_device_register(&device->pdev);
+ SET_NETDEV_DEV(dev,&device->pdev.dev);
+
/* If this name ends up conflicting with an existing registered
* netdevice, that is OK, register_netdev{,ice}() will notice this
* and fail.
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;
dev->tx_timeout = uml_net_tx_timeout;
dev->set_mac_address = uml_net_set_mac;
dev->change_mtu = uml_net_change_mtu;
- dev->do_ioctl = uml_net_ioctl;
+ dev->ethtool_ops = ¨_net_ethtool_ops;
dev->watchdog_timeo = (HZ >> 1);
dev->irq = UM_ETH_IRQ;
free_netdev(dev);
return 1;
}
- 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),
+ .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);
+ spin_lock_init(&lp->lock);
lp->tl.function = uml_net_user_timer_expire;
if (lp->have_mac)
memcpy(lp->mac, device->mac, sizeof(lp->mac));
if (device->have_mac)
set_ether_mac(dev, device->mac);
- return(0);
+
+ return 0;
}
static struct uml_net *find_device(int n)
" Configure a network device.\n\n"
);
+#if 0
static int eth_init(void)
{
struct list_head *ele, *next;
return(1);
}
-
__initcall(eth_init);
+#endif
static int net_config(char *str)
{
err = eth_parse(str, &n, &str);
if(err) return(err);
- str = uml_strdup(str);
+ str = kstrdup(str, GFP_KERNEL);
if(str == NULL){
printk(KERN_ERR "net_config failed to strdup string\n");
return(-1);
return(err);
}
-static int net_remove(char *str)
+static int net_id(char **str, int *start_out, int *end_out)
+{
+ char *end;
+ int n;
+
+ n = simple_strtoul(*str, &end, 0);
+ if((*end != '\0') || (end == *str))
+ return -1;
+
+ *start_out = n;
+ *end_out = n;
+ *str = end;
+ return n;
+}
+
+static int net_remove(int n)
{
struct uml_net *device;
struct net_device *dev;
struct uml_net_private *lp;
- char *end;
- int n;
-
- n = simple_strtoul(str, &end, 0);
- if((*end != '\0') || (end == str))
- return(-1);
device = find_device(n);
if(device == NULL)
- return(0);
+ return -ENODEV;
dev = device->dev;
lp = dev->priv;
- if(lp->fd > 0) return(-1);
+ if(lp->fd > 0)
+ return -EBUSY;
if(lp->remove != NULL) (*lp->remove)(&lp->user);
unregister_netdev(dev);
+ platform_device_unregister(&device->pdev);
list_del(&device->list);
- free_netdev(device);
- return(0);
+ kfree(device);
+ free_netdev(dev);
+ return 0;
}
static struct mc_device net_mc = {
.name = "eth",
.config = net_config,
.get_config = NULL,
+ .id = net_id,
.remove = net_remove,
};
void *ptr)
{
struct in_ifaddr *ifa = ptr;
- u32 addr = ifa->ifa_address;
- u32 netmask = ifa->ifa_mask;
struct net_device *dev = ifa->ifa_dev->dev;
struct uml_net_private *lp;
void (*proc)(unsigned char *, unsigned char *, void *);
break;
}
if(proc != NULL){
- addr_buf[0] = addr & 0xff;
- addr_buf[1] = (addr >> 8) & 0xff;
- addr_buf[2] = (addr >> 16) & 0xff;
- addr_buf[3] = addr >> 24;
- netmask_buf[0] = netmask & 0xff;
- netmask_buf[1] = (netmask >> 8) & 0xff;
- netmask_buf[2] = (netmask >> 16) & 0xff;
- netmask_buf[3] = netmask >> 24;
+ memcpy(addr_buf, &ifa->ifa_address, sizeof(addr_buf));
+ memcpy(netmask_buf, &ifa->ifa_mask, sizeof(netmask_buf));
(*proc)(addr_buf, netmask_buf, &lp->user);
}
return(NOTIFY_DONE);
static void close_devices(void)
{
struct list_head *ele;
- struct uml_net_private *lp;
+ struct uml_net_private *lp;
list_for_each(ele, &opened){
lp = list_entry(ele, struct uml_net_private, list);
- if(lp->close != NULL) (*lp->close)(lp->fd, &lp->user);
+ free_irq(lp->dev->irq, lp->dev);
+ if((lp->close != NULL) && (lp->fd >= 0))
+ (*lp->close)(lp->fd, &lp->user);
if(lp->remove != NULL) (*lp->remove)(&lp->user);
}
}
return(1);
}
-void dev_ip_addr(void *d, char *buf, char *bin_buf)
+void dev_ip_addr(void *d, unsigned char *bin_buf)
{
struct net_device *dev = d;
struct in_device *ip = dev->ip_ptr;
struct in_ifaddr *in;
- u32 addr;
if((ip == NULL) || ((in = ip->ifa_list) == NULL)){
printk(KERN_WARNING "dev_ip_addr - device not assigned an "
"IP address\n");
return;
}
- addr = in->ifa_address;
- sprintf(buf, "%d.%d.%d.%d", addr & 0xff, (addr >> 8) & 0xff,
- (addr >> 16) & 0xff, addr >> 24);
- if(bin_buf){
- bin_buf[0] = addr & 0xff;
- bin_buf[1] = (addr >> 8) & 0xff;
- bin_buf[2] = (addr >> 16) & 0xff;
- bin_buf[3] = addr >> 24;
- }
+ memcpy(bin_buf, &in->ifa_address, sizeof(in->ifa_address));
}
void set_ether_mac(void *d, unsigned char *addr)
if(ip == NULL) return;
in = ip->ifa_list;
while(in != NULL){
- address[0] = in->ifa_address & 0xff;
- address[1] = (in->ifa_address >> 8) & 0xff;
- address[2] = (in->ifa_address >> 16) & 0xff;
- address[3] = in->ifa_address >> 24;
- netmask[0] = in->ifa_mask & 0xff;
- netmask[1] = (in->ifa_mask >> 8) & 0xff;
- netmask[2] = (in->ifa_mask >> 16) & 0xff;
- netmask[3] = in->ifa_mask >> 24;
+ memcpy(address, &in->ifa_address, sizeof(address));
+ memcpy(netmask, &in->ifa_mask, sizeof(netmask));
(*cb)(address, netmask, arg);
in = in->ifa_next;
}