# HG changeset patch
# User Thomas Sanchez <thomas.sanchz@gmail.com>, Louis Opter <louis@lse.epita.fr>
# Parent 3a7169517431da7a5a9a3c411ff95927c55f214f
rathaxes: add the beginning of a Linux Intel e1000 network card driver

- With working LKM/PCI/Ethernet parts;
- Some interruption handling POC;
- Directly compiles to a .ko object.

diff --git a/maintainers/CMakeScripts/Templates/MakefileLKM.in b/maintainers/CMakeScripts/Templates/MakefileLKM.in
--- a/maintainers/CMakeScripts/Templates/MakefileLKM.in
+++ b/maintainers/CMakeScripts/Templates/MakefileLKM.in
@@ -1,5 +1,9 @@
-KDIR	= /lib/modules/$(shell uname -r)/build
-obj-m	:= @LKM_OBJECTS@
+# Disable this "coding-style" warning (seriously, you have to compile with
+# -pedantic to get it...)
+EXTRA_CFLAGS	= -Wno-declaration-after-statement
+
+KDIR		= /lib/modules/$(shell uname -r)/build
+obj-m		:= @LKM_OBJECTS@
 
 all:
 	$(MAKE) -C $(KDIR) SUBDIRS=$(shell pwd) modules
diff --git a/maintainers/CMakeScripts/UseRathaxes.cmake b/maintainers/CMakeScripts/UseRathaxes.cmake
--- a/maintainers/CMakeScripts/UseRathaxes.cmake
+++ b/maintainers/CMakeScripts/UseRathaxes.cmake
@@ -193,6 +193,8 @@
 
         SET(KERNEL_OBJECT_NAME "${RATHAXES_SOURCE}_${SYSTEM}.ko")
         ADD_CUSTOM_COMMAND(OUTPUT "${KERNEL_OBJECT_NAME}"
+                           # …
+                           COMMAND "sed" "-i" "/TARTE/ d" "${RATHAXES_SOURCE}_${SYSTEM}.c"
                            # The linux Makefile to build kernel module is quite
                            # picky about file location and its own name. Let's
                            # copy our source side by side with the Makefile:
diff --git a/rathaxes/samples/CMakeLists.txt b/rathaxes/samples/CMakeLists.txt
--- a/rathaxes/samples/CMakeLists.txt
+++ b/rathaxes/samples/CMakeLists.txt
@@ -1,2 +1,3 @@
 ADD_SUBDIRECTORY(helloworld)
 ADD_SUBDIRECTORY(syntax)
+ADD_SUBDIRECTORY(e1000)
diff --git a/rathaxes/samples/e1000/CMakeLists.txt b/rathaxes/samples/e1000/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/CMakeLists.txt
@@ -0,0 +1,7 @@
+ADD_RATHAXES_SOURCES(e1000_src lkm.rtx
+                     RTI log.rti lkm.rti pci.rti socket.rti ethernet.rti e1000.rti
+                     BLT log.blt lkm.blt pci.blt socket.blt ethernet.blt e1000.blt)
+
+# We can't name lkm since it's already used as the target name to generate the
+# source (with ADD_RATHAXES_SOURCES).
+ADD_RATHAXES_LKM(e1000 e1000_src)
diff --git a/rathaxes/samples/e1000/e1000.blt b/rathaxes/samples/e1000/e1000.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/e1000.blt
@@ -0,0 +1,435 @@
+with e1000, Ethernet, Socket, PCI, LKM, Log
+{
+    template type   e1000::Context()
+    {
+        chunk   LKM::includes()
+        {
+            /*
+             * Force the generation of the structure in the "headers" part, we
+             * have to do this since we do not use the structure in this blt
+             * (we hacked a bit and used it in ethernet.blt directly).
+             */
+            typedef int ${e1000::Context}; /* CNorm __std__ workaround */
+            static const ${e1000::Context} force_struct_rtx_10000_ctx_decl;
+        }
+
+        chunk   ::decl()
+        {
+            struct rtx_e1000_ctx
+            {
+                int                             bars;
+                unsigned char /* __iomem */     *ioaddr;
+                int                             irq;
+            };
+        }
+
+        chunk   ::init(bars, ioaddr)
+        {
+            ${self}.bars = ${bars};
+            ${self}.ioaddr = ${ioaddr};
+        }
+
+        map
+        {
+        }
+    }
+
+    template type   e1000::Register()
+    {
+        chunk   LKM::includes()
+        {
+            typedef int ${e1000::Register};
+            static const ${e1000::Register}  force_enum_rtx_e1000_registers_decl;
+        }
+
+        chunk   ::decl()
+        {
+            enum rtx_e1000_registers
+            {
+                E1000_CTRL          = 0x00000, /* Device Control - RW */
+                E1000_CTRL_DUP      = 0x00004, /* Device Control Duplicate (Shadow) - RW */
+                E1000_STATUS        = 0x00008, /* Device Status - RO */
+                E1000_EEPROM_FLASH  = 0x00010, /* EEPROM/Flash Control - RW */
+                E1000_EEPROM_READ   = 0x00014, /* EEPROM Read - RW */
+                E1000_CTRL_EXT      = 0x00018, /* Extended Device Control - RW */
+                E1000_FLA           = 0x0001C, /* Flash Access - RW */
+                E1000_MDIC          = 0x00020, /* MDI Control - RW */
+                E1000_IMS           = 0x000D0, /* Interrupt Mask Set */
+                E1000_IMC           = 0x000D8, /* Interrupt Mask Clear */
+                E1000_ICR           = 0x000C0  /* Interrupt Cause Read - R/clr */
+            };
+        }
+
+        chunk   ::init(value)
+        {
+            ${self} = ${value};
+        }
+
+        map
+        {
+        }
+    }
+
+    template type   e1000::Commands()
+    {
+        chunk   LKM::includes()
+        {
+            typedef int ${e1000::Commands};
+            static const ${e1000::Commands} force_enum_rtx_e1000_commands_decls;
+        }
+
+        chunk   ::decl()
+        {
+            enum rtx_e1000_commands
+            {
+                E1000_CMD_FD                    = 0x00000001, /* Full duplex.0=half; 1=full */
+                E1000_CMD_BEM                   = 0x00000002, /* Endian Mode.0=little,1=big */
+                E1000_CMD_PRIOR                 = 0x00000004, /* Priority on PCI. 0=rx,1=fair */
+                E1000_CMD_GIO_MASTER_DISABLE    = 0x00000004, /* Blocks new Master requests */
+                E1000_CMD_LRST                  = 0x00000008, /* Link reset. 0=normal,1=reset */
+                E1000_CMD_TME                   = 0x00000010, /* Test mode. 0=normal,1=test */
+                E1000_CMD_SLE                   = 0x00000020, /* Serial Link on 0=dis,1=en */
+                E1000_CMD_ASDE                  = 0x00000020, /* Auto-speed detect enable */
+                E1000_CMD_SLU                   = 0x00000040, /* Set link up (Force Link) */
+                E1000_CMD_ILOS                  = 0x00000080, /* Invert Loss-Of Signal */
+                E1000_CMD_SPD_SEL               = 0x00000300, /* Speed Select Mask */
+                E1000_CMD_SPD_10                = 0x00000000, /* Force 10Mb */
+                E1000_CMD_SPD_100               = 0x00000100, /* Force 100Mb */
+                E1000_CMD_SPD_1000              = 0x00000200, /* Force 1Gb */
+                E1000_CMD_BEM32                 = 0x00000400, /* Big Endian 32 mode */
+                E1000_CMD_FRCSPD                = 0x00000800, /* Force Speed */
+                E1000_CMD_FRCDPX                = 0x00001000, /* Force Duplex */
+                E1000_CMD_D_UD_EN               = 0x00002000, /* Dock/Undock enable */
+                E1000_CMD_D_UD_POLARITY         = 0x00004000, /* Defined polarity of Dock/Undock indication in SDP[0] */
+                E1000_CMD_FORCE_PHY_RESET       = 0x00008000, /* Reset both PHY ports, through PHYRST_N pin */
+                E1000_CMD_EXT_LINK_EN           = 0x00010000, /* enable link status from external LINK_0 and LINK_1 pins */
+                E1000_CMD_SWDPIN0               = 0x00040000, /* SWDPIN 0 value */
+                E1000_CMD_SWDPIN1               = 0x00080000, /* SWDPIN 1 value */
+                E1000_CMD_SWDPIN2               = 0x00100000, /* SWDPIN 2 value */
+                E1000_CMD_SWDPIN3               = 0x00200000, /* SWDPIN 3 value */
+                E1000_CMD_SWDPIO0               = 0x00400000, /* SWDPIN 0 Input or output */
+                E1000_CMD_SWDPIO1               = 0x00800000, /* SWDPIN 1 input or output */
+                E1000_CMD_SWDPIO2               = 0x01000000, /* SWDPIN 2 input or output */
+                E1000_CMD_SWDPIO3               = 0x02000000, /* SWDPIN 3 input or output */
+                E1000_CMD_RST                   = 0x04000000, /* Global reset */
+                E1000_CMD_RFCE                  = 0x08000000, /* Receive Flow Control enable */
+                E1000_CMD_TFCE                  = 0x10000000, /* Transmit flow control enable */
+                E1000_CMD_RTE                   = 0x20000000, /* Routing tag enable */
+                E1000_CMD_VME                   = 0x40000000, /* IEEE VLAN mode enable */
+                E1000_CMD_PHY_RST               = 0x80000000, /* PHY Reset */
+                E1000_CMD_SW2FW_INT             = 0x02000000, /* Initiate an interrupt to manageability engine */
+                E1000_INTR_TXDW                 = 0x00000001, /* Transmit desc written back */
+                E1000_INTR_TXQE                 = 0x00000002, /* Transmit Queue empty */
+                E1000_INTR_LSC                  = 0x00000004, /* Link Status Change */
+                E1000_INTR_RXSEQ                = 0x00000008, /* rx sequence error */
+                E1000_INTR_RXDMT0               = 0x00000010, /* rx desc min. threshold (0) */
+                E1000_INTR_RXO                  = 0x00000040, /* rx overrun */
+                E1000_INTR_RXT0                 = 0x00000080, /* rx timer intr (ring 0) */
+                E1000_INTR_MDAC                 = 0x00000200  /* MDIO access complete */
+            };
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   e1000::create_device()
+    {
+        chunk Ethernet::create_device()
+        {
+            rtx_ether_ctx->hw_ctx.irq = pdev->irq;
+            rtx_ether_ctx->hw_ctx.bars = pci_select_bars(pdev, IORESOURCE_MEM);
+            if (pci_enable_device_mem(pdev))
+            {
+                ${Log::info("e1000::create: pci_enable_device_mem failed")};
+            }
+
+            if (pci_request_selected_regions(pdev, rtx_ether_ctx->hw_ctx.bars, ${config.name}))
+            {
+                ${Log::info("e1000::create: pci_request_selected_regions failed")};
+            }
+
+            if (${config.set_master})
+            {
+                pci_set_master(pdev);
+            }
+
+            /* 0 here is for BAR_0: */
+            rtx_ether_ctx->hw_ctx.ioaddr = pci_ioremap_bar(pdev, 0);
+            if (!rtx_ether_ctx->hw_ctx.ioaddr)
+            {
+                ${Log::info("e1000::create: pci_ioremap_bar failed")};
+            }
+
+            /* Reset the card */
+            rtx_e1000_register_write32(&rtx_ether_ctx->hw_ctx, E1000_CTRL, E1000_CMD_RST);
+            udelay(10);
+
+            /* Now we can load its mac address */
+            int i = 0;
+            for (i = 0 /* < this is not generated! */; i < 3; ++i)
+            {
+                rtx_e1000_register_write32(&rtx_ether_ctx->hw_ctx, E1000_EEPROM_READ, (i << 8) | 1);
+
+                int value;
+                /* Should be a do { } while(); but the compiler doesn't do { } while(); yet. */
+                value = rtx_e1000_register_read32(&rtx_ether_ctx->hw_ctx, E1000_EEPROM_READ);
+                while ((value & (1 << 4)) == 0)
+                    value = rtx_e1000_register_read32(&rtx_ether_ctx->hw_ctx, E1000_EEPROM_READ);
+                value >>= 16;
+
+                rtx_ether_ctx->net_dev->dev_addr[i * 2] = value & 0xff;
+                rtx_ether_ctx->net_dev->dev_addr[i * 2 + 1] = (value >> 8) & 0xff;
+            }
+
+            memcpy(rtx_ether_ctx->net_dev->perm_addr,
+                   rtx_ether_ctx->net_dev->dev_addr,
+                   rtx_ether_ctx->net_dev->addr_len);
+
+            { /* < mais lol. */
+                ${Log::info("e1000::create: mac address loaded from the EEPROM")};
+            }
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+
+    template sequence   e1000::destroy_device()
+    {
+        chunk   Ethernet::destroy_device
+        {
+            /*
+             * Here, we should have some checks to avoid to free resources that
+             * haven't been allocated. (e.g: in case of previous errors).
+             */
+            struct rtx_ethernet_dev* rtx_ether_ctx = netdev_priv(net_dev);
+            iounmap(rtx_ether_ctx->hw_ctx.ioaddr);
+            pci_release_selected_regions(pdev, rtx_ether_ctx->hw_ctx.bars);
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    template sequence   e1000::print_status()
+    {
+        chunk   LKM::prototypes()
+        {
+            static void rtx_e1000_print_status(struct rtx_e1000_ctx *);
+        }
+
+        chunk   LKM::code()
+        {
+            static void rtx_e1000_print_status(struct rtx_e1000_ctx *ctx)
+            {
+                unsigned int status = rtx_e1000_register_read32(ctx, E1000_STATUS);
+                pr_info("rtx_e1000 status: \n");
+                pr_info("\tRegister value: 0x%x\n", status);
+                pr_info("\tMode: %s\n", (status & 1) ? "Full": "Half");
+                pr_info("\tLink: %s\n", (status & 2) ? "Up" : "Down");
+                pr_info("\tTransmission: %s\n", (status & 4) ? "Paused" : "Ok");
+                pr_info("\tInterface: %s\n", (status & 3) == 3 ? "Up" : "Down");
+            }
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    /*
+     * We should have been able to do something along those lines, but
+     * it didn't work so we made the call manually.
+     *
+     * Ideally:
+     * ${e1000::register_read32(rtx_ether_ctx->hw_ctx, E1000_STATUS)};
+     *
+     * Ideally2, not sure about the syntax on the register parameter:
+     * ${e1000::register_read32(rtx_ether_ctx->hw_ctx, ${e1000::Register.E1000_STATUS})};
+     *
+     * "Acceptable":
+     * typedef int ${e1000::Register}; // cnorm __std__ workaround
+     * ${e1000::Register} reg_status;
+     * ${e1000.init(E1000_STATUS); // didn't work, so we used the next line
+     * reg_status = E1000_STATUS;
+     * ${e1000::register_read32(rtx_ether_ctx->hw_ctx, reg_status)};
+     */
+    template sequence   e1000::register_read32(e1000::Context ctx, e1000::Register reg_offset)
+    {
+        chunk   LKM::prototypes()
+        {
+            static unsigned int    rtx_e1000_register_read32(struct rtx_e1000_ctx *, unsigned int);
+        }
+
+        chunk   LKM::code()
+        {
+            static unsigned int    rtx_e1000_register_read32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset)
+            {
+                return ioread32(ctx->ioaddr + reg_offset);
+            }
+        }
+
+        chunk   ::CALL()
+        {
+            rtx_e1000_register_read32(&${ctx}, ${reg_offset});
+        }
+    }
+
+    template sequence   e1000::register_write32(e1000::Context ctx, e1000::Register reg_offset, ::number value)
+    {
+        chunk   LKM::prototypes()
+        {
+            static void rtx_e1000_register_write32(struct rtx_e1000_ctx *, unsigned int, unsigned int);
+        }
+
+        chunk   LKM::code()
+        {
+            static void rtx_e1000_register_write32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
+            {
+                iowrite32(value, ctx->ioaddr + reg_offset);
+            }
+        }
+
+        chunk   ::CALL()
+        {
+            rtx_e1000_register_write32(&${ctx}, ${reg_offset});
+        }
+    }
+
+    template sequence   e1000::register_set32(e1000::Context ctx, e1000::Register reg_offset, ::number value)
+    {
+        chunk   LKM::prototypes()
+        {
+            static void rtx_e1000_register_set32(struct rtx_e1000_ctx *, unsigned int, unsigned int);
+        }
+
+        chunk   LKM::code()
+        {
+            static void rtx_e1000_register_set32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
+            {
+                iowrite32(rtx_e1000_register_read32(ctx, reg_offset) | value, ctx->ioaddr + reg_offset);
+            }
+        }
+
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   e1000::setup_interrupt_handler()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/interrupt.h>
+        }
+        chunk   LKM::prototypes()
+        {
+            static int e1000_setup_interrupt_handler(struct rtx_ethernet_dev *);
+        }
+
+        chunk   LKM::code()
+        {
+            static int e1000_setup_interrupt_handler(struct rtx_ethernet_dev *ethernet_ctx)
+            {
+                int error;
+
+                error = request_irq(ethernet_ctx->hw_ctx.irq,
+                        rtx_ethernet_interrupt_handler,
+                        IRQF_SHARED,
+                        ${config.name},
+                        ethernet_ctx);
+
+                if (error)
+                {
+                    ${Log::info("Cannot register the interruption")};
+                }
+
+                return error;
+            }
+        }
+
+        chunk   ::CALL()
+        {
+            // this is an hack for the scope
+            (void)1;
+            {
+                int error;
+                error = e1000_setup_interrupt_handler(rtx_ether_dev);
+                if (error)
+                {
+                    return error;
+                }
+            }
+        }
+    }
+
+    template sequence   e1000::free_interrupt_handler()
+    {
+        chunk   LKM::prototypes()
+        {
+            static void e1000_free_interrupt_handler(struct rtx_ethernet_dev *ethernet_ctx);
+        }
+
+        chunk LKM::code()
+        {
+            static void e1000_free_interrupt_handler(struct rtx_ethernet_dev *ethernet_ctx)
+            {
+
+                free_irq(ethernet_ctx->hw_ctx.irq, ethernet_ctx);
+            }
+        }
+
+        chunk ::CALL()
+        {
+            e1000_free_interrupt_handler(rtx_ether_dev);
+        }
+    }
+
+    template sequence   e1000::activate_device_interruption()
+    {
+        chunk  ::CALL()
+        {
+            rtx_e1000_register_write32(ctx, E1000_IMS,
+                                       E1000_INTR_TXDW |
+                                       E1000_INTR_TXQE |
+                                       E1000_INTR_LSC  |
+                                       E1000_INTR_RXO  |
+                                       E1000_INTR_RXT0);
+        }
+    }
+
+    template sequence   e1000::set_up_device()
+    {
+        chunk  ::CALL()
+        {
+            rtx_e1000_register_set32(ctx, E1000_CTRL,
+                                     E1000_CMD_ASDE |
+                                     E1000_CMD_SLU  |
+                                     E1000_CMD_LRST |
+                                     E1000_CMD_PHY_RST);
+        }
+    }
+
+    template sequence   e1000::handle_intr()
+    {
+        chunk   ::CALL()
+        {
+            int intr;
+
+            intr = rtx_e1000_register_read32(ctx, E1000_ICR);
+            if (intr & E1000_INTR_LSC)
+            {
+                ${Log::info("Link status changed")};
+            }
+
+            if (intr)
+            {
+                return IRQ_HANDLED;
+            }
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/e1000.rti b/rathaxes/samples/e1000/e1000.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/e1000.rti
@@ -0,0 +1,89 @@
+interface e1000 : Socket, Ethernet, PCI, LKM
+{
+    provided type   e1000::Context;
+    provided type   e1000::Register;
+    provided type   e1000::Commands;
+
+    /*
+     * This sequence should receive an argument like Ethernet::Device, but it is
+     * unclear about how this argument should be bound to a variable/argument in
+     * the instrumented C code.
+     *
+     * Here again, we rely on the fact that *we* wrote the parent context and
+     * named the C variables we need/use with the same name everywhere.
+     */
+    provided sequence   e1000::create_device()
+    {
+        provided chunk  Ethernet::create_device;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::destroy_device()
+    {
+        provided chunk  Ethernet::destroy_device;
+        provided chunk  ::CALL;
+    }
+
+    /*
+     * This should take an e1000::Context as the first argument but this was
+     * not working as wished.
+     */
+    provided sequence   e1000::print_status()
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::setup_interrupt_handler()
+    {
+        provided chunk  LKM::includes; // work without this one
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::free_interrupt_handler()
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::activate_device_interruption()
+    {
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::set_up_device()
+    {
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::handle_intr()
+    {
+        provided chunk  ::CALL;
+    }
+
+
+    provided sequence   e1000::register_read32(e1000::Context, e1000::Register)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::register_write32(e1000::Context, e1000::Register, ::number)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::register_set32(e1000::Context, e1000::Register, ::number)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+        provided chunk  ::CALL;
+    }
+}
diff --git a/rathaxes/samples/e1000/ethernet.blt b/rathaxes/samples/e1000/ethernet.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/ethernet.blt
@@ -0,0 +1,218 @@
+with Ethernet, PCI, LKM, Log
+{
+    template type   Ethernet::Device()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/netdevice.h>
+            #include <linux/etherdevice.h>
+        }
+
+        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;
+
+                /* while waiting on issue #8 */
+                struct rtx_e1000_ctx    hw_ctx;
+            };
+        }
+
+        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)
+            {
+                struct rtx_ethernet_dev* rtx_ether_dev =  netdev_priv(dev);
+                struct rtx_e1000_ctx* ctx = &rtx_ether_dev->hw_ctx;
+
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_xmit(struct sk_buff* skb, 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)
+            {
+                struct rtx_ethernet_dev* rtx_ether_dev =  netdev_priv(dev);
+                struct rtx_e1000_ctx* ctx = &rtx_ether_dev->hw_ctx;
+
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        /*
+         * We can't use the irqreturn_t type here because CNornm doesn't know
+         * it.
+         */
+        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)
+            {
+                struct rtx_ethernet_dev* rtx_ether_dev;
+                struct rtx_e1000_ctx* ctx;
+
+                rtx_ether_dev = dev_id;
+                ctx = &rtx_ether_dev->hw_ctx;
+                ${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 = rtx_ethernet_xmit,
+            };
+        }
+
+        chunk PCI::pci_probe_hook()
+        {
+            /*
+             * 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")};
+                /*
+                 * Again, the error should be "raised" in the parent context.
+                 *
+                 * Here we know that we should return ENOMEM because *we* wrote
+                 * the parent context.
+                 */
+                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")};
+                return error;
+            }
+
+            /* same problem as above with ${pdev} */
+            //pci_set_drvdata(${pdev}, net_dev);
+            pci_set_drvdata(pdev, net_dev);
+
+            ${pointcut Ethernet::create_device};
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    template sequence   Ethernet::exit(PCI::Device pdev)
+    {
+        chunk   PCI::pci_remove_hook()
+        {
+            struct net_device *net_dev = pci_get_drvdata(pdev);
+
+            ${pointcut Ethernet::destroy_device};
+
+            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);
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+}
+
diff --git a/rathaxes/samples/e1000/ethernet.rti b/rathaxes/samples/e1000/ethernet.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/ethernet.rti
@@ -0,0 +1,47 @@
+interface Ethernet : Socket, 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::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        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;
+    }
+
+    provided sequence   Ethernet::init(PCI::Device)
+    {
+        provided chunk  LKM::data;
+        provided chunk  PCI::pci_probe_hook;
+        provided chunk  ::CALL;
+
+        provided pointcut   Ethernet::create_device;
+    }
+
+    provided sequence   Ethernet::exit(PCI::Device)
+    {
+        provided chunk  ::CALL;
+        provided chunk  PCI::pci_remove_hook;
+
+        provided pointcut   Ethernet::destroy_device;
+    }
+}
diff --git a/rathaxes/samples/e1000/lkm.blt b/rathaxes/samples/e1000/lkm.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/lkm.blt
@@ -0,0 +1,55 @@
+with LKM
+{
+    /* Skel of the generated C file: */
+    ${pointcut LKM::includes};
+    ${pointcut LKM::prototypes};
+    ${pointcut LKM::data};
+    ${pointcut LKM::code};
+
+    template sequence   LKM::init()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/module.h>
+            #include <linux/kernel.h>
+        }
+
+        chunk   LKM::data()
+        {
+            MODULE_DESCRIPTION(${config.description});
+            MODULE_AUTHOR(${config.author});
+            MODULE_LICENSE(${config.license});
+        }
+
+        chunk   LKM::code()
+        {
+            /*
+             * Rathaxes doesn't yet support arbitrary "decorators" like __init
+             * or __exit.
+             */
+            static int __attribute__((__section__(".init.text")))  rtx_module_init(void)
+            {
+                ${pointcut ::IMPLEMENTATION};
+                ${pointcut LKM::init_bus_hook};
+
+                return 0;
+            }
+
+            module_init(rtx_module_init);
+        }
+    }
+
+    template sequence   LKM::exit()
+    {
+        chunk   LKM::code()
+        {
+            static void __attribute__((__section__(".exit.text")))   rtx_module_exit(void)
+            {
+                ${pointcut ::IMPLEMENTATION};
+                ${pointcut LKM::deinit_bus_hook};
+            }
+
+            module_exit(rtx_module_exit);
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/lkm.rti b/rathaxes/samples/e1000/lkm.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/lkm.rti
@@ -0,0 +1,25 @@
+interface LKM
+{
+    provided pointcut   LKM::includes;
+    /* maybe it should be possible to use chunk ::decl in sequence templates? */
+    provided pointcut   LKM::prototypes;
+    provided pointcut   LKM::data;
+    provided pointcut   LKM::code;
+
+    required variable ::string  LKM::author;
+    required variable ::string  LKM::description;
+    required variable ::string  LKM::license;
+
+    required sequence   LKM::init()
+    {
+        provided chunk      LKM::includes;
+        provided chunk      LKM::code;
+        provided pointcut   LKM::init_bus_hook;
+    }
+
+    required sequence   LKM::exit()
+    {
+        provided chunk      LKM::code;
+        provided pointcut   LKM::deinit_bus_hook;
+    }
+}
diff --git a/rathaxes/samples/e1000/lkm.rtx b/rathaxes/samples/e1000/lkm.rtx
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/lkm.rtx
@@ -0,0 +1,52 @@
+device LKM use LKM, PCI, Ethernet, Log
+{
+    Ethernet::open(Ethernet::Device dev)
+    {
+        Log::info("Open the device");
+        e1000::setup_interrupt_handler();
+        Log::info("Interrupt handler installed");
+        e1000::set_up_device();
+        e1000::activate_device_interruption();
+    }
+
+    Ethernet::close(Ethernet::Device dev)
+    {
+        Log::info("Close the device");
+        e1000::free_interrupt_handler();
+    }
+
+    Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        Log::info("Got an interruption");
+        e1000::handle_intr();
+    }
+
+    Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        Log::info("We have one packet to transmit!");
+    }
+
+    LKM::init()
+    {
+        Log::info("Hello this is LKM");
+    }
+
+    LKM::exit()
+    {
+        Log::info("Good bye this was LKM");
+    }
+}
+
+configuration
+{
+    LKM::name = "hello";
+    LKM::author = "Rathaxes";
+    LKM::description = "Hello World Loadable Kernel Module (LKM)";
+    LKM::license = "GPL";
+
+    PCI::vendor_id = 0x8086;
+    PCI::product_id = 0x100f;
+    PCI::set_master = true;
+
+    Ethernet::ifname = "rtx%d";
+}
diff --git a/rathaxes/samples/e1000/log.blt b/rathaxes/samples/e1000/log.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/log.blt
@@ -0,0 +1,10 @@
+with Log
+{
+    template sequence   Log::info(::string msg)
+    {
+        chunk   ::CALL
+        {
+            pr_info("%s\n", ${msg});
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/log.rti b/rathaxes/samples/e1000/log.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/log.rti
@@ -0,0 +1,7 @@
+interface Log
+{
+    provided sequence   Log::info(::string)
+    {
+        provided chunk  ::CALL;
+    }
+}
diff --git a/rathaxes/samples/e1000/pci.blt b/rathaxes/samples/e1000/pci.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/pci.blt
@@ -0,0 +1,143 @@
+with PCI, LKM, Log
+{
+    template type   PCI::Device()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/pci.h>
+        }
+
+        chunk   ::decl()
+        {
+            struct pci_dev;
+        }
+
+        chunk   ::init(pci_dev)
+        {
+            ${self} = ${pci_dev};
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   PCI::probe(PCI::Device pdev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int /* __devinit */  rtx_pci_probe(struct pci_dev *,
+                                                      const struct pci_device_id *);
+        }
+
+        chunk LKM::code()
+        {
+            static int /* __devinit */  rtx_pci_probe(struct pci_dev *pdev,
+                                                      const struct pci_device_id *pdev_id)
+            {
+                int             err;
+
+                err = pci_enable_device(pdev);
+                if (err < 0)
+                    goto fail;
+
+                ${pointcut PCI::pci_probe_hook};
+
+                return 0;
+
+                fail:
+                    return err;
+            }
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    template sequence   PCI::remove(PCI::Device pdev)
+    {
+        chunk LKM::prototypes()
+        {
+            static void rtx_pci_remove(struct pci_dev *);
+        }
+
+        chunk LKM::code()
+        {
+            static void rtx_pci_remove(struct pci_dev *pdev)
+            {
+                ${pointcut PCI::pci_remove_hook};
+
+                pci_disable_device(pdev);
+            }
+        }
+
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   PCI::register()
+    {
+        chunk LKM::data()
+        {
+            /*
+             * CNorm doesn't seem to like "dynamic" arrays (i.e: you always
+             * have to specify the exact size).
+             */
+            static struct pci_device_id rtx_pci_device_table[2] = {
+                { ${config.vendor_id}, ${config.product_id}, PCI_ANY_ID, PCI_ANY_ID },
+                { 0, }
+            };
+
+            static struct pci_driver rtx_pci_driver = {
+                .name = ${config.name},
+                .id_table = rtx_pci_device_table,
+                .probe = rtx_pci_probe,
+                .remove = rtx_pci_remove
+            };
+        }
+
+        chunk LKM::init_bus_hook()
+        {
+            int error;
+            if ((error = pci_register_driver(&rtx_pci_driver)))
+            {
+                 ${Log::info("Cannot register pci driver")};
+                 /*
+                  * So we catched the error but how do we return it to the
+                  * parent context?
+                  *
+                  * Here we know that we can just return error, but that's just
+                  * a coincidence (and, in this case, *we* wrote the parent
+                  * context).
+                  */
+                 return error;
+            }
+        }
+
+        chunk ::CALL()
+        {
+            /*
+             * The implementation of ::CALL is empty. This template sequence is
+             * actually not provided nor required.
+             *
+             * This sequence is just "intermediate" code that will just inject
+             * itself in the hook LKM::init_bus_hook for which this sequence
+             * has a chunk (see above chunk).
+             */
+        }
+    }
+
+    template sequence   PCI::unregister()
+    {
+        chunk   LKM::deinit_bus_hook()
+        {
+            pci_unregister_driver(&rtx_pci_driver);
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/pci.rti b/rathaxes/samples/e1000/pci.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/pci.rti
@@ -0,0 +1,36 @@
+interface PCI : LKM
+{
+    provided type       PCI::Device;
+
+    required variable ::number  PCI::vendor_id;
+    required variable ::number  PCI::product_id;
+
+    provided sequence   PCI::register()
+    {
+        provided chunk  ::CALL;
+        provided chunk  LKM::data;
+        provided chunk  LKM::init_bus_hook;
+    }
+
+    provided sequence   PCI::unregister()
+    {
+        provided chunk  ::CALL;
+        provided chunk  LKM::deinit_bus_hook;
+    }
+
+    provided sequence   PCI::probe(PCI::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+
+        provided pointcut   PCI::pci_probe_hook;
+    }
+
+    provided sequence   PCI::remove(PCI::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+
+        provided pointcut   PCI::pci_remove_hook;
+    }
+}
diff --git a/rathaxes/samples/e1000/socket.blt b/rathaxes/samples/e1000/socket.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/socket.blt
@@ -0,0 +1,27 @@
+with Socket, LKM
+{
+    template type Socket::SKBuff()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/skbuff.h>
+        }
+
+        chunk ::decl()
+        {
+            struct sk_buff;
+        }
+
+        chunk ::init()
+        {
+        }
+
+        map
+        {
+            // some work may have to be done here in order
+            // to access to some field of the sk_buff.
+            // We should determine if all the sk_buff management
+            // can be abstracted from the user.
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/socket.rti b/rathaxes/samples/e1000/socket.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/socket.rti
@@ -0,0 +1,4 @@
+interface Socket : LKM
+{
+    provided type Socket::SKBuff;
+}
