# HG changeset patch
# Parent 06f2b107580ea4f01d8e9333161d1e1b5f0de759
rathaxes: rewrite/refactor all the e1000 device dependent code

diff --git a/notes.txt b/notes.txt
new file mode 100644
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1,54 @@
+Remarks for David & Lionel:
+
+- Too much changes to not start over;
+- Lack of methods is extremely annoying and requires a lot of workarounds (e.g:
+  see the register read/write/set/unset methods on e1000::Context);
+- I'm using a pointcut inside the ethernet context "decl data_types" to inject
+  my hardware context; it's impossible to get it back without hardcoding stuff,
+  because the ethernet subsystem isn't aware of the type of the field (so I
+  can't write an attribute). Being able to just inject a type (instead of a
+  whole structure field) might not be the best solution but would solve this
+  issue/use case;
+- Can I use the same name for a pointcut and a sequence (e.g: Ethernet::send
+  bot a sequence name and a pointcut same thing for the interrupt handler);
+- Lack of support for circular dependencies make some code annoying or
+  incorrect (circular dependencies between abstract and concrete types via the
+  attribute or circular dependencies between the rings and the hardware
+  context);
+- It would be really cool to passthrough the comments;
+- Not being able to use a Rathaxes type in the attributes is really annoying
+  and forces me to hardcode a lot of stuff;
+- Pointcuts are scoped by subsystems which is kinda weird because different
+  subsystems are going to share the same pointcuts (i.e: the PCI and USB
+  susystem are going to expose the same pointcuts that can be used by the
+  Ethernet subsystem);
+- The compiler adds a & when I try to expand e1000::Context.io_addr regardless
+  of whether the attribute is declared as Builtin::symbol.ref or .scalar;
+- No understanding of arrays, e.g: ${local.self.ring.buffs[local.i].sk_buffs}
+  (I'm not saying this is the right syntax).
+
+Todo/Totry:
+
+- Use the rtx_ether_ctx attribute on AbstractDevice to initialize Device
+  objects in ethernet.bl;
+- Worry about the code being executed concurrently, e.g: what happens if the
+  interrupt handler is called right before we disable it in the close path?
+- Why the DMA sequences are taking an AbstractDMAHandle instead of a DMAHandle?
+- Right now the Ethernet subsystem hooks into the PCI subsystem via the
+  Ethernet::init sequence, maybe the content of this sequence should be moved
+  under the Ethernet::Device type definition (maybe we could solve the
+  free_netdev call in case of failure at the same time);
+- Likewise sequences in the PCI subsystem could be moved to the PCI::Device
+  type definition;
+- PCI::Device.device should be PCI::Device.k_device;
+- The Ethernet's chunk for PCI::pci_remove_hook should probably call cleanup
+  functions from the Context and the Rings, unless the kernel alway calls
+  rtx_ethernet_close first automatically.
+
+Questions:
+
+Compiler bugs:
+
+- Trying to expand a type that doesn't exists crashes the compiler instead of
+  displaying something more useful (took me 15 minutes to figure out that I
+  typed 3 colons instead of 2 in a placeholder).
diff --git a/rathaxes/compiler/passes/back/rtxIntrospect.inc.cws b/rathaxes/compiler/passes/back/rtxIntrospect.inc.cws
--- a/rathaxes/compiler/passes/back/rtxIntrospect.inc.cws
+++ b/rathaxes/compiler/passes/back/rtxIntrospect.inc.cws
@@ -890,6 +890,8 @@
     local to_remove;
     foreach placeHolder in local_node.body.compile
     {
+        if (!placeHolder.node.body.type)
+            traceLine(RED + "BUG: placeHolder.node.body.type is missing in rtxIntrospect_walk<\"__rtx_chunk__\">, placeHolder = " + toString(placeHolder) + DEFAULT_COLOR);
         if (rtxIntrospect_InferPlaceHolderTypes<placeHolder.node.body.type>(placeHolder.node.body, local_node) == false)
         {
             pushItem to_remove;
diff --git a/rathaxes/samples/e1000/CMakeLists.txt b/rathaxes/samples/e1000/CMakeLists.txt
--- a/rathaxes/samples/e1000/CMakeLists.txt
+++ b/rathaxes/samples/e1000/CMakeLists.txt
@@ -17,8 +17,8 @@
                      dma.blt
                      pci.blt
                      socket.blt
-                     e1000.blt
-                     ethernet.blt)
+                     ethernet.blt
+                     e1000.blt)
 
 IF (LINUX_KBUILD_DIR)
     ADD_RATHAXES_LKM(e1000 e1000_src)
diff --git a/rathaxes/samples/e1000/e1000.blt b/rathaxes/samples/e1000/e1000.blt
--- a/rathaxes/samples/e1000/e1000.blt
+++ b/rathaxes/samples/e1000/e1000.blt
@@ -1,564 +1,8 @@
-with e1000, Ethernet, Socket, DMA, PCI, LKM, Log, Builtin
+with e1000, Ethernet, Socket, Device, DMA, PCI, LKM, Log, Builtin
 {
-    template type   e1000::RxDescriptor()
+    template type e1000::Register()
     {
-        decl    data_types()
-        {
-            __le64          buff_addr;
-            __le16          length;
-            __le16          csum;
-            unsigned char   status;
-            unsigned char   errors;
-            __le16          special;
-        }
-
-        chunk   LKM::includes()
-        {
-            #include <linux/types.h>
-        }
-
-        method  init()
-        {
-        }
-
-        map
-        {
-        }
-    }
-
-    /*
-     * This is a generic tx descriptor for the e1000. When you use TCP
-     * Segmentation Offload (TSO) the hardware actually uses two types of
-     * tx descriptors in its tx ring:
-     * - context descriptors: this descriptor doesn't actually point to data to
-     *   send but initialize the offloading engine for the data descriptor that
-     *   follow;
-     * - data descriptors: this descriptor points to data from the skbuffs.
-     */
-    template type   e1000::TxDescriptor()
-    {
-        decl    data_types()
-        {
-            __le64                  buff_addr;
-            union
-            {
-                __le32              data;
-                struct
-                {
-                    __le16          length;
-                    unsigned char   csum_offset; /* CSO */
-                    unsigned char   cmd;
-                }                   fields;
-            }                       lower;
-            union
-            {
-                __le32              data;
-                struct
-                {
-                    unsigned char   status;
-                    unsigned char   csum_start; /* CSS */
-                    __le16          special;
-                }                   fields;
-            }                       upper;
-        }
-
-        method init()
-        {
-        }
-
-        map
-        {
-        }
-    }
-
-    /*
-     * Ring of e1000::RxDescriptors and their corresponding skbuffs.
-     *
-     * - size: total size of the ring in bytes.
-     * - base: address of the ring (we can't use the typedef here until we get
-     *   CNorm unstrict);
-     * - dma_base: (physical) address of the ring where the device can access
-     *   the different descriptors;
-     * - skbuffs: array of the skbuffs and their dma (physical) address
-     *   associated with each descriptor.
-     */
-    template type   e1000::RxRing()
-    {
-        decl    data_types()
-        {
-            unsigned int                    size;
-            ${e1000::RxDescriptor.ref}      base;
-            dma_addr_t                      dma_base;
-            ${Socket::SKBuff}               skbuffs[${config.rx_ring_size}];
-        }
-
-        method init()
-        {
-        }
-
-        chunk   Ethernet::adapter_init_rx(Ethernet::Device rtx_ether_ctx)
-        {
-            {
-                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-
-                /*
-                 * Receive initialization (section 14.4):
-                 *
-                 * 1. Program the receive address, in RAL/RAH;
-                 * 2. Initialize the Multicast Table Array;
-                 * 3. Program the interrupt mask register (done in
-                 *    e1000::activate_device_interruption);
-                 * 4. Allocate the receive descriptor ring and map it to make it
-                 *    accessible by the device;
-                 * 5. Write the start address of the ring in RDBAL/RDBAH and set
-                 *    RDLEN (Receive Descriptor Length) to the size of the ring;
-                 * 6. Set the RDH/RDT (Receive Descriptor Head/Tail) indexes to the
-                 *    beginning and end of the ring;
-                 * 7. Make sure that RCTL.BSIZE and .BSEX are at 0 to configure the
-                 *    receive buffer size to 2048 bytes (e1000::rx_buffer_len).
-                 * 8. Set RCTL.EN to enable the receiver.
-                 *
-                 * The ugly casts here are caused by the lack of CNorm unstrict.
-                 */
-
-                int i;
-
-                /* 1. Program the receive address */
-
-                /* (We should use uint{32,16}_t but CNorm doesn't know them yet) */
-                rtx_e1000_register_write32(hw_ctx, E1000_RAL,
-                        *(unsigned int *)(${rtx_ether_ctx.dev_addr}));
-                /*
-                 * The 16 upper bits of RAH also store the AS bits (which should be
-                 * 0) and the AV bit (should be 1 to set the address as valid).
-                 */
-                rtx_e1000_register_write32(hw_ctx, E1000_RAH,
-                        *(unsigned short *)(&${rtx_ether_ctx.dev_addr}[4]));
-                rtx_e1000_register_set32(hw_ctx, E1000_RAH, E1000_RAH_AV);
-
-                ${Log::info("adapter_init_rx: receive address programmed")};
-
-                /* 2. Initialize the MTA */
-
-                for (i = 0; i != 128; ++i)
-                    rtx_e1000_register_write32(hw_ctx, E1000_MTA + i * 4, 0);
-
-                ${Log::info("adapter_init_rx: MTA init done")};
-
-                /* 4. Setup the receive descriptor ring */
-
-                /* Allocate the descriptors */
-                hw_ctx->rx_ring.size = ${config.rx_ring_size} * sizeof(*hw_ctx->rx_ring.base);
-                hw_ctx->rx_ring.size = ALIGN(hw_ctx->rx_ring.size, 4096);
-                hw_ctx->rx_ring.base = ${DMA::alloc_coherent(
-                    rtx_ether_ctx.device,
-                    local.hw_ctx.rx_ring.size,
-                    local.hw_ctx.rx_ring.dma_base.dma_handle
-                )};
-                if (!hw_ctx->rx_ring.base)
-                {
-                    ${Log::info("adapter_init_rx: cannot allocate the descriptors for the rx ring")};
-                    goto err_rx_ring_alloc;
-                }
-
-                ${Log::info("adapter_init_rx: rx descriptors allocated")};
-
-                /*
-                 * Allocate the skbuffs, map them for DMA, and write their address
-                 * in the corresponding descriptor.
-                 */
-                for (i = 0; i != ${config.rx_ring_size}; ++i)
-                {
-                    ${Socket::SKBuff.ref} skbuff = &hw_ctx->rx_ring.skbuffs[i];
-                    // XXX #46: ${rtx_ether_ctx.init_rx_skbuff(local.skbuff, config.rx_buffer_len)};
-                    if (rtx_ethernet_init_rx_skbuff(${local.skbuff}, ${config.rx_buffer_len}))
-                    {
-                        ${Log::info("adapter_init_rx: cannot allocate a skbuff for the rx ring")};
-                        goto err_skbuffs_alloc;
-                    }
-                    // XXX #46: ${local.skbuff.map_from(rtx_ether_ctx.device)};
-                    if (rtx_socket_skbuff_map(${local.skbuff}, ${rtx_ether_ctx.device}, RTX_DMA_FROM_DEVICE))
-                    {
-                        ${Log::info("adapter_init_rx: cannot dma-map a skbuff for the rx ring")};
-                        goto err_skbuffs_map;
-                    }
-                    hw_ctx->rx_ring.base[i].buff_addr = cpu_to_le64(${local.skbuff.sk_buff});
-                }
-
-                // ${Log::info("adapter_init_rx: skbuffs allocated};
-                pr_info("rtx_e1k: adapter_init_rx: skbuffs allocated, headlen=%d", skb_headlen((struct sk_buff *)hw_ctx->rx_ring.skbuffs[i - 1].skbuff));
-
-                /* 5. Save the emplacement and the size of the ring in RDBA/RDLEN */
-                rtx_e1000_register_write32(hw_ctx, E1000_RDBAL, hw_ctx->rx_ring.dma_base & 0xffffffff);
-                rtx_e1000_register_write32(hw_ctx, E1000_RDBAH, hw_ctx->rx_ring.dma_base >> 32);
-                rtx_e1000_register_write32(hw_ctx, E1000_RDLEN, hw_ctx->rx_ring.size);
-
-                /* 6. Setup RDH/RDT */
-                rtx_e1000_register_write32(hw_ctx, E1000_RDH, 0);
-                rtx_e1000_register_write32(hw_ctx, E1000_RDT, ${config.rx_ring_size} - 1);
-
-                /* 7. Configure the buffer size, */
-                rtx_e1000_register_set32(hw_ctx, E1000_RCTL, E1000_RCTL_BSIZE_${config.rx_buffer_len});
-
-                /* 8. Enable the receiver */
-                rtx_e1000_register_set32(hw_ctx, E1000_RCTL, E1000_RCTL_EN);
-
-                ${Log::info("adapter_init_rx: receive registers configured and receiver enabled")};
-
-                /*
-                 * XXX: We can't return here since we are not in a function but
-                 * in a chunk of code (injected in a function).
-                 */
-                goto init_rx_ok;
-
-            err_skbuffs_alloc:
-                while (i--)
-                {
-                    dma_unmap_single(
-                            ${rtx_ether_ctx.device},
-                            /* XXX Leaking cast because of the array: */
-                            *((dma_addr_t *)&(hw_ctx->rx_ring.skbuffs[i].dma_handle)),
-                            ${config.rx_buffer_len},
-                            DMA_FROM_DEVICE);
-            err_skbuffs_map:
-                    /* XXX leaking cast: */
-                    dev_kfree_skb((struct sk_buff *)hw_ctx->rx_ring.skbuffs[i].skbuff);
-                }
-
-                dma_free_coherent(${rtx_ether_ctx.device}, hw_ctx->rx_ring.size,
-                        hw_ctx->rx_ring.base, hw_ctx->rx_ring.dma_base);
-            err_rx_ring_alloc:
-                /*
-                 * XXX: Likewise, if there is something else to rollback in the
-                 * enclosing function, this won't be done.
-                 */
-                return -ENOMEM;
-
-            init_rx_ok: (void)0; /* NOP, to make this a valid label. */
-            }
-        }
-
-        map
-        {
-            size: ((${self}).size);
-            dma_base: ((${self}).dma_base);
-        }
-    }
-
-    /*
-     * Ring of e1000::TxDescriptors, this is a bit similar to the Rx ring except
-     * that we don't really have to manage the skbuffs themselves (they are
-     * given to use by the kernel).
-     *
-     * - size: total size of the ring in bytes.
-     * - base: address of the ring (we can't use the typedef here until we get
-     *   CNorm unstrict);
-     * - dma_base: (physical) address of the ring where the device can access
-     *   the different descriptors;
-     * - skbuffs: the skbuffs associated with each descriptor of the ring;
-     * - head: index on the head of the ring;
-     * - tail: index on the tail of the ring.
-     *
-     * Keep in mind that the head and tail fields are, obviously, not
-     * synchronized with TDT/TDH on the device.
-     */
-    template type   e1000::TxRing()
-    {
-        decl    data_types()
-        {
-            unsigned int                    size;
-            /* XXX: can't use ${e1000::TxDescriptor} here: */
-            ${e1000::TxDescriptor.ref}      base; /* rename to descs */
-            dma_addr_t                      dma_base;
-            ${Socket::SKBuff}               skbuffs[${config.tx_ring_size}];
-            unsigned short                  head;
-            unsigned short                  tail;
-        }
-
-        chunk   LKM::prototypes()
-        {
-            static void         rtx_e1000_tx_ring_clean(${e1000::TxRing.ref});
-            static unsigned int rtx_e1000_tx_ring_descriptors_remaining(${e1000::TxRing.ref});
-            static int          rtx_e1000_tx_ring_tso_cksum_offload(${e1000::TxRing.ref}, ${Socket::SKBuff.ref});
-            static void         rtx_e1000_tx_ring_put(${e1000::TxRing.ref}, ${Socket::SKBuff.ref});
-            /* FIXME: See issue #54 */
-            static void         rtx_e1000_tx_ring_start_xmit(${e1000::TxRing.ref}, /*const*/ ${e1000::Context.ref});
-        }
-
-        chunk   LKM::code()
-        {
-            static void         rtx_e1000_tx_ring_clean(${e1000::TxRing.ref} self)
-            {
-                ${e1000::TxDescriptor.ref}  tx_desc;
-                bool                        done;
-
-                for (; self->head != self->tail; self->head++)
-                {
-                    tx_desc = &self->base[self->head];
-                    done = tx_desc->upper.fields.status & E1000_TXD_STAT_DD;
-                    if (!done)
-                        break ;
-                }
-
-                pr_info("%s: tx_ring_clean: moving head to %d/%d", ${config.name}, self->head, ${config.tx_ring_size});
-            }
-
-            static unsigned int rtx_e1000_tx_ring_descriptors_remaining(${e1000::TxRing.ref} self)
-            {
-                if (self->tail == self->head) /* ring is empty */
-                    return 256; /* XXX: ${config.tx_ring_size}; */
-                if (self->tail > self->head)
-                    /* XXX: ${config.tx_ring_size} */
-                    return 256 - (self->tail - self->head);
-                return self->head - self->tail;
-            }
-
-            static int          rtx_e1000_tx_ring_tso_cksum_offload(${e1000::TxRing.ref} self, ${Socket::SKBuff.ref} skb)
-            {
-                ${Socket::AbstractSKBuff.ref} k_skb = skb->skbuff;
-                return skb_is_gso(${local.k_skb.k_sk_buff}) || ${local.k_skb.k_sk_buff}->ip_summed == CHECKSUM_PARTIAL;
-            }
-
-            static void         rtx_e1000_tx_ring_put(${e1000::TxRing.ref} self, ${Socket::SKBuff.ref} skb)
-            {
-                WARN_ON(!skb);
-
-                /*
-                 * Mark it as the last buffer (EOP) and ask the card to
-                 * insert the Ethernet FCS (Frame Check Sequence).
-                 *
-                 * XXX: it sucks to use skb_headlen() here (this part of the
-                 * code shouldn't be aware of it and use something more
-                 * abstract.
-                 */
-                ${Socket::AbstractSKBuff.ref} k_skb = skb->skbuff;
-                ${e1000::TxDescriptor.ref} tx_desc = &self->base[self->tail];
-                tx_desc->lower.data = cpu_to_le32(
-                        E1000_TXD_CMD_EOP  |
-                        E1000_TXD_CMD_IFCS |
-                        E1000_TXD_CMD_RS   |
-                        skb_headlen(${local.k_skb.k_sk_buff}));
-                tx_desc->upper.data = 0;
-                tx_desc->buff_addr = cpu_to_le64(${local.skb.dma_handle.k_dma_handle});
-                memcpy(&self->skbuffs[self->tail], ${local.k_skb.k_sk_buff}, sizeof(*${local.k_skb.k_sk_buff}));
-                self->tail = (self->tail + 1) % ${config.tx_ring_size};
-            }
-
-            /* FIXME: See issue #54 */
-            static void         rtx_e1000_tx_ring_start_xmit(${e1000::TxRing.ref} self, /*const*/ ${e1000::Context.ref} hw_ctx)
-            {
-                pr_info("%s: start_xmit: moving tail to %d/%d", ${config.name}, self->tail, ${config.tx_ring_size});
-                rtx_e1000_register_write32(hw_ctx, E1000_TDT, self->tail);
-            }
-        }
-
-        chunk   Ethernet::adapter_init_tx(Ethernet::Device rtx_ether_ctx)
-        {
-            {
-                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-
-                /*
-                 * Transmission initialization (section 14.5):
-                 *
-                 * 1. Allocate the transmit descriptors ring and map it to make it
-                 *    accessible by the device;
-                 * 2. Write the start address of the ring in TDBAL/TDBAH and set
-                 *    TDLEN to the size of the ring;
-                 * 3. Set the TDH/TDT indexes to the beginning and end of the ring;
-                 * 4. Set TCTL.PSP to pad short packets and TCTL.EN to enable the
-                 *    transmitter.
-                 */
-
-                /* 1. Allocate the tx ring */
-                hw_ctx->tx_ring.size = ${config.tx_ring_size} * sizeof(*hw_ctx->tx_ring.base);
-                hw_ctx->tx_ring.size = ALIGN(hw_ctx->tx_ring.size, 4096);
-                hw_ctx->tx_ring.base = dma_alloc_coherent(
-                        ${rtx_ether_ctx.device},
-                        hw_ctx->tx_ring.size,
-                        &hw_ctx->tx_ring.dma_base,
-                        GFP_KERNEL);
-                if (!hw_ctx->rx_ring.base)
-                {
-                    ${Log::info("adapter_init_tx: cannot allocate the descriptors for the tx ring")};
-                    /*
-                     * XXX: If there is something else to rollback in the enclosing
-                     * function, this won't be done.
-                     */
-                    return -ENOMEM;
-                }
-
-                ${Log::info("adapter_init_tx: tx descriptors allocated")};
-
-                /* 2. Save the emplacement and the size of the ring in TDBA/TDLEN */
-                rtx_e1000_register_write32(hw_ctx, E1000_TDBAL, hw_ctx->tx_ring.dma_base & 0xffffffff);
-                rtx_e1000_register_write32(hw_ctx, E1000_TDBAH, hw_ctx->tx_ring.dma_base >> 32);
-                rtx_e1000_register_write32(hw_ctx, E1000_TDLEN, hw_ctx->tx_ring.size);
-
-                /* 3. Setup TDH/TDT to zero: the queue is empty */
-                rtx_e1000_register_write32(hw_ctx, E1000_TDH, 0);
-                rtx_e1000_register_write32(hw_ctx, E1000_TDT, 0);
-                hw_ctx->tx_ring.head = 0;
-                hw_ctx->tx_ring.tail = 0;
-
-                /* 4. Set TCTL.PSP and enable the transmitter */
-                rtx_e1000_register_set32(hw_ctx, E1000_TCTL, E1000_TCTL_PSP|E1000_TCTL_EN);
-
-                ${Log::info("adapter_init_tx: transmit registers configured and transmitter enabled")};
-            }
-        }
-
-        method  clean()
-        {
-            rtx_e1000_tx_ring_clean(${self});
-        }
-
-        method   descriptors_remaining()
-        {
-            rtx_e1000_tx_ring_descriptors_remaining(${self});
-        }
-
-        method   tso_cksum_offload(Socket::SKBuff skb)
-        {
-        }
-
-        method   put(Socket::SKBuff skb)
-        {
-            rtx_e1000_tx_ring_put(${self}, &${skb});
-        }
-
-        method   start_xmit(e1000::Context ctx)
-        {
-            rtx_e1000_tx_ring_start_xmit(${self}, ${ctx});
-        }
-
-        method init()
-        {
-        }
-
-        map
-        {
-        }
-    }
-
-    template type   e1000::Context()
-    {
-        decl    data_types()
-        {
-            int                         bars;
-            unsigned char /* __iomem */ *ioaddr;
-            ${e1000::RxRing.scalar}     rx_ring;
-            ${e1000::TxRing.scalar}     tx_ring;
-        }
-
-        chunk   Ethernet::HardwareContext()
-        {
-            /*
-             * 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).
-             */
-            ${e1000::Context}   hw_ctx;
-        }
-
-        chunk   Ethernet::adapter_init_context(Ethernet::Device rtx_ether_ctx,
-                                               Builtin::number bars,
-                                               Builtin::symbol ioaddr)
-        {
-            {
-                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-                hw_ctx->bars = ${bars};
-                hw_ctx->ioaddr = ${ioaddr};
-            }
-        }
-
-        chunk   Ethernet::adapter_reset(Ethernet::Device rtx_ether_ctx)
-        {
-            {
-                /* XXX Naming this variable 'hw_ctx' kicks the decl out of the generated code */
-                ${e1000::Context.ref} tmp_hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-                rtx_e1000_register_write32(tmp_hw_ctx, E1000_CTRL, E1000_CMD_RST);
-                udelay(10);
-            }
-        }
-
-        chunk   Ethernet::adapter_load_mac_address(Ethernet::Device rtx_ether_ctx)
-        {
-            {
-                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-                /* Shamelessly borrowed from Minix */
-                for (int i = 0; i < 3; ++i)
-                {
-                    rtx_e1000_register_write32(hw_ctx, E1000_EEPROM_READ, (i << 8) | 1);
-                    int value;
-                    do
-                        value = rtx_e1000_register_read32(hw_ctx, E1000_EEPROM_READ);
-                    while ((value & (1 << 4)) == 0);
-                    value >>= 16;
-                    /*
-                     * NOTE: I'm not sure if Ethernet::Device should be
-                     * accessed directly here. But since we need to take it in
-                     * parameter (so we can get back our e1000::Context) it
-                     * seems inadequate to set this in another way:
-                     */
-                    ${rtx_ether_ctx.dev_addr}[i * 2] = value & 0xff;
-                    ${rtx_ether_ctx.dev_addr}[i * 2 + 1] = (value >> 8) & 0xff;
-                }
-                ${Log::info("e1000::create: mac address loaded from the EEPROM")};
-            }
-        }
-
-        chunk   Ethernet::adapter_setup(Ethernet::Device rtx_ether_ctx)
-        {
-            {
-                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-
-                /*
-                 * "General Configuration" (section 14.3):
-                 *
-                 * - CTRL.ASDE/CTRL.SLU: Let the PHY handle the speed detection &
-                 *   negociation;
-                 * - CTRL.LRST/FRCSPD: Unset them to initiate the auto-negociation;
-                 * - CTRL.PHY_RST: Unset it;
-                 * - CTRL.ILOS: Unset it (ILOS is Invert Loss Of Signal);
-                 * - CTRL.VME: Make sure it's not set to disable VLAN support;
-                 * - Set the control flow registers to 0;
-                 * - Finally, initialize all the statistic registers from
-                 *   E1000_CRCERRS to E1000_TSCTFC.
-                 */
-                rtx_e1000_register_set32(hw_ctx, E1000_CTRL,
-                                         E1000_CMD_ASDE |
-                                         E1000_CMD_SLU);
-                rtx_e1000_register_unset32(hw_ctx, E1000_CTRL,
-                                           E1000_CMD_LRST    |
-                                           E1000_CMD_FRCSPD  |
-                                           E1000_CMD_PHY_RST |
-                                           E1000_CMD_ILOS    |
-                                           E1000_CMD_VME);
-                rtx_e1000_register_write32(hw_ctx, E1000_FCAH, 0);
-                rtx_e1000_register_write32(hw_ctx, E1000_FCAL, 0);
-                rtx_e1000_register_write32(hw_ctx, E1000_FCT, 0);
-                rtx_e1000_register_write32(hw_ctx, E1000_FCTTV, 0);
-                /*
-                 * XXX: Using int i clashes with another int i from the
-                 * "parent" chunk:
-                 */
-                for (int j = 0; j != 64; ++j)
-                    rtx_e1000_register_write32(hw_ctx, E1000_CRCERRS + j * 4, 0);
-
-                ${Log::info("adapter_setup: general configuration done")};
-            }
-        }
-
-        map
-        {
-            rx_ring: ((${self})->rx_ring);
-            //tx_ring: ((${self})->tx_ring); XXX Circular dep with Context
-        }
-    }
-
-    template type   e1000::Register()
-    {
-        decl    data_types()
+        decl data_types()
         {
             E1000_CTRL          = 0x00000, /* Device Control - RW */
             E1000_CTRL_DUP      = 0x00004, /* Device Control Duplicate (Shadow) - RW */
@@ -593,19 +37,14 @@
             E1000_TDT           = 0x03818, /* Transmit Descriptor Tail */
         }
 
-        method init(Builtin::number value)
-        {
-            ${self} = ${value};
-        }
-
         map
         {
         }
     }
 
-    template type   e1000::Commands()
+    template type e1000::Command()
     {
-        decl    data_types()
+        decl data_types()
         {
             E1000_CMD_FD                    = 0x00000001, /* Full duplex.0=half; 1=full */
             E1000_CMD_BEM                   = 0x00000002, /* Endian Mode.0=little,1=big */
@@ -670,9 +109,9 @@
         }
     }
 
-    template type   e1000::TxDescriptorFlags()
+    template type e1000::TxDescriptorFlag()
     {
-        decl  data_types()
+        decl data_types()
         {
             E1000_TXD_DTYP_D        = 0x00100000, /* Data Descriptor */
             E1000_TXD_DTYP_C        = 0x00000000, /* Context Descriptor */
@@ -701,326 +140,721 @@
         }
     }
 
-    /* TODO: make that a method of e1000::Context */
-    template sequence   e1000::print_status(Ethernet::Device rtx_ether_ctx)
+    template type e1000::RxDescriptor()
     {
-        chunk   LKM::prototypes()
+        decl data_types()
+        {
+            __le64          buff_addr;
+            __le16          length;
+            __le16          csum;
+            unsigned char   status;
+            unsigned char   errors;
+            __le16          special;
+        }
+
+        map
+        {
+        }
+    }
+
+    // This is a generic tx descriptor for the e1000. When you use TCP
+    // Segmentation Offload (TSO) the hardware actually uses two types of
+    // tx descriptors in its tx ring:
+    // - context descriptors: this descriptor doesn't actually point to data to
+    //   send but initialize the offloading engine for the data descriptor that
+    //   follow;
+    // - data descriptors: this descriptor points to data from the skbuffs.
+    template type e1000::TxDescriptor()
+    {
+        decl data_types()
+        {
+            __le64                  buff_addr;
+            union {
+                __le32              data;
+                struct {
+                    __le16          length;
+                    unsigned char   csum_offset; /* CSO */
+                    unsigned char   cmd;
+                }                   fields;
+            }                       lower;
+            union {
+                __le32              data;
+                struct {
+                    unsigned char   status;
+                    unsigned char   csum_start; /* CSS */
+                    __le16          special;
+                }                   fields;
+            }                       upper;
+        }
+
+        map
+        {
+        }
+    }
+
+    template type e1000::Buffer()
+    {
+        decl data_types()
+        {
+            ${Socket::SKBuff}   sk_buff;
+            ${DMA::DMAHandle}   dma;
+        }
+
+        method init(Socket::SKBuff sk_buff, DMA::DMAHandle dma)
+        {
+            ${self.sk_buff} = ${sk_buff};
+            ${self.dma} = ${dma};
+        }
+
+        map
+        {
+            sk_buff: ${self}->sk_buff;
+            dma: ${self}->dma;
+        }
+    }
+
+    template type e1000::Context()
+    {
+        decl data_types()
+        {
+            ${Ethernet::Device.ref} net_dev;
+            // XXX should be annoted with __iomem:
+            unsigned char           *io_addr;
+            // XXX Forced to hardcode the type because there is a circular
+            // dependency between the rings and the context.
+            rtxType_e1000_TxRing    tx_ring;
+            rtxType_e1000_RxRing    rx_ring;
+        }
+
+        chunk LKM::includes()
+        {
+            #include <linux/types.h>
+        }
+
+        chunk LKM::prototypes()
         {
             static void rtx_e1000_print_status(${e1000::Context.ref});
+
+            // XXX Needed until we can call methods (issue #46):
+            static unsigned int rtx_e1000_reg_read32(${e1000::Context}, ${e1000::Register});
+            static void rtx_e1000_reg_write32(${e1000::Context}, ${e1000::Register}, unsigned int);
+            static void rtx_e1000_reg_set32(${e1000::Context}, ${e1000::Register}, unsigned int);
+            static void rtx_e1000_reg_unset32(${e1000::Context}, ${e1000::Register}, unsigned int);
         }
 
-        chunk   LKM::code()
+        chunk LKM::code()
         {
-            static void rtx_e1000_print_status(${e1000::Context.ref} hw_ctx)
+            static void rtx_e1000_print_status(${e1000::Context.ref} self)
             {
-                unsigned int status = rtx_e1000_register_read32(hw_ctx, E1000_STATUS);
-                    ${Log::info("card status:")};
-                /*
-                 * we can't use Log::info below because it just accept a string
-                 * (as opposed to a format string with its parameters).
-                 */
+                unsigned int status = rtx_e1000_reg_read32(${local.self}, E1000_STATUS);
+                ${Log::info("card status:")};
+                // XXX We can't use Log::info below because it just accepts a
+                // string (as opposed to a format string with its arguments):
                 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");
             }
+
+            static unsigned int rtx_e1000_reg_read32(${e1000::Context} self, ${e1000::Register} reg)
+            {
+                // XXX The compiler generates &self->io_addr instead of
+                // self->io_addr if I use ${local.self.io_addr}:
+                return ioread32(self->io_addr + reg);
+            }
+
+            static void rtx_e1000_reg_write32(${e1000::Context} self,
+                                              ${e1000::Register} reg,
+                                              unsigned int value)
+            {
+                return iowrite32(value, self->io_addr + reg);
+            }
+
+            static void rtx_e1000_reg_set32(${e1000::Context} self,
+                                            ${e1000::Register} reg,
+                                            unsigned int value)
+            {
+                return iowrite32(
+                    rtx_e1000_reg_read32(${local.self}, reg) | value,
+                    self->io_addr + reg
+                );
+            }
+
+            static void rtx_e1000_reg_unset32(${e1000::Context} self,
+                                              ${e1000::Register} reg,
+                                              unsigned int value)
+            {
+                return iowrite32(rtx_e1000_reg_read32(
+                    ${local.self}, reg) & ~value,
+                    self->io_addr + reg
+                );
+            }
         }
 
-        chunk   ::CALL()
+        method init(Ethernet::Device rtx_ether_ctx, Builtin::symbol io_addr)
         {
-            rtx_e1000_print_status(&${rtx_ether_ctx}->hw_ctx);
+            ${self.net_dev} = ${rtx_ether_ctx};
+            ${self.io_addr} = ${io_addr};
+        }
+
+        method reg_read32(e1000::Register reg)
+        {
+            ioread32(${self.io_addr} + ${local.reg});
+        }
+
+        method reg_write32(e1000::Register reg, Builtin::number value)
+        {
+            iowrite32(${value}, ${self.io_addr} + ${reg});
+        }
+
+        method reg_set32(e1000::Register reg, Builtin::number value)
+        {
+            iowrite32(ioread32(${self.io_addr} + ${reg}) | ${value}, ${self.io_addr} + ${reg});
+        }
+
+        method reg_unset32(e1000::Register reg, Builtin::number value)
+        {
+            iowrite32(ioread32(${self.io_addr} + ${reg}) & ~${value}, ${self.io_addr} + ${reg});
+        }
+
+        chunk Ethernet::HardwareContext()
+        {
+            ${e1000::Context} hw_ctx;
+        }
+
+        chunk Ethernet::adapter_init_context(Ethernet::Device rtx_ether_ctx, Builtin::symbol io_addr)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+                // XXX doesn't work: ${local.hw_ctx.init(rtx_ether_ctx, io_addr)};
+                ${local.hw_ctx.net_dev} = ${rtx_ether_ctx};
+                ${local.hw_ctx.io_addr} = ${io_addr};
+                // XXX doesn't work: ${local.hw_ctx.rx_ring.init(local.hw_ctx, config.rx_ring_size)};
+                rtx_e1000_ring_init(
+                    &${local.hw_ctx.rx_ring},
+                    ${config.rx_ring_size},
+                    sizeof(rtxType_e1000_RxDescriptor)
+                );
+                // XXX doesn't work: ${local.hw_ctx.tx_ring.init(local.hw_ctx, config.tx_ring_size)};
+                rtx_e1000_ring_init(
+                    &${local.hw_ctx.tx_ring},
+                    ${config.tx_ring_size},
+                    sizeof(rtxType_e1000_TxDescriptor)
+                );
+            }
+        }
+
+        chunk Ethernet::adapter_reset(Ethernet::Device rtx_ether_ctx)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+                // XXX #46: ${local.hw_ctx.io.write32(E1000_CTRL, E1000_CMD_RST)};
+                rtx_e1000_reg_write32(hw_ctx, E1000_CTRL, E1000_CMD_RST);
+                udelay(10); // TODO: abstract udelay too...
+                ${Log::info("adapter has been reset")};
+            }
+        }
+
+        chunk Ethernet::adapter_load_mac_address(Ethernet::Device rtx_ether_ctx)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+                // Shamelessly borrowed from Minix
+                for (int i = 0; i < 3; ++i) {
+                    rtx_e1000_reg_write32(hw_ctx, E1000_EEPROM_READ, (i << 8) | 1);
+                    int value;
+                    do {
+                        value = rtx_e1000_reg_read32(hw_ctx, E1000_EEPROM_READ);
+                    } while ((value & (1 << 4)) == 0);
+                    value >>= 16;
+                    // NOTE: I'm not sure if Ethernet::Device should be
+                    // accessed directly here. But since we need to take it in
+                    // parameter (so we can get back our e1000::Context) it
+                    // seems inadequate to set this in another way:
+                    ${rtx_ether_ctx.dev_addr}[i * 2] = value & 0xff;
+                    ${rtx_ether_ctx.dev_addr}[i * 2 + 1] = (value >> 8) & 0xff;
+                }
+
+                ${Log::info("mac address loaded from the EEPROM")};
+            }
+        }
+
+        // For e1000, this part is documented in the Intel Gigabit Ethernet
+        // Controller Software Developper manual. (You can find it in the
+        // doc/hardware directory).
+        chunk Ethernet::adapter_setup_rx_tx(Ethernet::Device rtx_ether_ctx)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+
+                // "General Configuration" (section 14.3):
+                //
+                // - CTRL.ASDE/CTRL.SLU: Let the PHY handle the speed detection &
+                //   negociation;
+                // - CTRL.LRST/FRCSPD: Unset them to initiate the auto-negociation;
+                // - CTRL.PHY_RST: Unset it;
+                // - CTRL.ILOS: Unset it (ILOS is Invert Loss Of Signal);
+                // - CTRL.VME: Make sure it's not set to disable VLAN support;
+                // - Set the control flow registers to 0;
+                // - Finally, initialize all the statistic registers from
+                //   E1000_CRCERRS to E1000_TSCTFC.
+
+                // XXX #46: Use the read/write/set/unset methods on Context
+                rtx_e1000_reg_set32(hw_ctx, E1000_CTRL, E1000_CMD_ASDE|E1000_CMD_SLU);
+                rtx_e1000_reg_unset32(
+                    hw_ctx,
+                    E1000_CTRL,
+                    E1000_CMD_LRST|E1000_CMD_FRCSPD|E1000_CMD_PHY_RST|
+                    E1000_CMD_ILOS|E1000_CMD_VME
+                );
+                rtx_e1000_reg_write32(hw_ctx, E1000_FCAH, 0);
+                rtx_e1000_reg_write32(hw_ctx, E1000_FCAL, 0);
+                rtx_e1000_reg_write32(hw_ctx, E1000_FCT, 0);
+                rtx_e1000_reg_write32(hw_ctx, E1000_FCTTV, 0);
+                // XXX int i leaks from another chunk and will not be generated
+                // here, let's use a different index name:
+                for (int j = 0; j != 64; ++j)
+                    rtx_e1000_reg_write32(hw_ctx, E1000_CRCERRS + j * 4, 0);
+
+                ${Log::info("adapter_setup_rx_tx: general configuration done")};
+
+                int err;
+
+                err = rtx_e1000_rx_ring_alloc_resources(&hw_ctx->rx_ring);
+                if (err)
+                    goto err_alloc_rx_ring;
+                rtx_e1000_rx_ring_configure(&hw_ctx->rx_ring);
+
+                err = rtx_e1000_tx_ring_alloc_resources(&hw_ctx->tx_ring);
+                if (err)
+                    goto err_alloc_tx_ring;
+                rtx_e1000_tx_ring_configure(&hw_ctx->tx_ring);
+
+                err_alloc_tx_ring:
+                    rtx_e1000_rx_ring_free_resources(&hw_ctx->rx_ring);
+                err_alloc_rx_ring:
+                    // XXX Can't return here since we don't know the context,
+                    // anyway, hardcoded goto:
+                    goto rtx_ethernet_open_fail;
+            }
+        }
+
+        chunk Ethernet::adapter_enable_interrupts(Ethernet::Device rtx_ether_ctx)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+                rtx_e1000_reg_write32(
+                    ${local.hw_ctx},
+                    E1000_IMS,
+                    E1000_INTR_TXDW|E1000_INTR_TXQE|E1000_INTR_LSC|
+                    E1000_INTR_RXO|E1000_INTR_RXT0
+                );
+
+                // XXX We should probably move that elsewhere (it just used to
+                // be done right after we enabled interrupts when this was
+                // still in lkm.rtx):
+                // XXX #46: ${local.hw_ctx.print_status()};
+                rtx_e1000_print_status(${local.hw_ctx});
+            }
+        }
+
+        chunk Ethernet::adapter_disable_interrupts(Ethernet::Device rtx_ether_ctx)
+        {
+            { ${Log::info("adapter_disable_interrupts: TBD...")}; }
+        }
+
+        chunk Ethernet::adapter_handle_interrupt(Ethernet::Device rtx_ether_ctx)
+        {
+            {
+                ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+                unsigned int icr = rtx_e1000_reg_read32(hw_ctx, E1000_ICR);
+                pr_info("%s: interrupt received, ICR: 0x%x", ${config.name}, icr);
+                if (icr) {
+                    if (icr & E1000_INTR_LSC) {
+                        ${Log::info("handle_interrupt: cable link status changed, dumping card status:")};
+                        // XXX #46: ${local.hw_ctx.print_status()};
+                        rtx_e1000_print_status(hw_ctx);
+                    }
+                    if (icr & (E1000_INTR_TXQE|E1000_INTR_TXDW)) {
+                        ${Log::info("handle_interrupt: TxRing: packet(s) sent")};
+                    }
+                    if (icr & E1000_INTR_RXT0) {
+                        ${Log::info("handle_interrupt: RxRing: packet(s) received")};
+                    }
+                    if (icr & E1000_INTR_RXO) {
+                        ${Log::info("handle_interrupt: RxRing: overrun")};
+                    }
+
+                    // XXX: This sucks since we don't know the pointcut context:
+                    return IRQ_HANDLED;
+                }
+            }
+        }
+
+        chunk Ethernet::adapter_xmit(Ethernet::Device rtx_ether_ctx, Socket::SKBuff rtx_skb)
+        {
+            { ${Log::info("adapter_xmit: TBD...")}; }
+        }
+
+        chunk Ethernet::adapter_disable_rx(Ethernet::Device rtx_ether_ctx)
+        {
+            { ${Log::info("adapter_disable_rx: TBD..")}; }
+        }
+
+        chunk Ethernet::adapter_disable_tx(Ethernet::Device rtx_ether_ctx)
+        {
+            { ${Log::info("adapter_disable_tx: TBD..")}; }
+        }
+
+        chunk Ethernet::adapter_free_rx_tx(Ethernet::Device rtx_ether_ctx)
+        {
+            ${e1000::Context.ref} hw_ctx = &${rtx_ether_ctx}->hw_ctx;
+
+            { ${Log::info("adapter_free_rx_tx: TBD..")}; }
+
+            rtx_e1000_rx_ring_free_resources(${local.hw_ctx.rx_ring});
+            rtx_e1000_tx_ring_free_ressources(${local.hw_ctx.tx_ring});
+        }
+
+        method print_status()
+        {
+            rtx_e1000_print_status(${self});
+        }
+
+        map
+        {
+            io_addr: ${self}->io_addr;
+            rx_ring: ${self}->rx_ring;
+            tx_ring: ${self}->tx_ring;
+            net_dev: ${self}->net_dev;
         }
     }
 
-    /*
-     * 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":
-     * ${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)};
-     *
-     * TODO: make them methods of e1000::Context
-     */
-    template sequence   e1000::register_read32(e1000::Context ctx, e1000::Register reg_offset)
+    template type e1000::Ring()
     {
-        chunk   LKM::prototypes()
+        decl data_types()
         {
-            /* FIXME: See issue #54 */
-            static unsigned int    rtx_e1000_register_read32(/*const*/ ${e1000::Context.ref}, unsigned int);
+            ${e1000::Context}       hw_ctx;
+            ${DMA::DMAHandle}       dma;
+            unsigned int            size;
+            ${Builtin::symbol.ref}  descs;
+            ${e1000::Buffer.ref}    buffs;
         }
 
-        chunk   LKM::code()
+        chunk LKM::prototypes()
         {
-            /* FIXME: See issue #54 */
-            static unsigned int    rtx_e1000_register_read32(/*const*/ ${e1000::Context.ref} ctx, unsigned int reg_offset)
+            static void rtx_e1000_ring_init(${e1000::Ring.ref}, ${e1000::Context}, int, int);
+        }
+
+        chunk LKM::code()
+        {
+            static void rtx_e1000_ring_init(${e1000::Ring.ref} self, ${e1000::Context} hw_ctx,
+                                            int desc_count, int desc_size)
             {
-                return ioread32(ctx->ioaddr + reg_offset);
+                BUG_ON(desc_count <= 0 || desc_size <= 0);
+                memset(self, 0, sizeof(*self));
+                self->size = ALIGN(desc_count * desc_size, 4096);
+                self->hw_ctx = hw_ctx;
             }
         }
 
-        chunk   ::CALL()
+        method init(e1000::Context hw_ctx, Builtin::number desc_count, Builtin::number desc_size)
         {
-            rtx_e1000_register_read32(${ctx}, ${reg_offset});
+            rtx_e1000_ring_init(&${self}, ${hw_ctx}, ${desc_count}, ${desc_size});
+        }
+
+        map
+        {
+            hw_ctx: ${self}->hw_ctx;
+            dma: ${self}->dma;
+            size: ${self}->size;
+            descs: ${self}->descs;
+            buffs: ${self}->buffs;
         }
     }
 
-    template sequence   e1000::register_write32(e1000::Context ctx, e1000::Register reg_offset, ::number value)
+    template type e1000::RxRing()
     {
-        chunk   LKM::prototypes()
+        decl data_types()
         {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_write32(/*const*/ ${e1000::Context.ref}, unsigned int, unsigned int);
+            ${e1000::Ring}  ring;
         }
 
-        chunk   LKM::code()
+        chunk LKM::prototypes()
         {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_write32(/*const*/ ${e1000::Context.ref} ctx, unsigned int reg_offset, unsigned int value)
+            static int rtx_e1000_rx_ring_alloc_resources(${e1000::RxRing.ref});
+            static void rtx_e1000_rx_ring_free_resources(${e1000::RxRing.ref});
+            static void rtx_e1000_rx_ring_configure(${e1000::RxRing.ref});
+        }
+
+        chunk LKM::code()
+        {
+            static int rtx_e1000_rx_ring_alloc_resources(${e1000::RxRing.ref} self)
             {
-                iowrite32(value, ctx->ioaddr + reg_offset);
+                // 4. Setup the receive descriptor ring
+
+                int i;
+
+                // Allocate the descriptors
+                ${local.self.descs} = ${DMA::alloc_coherent(
+                    local.self.hw_ctx.net_dev.device, local.self.size,
+                    local.self.dma.dma_handle
+                )};
+
+                if (!${local.self.descs}) {
+                    ${Log::info("adapter_init_rx: cannot allocate the descriptors for the rx ring")};
+                    goto err_rx_ring_alloc;
+                }
+
+                // XXX The generated code is totally wrong if this is not scoped:
+                { ${Log::info("adapter_init_rx: rx descriptors allocated")}; }
+
+                // Allocate the skbuffs, map them for DMA, and write their address
+                // in the corresponding descriptor.
+                for (i = 0; i != ${config.rx_ring_size}; ++i) {
+                    // XXX No support for arrays:
+                    ${e1000::Buffer.ref} buff = &${local.self.buffs}[i];
+                    // XXX #46: ${rtx_ether_ctx.alloc_rx_skbuff(local.buff.skbuff, config.rx_buffer_len)};
+                    if (rtx_ethernet_alloc_rx_skbuff(&${local.buff.sk_buff}, ${config.rx_buffer_len})) {
+                        ${Log::info("adapter_init_rx: cannot allocate a skbuff for the rx ring")};
+                        goto err_skbuffs_alloc;
+                    }
+                    // XXX I couldn't manage to get this call working:
+                    //${local.buff.dma.dma_handle} = ${DMA::map(
+                    //    local.self.hw_ctx.net_dev.device, local.buff.sk_buff.data,
+                    //    config.rx_buffer_len, RTX_DMA_FROM_DEVICE
+                    //)};
+                    ${local.buff.dma.dma_handle.k_dma_handle} = dma_map_single(
+                        ${local.self.hw_ctx.net_dev.device.k_device}, ${local.buff.sk_buff.data},
+                        ${config.rx_buffer_len}, RTX_DMA_FROM_DEVICE
+                    );
+                    int err = ${DMA::mapping_error(
+                        local.self.hw_ctx.net_dev.device, local.buff.dma.dma_handle
+                    )};
+                    if (err) {
+                        ${Log::info("adapter_init_rx: cannot dma-map a skbuff for the rx ring")};
+                        goto err_skbuffs_map;
+                    }
+                    ${local.self.descs}[i].buff_addr = cpu_to_le64(${local.buff.dma.dma_handle.k_dma_handle});
+                }
+
+                { ${Log::info("adapter_init_rx: skbuffs allocated")}; }
+
+                return 0;
+
+            err_skbuffs_alloc:
+                while (i--) {
+                    ${e1000::Buffer.ref} buff = &${local.self.buffs}[i];
+                    // XXX I couldn't manage to get this call working
+                    //${DMA::unmap(
+                    //    local.self.hw_ctx.net_dev.device,
+                    //    local.buff.dma.dma_handle,
+                    //    config.rx_buffer_len,
+                    //    RTX_DMA_FROM_DEVICE
+                    //)};
+                    dma_unmap_single(
+                        ${local.self.hw_ctx.net_dev.device.k_device},
+                        ${local.buff.dma.dma_handle.k_dma_handle},
+                        ${config.rx_buffer_len},
+                        RTX_DMA_FROM_DEVICE
+                    );
+            err_skbuffs_map:
+                    // XXX #46: ${local.buff.sk_buff.free()};
+                    dev_kfree_skb(${local.buff.sk_buff.sk_buff.k_sk_buff});
+                }
+
+                void *descs = ${local.self.descs};
+                ${cast local.descs as Builtin::symbol.ref};
+                ${DMA::free_coherent(
+                    local.self.hw_ctx.net_dev.device, local.self.size,
+                    local.descs, local.self.dma.dma_handle
+                )};
+            err_rx_ring_alloc:
+                return -ENOMEM;
+            }
+
+            static void rtx_e1000_rx_ring_free_resources(${e1000::RxRing.ref} self)
+            {
+                ${Log::info("e1000_rx_ring_free_resources: TBD...")};
+            }
+
+            static void rtx_e1000_rx_ring_configure(${e1000::RxRing.ref} self)
+            {
+                // Receive initialization (section 14.4):
+                //
+                // 1. Program the receive address, in RAL/RAH;
+                // 2. Initialize the Multicast Table Array;
+                // 3. Program the interrupt mask register (done in
+                //    e1000::activate_device_interruption);
+                // 4. Allocate the receive descriptor ring and map it to make it
+                //    accessible by the device;
+                // 5. Write the start address of the ring in RDBAL/RDBAH and set
+                //    RDLEN (Receive Descriptor Length) to the size of the ring;
+                // 6. Set the RDH/RDT (Receive Descriptor Head/Tail) indexes to the
+                //    beginning and end of the ring;
+                // 7. Make sure that RCTL.BSIZE and .BSEX are at 0 to configure the
+                //    receive buffer size to 2048 bytes (e1000::rx_buffer_len).
+                // 8. Set RCTL.EN to enable the receiver.
+                //
+                // The ugly casts here are caused by the lack of CNorm unstrict.
+
+                int i;
+
+                // 1. Program the receive address
+
+                // (We should use uint{32,16}_t but CNorm doesn't know them yet)
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RAL,
+                    *(unsigned int *)(${local.self.hw_ctx.net_dev.dev_addr})
+                );
+                // The 16 upper bits of RAH also store the AS bits (which should be
+                // 0) and the AV bit (should be 1 to set the address as valid).
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RAH,
+                    *(unsigned short *)(&${local.self.hw_ctx.net_dev.dev_addr}[4])
+                );
+                rtx_e1000_reg_set32(${local.self.hw_ctx}, E1000_RAH, E1000_RAH_AV);
+
+                ${Log::info("rx_ring_configure: receive address programmed")};
+
+                // 2. Initialize the MTA
+
+                for (i = 0; i != 128; ++i) // TODO Remove that hardcoded 128
+                    rtx_e1000_reg_write32(${local.self.hw_ctx}, E1000_MTA + i * 4, 0);
+
+                ${Log::info("rx_ring_configure: MTA init done")};
+
+                // 5. Save the emplacement and the size of the ring in RDBA/RDLEN
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RDBAL,
+                    ${local.self.dma.dma_handle.k_dma_handle} & 0xffffffff
+                );
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RDBAH,
+                    ${local.self.dma.dma_handle.k_dma_handle} >> 32
+                );
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RDLEN,
+                    ${local.self.size}
+                );
+
+                // 6. Setup RDH/RDT
+                rtx_e1000_reg_write32(${local.self.hw_ctx}, E1000_RDH, 0);
+                rtx_e1000_reg_write32(
+                    ${local.self.hw_ctx}, E1000_RDT,
+                    ${config.rx_ring_size} - 1
+                );
+
+                // 7. Configure the buffer size
+                rtx_e1000_reg_set32(
+                    ${local.self.hw_ctx}, E1000_RCTL,
+                    E1000_RCTL_BSIZE_${config.rx_buffer_len}
+                );
+
+                // 8. Enable the receiver
+                rtx_e1000_reg_set32(${local.self.hw_ctx}, E1000_RCTL, E1000_RCTL_EN);
+
+                ${Log::info("rx_ring_configure: receive registers configured and receiver enabled")};
+
+                return 0;
             }
         }
 
-        chunk   ::CALL()
+        method init(e1000::Context hw_ctx, Builtin::number desc_count)
         {
-            rtx_e1000_register_write32(${ctx}, ${reg_offset}, ${value});
+            // XXX doesn't work: ${self.ring.init(hw_ctx, desc_count, self.desc_size)};
+            rtx_e1000_ring_init(&${self}, ${hw_ctx}, ${desc_count}, ${self.desc_size});
+        }
+
+        method alloc()
+        {
+            rtx_e1000_rx_ring_alloc_resources(${self});
+        }
+
+        method free()
+        {
+            rtx_e1000_rx_ring_free_resources(${self});
+        }
+
+        method configure()
+        {
+            rtx_e1000_rx_ring_configure(${self});
+        }
+
+        map
+        {
+            hw_ctx: ${self}->ring.hw_ctx;
+            dma: ${self}->ring.dma;
+            size: ${self}->ring.size;
+            descs: ${self}->ring.descs;
+            buffs: ${self}->ring.buffs;
+            desc_size: sizeof(rtxType_e1000_RxDescriptor); // XXX hardcoded
         }
     }
 
-    template sequence   e1000::register_set32(e1000::Context ctx, e1000::Register reg_offset, ::number value)
+    template type e1000::TxRing()
     {
-        chunk   LKM::prototypes()
+        decl data_types()
         {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_set32(/*const*/ ${e1000::Context.ref}, unsigned int, unsigned int);
+            ${e1000::Ring}  ring;
         }
 
-        chunk   LKM::code()
+        chunk LKM::prototypes()
         {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_set32(/*const*/ ${e1000::Context.ref} ctx, unsigned int reg_offset, unsigned int value)
+            static int rtx_e1000_tx_ring_alloc_resources(${e1000::TxRing.ref});
+            static void rtx_e1000_tx_ring_free_resources(${e1000::TxRing.ref});
+            static void rtx_e1000_tx_ring_configure(${e1000::TxRing.ref});
+        }
+
+        chunk LKM::code()
+        {
+            static int rtx_e1000_tx_ring_alloc_resources(${e1000::TxRing.ref} self)
             {
-                iowrite32(rtx_e1000_register_read32(ctx, reg_offset) | value, ctx->ioaddr + reg_offset);
+                ${Log::info("e1000_tx_ring_alloc_resources: TBD...")};
+                return 0;
+            }
+
+            static void rtx_e1000_tx_ring_free_resources(${e1000::TxRing.ref} self)
+            {
+                ${Log::info("e1000_tx_ring_free_resources: TBD...")};
+            }
+
+            static void rtx_e1000_tx_ring_configure(${e1000::TxRing.ref} self)
+            {
+                ${Log::info("e1000_tx_ring_configure: TBD...")};
             }
         }
 
-        chunk   ::CALL()
+        method init(e1000::Context hw_ctx, Builtin::number desc_count)
         {
-            rtx_e1000_register_set32(${ctx}, ${reg_offset}, ${value});
-        }
-    }
-
-    template sequence   e1000::register_unset32(e1000::Context ctx, e1000::Register reg_offset, ::number value)
-    {
-        chunk   LKM::prototypes()
-        {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_unset32(/*const*/ ${e1000::Context.ref}, unsigned int, unsigned int);
+            // XXX doesn't work: ${self.ring.init(hw_ctx, desc_count, self.desc_size)};
+            rtx_e1000_ring_init(&${self}, ${hw_ctx}, ${desc_count}, ${self.desc_size});
         }
 
-        chunk   LKM::code()
+        method alloc()
         {
-            /* FIXME: See issue #54 */
-            static void rtx_e1000_register_unset32(/*const*/ ${e1000::Context.ref} ctx, unsigned int reg_offset, unsigned int value)
-            {
-                iowrite32(rtx_e1000_register_read32(ctx, reg_offset) & ~value, ctx->ioaddr + reg_offset);
-            }
+            rtx_e1000_tx_ring_alloc_resources(${self});
         }
 
-        chunk   ::CALL()
+        method free()
         {
-            rtx_e1000_register_unset32(${ctx}, ${reg_offset}, ${value});
+            rtx_e1000_tx_ring_free_resources(${self});
         }
-    }
 
-    template sequence   activate_device_interruption(Ethernet::Device rtx_ether_ctx)
-    {
-        chunk  ::CALL()
+        method configure()
         {
-            rtx_e1000_register_write32(&${rtx_ether_ctx}->hw_ctx, E1000_IMS,
-                                       E1000_INTR_TXDW |
-                                       E1000_INTR_TXQE |
-                                       E1000_INTR_LSC  |
-                                       E1000_INTR_RXO  |
-                                       E1000_INTR_RXT0);
+            rtx_e1000_tx_ring_configure(${self});
         }
-    }
 
-    /* TODO:
-     *
-     * Refactor into two methods (one in RxRing and one in TxRing) and make use
-     * of the new methods in Socket::SKBuff.
-     */
-    template sequence   free_rx_tx(Ethernet::Device rtx_ether_ctx)
-    {
-        chunk   ::CALL()
+        map
         {
-            /*
-             * XXX: Not generated if named "hw_ctx" (which is funny because
-             * it's used and works in the template right above this one):
-             */
-            ${e1000::Context.ref} hw_ctx_;
-            hw_ctx_ = &${rtx_ether_ctx}->hw_ctx;
-
-            /*
-             * Free the rx ring:
-             * - Unmap and free the skbuffs;
-             * - Free the descriptors array.
-             */
-            for (int i = 0; i != ${config.rx_ring_size}; ++i)
-            {
-                /*
-                 * XXX Leaking casts:
-                 *
-                 * We should go through the rtx types (Socket::SKBuff,
-                 * AbstractSKBuff), but we can't because of the array here,
-                 * which is not supported by the compiler.
-                 */
-                dma_unmap_single(
-                        ${rtx_ether_ctx.device},
-                        *((dma_addr_t *)&(hw_ctx_->rx_ring.skbuffs[i].dma_handle)),
-                        ${config.rx_buffer_len},
-                        DMA_FROM_DEVICE);
-                dev_kfree_skb((struct sk_buff *)hw_ctx_->rx_ring.skbuffs[i].skbuff);
-            }
-            dma_free_coherent(${rtx_ether_ctx.device}, hw_ctx_->rx_ring.size,
-                    hw_ctx_->rx_ring.base, hw_ctx_->rx_ring.dma_base);
-            ${Log::info("free_rx_tx: rx ring free'ed")};
-
-            /*
-             * Free the tx ring:
-             * - Free the descriptors array.
-             */
-            dma_free_coherent(${rtx_ether_ctx.device}, hw_ctx_->tx_ring.size,
-                    hw_ctx_->tx_ring.base, hw_ctx_->tx_ring.dma_base);
-            ${Log::info("free_rx_tx: tx ring free'ed")};
-        }
-    }
-
-    template sequence   handle_interrupt(Ethernet::Device rtx_ether_ctx)
-    {
-        chunk   ::CALL()
-        {
-            unsigned int icr = rtx_e1000_register_read32(&${rtx_ether_ctx}->hw_ctx, E1000_ICR);
-            pr_info("%s: interrupt received, ICR: 0x%x", ${config.name}, icr);
-            if (icr)
-            {
-                if (icr & E1000_INTR_LSC)
-                {
-                    ${Log::info("handle_interrupt: cable link status changed, dumping card status:")};
-                    ${e1000::print_status(rtx_ether_ctx)};
-                }
-                if (icr & (E1000_INTR_TXQE|E1000_INTR_TXDW))
-                {
-                    ${Log::info("handle_interrupt: TxRing: packet(s) sent")};
-                    /*
-                     * XXX Do a Rathaxes call (how can I bind
-                     * "&${rtx_ether_ctx}->hw_ctx.tx_ring" to e1000::TxRing easily?)
-                     */
-                    rtx_e1000_tx_ring_clean(&${rtx_ether_ctx}->hw_ctx.tx_ring);
-                }
-                if (icr & E1000_INTR_RXT0)
-                {
-                    ${Log::info("handle_interrupt: RxRing: packet(s) received")};
-                }
-                if (icr & E1000_INTR_RXO)
-                {
-                    ${Log::info("handle_interrupt: RxRing: overrun")};
-                }
-
-                return IRQ_HANDLED;
-            }
-        }
-    }
-
-    template sequence   e1000::xmit(Ethernet::Device rtx_ether_ctx, Socket::AbstractSKBuff kernel_skb)
-    {
-        chunk   ::CALL()
-        {
-            /*
-             * Put packets on the TX ring, must return NETDEV_TX_OK or
-             * NETDEV_TX_BUSY.
-             */
-
-            ${Socket::SKBuff} skb;
-            ${e1000::Context.ref} hw_ctx;
-            ${e1000::TxRing.ref} tx_ring;
-            ${Device::AbstractDevice.ref} devp;
-
-            ${local.skb.init(kernel_skb)};
-            hw_ctx = &${rtx_ether_ctx}->hw_ctx;
-            tx_ring = &hw_ctx->tx_ring;
-            devp = (${Device::AbstractDevice.ref})${rtx_ether_ctx.device};
-
-            ${Log::info("xmit: skbuff details:")};
-            /*
-             * skb does not expand on the bound C variable (should be
-             * rtx_skbuff), which is funny because it works for the
-             * sequence template call right after.
-             */
-            /*
-             * XXX: doesn't work (I tried to pass self explicitely too):
-             * ${local.skb.dump_infos()};
-             */
-            rtx_socket_skbuff_dump_infos(&skb);
-
-            /*
-             * The transmission is going to be several steps:
-             * 1. TCP Segmentation Offload & Checksum Offloading: pick a
-             *    descriptor from the tx ring and fill it as a context
-             *    descriptor to allow the card to slice into several packets
-             *    according to the MSS;
-             * 2. DMA Map the skbuff data as slices of 4096;
-             * 3. Signal the hardware that data is available via a tx desc.
-             */
-
-            /* XXX: same thing wanted to use: ${local.tx_ring.descriptors_remaining()} */
-            if (!rtx_e1000_tx_ring_descriptors_remaining(tx_ring))
-                return NETDEV_TX_BUSY;
-
-            /* 1. Offloading */
-
-            /* XXX: ${local.tx_ring.tso_cksum_offload(skb)}; */
-            if (rtx_e1000_tx_ring_tso_cksum_offload(tx_ring, &skb))
-            {
-                ${Log::info("xmit: the packet needs to be fragmented and/or checksummed but this not implemented yet!")};
-                goto err_offload;
-            }
-
-            /* 2. Map the data */
-
-            /* XXX: ${local.skb.map_to(devp.k_device)}; */
-            if (rtx_socket_skbuff_map(&skb, ${devp.k_device}, DMA_TO_DEVICE))
-            {
-                ${Log::info("xmit: can't DMA map a SKbuff")};
-                goto err_skb_map_to;
-            }
-
-            /* 3. Update the TX Ring and signal the hardware */
-
-            /* XXX: ${local.tx_ring.put(skb)}; */
-            rtx_e1000_tx_ring_put(tx_ring, &skb);
-
-            /* XXX: ${local.tx_ring.start_xmit(hw_ctx)}; */
-            rtx_e1000_tx_ring_start_xmit(tx_ring, hw_ctx);
-
-            return NETDEV_TX_OK;
-
-        err_offload:
-        err_skb_map_to:
-            /* XXX: ${local.skb.unmap_to_and_free(local.dev)}; */
-            rtx_socket_skbuff_unmap_and_free(&skb, ${devp.k_device}, DMA_TO_DEVICE);
-            return NETDEV_TX_OK;
+            hw_ctx: ${self}->ring.hw_ctx;
+            dma: ${self}->ring.dma;
+            size: ${self}->ring.size;
+            descs: ${self}->ring.descs;
+            buffs: ${self}->ring.buffs;
+            desc_size: sizeof(rtxType_e1000_TxDescriptor); // XXX hardcoded
         }
     }
 }
diff --git a/rathaxes/samples/e1000/e1000.rti b/rathaxes/samples/e1000/e1000.rti
--- a/rathaxes/samples/e1000/e1000.rti
+++ b/rathaxes/samples/e1000/e1000.rti
@@ -1,157 +1,125 @@
-interface e1000 : Socket, Ethernet, DMA, PCI, LKM, Builtin
+interface e1000 : Socket, Ethernet, Device, DMA, PCI, LKM, Builtin
 {
-    required variable   Builtin::number rx_ring_size;
-    required variable   Builtin::number tx_ring_size;
-    required variable   Builtin::number rx_buffer_len;
-    required variable   Builtin::number tx_max_data_per_desc;
+    required variable Builtin::number rx_ring_size;
+    required variable Builtin::number tx_ring_size;
+    required variable Builtin::number rx_buffer_len;
+    required variable Builtin::number tx_max_data_per_desc;
 
-    provided type   RxDescriptor
+    // Hardware values/data structures, should probably be in the front-end:
+    provided type   Register            { decl data_types(); }
+    provided type   Command             { decl data_types(); }
+    provided type   TxDescriptorFlag    { decl data_types(); }
+    provided type   RxDescriptor        { decl data_types(); }
+    provided type   TxDescriptor        { decl data_types(); }
+
+    provided type   Buffer
     {
-        chunk       LKM::includes();
         decl        data_types();
-        method      init();
+
+        method      init(Socket::SKBuff, DMA::DMAHandle);
+
+        attribute   Socket::SKBuff.scalar   sk_buff;
+        attribute   DMA::DMAHandle.scalar   dma;
     }
 
-    provided type   TxDescriptor
+    provided type   Context
     {
         decl        data_types();
-        method      init();
+
+        chunk       LKM::includes();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
+        chunk       Ethernet::HardwareContext();
+
+        // NOTE: Those callbacks/hooks should probably be in the front-end:
+
+        chunk       Ethernet::adapter_init_context(Ethernet::Device, Builtin::symbol);
+        chunk       Ethernet::adapter_reset(Ethernet::Device);
+        chunk       Ethernet::adapter_load_mac_address(Ethernet::Device);
+        chunk       Ethernet::adapter_setup_rx_tx(Ethernet::Device);
+        chunk       Ethernet::adapter_enable_interrupts(Ethernet::Device);
+        chunk       Ethernet::adapter_disable_interrupts(Ethernet::Device);
+        chunk       Ethernet::adapter_handle_interrupt(Ethernet::Device);
+        chunk       Ethernet::adapter_xmit(Ethernet::Device, Socket::SKBuff);
+        chunk       Ethernet::adapter_disable_rx(Ethernet::Device);
+        chunk       Ethernet::adapter_disable_tx(Ethernet::Device);
+        chunk       Ethernet::adapter_free_rx_tx(Ethernet::Device);
+
+        method      init(Ethernet::Device net_dev, Builtin::symbol);
+        method      print_status();
+        method      reg_read32(Register);
+        method      reg_write32(Register, Builtin::number);
+        method      reg_set32(Register, Builtin::number);
+        method      reg_unset32(Register, Builtin::number);
+
+        // XXX: Circular dependency with the rings, this will prevent us to
+        // call methods even when issue $46 is resolved, it also forces us to
+        // hardcode generated types in the definition of the type, and they
+        // can't be "dereferenced" (e.g: local.hw_ctx.rx_ring.desc_size can't
+        // work since local.hw_ctx.rx_ring is just going to be a symbol).
+        attribute   Builtin::symbol.scalar  rx_ring;
+        attribute   Builtin::symbol.scalar  tx_ring;
+        attribute   Builtin::symbol.ref     io_addr;
+        attribute   Ethernet::Device.ref    net_dev;
+    }
+
+    provided type   Ring
+    {
+        decl        data_types();
+
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
+
+        method      init(e1000::Context, Builtin::number, Builtin::number);
+
+        // Keep a backref to The context since it's going to be needed for of
+        // operations involving the ethernet device, flags on the context, etc.
+        attribute   Context.ref             hw_ctx;
+        attribute   DMA::DMAHandle.scalar   dma;
+        attribute   Builtin::number.scalar  size;   // Total size in bytes
+        attribute   Builtin::symbol.ref     descs;
+        attribute   Buffer.ref              buffs;
     }
 
     provided type   RxRing
     {
         decl        data_types();
-        method      init();
 
-        /* XXX: Callback that should be in the front-end: */
-        chunk       Ethernet::adapter_init_rx(Ethernet::Device);
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
 
-        attribute   DMA::DMAHandle.scalar   dma_base;
+        method      init(e1000::Context, Builtin::number);
+        method      alloc(); // Returns != 0 on failure
+        method      free();
+        method      configure();
+
+        // Re-expose all the Ring attributes + the size of a single descriptor:
+        attribute   Context.ref             hw_ctx;
+        attribute   DMA::DMAHandle.scalar   dma;
         attribute   Builtin::number.scalar  size;
-    }
-
-    provided type   Context
-    {
-        chunk       Ethernet::HardwareContext();
-        decl        data_types();
-
-        /* XXX:
-         * These callbacks/Hooks which should probably be in the front-end.
-         * Also, I'm not too happy about the names, it's difficult to make
-         * the difference between the probe and open parts.
-         */
-        chunk       Ethernet::adapter_init_context(Ethernet::Device,
-                                                   Builtin::number,
-                                                   Builtin::symbol);
-        chunk       Ethernet::adapter_reset(Ethernet::Device);
-        chunk       Ethernet::adapter_load_mac_address(Ethernet::Device);
-        chunk       Ethernet::adapter_setup(Ethernet::Device);
-
-        attribute   RxRing.scalar   rx_ring;
-        /* XXX: circular dependency with Contex: */
-        //attribute   TxRing.scalar   tx_ring;
+        attribute   RxDescriptor.ref        descs;
+        attribute   Builtin::number.scalar  desc_size;
+        attribute   Buffer.ref              buffs;
     }
 
     provided type   TxRing
     {
+        decl        data_types();
+
         chunk       LKM::prototypes();
         chunk       LKM::code();
-        decl        data_types();
-        method      init();
 
-        /* XXX: Callback that should be in the front-end: */
-        chunk       Ethernet::adapter_init_tx(Ethernet::Device);
+        method      init(e1000::Context, Builtin::number);
+        method      alloc(); // Returns != 0 on failure
+        method      free();
+        method      configure();
 
-        /* Clean the ring (i.e: move the head closer to the tail): */
-        method      clean();
-        /* Return the number of clean descriptors left in the ring: */
-        method      descriptors_remaining();
-        method      tso_cksum_offload(Socket::SKBuff);
-        method      put(Socket::SKBuff);
-        /* Signal the device that new dirty descriptors are on the ring: */
-        method      start_xmit(e1000::Context);
-    }
-
-    /*
-     * These two types should actually be registers definitions in the frontend:
-     */
-    provided type   Register
-    {
-        decl        data_types();
-        method      init(Builtin::number);
-    }
-
-    provided type   Commands
-    {
-        decl        data_types();
-    }
-
-    provided type   TxDescriptorFlags
-    {
-        decl        data_types();
-    }
-
-    /*
-     * This should take an e1000::Context as the first argument but this was
-     * not working as wished.
-     */
-    provided sequence   print_status(Ethernet::Device)
-    {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   activate_device_interruption(Ethernet::Device)
-    {
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   set_up_device(Ethernet::Device)
-    {
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   free_rx_tx(Ethernet::Device dev)
-    {
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   handle_interrupt(Ethernet::Device)
-    {
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   xmit(Ethernet::Device, Socket::AbstractSKBuff)
-    {
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   register_read32(e1000::Context, e1000::Register)
-    {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   register_write32(e1000::Context, e1000::Register, ::number)
-    {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   register_set32(e1000::Context, e1000::Register, ::number)
-    {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
-        provided chunk  ::CALL();
-    }
-
-    provided sequence   register_unset32(e1000::Context, e1000::Register, ::number)
-    {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
-        provided chunk  ::CALL();
+        // Re-expose all the Ring attributes + the size of a single descriptor:
+        attribute   Context.ref             hw_ctx;
+        attribute   DMA::DMAHandle.scalar   dma;
+        attribute   Builtin::number.scalar  size;
+        attribute   TxDescriptor.ref        descs;
+        attribute   Builtin::number.scalar  desc_size;
+        attribute   Buffer.ref              buffs;
     }
 }
diff --git a/rathaxes/samples/e1000/ethernet.blt b/rathaxes/samples/e1000/ethernet.blt
--- a/rathaxes/samples/e1000/ethernet.blt
+++ b/rathaxes/samples/e1000/ethernet.blt
@@ -1,4 +1,4 @@
-with Ethernet, PCI, LKM, Log, Builtin
+with Ethernet, Socket, PCI, LKM, Log, Builtin
 {
     template type   Ethernet::ProtocolId()
     {
@@ -9,7 +9,7 @@
 
         chunk LKM::prototypes()
         {
-            static const char   *rtx_ethernet_protocol_id_to_str(unsigned short);
+            static const char *rtx_ethernet_protocol_id_to_str(unsigned short);
         }
 
         chunk LKM::data()
@@ -28,7 +28,7 @@
 
         chunk LKM::code()
         {
-            static const char   *rtx_ethernet_protocol_id_to_str(unsigned short proto_id)
+            static const char *rtx_ethernet_protocol_id_to_str(unsigned short proto_id)
             {
                 for (int i = 0;
                      i != sizeof(rtx_ethernet_proto_table) / sizeof(rtx_ethernet_proto_table[0]);
@@ -61,7 +61,7 @@
 
         method  init(Builtin::symbol dev)
         {
-            ${self} = (${Ethernet::AbstractDevice} *)${dev};
+            ${self} = (${Ethernet::AbstractDevice.ref})${dev};
         }
 
         map
@@ -104,9 +104,12 @@
         {
             static int rtx_ethernet_alloc_rx_skbuff(${Ethernet::Device.ref} self, ${Socket::SKBuff.ref} sk_buff, ${Builtin::number} size)
             {
-                ${Socket::AbstractSKBuff} k_sk_buff = netdev_alloc_skb(${local.self.net_device.k_net_dev}, ${local.size});
-                if (${local.k_sk_buff) {
-                    ${local.sk_buff.init(local.k_sk_buff, local.size)};
+                ${Socket::AbstractSKBuff.ref} k_skb = netdev_alloc_skb(${local.self.net_device.k_net_dev}, ${local.size});
+                if (${local.k_skb}) {
+                    // XXX meh, can't use the init method since we get a copy
+                    // of the pointer, so we have to do this which is
+                    // inconsistent with the rest of code:
+                    sk_buff->skbuff = k_skb;
                     return 0;
                 }
                 return 1;
@@ -148,7 +151,7 @@
         }
     }
 
-    template sequence   Ethernet::open(Ethernet::Device dev)
+    template sequence   Ethernet::open()
     {
         chunk   LKM::includes()
         {
@@ -157,100 +160,115 @@
 
         chunk LKM::prototypes()
         {
-            static int  rtx_ethernet_open(struct net_device *);
+            static int rtx_ethernet_open(struct net_device *);
         }
 
         chunk LKM::code()
         {
-            static int  rtx_ethernet_open(struct net_device *dev)
+            static int rtx_ethernet_open(struct net_device *dev)
             {
-                /*
-                 * XXX The casts are here because the compiler doesn't resolve
-                 * "enclosed" type (e.g: local.var.enclosed) correctly.
-                 */
                 ${Ethernet::AbstractDevice.ref} rtx_net_dev;
-                { /* XXX: I end up with a placeholder if I don't open a scope */
-                    ${local.rtx_net_dev.init(local.dev)};
-                }
+                ${local.rtx_net_dev.init(local.dev)};
                 ${Ethernet::Device.ref} rtx_ether_ctx = ${local.rtx_net_dev.rtx_ether_ctx};
 
-                int error;
-                {
-                    ${Log::info("installing the interrupt handler")};
-                }
-                error = request_irq(${local.rtx_ether_ctx.irq},
-                                    rtx_ethernet_interrupt_handler,
-                                    IRQF_SHARED,
-                                    ${config.name},
-                                    dev);
-                if (error)
-                {
+                ${pointcut Ethernet::adapter_setup_rx_tx(local.rtx_ether_ctx)};
+
+                ${Log::info("Installing the interrupt handler")};
+                int error = request_irq(
+                    ${local.rtx_ether_ctx.irq},
+                    rtx_ethernet_interrupt_handler,
+                    IRQF_SHARED,
+                    ${config.name},
+                    dev
+                );
+                if (error) {
                     ${Log::info("Cannot register the interrupt handler")};
                     return error;
                 }
-                ${pointcut Ethernet::adapter_setup(local.rtx_ether_ctx)};
-                ${pointcut Ethernet::adapter_init_rx(local.rtx_ether_ctx)};
-                ${pointcut Ethernet::adapter_init_tx(local.rtx_ether_ctx)};
-                ${pointcut ::IMPLEMENTATION(local.rtx_ether_ctx)};
+
+                ${pointcut Ethernet::adapter_enable_interrupts(local.rtx_ether_ctx)};
+
+                return 0;
+
+            rtx_ethernet_open_fail:
+                return -EIO; // XXX Probably not appropriate.
+            }
+        }
+
+        /* XXX This chunk should be removed (see #26) */
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   Ethernet::send()
+    {
+        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 *k_skb, struct net_device *net_dev)
+            {
+                ${Ethernet::Device.ref} rtx_ether_ctx = netdev_priv(net_dev);
+                ${Socket::SKBuff.ref} rtx_skb;
+
+                ${cast local.k_skb as Socket::AbstractSKBuff.ref};
+                ${local.rtx_skb.init(local.k_skb)};
+
+                ${Log::info("we have one packet to transmit!")};
+                ${pointcut Ethernet::adapter_xmit(local.rtx_ether_ctx, local.rtx_skb)};
+            }
+        }
+
+        /* XXX This chunk should be removed (see #26) */
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   Ethernet::close()
+    {
+        chunk LKM::prototypes()
+        {
+            static int rtx_ethernet_close(struct net_device *);
+        }
+
+        chunk LKM::code()
+        {
+            static int rtx_ethernet_close(struct net_device *net_dev)
+            {
+                ${cast local.net_dev as Ethernet::AbstractDevice.ref};
+                ${Ethernet::Device.ref} rtx_ether_ctx = ${local.net_dev.rtx_ether_ctx};
+
+                ${pointcut Ethernet::adapter_disable_rx(local.rtx_ether_ctx)};
+
+                netif_tx_disable(net_dev);
+                ${pointcut Ethernet::adapter_disable_tx(local.rtx_ether_ctx)};
+
+                ${pointcut Ethernet::adapter_disable_interrupts(local.rtx_ether_ctx)};
+                free_irq(${local.rtx_ether_ctx.irq}, dev);
+                ${Log::info("interrupt handler free'ed")};
+
+                // TODO/XXX: There is definitely more stuff to do around here
+                // (e.g: clean the rings, flush some stuff...)
+
+                ${pointcut Ethernet::adapter_reset(local.rtx_ether_ctx)};
+                ${pointcut Ethernet::adapter_free_rx_tx(local.rtx_ether_ctx)};
 
                 return 0;
             }
         }
-    }
 
-    template sequence   Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb)
-    {
-        chunk LKM::prototypes()
+        /* XXX This chunk should be removed (see #26) */
+        chunk   ::CALL()
         {
-            static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev);
-        }
-
-        chunk LKM::code()
-        {
-            static int  rtx_ethernet_xmit(struct sk_buff* kernel_skb, struct net_device *net_dev)
-            {
-                ${Ethernet::Device.ref} rtx_ether_ctx = netdev_priv(net_dev);
-                ${Socket::AbstractSKBuff.ref} rtx_skb = (${Socket::AbstractSKBuff.ref}) kernel_skb;
-
-                ${pointcut ::IMPLEMENTATION(local.rtx_ether_ctx, local.rtx_skb)};
-            }
         }
     }
 
-    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)
-            {
-                ${Ethernet::AbstractDevice.ref} rtx_net_dev;
-                { /* XXX: I end up with a placeholder if I don't open a scope */
-                    ${local.rtx_net_dev.init(local.dev)};
-                }
-
-                ${Ethernet::Device.ref} rtx_ether_ctx = ${local.rtx_net_dev.rtx_ether_ctx};
-
-                /* TODO: change this pointcut into a pointcut/adapter/callback: */
-                {
-                    ${pointcut ::IMPLEMENTATION(local.rtx_ether_ctx)};
-                }
-
-                free_irq(${local.rtx_ether_ctx.irq}, dev);
-                {
-                    ${Log::info("interrupt handler free'ed")};
-                }
-
-                return 0;
-            }
-        }
-    }
-
-    template sequence   Ethernet::interrupt_handler(Ethernet::Device dev)
+    template sequence   Ethernet::interrupt_handler()
     {
         /*
          * We can't use the irqreturn_t type here because CNornm doesn't know
@@ -258,29 +276,34 @@
          */
         chunk LKM::prototypes()
         {
-            static enum irqreturn   rtx_ethernet_interrupt_handler(int, void *);
+            static enum irqreturn rtx_ethernet_interrupt_handler(int, void *);
         }
 
         chunk LKM::code()
         {
-            static enum irqreturn   rtx_ethernet_interrupt_handler(int irq, void *dev_id)
+            static enum irqreturn rtx_ethernet_interrupt_handler(int irq, void *dev_id)
             {
                 ${Ethernet::AbstractDevice.ref} rtx_net_dev = dev_id;
                 ${Ethernet::Device.ref} rtx_ether_ctx;
                 rtx_ether_ctx = ${local.rtx_net_dev.rtx_ether_ctx};
 
-                ${pointcut ::IMPLEMENTATION(local.rtx_ether_ctx)};
+                ${pointcut Ethernet::adapter_handle_interrupt(local.rtx_ether_ctx)};
 
                 return IRQ_NONE;
             }
         }
+
+        /* XXX This chunk should be removed (see #26) */
+        chunk   ::CALL()
+        {
+        }
     }
 
     template sequence   Ethernet::init()
     {
         chunk LKM::data()
         {
-            static const struct net_device_ops  rtx_ether_ops =
+            static const struct net_device_ops rtx_ether_ops =
             {
                 .ndo_open = rtx_ethernet_open,
                 .ndo_stop = rtx_ethernet_close,
@@ -288,74 +311,65 @@
             };
         }
 
-        /*
-         * NOTE: for now, the error handling is leaking from PCI::probe(), but
-         * it's better than doing it at all.
-         *
-         * XXX: the chunk argument isn't correctly expanded by the
-         * compiler I have to use the same name as the actual C
-         * variable:
-         */
+        // NOTE: for now, the error handling is leaking from PCI::probe(), but
+        // it's better than not doing it at all.
+        //
+        // XXX: the chunk argument isn't correctly expanded by the compiler I
+        // have to use the same name as the actual C variable:
         chunk PCI::pci_probe_hook(PCI::Device rtx_pci_dev)
         {
-            ${Ethernet::AbstractDevice.ref} rtx_net_dev;
-            ${Ethernet::Device.ref} rtx_ether_ctx;
+            {
+                ${Ethernet::AbstractDevice.ref} rtx_net_dev;
+                ${Ethernet::Device.ref} rtx_ether_ctx;
 
-            rtx_net_dev = (${Ethernet::AbstractDevice.ref})alloc_etherdev(sizeof(*rtx_ether_ctx));
-            if (!rtx_net_dev)
-            {
-                ${Log::info("cannot allocate the ethernet device context")};
-                error = -ENOMEM;
-                goto fail;
+                rtx_net_dev = (${Ethernet::AbstractDevice.ref})alloc_etherdev(sizeof(*rtx_ether_ctx));
+                if (!rtx_net_dev)
+                {
+                    ${Log::info("cannot allocate the ethernet device context")};
+                    error = -ENOMEM;
+                    goto fail;
+                }
+                // XXX Past this point we should have another label to do
+                // free_netdev(rtx_net_dev) in case of failure but we don't
+                // have a nice way of doing that.
+                SET_NETDEV_DEV(${local.rtx_net_dev.k_net_dev}, ${rtx_pci_dev.device});
+                strlcpy(${local.rtx_net_dev.k_net_dev}->name,
+                        ${config.ifname},
+                        sizeof(${local.rtx_net_dev.k_net_dev}->name));
+                ${local.rtx_net_dev.k_net_dev}->irq = ${rtx_pci_dev.irq};
+                ${local.rtx_net_dev.k_net_dev}->netdev_ops = &rtx_ether_ops;
+
+                error = register_netdev(${local.rtx_net_dev.k_net_dev});
+                if (error)
+                {
+                    ${Log::info("cannot register the driver in the net subsystem")};
+                    goto fail;
+                }
+
+                /* Initialize our context held by the net_device structure */
+                { /* XXX: I end up with a placeholder if I don't open a scope */
+                    ${local.rtx_ether_ctx.init(local.rtx_net_dev, rtx_pci_dev.pci_device)};
+                }
+
+                /* Register ourselves in the parent context: */
+                /* ${rtx_pci_dev.set_context(local.rtx_ether_ctx)}; */
+                ${rtx_pci_dev.rtx_drv_context} = rtx_ether_ctx;
+
+                /*
+                 * XXX: the asssignments/casts are here to circumvent
+                 * typing issues in the compiler (see previous XXX).
+                 */
+                unsigned char /* __iomem */ *io_addr = ${rtx_pci_dev.ioaddr};
+                ${pointcut Ethernet::adapter_init_context(local.rtx_ether_ctx, local.io_addr)};
+                ${pointcut Ethernet::adapter_reset(local.rtx_ether_ctx)};
+                ${pointcut Ethernet::adapter_load_mac_address(local.rtx_ether_ctx)};
+                memcpy(${local.rtx_ether_ctx.perm_addr},
+                       ${local.rtx_ether_ctx.dev_addr},
+                       ${local.rtx_net_dev.k_net_dev}->addr_len);
             }
-            SET_NETDEV_DEV(${local.rtx_net_dev.k_net_dev}, ${rtx_pci_dev.device});
-            strlcpy(${local.rtx_net_dev.k_net_dev}->name,
-                    ${config.ifname},
-                    sizeof(${local.rtx_net_dev.k_net_dev}->name));
-            ${local.rtx_net_dev.k_net_dev}->irq = ${rtx_pci_dev.irq};
-            ${local.rtx_net_dev.k_net_dev}->netdev_ops = &rtx_ether_ops;
-
-            error = register_netdev(${local.rtx_net_dev.k_net_dev});
-            if (error)
-            {
-                ${Log::info("cannot register the driver in the net subsystem")};
-                goto fail;
-            }
-
-            /* Initialize our context held by the net_device structure */
-            /*
-             * XXX: the cast is here because the compiler resolve the
-             * type of rtx_pci_dev.pci_device to the type of
-             * rtx_pci_dev instead of the type of rtx_pci_dev.pci_device.
-             */
-            ${PCI::AbstractDevice.ref} workaround = ${rtx_pci_dev.pci_device};
-            ${cast local.workaround as PCI::AbstractDevice};
-            { /* XXX: I end up with a placeholder if I don't open a scope */
-                ${local.rtx_ether_ctx.init(local.rtx_net_dev, local.workaround)};
-            }
-
-            /* Register ourselves in the parent context: */
-            /* ${rtx_pci_dev.set_context(local.rtx_ether_ctx)}; */
-            ${rtx_pci_dev}->context = rtx_ether_ctx;
-
-            /*
-             * XXX: the asssignments/casts are here to circumvent
-             * typing issues in the compiler (see previous XXX).
-             */
-            int bars = ${rtx_pci_dev.bars};
-            unsigned char /* __iomem */ *ioaddr = ${rtx_pci_dev.ioaddr};
-            ${cast local.bars as Builtin::number};
-            ${pointcut Ethernet::adapter_init_context(local.rtx_ether_ctx,
-                                                      local.bars,
-                                                      local.ioaddr)};
-            ${pointcut Ethernet::adapter_reset(local.rtx_ether_ctx)};
-            ${pointcut Ethernet::adapter_load_mac_address(local.rtx_ether_ctx)};
-            memcpy(${local.rtx_ether_ctx.perm_addr},
-                   ${local.rtx_ether_ctx.dev_addr},
-                   ${local.rtx_net_dev.k_net_dev}->addr_len);
         }
 
-        /* This chunk should be removed (see #26) */
+        /* XXX This chunk should be removed (see #26) */
         chunk   ::CALL()
         {
         }
@@ -369,15 +383,17 @@
          */
         chunk   PCI::pci_remove_hook(PCI::Device rtx_pci_dev)
         {
-            ${Ethernet::Device.ref} rtx_ether_ctx = ${rtx_pci_dev.rtx_drv_context};
-            BUG_ON(!rtx_ether_ctx);
+            {
+                ${Ethernet::Device.ref} rtx_ether_ctx = ${rtx_pci_dev.rtx_drv_context};
+                BUG_ON(!rtx_ether_ctx);
 
-            ${Ethernet::AbstractDevice.ref} rtx_net_dev = ${local.rtx_ether_ctx.net_device};
-            unregister_netdev(${local.rtx_net_dev.k_net_dev});
-            free_netdev(${local.rtx_net_dev.k_net_dev});
+                ${Ethernet::AbstractDevice.ref} rtx_net_dev = ${local.rtx_ether_ctx.net_device};
+                unregister_netdev(${local.rtx_net_dev.k_net_dev});
+                free_netdev(${local.rtx_net_dev.k_net_dev});
+            }
         }
 
-        /* This chunk should be removed (see #26) */
+        /* XXX This chunk should be removed (see #26) */
         chunk ::CALL()
         {
         }
diff --git a/rathaxes/samples/e1000/ethernet.rti b/rathaxes/samples/e1000/ethernet.rti
--- a/rathaxes/samples/e1000/ethernet.rti
+++ b/rathaxes/samples/e1000/ethernet.rti
@@ -2,16 +2,16 @@
 {
     required variable   Builtin::string ifname;
 
-     provided type   ProtocolId
-     {
-         chunk       LKM::prototypes();
-         chunk       LKM::data();
-         chunk       LKM::code();
-         decl        data_types();
+    provided type   ProtocolId
+    {
+        chunk       LKM::prototypes();
+        chunk       LKM::data();
+        chunk       LKM::code();
+        decl        data_types();
 
-         attribute  Builtin::number.scalar id;
-         attribute  Builtin::string.scalar str;
-     }
+        attribute  Builtin::number.scalar id;
+        attribute  Builtin::string.scalar str;
+    }
 
     provided type   AbstractDevice
     {
@@ -28,72 +28,88 @@
 
     provided type   Device
     {
+        decl        data_types();
+
         chunk       LKM::includes();
-        decl        data_types();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
+
         pointcut    Ethernet::HardwareContext();
 
         method      init(Ethernet::AbstractDevice, PCI::AbstractDevice);
-        /*
-         * Alloc (the AbstractSKBuff inside) the given SKBuff and initialize
-         * the SKBuff, return 1 if the allocation failed, 0 on success.
-         */
+
+        // Alloc (the AbstractSKBuff inside) the given SKBuff and initialize
+        // the SKBuff, return 1 if the allocation failed, 0 on success:
         method      alloc_rx_skbuff(Socket::SKBuff, Builtin::number);
 
         attribute   Device::AbstractDevice.ref      device;
         attribute   PCI::AbstractDevice.ref         pci_device;
         attribute   Ethernet::AbstractDevice.ref    net_device;
-        /*
-         * I'd like to use better names here, but I'd like to understand the
-         * difference between the two first:
-         */
+
+        // I'd like to use better names here, but I'd like to understand the
+        // difference between the two first:
         attribute   Builtin::symbol.ref     perm_addr;
         attribute   Builtin::symbol.ref     dev_addr;
         attribute   Builtin::symbol.scalar  irq;
     }
 
-    required sequence   open(Ethernet::Device)
+    provided sequence       open()
     {
-        provided chunk  LKM::includes();
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
+        provided chunk      LKM::includes();
+        provided chunk      LKM::prototypes();
+        provided chunk      LKM::code();
 
-        /*
-         * For e1000, this part is documented in the Intel Gigabit Ethernet
-         * Controller Software Developper manual. (You can find it in the
-         * doc/hardware directory).
-         */
-        provided pointcut   Ethernet::adapter_setup(Ethernet::Device);
-        provided pointcut   Ethernet::adapter_init_rx(Ethernet::Device);
-        provided pointcut   Ethernet::adapter_init_tx(Ethernet::Device);
+        // Prepare the device and the resources for rx/tx:
+        provided pointcut   Ethernet::adapter_setup_rx_tx(Ethernet::Device);
+
+        // Enable interrupts:
+        provided pointcut   Ethernet::adapter_enable_interrupts(Ethernet::Device);
     }
 
-    required sequence   send(Ethernet::Device, Socket::AbstractSKBuff)
+    provided sequence       send()
     {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
+        provided chunk      LKM::prototypes();
+        provided chunk      LKM::code();
+
+        // Put a packet on the tx ring and signal the device:
+        provided pointcut   Ethernet::adapter_xmit(Ethernet::Device, Socket::SKBuff);
     }
 
-    required sequence   close(Ethernet::Device)
+    provided sequence       close()
     {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
+        provided chunk      LKM::prototypes();
+        provided chunk      LKM::code();
+
+        provided pointcut   Ethernet::adapter_disable_rx(Ethernet::Device);
+        provided pointcut   Ethernet::adapter_disable_tx(Ethernet::Device);
+        provided pointcut   Ethernet::adapter_disable_interrupts(Ethernet::Device);
+
+        // Clean and free all resources on the rx/tx rings:
+        provided pointcut   Ethernet::adapter_free_rx_tx(Ethernet::Device);
     }
 
-    required sequence   interrupt_handler(Ethernet::Device)
+    provided sequence       interrupt_handler()
     {
-        provided chunk  LKM::prototypes();
-        provided chunk  LKM::code();
+        provided chunk      LKM::prototypes();
+        provided chunk      LKM::code();
+
+        // Interrupt handler:
+        provided pointcut   Ethernet::adapter_handle_interrupt(Ethernet::Device);
     }
 
-    provided sequence   init()
+    provided sequence       init()
     {
-        provided chunk  LKM::data();
-        provided chunk  PCI::pci_probe_hook(PCI::Device);
+        provided chunk      LKM::data();
+        provided chunk      PCI::pci_probe_hook(PCI::Device);
 
-        provided pointcut   Ethernet::adapter_init_context(Ethernet::Device,
-                                                           Builtin::number,
-                                                           Builtin::symbol);
+        // Init the hardware context structure, doesn't allocate anything:
+        provided pointcut   Ethernet::adapter_init_context(Ethernet::Device, Builtin::symbol);
+
+        // Reset the adapter:
         provided pointcut   Ethernet::adapter_reset(Ethernet::Device);
+
+        // Load the MAC address from the EEPROM and save it into the
+        // dev_addr field/attribute of Ethernet::Device:
         provided pointcut   Ethernet::adapter_load_mac_address(Ethernet::Device);
     }
 
diff --git a/rathaxes/samples/e1000/lkm.rtx b/rathaxes/samples/e1000/lkm.rtx
--- a/rathaxes/samples/e1000/lkm.rtx
+++ b/rathaxes/samples/e1000/lkm.rtx
@@ -1,40 +1,5 @@
 device LKM use LKM, PCI, Ethernet, Log, Socket
 {
-    Ethernet::open(Ethernet::Device dev)
-    {
-        Log::info("opening the device");
-
-        e1000::activate_device_interruption(dev);
-        Log::info("interruption enabled");
-
-        e1000::print_status(dev);
-    }
-
-    Ethernet::close(Ethernet::Device dev)
-    {
-        Log::info("closing the device");
-
-        /*
-         * Note: some calls to release resources must be done when IRQs are
-         * enabled (dma_free_coherent() for example). So we have to cleanup our
-         * stuff before free_interrupt_handler().
-         */
-        e1000::free_rx_tx(dev);
-        Log::info("free'ed up rx/tx resources");
-    }
-
-    Ethernet::interrupt_handler(Ethernet::Device dev)
-    {
-        Log::info("got an interruption");
-        e1000::handle_interrupt(dev);
-    }
-
-    Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb)
-    {
-        Log::info("we have one packet to transmit!");
-        e1000::xmit(dev, skb);
-    }
-
     LKM::init()
     {
         Log::info("loading module");
@@ -50,7 +15,7 @@
 {
     LKM::name = "rtx_e1k";
     LKM::author = "Rathaxes";
-    LKM::description = "Hello World Loadable Kernel Module (LKM)";
+    LKM::description = "Rathaxes Intel PCI Gigabit NIC sample driver";
     LKM::license = "GPL";
 
     /*
diff --git a/rathaxes/samples/e1000/e1000.blt b/rathaxes/samples/e1000/old_e1000.blt
copy from rathaxes/samples/e1000/e1000.blt
copy to rathaxes/samples/e1000/old_e1000.blt
--- a/rathaxes/samples/e1000/e1000.blt
+++ b/rathaxes/samples/e1000/old_e1000.blt
@@ -170,14 +170,15 @@
                 for (i = 0; i != ${config.rx_ring_size}; ++i)
                 {
                     ${Socket::SKBuff.ref} skbuff = &hw_ctx->rx_ring.skbuffs[i];
-                    // XXX #46: ${rtx_ether_ctx.init_rx_skbuff(local.skbuff, config.rx_buffer_len)};
-                    if (rtx_ethernet_init_rx_skbuff(${local.skbuff}, ${config.rx_buffer_len}))
+                    // XXX #46: ${rtx_ether_ctx.alloc_rx_skbuff(local.skbuff, config.rx_buffer_len)};
+                    if (rtx_ethernet_alloc_rx_skbuff(${local.skbuff}, ${config.rx_buffer_len}))
                     {
                         ${Log::info("adapter_init_rx: cannot allocate a skbuff for the rx ring")};
                         goto err_skbuffs_alloc;
                     }
-                    // XXX #46: ${local.skbuff.map_from(rtx_ether_ctx.device)};
-                    if (rtx_socket_skbuff_map(${local.skbuff}, ${rtx_ether_ctx.device}, RTX_DMA_FROM_DEVICE))
+                    /* XXX: recuperer le dma handle et le placer correctement dans le descripteur. */
+                    ${DMA::map(local.rtx_ether_ctx.device, local.skbuff.data, local.skbuff.len, RTX_DMA_FROM_DEVICE)}
+                    if (${DMA::map(local.rtx_ether_ctx.device, local.skbuff.data, local.skbuff.len, RTX_DMA_FROM_DEVICE)})
                     {
                         ${Log::info("adapter_init_rx: cannot dma-map a skbuff for the rx ring")};
                         goto err_skbuffs_map;
diff --git a/rathaxes/samples/e1000/e1000.rti b/rathaxes/samples/e1000/old_e1000.rti
copy from rathaxes/samples/e1000/e1000.rti
copy to rathaxes/samples/e1000/old_e1000.rti
diff --git a/rathaxes/samples/e1000/pci.blt b/rathaxes/samples/e1000/pci.blt
--- a/rathaxes/samples/e1000/pci.blt
+++ b/rathaxes/samples/e1000/pci.blt
@@ -73,9 +73,8 @@
 
         method  init(PCI::AbstractDevice pdev)
         {
-            ${PCI::AbstractDevice.ref} workaround = (${PCI::AbstractDevice.ref})pdev;
             ${self}->pdev = ${pdev};
-            ${self}->bars = pci_select_bars(${local.workaround.k_pci_dev}, IORESOURCE_MEM);
+            ${self}->bars = pci_select_bars(${pdev.k_pci_dev}, IORESOURCE_MEM);
             ${self}->ioaddr = NULL;
             ${self}->context = NULL;
         }
@@ -92,13 +91,12 @@
 
         method  select_ioaddr(Builtin::number bar)
         {
-            ${PCI::AbstractDevice.ref} select_ioaddr_pdev = ${self}->pdev;
-            ${self}->ioaddr = pci_ioremap_bar(${local.select_ioaddr_pdev.k_pci_dev}, ${bar});
+            ${self.ioaddr} = pci_ioremap_bar(${self.pci_device.k_pci_dev}, ${bar});
         }
 
         method  set_rtx_drv_context(Builtin::symbol ctx)
         {
-            ${self}->context = ctx;
+            ${self.rtx_drv_context} = ${ctx};
         }
 
         map
@@ -127,7 +125,7 @@
                                                       const struct pci_device_id *pdev_id)
             {
                 int error;
-                ${PCI::Device.ref} rtx_pci_dev;
+                ${PCI::Device.ref} rtx_pci_dev = NULL;
                 ${PCI::AbstractDevice.ref} rtx_pdev = (${PCI::AbstractDevice.ref})pdev;
 
                 rtx_pci_dev = kmalloc(sizeof(*rtx_pci_dev), GFP_KERNEL);
@@ -138,13 +136,7 @@
                     goto fail;
                 }
 
-                /*
-                 * XXX: I'm getting placeholder in the generated code if I don't
-                 * open a scope here:
-                 */
-                {
-                    ${local.rtx_pci_dev.init(local.rtx_pdev)};
-                }
+                ${local.rtx_pci_dev.init(local.rtx_pdev)};
 
                 /* ${local.pdev.set_rtx_context(local.rtx_pci_dev)}; */
                 pci_set_drvdata(pdev, rtx_pci_dev);
@@ -157,14 +149,14 @@
                     goto fail;
                 }
 
-                /* ${local.rtx_pci_dev.select_ioaddr(local.rtx_pci_dev.BAR_0)}; */
-                rtx_pci_dev->ioaddr = pci_ioremap_bar(
+                // XXX #46: ${local.rtx_pci_dev.select_ioaddr(local.rtx_pci_dev.BAR_0)};
+                ${local.rtx_pci_dev.ioaddr} = pci_ioremap_bar(
                     pdev, ${local.rtx_pci_dev.BAR_0}
                 );
                 if (!${local.rtx_pci_dev.ioaddr})
                 {
                     ${Log::info("can't map the device address space")};
-                    error = 1; /* XXX anything more approriate? */
+                    error = -EIO; /* XXX anything more approriate? */
                     goto fail;
                 }
 
@@ -173,7 +165,7 @@
                 return 0;
 
             fail:
-                /* ${local.pdev.set_rtx_drv_context(NULL)}; */
+                // ${local.pdev.set_rtx_drv_context(NULL)};
                 pci_set_drvdata(pdev, NULL);
                 kfree(rtx_pci_dev);
                 return error;
diff --git a/rathaxes/samples/e1000/socket.blt b/rathaxes/samples/e1000/socket.blt
--- a/rathaxes/samples/e1000/socket.blt
+++ b/rathaxes/samples/e1000/socket.blt
@@ -22,16 +22,12 @@
     {
         decl  data_types()
         {
-            ${Socket::AbstractSKBuff.ref}       skbuff;
-            ${DMA::AbstractDMAHandle.scalar}    dma_handle;
-            unsigned int                        size;
+            ${Socket::AbstractSKBuff.ref}   skbuff;
         }
 
         chunk   LKM::prototypes()
         {
             static void rtx_socket_skbuff_dump_infos(${Socket::SKBuff.ref});
-            static int  rtx_socket_skbuff_map(${Socket::SKBuff.ref}, ${Device::AbstractDevice.ref}, ${DMA::DMADirection.scalar});
-            static void rtx_socket_skbuff_unmap_and_free(${Socket::SKBuff.ref}, ${Device::AbstractDevice.ref}, ${DMA::DMADirection.scalar});
         }
 
         chunk   LKM::code()
@@ -52,96 +48,32 @@
                  * arguments yet.
                  */
                 pr_info(
-                        "\t protocol = %#-5x (%s) ip_summed = %d (%s)\n"
-                        "\t      len = %-5u data_len = %-5u head_len = %-5u\n"
-                        "\t nr_frags = %u\n"
-                        "\t gso_size = %-5u gso_segs = %-5u gso_type = %-5u",
-                        /* XXX: can't use ${local.ethernet_proto.id} here (issue #52): */
-                        ethernet_proto.id, ${local.ethernet_proto.str},
-                        ${local.self.sk_buff.k_sk_buff}->ip_summed, ip_summed_values[${local.self.sk_buff.k_sk_buff}->ip_summed],
-                        ${local.self.sk_buff.k_sk_buff}->len, ${local.self.sk_buff.k_sk_buff}->data_len, skb_headlen(${local.self.sk_buff.k_sk_buff}),
-                        shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type
+                    "\t protocol = %#-5x (%s) ip_summed = %d (%s)\n"
+                    "\t      len = %-5u data_len = %-5u head_len = %-5u\n"
+                    "\t nr_frags = %u\n"
+                    "\t gso_size = %-5u gso_segs = %-5u gso_type = %-5u",
+                    /* XXX: can't use ${local.ethernet_proto.id} here (issue #52): */
+                    ethernet_proto.id, ${local.ethernet_proto.str},
+                    ${local.self.sk_buff.k_sk_buff}->ip_summed, ip_summed_values[${local.self.sk_buff.k_sk_buff}->ip_summed],
+                    ${local.self.sk_buff.k_sk_buff}->len, ${local.self.sk_buff.k_sk_buff}->data_len, skb_headlen(${local.self.sk_buff.k_sk_buff}),
+                    shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type
                 );
             }
-
-            static int rtx_socket_skbuff_map(${Socket::SKBuff.ref} self,
-                                             ${Device::AbstractDevice.ref} dev,
-                                             ${DMA::DMADirection.scalar} direction)
-            {
-                WARN_ON(!${local.self.sk_buff});
-                WARN_ON(${local.self.dma_handle});
-                /*
-                 * TODO: we don't support skbuffs with paged data yet (see also
-                 * http://vger.kernel.org/~davem/skb_data.html).
-                 */
-                WARN_ON(skb_is_nonlinear(${local.self.sk_buff.k_sk_buff}));
-
-                unsigned int len = ${local.self.size};
-                ${cast local.len as Builtin::number};
-                ${local.self.dma_handle} = ${DMA::map(local.dev, local.self.sk_buff.k_sk_buff, local.len, local.direction)};
-                int err = ${DMA::mapping_error(local.dev, local.self.dma_handle)};
-                if (err)
-                {
-                    ${local.self.dma_handle} = 0;
-                    return err;
-                }
-                return 0;
-            }
-
-            static void rtx_socket_skbuff_unmap_and_free(${Socket::SKBuff.ref} self,
-                                                         ${Device::AbstractDevice.ref} dev,
-                                                         ${DMA::DMADirection} direction)
-            {
-                WARN_ON(!${local.self.sk_buff});
-                WARN_ON(skb_is_nonlinear(${local.self.sk_buff.k_sk_buff});
-
-                if (${local.self.dma_handle})
-                {
-                    unsigned int len = ${local.self.size};
-                    ${cast local.len as Builtin::number};
-                    ${DMA::unmap(local.dev, local.self.dma_handle, local.len, local.direction)};
-                    ${local.self.dma_handle} = 0;
-                }
-                dev_kfree_skb_any(${local.self.sk_buff.k_sk_buff});
-                ${local.self.sk_buff} = NULL;
-            }
         }
 
-        /*
-         * XXX: the rathaxes argument kernel_skb is not actually bound to the
-         * correct C variable from Ethernet::send() (so I named it as the C
-         * variable I needed)
-         */
-        method  init(Socket::AbstractSKBuff kernel_skb, Builtin::number size)
+        method init(Socket::AbstractSKBuff k_skb)
         {
-            ${self.sk_buff} = ${kernel_skb};
-            ${self.size} = ${size};
-            ${self.dma_handle} = 0;
+            ${self} = (${Socket::SKBuff.ref})(${k_skb});
         }
 
-        method  dump_infos()
+        method dump_infos()
         {
             rtx_socket_skbuff_dump_infos(${self});
         }
 
-        method   map_to(Device::AbstractDevice dev)
+        method free()
         {
-            rtx_socket_skbuff_map(${self}, ${dev}, RTX_DMA_TO_DEVICE);
-        }
-
-        method   map_from(Device::AbstractDevice dev)
-        {
-            rtx_socket_skbuff_map(${self}, ${dev}, RTX_DMA_FROM_DEVICE);
-        }
-
-        method   unmap_to_and_free(Device::AbstractDevice dev)
-        {
-            rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, RTX_DMA_TO_DEVICE);
-        }
-
-        method   unmap_from_and_free(Device::AbstractDevice dev)
-        {
-            rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, RTX_DMA_FROM_DEVICE);
+            dev_kfree_skb(${self.sk_buff.k_sk_buff});
         }
 
         map
@@ -151,13 +83,9 @@
             // management can be abstracted from the user. But this is at least
             // useful for internal use:
             sk_buff: (${self})->skbuff;
-            // XXX: We need to cast here so we can do things like
-            // var.dma_handle = 0; but the type shouldn't be hardcoded (at the
-            // same time ${DMA:AbstractDMAHandle} couldn't be used because that
-            // would yield to a struct type which you can't assign directly;
-            // but maybe doing the ->data in that case would be acceptable).
-            dma_handle: (*((dma_addr_t *)&(${self})->dma_handle));
-            size: (${self})->size;
+
+            data: ((struct sk_buff *)((${self})->skbuff))->data;
+            len: ((struct sk_buff *)((${self})->skbuff))->len;
         }
     }
 }
diff --git a/rathaxes/samples/e1000/socket.rti b/rathaxes/samples/e1000/socket.rti
--- a/rathaxes/samples/e1000/socket.rti
+++ b/rathaxes/samples/e1000/socket.rti
@@ -12,20 +12,15 @@
     {
         chunk   LKM::prototypes();
         chunk   LKM::code();
+
         decl    data_types();
-        method  init(Socket::AbstractSKBuff, Builtin::number);
+
+        method  init(Socket::AbstractSKBuff);
         method  dump_infos();
-        /*
-         * map_to and map_from return a non-zero value on failure (which
-         * doesn't correspond to an errno value):
-         */
-        method  map_to(Device::AbstractDevice);
-        method  map_from(Device::AbstractDevice);
-        method  unmap_to_and_free(Device::AbstractDevice);
-        method  unmap_from_and_free(Device::AbstractDevice);
+        method  free();
 
-        attribute   Socket::AbstractSKBuff.ref      sk_buff;
-        attribute   DMA::AbstractDMAHandle.scalar   dma_handle;
-        attribute   Builtin::number.scalar          size;
+        attribute   Socket::AbstractSKBuff.ref  sk_buff;
+        attribute   Builtin::symbol.ref         data;
+        attribute   Builtin::number.scalar      len;
     }
 }
