# HG changeset patch
# Parent 53c073d05449cc4cd6130bd79eb02c07239dd8d8
rathaxes: start to implement the Ethernet subsystem in linux LKM sample

diff -r 53c073d05449 rathaxes/samples/lkm/CMakeLists.txt
--- a/rathaxes/samples/lkm/CMakeLists.txt	Sat Jan 07 19:10:21 2012 +0100
+++ b/rathaxes/samples/lkm/CMakeLists.txt	Sat Jan 07 19:18:24 2012 +0100
@@ -1,6 +1,6 @@
 ADD_RATHAXES_SOURCES(lkm lkm.rtx
-                     RTI log.rti lkm.rti pci.rti
-                     BLT log.blt lkm.blt pci.blt)
+                     RTI log.rti lkm.rti pci.rti ethernet.rti
+                     BLT log.blt lkm.blt pci.blt ethernet.blt)
 
 # We can't name lkm since it's already used as the target name to generate the
 # source (with ADD_RATHAXES_SOURCES).
diff -r 53c073d05449 rathaxes/samples/lkm/ethernet.blt
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rathaxes/samples/lkm/ethernet.blt	Sat Jan 07 19:18:24 2012 +0100
@@ -0,0 +1,173 @@
+with Ethernet, PCI, LKM, Log
+{
+    template type   Ethernet::Device()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/netdevice.h>
+            #include <linux/etherdevice.h>
+
+            typedef int include_linux_net_system_stamp;
+        }
+
+        chunk ::decl()
+        {
+            struct rtx_ethernet_dev
+            {
+                /*
+                 * I think it's useless to use the ${PCI::Device} "abstraction"
+                 * here, since we are already in a Linux specific context here.
+                 */
+                struct pci_dev      *pci_dev;
+                struct net_device   *net_dev;
+            };
+        }
+
+        chunk ::init(net_dev)
+        {
+            ${self} = ${net_dev};
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   Ethernet::open(Ethernet::Device dev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_open(struct net_device *);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_open(struct net_device *dev)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::close(Ethernet::Device dev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_close(struct net_device *);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_close(struct net_device *dev)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        /*
+         * Why we can't use irqreturn_t here? (we are forced to use enum
+         * irqreturn, which is the real type).
+         */
+
+        chunk LKM::prototypes()
+        {
+            static enum irqreturn   rtx_ethernet_interrupt_handler(int, void *);
+        }
+
+        chunk LKM::code()
+        {
+            static enum irqreturn   rtx_ethernet_interrupt_handler(int irq, void *dev_id)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return IRQ_NONE;
+            }
+        }
+    }
+
+    template sequence   Ethernet::init(PCI::Device pdev)
+    {
+        chunk LKM::data()
+        {
+            static const struct net_device_ops  rtx_ether_ops =
+            {
+                .ndo_open = rtx_ethernet_open,
+                .ndo_stop = rtx_ethernet_close,
+                .ndo_start_xmit = NULL,
+            };
+        }
+
+        chunk ::CALL
+        {
+            /*
+             * This typedef is needed to workaround a bug in CNorm __std__
+             * dialect.
+             */
+            typedef int ${Ethernet::Device};
+            ${Ethernet::Device} *rtx_ether_ctx;
+            struct net_device   *net_dev;
+            int error;
+
+            error = 0;
+            net_dev = alloc_etherdev(sizeof(*rtx_ether_ctx));
+            if (net_dev == 0)
+            {
+                ${Log::info("Cannot allocate memory")};
+                // is it the thing to do?
+                return -ENOMEM;
+            }
+            strlcpy(net_dev->name, ${config.ifname}, sizeof(net_dev->name));
+            net_dev->irq = pdev->irq;
+            // Maybe we should try ${rtx_ether_ctx.init()} here:
+            rtx_ether_ctx = netdev_priv(net_dev);
+            //rtx_ether_ctx->pci_dev = ${pdev};
+            rtx_ether_ctx->pci_dev = pdev; // In the meantime do it directly
+            rtx_ether_ctx->net_dev = net_dev;
+
+            /*
+             * The substitution of ${pdev} fails here. I also tried to add a
+             * "substitute method" to the PCI::Device that was just doing
+             * "${self}" but it didn't work either (it was subsituted by a
+             * placeholder, e.g: _1).
+             *
+             * That's why we cheated a bit and named all the arguments pdev.
+             */
+            //SET_NETDEV_DEV(net_dev, &${pdev}->dev);
+            SET_NETDEV_DEV(net_dev, &pdev->dev);
+            net_dev->netdev_ops = &rtx_ether_ops;
+            if ((error = register_netdev(net_dev)))
+            {
+                ${Log::info("Cannot register the driver")};
+                // is it the thing to do?
+                return error;
+            }
+
+            /* same problem as above with ${pdev} */
+            //pci_set_drvdata(${pdev}, net_dev);
+            pci_set_drvdata(pdev, net_dev);
+        }
+    }
+
+    template sequence   Ethernet::exit(PCI::Device pdev)
+    {
+        chunk ::CALL
+        {
+            struct net_device   *net_dev;
+
+            net_dev = pci_get_drvdata(pdev); // should be ${pdev}, see above
+            unregister_netdev(net_dev);
+            /*
+             * If we had some cleanup todo with struct rtx_ether_ctx we would
+             * do a netdev_priv(net_dev) here and do it.
+             */
+            free_netdev(net_dev);
+        }
+    }
+}
diff -r 53c073d05449 rathaxes/samples/lkm/ethernet.rti
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rathaxes/samples/lkm/ethernet.rti	Sat Jan 07 19:18:24 2012 +0100
@@ -0,0 +1,37 @@
+interface Ethernet : PCI, LKM
+{
+    provided type       Ethernet::Device;
+
+    required variable ::string  Ethernet::ifname;
+
+    required sequence   Ethernet::open(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    required sequence   Ethernet::close(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    required sequence   Ethernet::interrupt_handler(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    /* Kinda extends PCI::probe */
+    provided sequence   Ethernet::init(PCI::Device)
+    {
+        provided chunk  LKM::data;
+        provided chunk  ::CALL;
+    }
+
+    /* Likely extends PCI::remove */
+    provided sequence   Ethernet::exit(PCI::Device)
+    {
+        provided chunk  ::CALL;
+    }
+}
diff -r 53c073d05449 rathaxes/samples/lkm/lkm.rtx
--- a/rathaxes/samples/lkm/lkm.rtx	Sat Jan 07 19:10:21 2012 +0100
+++ b/rathaxes/samples/lkm/lkm.rtx	Sat Jan 07 19:18:24 2012 +0100
@@ -1,13 +1,30 @@
 device LKM use LKM, PCI, Log
 {
+    Ethernet::open(Ethernet::Device dev)
+    {
+        Log::info("Open the device");
+    }
+
+    Ethernet::close(Ethernet::Device dev)
+    {
+        Log::info("Close the device");
+    }
+
+    Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        Log::info("Got an interruption");
+    }
+
     PCI::probe(PCI::Device dev)
     {
-
+        Log::info("Probe the device");
+        Ethernet::init(dev);
     }
 
     PCI::remove(PCI::Device dev)
     {
-
+        Log::info("Remove the pci device");
+        Ethernet::exit(dev);
     }
 
     LKM::init()
@@ -32,4 +49,6 @@
 
     PCI::vendor_id = 0x8086;
     PCI::product_id = 0x100f;
+
+    Ethernet::ifname = "rtx%d";
 }
diff -r 53c073d05449 rathaxes/samples/lkm/pci.blt
--- a/rathaxes/samples/lkm/pci.blt	Sat Jan 07 19:10:21 2012 +0100
+++ b/rathaxes/samples/lkm/pci.blt	Sat Jan 07 19:18:24 2012 +0100
@@ -11,15 +11,12 @@
 
         chunk   ::decl()
         {
-            struct  rtx_pci_device
-            {
-                struct pci_dev  *pci_dev;
-            };
+            struct pci_dev;
         }
 
         chunk   ::init(pci_dev)
         {
-            ${self}.pci_dev = pci_dev;
+            ${self} = ${pci_dev};
         }
 
         map
@@ -27,7 +24,17 @@
         }
     }
 
-    template sequence   PCI::probe(PCI::Device dev)
+    /*
+     * The PCI::probe sequence is a "required" sequence which means that its
+     * implementation will be done in the .rtx. Here we just define the context
+     * were this implementation will be inserted. The implementation should be
+     * able to access to the struct pci_dev (here marked as the "argument"
+     * PCI::Device). How do we bind this PCI::Device argument with the pdev
+     * struct pci_dev pointer defined in the LKM::code chunk?
+     *
+     * The only thing I can imagine is: ${pointcut ::IMPLEMENTATION(pdev)};
+     */
+    template sequence   PCI::probe(PCI::Device pdev)
     {
         chunk LKM::prototypes()
         {
@@ -40,11 +47,7 @@
             static int /* __devinit */  rtx_pci_probe(struct pci_dev *pdev,
                                                       const struct pci_device_id *pdev_id)
             {
-                /* workaround for CNorm __std__ dialect, shouldn't be here */
-                typedef int ${PCI::Device};
-
                 int             err;
-                ${PCI::Device}  *dev = NULL;
 
                 err = pci_enable_device(pdev);
                 if (err < 0)
@@ -52,8 +55,6 @@
 
                 ${pointcut ::IMPLEMENTATION};
 
-                pci_set_drvdata(pdev, dev);
-
                 return 0;
 
                 fail:
@@ -62,7 +63,7 @@
         }
     }
 
-    template sequence   PCI::remove(PCI::Device dev)
+    template sequence   PCI::remove(PCI::Device pdev)
     {
         chunk LKM::prototypes()
         {
@@ -73,9 +74,9 @@
         {
             static void rtx_pci_remove(struct pci_dev *pdev)
             {
+                ${pointcut ::IMPLEMENTATION};
+
                 pci_disable_device(pdev);
-
-                ${pointcut ::IMPLEMENTATION};
             }
         }
     }
@@ -89,7 +90,7 @@
              * have to specify the exact size).
              */
             static struct pci_device_id rtx_pci_device_table[2] = {
-                { ${config.vendor_id}, ${config.product_id}, 0, PCI_ANY_ID, PCI_ANY_ID },
+                { ${config.vendor_id}, ${config.product_id}, PCI_ANY_ID, PCI_ANY_ID },
                 { 0, }
             };
 
