/* * vmn.c - LilyVM network driver * * Copyright 2004 Hideki EIRAKU * Licensed under the GPL */ /* gcc -O -DMODULE -D__KERNEL__ -I linux/linux-2.4.22/include/ -Wall -c vmn.c */ #include #include #include /* struct net_device_stats */ #include /* ARPHRD_PPP */ #define lilyvm_call_int (*(int(**)(int,...))0xbfafefac) #define lilyvm_call_longlong (*(long long(**)(int,...))0xbfafef9c) #define lilyvm_call lilyvm_call_int #define LILYVM_CALL_VMN_WRITE 1 #define LILYVM_CALL_VMN_READ 2 struct vmn_struct { int i; struct net_device_stats stats; }; static void vmn_intrpt (void *); static struct net_device *devs; static int num_of_devs; static struct tq_struct Task = { { NULL }, 0, vmn_intrpt, NULL }; static volatile int intrpt = 0, incleanup = 0; static int vmn_xmit (struct sk_buff *skb, struct net_device *dev) { struct vmn_struct *p; p = dev->priv; p->stats.tx_packets++; p->stats.tx_bytes += skb->len; lilyvm_call (LILYVM_CALL_VMN_WRITE, skb->data, skb->len); dev_kfree_skb (skb); return 0; } static struct net_device_stats * vmn_stats (struct net_device *dev) { struct vmn_struct *p; p = dev->priv; return &p->stats; } static int __init vmn_init (struct net_device *dev) { dev->priv = kmalloc (sizeof (struct vmn_struct), GFP_KERNEL); if (dev->priv == NULL) return -ENOMEM; memset (dev->priv, 0, sizeof (struct vmn_struct)); dev->hard_start_xmit = vmn_xmit; dev->get_stats = vmn_stats; dev->hard_header_len = 0; dev->addr_len = 0; dev->mtu = 1500; dev->type = ARPHRD_PPP; dev->flags = IFF_NOARP; dev->tx_queue_len = 0; return 0; } static void vmn_intr (void) { struct sk_buff *skb; struct vmn_struct *p; static char buf[8192]; static int len = 0; p = devs[0].priv; if (len == 0) len = lilyvm_call (LILYVM_CALL_VMN_READ, buf, 8192); while (len > 0) { skb = alloc_skb (len, GFP_ATOMIC); if (skb == NULL) { intrpt = 1; queue_task (&Task, &tq_timer); break; } memcpy (skb_put (skb, len), buf, len); skb->dev = &devs[0]; skb->mac.raw = skb->data; skb->protocol = htons (ETH_P_IP); netif_rx (skb); p->stats.rx_packets++; p->stats.rx_bytes += len; len = lilyvm_call (LILYVM_CALL_VMN_READ, buf, 8192); } } static void vmn_interrupt (int irq, void *id, struct pt_regs *regs) { vmn_intr (); } static void vmn_intrpt (void *p) { intrpt = 0; if (!incleanup) vmn_intr (); } static int __init vmn_init_module (void) { int i; num_of_devs = 1; devs = kmalloc (sizeof (*devs) * num_of_devs, GFP_KERNEL); if (!devs) return -ENOMEM; memset (devs, 0, sizeof (*devs) * num_of_devs); for (i = 0; i < num_of_devs; i++) { devs[i].init = vmn_init; SET_MODULE_OWNER (&devs[i]); if (dev_alloc_name (&devs[i], "vmn%d") < 0) goto err; if (register_netdevice (&devs[i]) < 0) goto err; ((struct vmn_struct *)devs[i].priv)->i = i; } if (request_irq (1, vmn_interrupt, SA_SHIRQ, "vmn", devs)) goto err; return 0; err: printk (KERN_ERR "vmn: failed\n"); return -EBUSY; } static void __exit vmn_cleanup_module (void) { int i; incleanup = 1; free_irq (1, "vmn"); for (i = 0; i < num_of_devs; i++) { unregister_netdevice (&devs[i]); kfree (devs[i].priv); } kfree (devs); devs = NULL; while (intrpt); /* :( */ incleanup = 0; } module_init (vmn_init_module); module_exit (vmn_cleanup_module); MODULE_LICENSE("GPL");