view e1000_implement_the_frame_transmission_chunk.patch @ 90:4968acb39c7b

Finalize work on minimal TX
author Louis Opter <louis@lse.epitech.net>
date Mon, 15 Oct 2012 06:17:32 +0200
parents a9b47a2f8b98
children
line wrap: on
line source

# HG changeset patch
# Parent 87ba2a19a59fb7be346ad40a57439b6b752b152e
rathaxes: add the transmission code for very simple packets in the e1000 sample

Very simple packets are those which don't need to be fragmented nor checksummed.

The transmission code is defined in different chunks/methods of the TxRing type.
An e1000::xmit method template has been added to call the different methods on
the TxRing object in the e1000::Context object. The interrupt handler has been
reworked to handle transmission interrupts and cleanup the TxRing accordingly.

In parallel to the transmission code, the Socket::SKBuff type is now a real
Rathaxes type with its own debug and DMA operations methods.

Also, sorry, this changeset is mixed with other changes:

- Small improvements everywhere now that the compiler uses CNorm unstrict;
- Add the Device::AbstractDevice type; this Rathaxes represents the generic type
  used by the kernel to represent a device;
- Add the Socket::AbstractSKBuff type too to represent the SKBuff type used by
  the kernel;
- Likewise the Ethernet::AbstractDevice type represents the type used by the
  kernel for Ethernet devices;
- Socket::SKBuff is now a real Rathaxes type which aggregates a KernelSKBuff
  object.

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
@@ -1,6 +1,22 @@
-ADD_RATHAXES_SOURCES(e1000_src lkm.rtx
-                     RTI builtin.rti log.rti lkm.rti pci.rti socket.rti ethernet.rti e1000.rti
-                     BLT log.blt lkm.blt pci.blt socket.blt ethernet.blt e1000.blt)
+ADD_RATHAXES_SOURCES(e1000_src
+                     lkm.rtx
+                     RTI
+                     builtin.rti
+                     log.rti
+                     lkm.rti
+                     device.rti
+                     pci.rti
+                     socket.rti
+                     ethernet.rti
+                     e1000.rti
+                     BLT
+                     log.blt
+                     lkm.blt
+                     pci.blt
+                     device.blt
+                     socket.blt
+                     ethernet.blt
+                     e1000.blt)
 
 IF (LINUX_KBUILD_DIR)
     ADD_RATHAXES_LKM(e1000 e1000_src)
diff --git a/rathaxes/samples/e1000/device.blt b/rathaxes/samples/e1000/device.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/device.blt
@@ -0,0 +1,25 @@
+with Device, LKM
+{
+    template type   Device::AbstractDevice()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/device.h>
+
+            static const ${Device::AbstractDevice}  force_rtx_device_decl;
+        }
+
+        chunk   decl()
+        {
+            typedef struct device   *rtx_device_p;
+        }
+
+        chunk   init()
+        {
+        }
+
+        map
+        {
+        }
+    }
+}
diff --git a/rathaxes/samples/e1000/device.rti b/rathaxes/samples/e1000/device.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/e1000/device.rti
@@ -0,0 +1,9 @@
+interface Device : LKM
+{
+    provided type   AbstractDevice
+    {
+        chunk       LKM::includes();
+        method      decl();
+        method      init();
+    }
+}
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
@@ -11,16 +11,15 @@
 
         chunk   ::decl()
         {
-            typedef struct rtx_e1000_rx_descriptor
+            struct rtx_e1000_rx_descriptor
             {
-                /* actual types are in comments */
-                unsigned long int   /* __le64 */    buff_addr;
-                unsigned short      /* __le16 */    length;
-                unsigned short      /* __le16 */    csum;
-                unsigned char                       status;
-                unsigned char                       errors;
-                unsigned short      /* __le16 */    special;
-            } *rtx_e1000_rx_descriptor_p;
+                __le64          buff_addr;
+                __le16          length;
+                __le16          csum;
+                unsigned char   status;
+                unsigned char   errors;
+                __le16          special;
+            };
         }
 
         chunk   ::init()
@@ -50,30 +49,30 @@
 
         chunk   ::decl()
         {
-            typedef struct rtx_e1000_tx_descriptor
+            struct rtx_e1000_tx_descriptor
             {
-                unsigned long int   /* __le64 */    buff_addr;
+                __le64                  buff_addr;
                 union
                 {
-                    unsigned int    /* __le32 */    data;
+                    __le32              data;
                     struct
                     {
-                        unsigned short /* __le16 */ length;
-                        unsigned char               csum_offset; /* CSO */
-                        unsigned char               cmd;
-                    }                               fields;
-                }                                   lower;
+                        __le16          length;
+                        unsigned char   csum_offset; /* CSO */
+                        unsigned char   cmd;
+                    }                   fields;
+                }                       lower;
                 union
                 {
-                    unsigned int    /* __le32 */    data;
+                    __le32              data;
                     struct
                     {
-                        unsigned char               status;
-                        unsigned char               csum_start; /* CSS */
-                        unsigned short /* __le16 */ special;
-                    }                               fields;
-                }                                   upper;
-            } *rtx_e1000_tx_descriptor_p;
+                        unsigned char   status;
+                        unsigned char   csum_start; /* CSS */
+                        __le16          special;
+                    }                   fields;
+                }                       upper;
+            };
         }
 
         chunk   ::init()
@@ -93,9 +92,8 @@
      *   CNorm unstrict);
      * - dma_base: (physical) address of the ring where the device can access
      *   the different descriptors;
-     * - skbuffs: array of the skbuffs associated with each descriptor;
-     * - dma_skbuffs: (physical) address of each skbuff where the device can
-     *   write the received packets;
+     * - skbuffs: array of the skbuffs and their dma (physical) address
+     *   associated with each descriptor.
      */
     template type   e1000::RxRing()
     {
@@ -110,9 +108,8 @@
             {
                 unsigned int                    size;
                 struct rtx_e1000_rx_descriptor  *base;
-                void*   /* dma_addr_t */        dma_base;
-                struct sk_buff                  *skbuffs[256 /* ${config.rx_ring_size} */];
-                void*   /* dma_addr_t */        dma_skbuffs[256 /* ${config.rx_ring_size} */];
+                dma_addr_t                      dma_base;
+                ${Socket::SKBuff}               skbuffs[${config.rx_ring_size}];
             };
         }
 
@@ -134,7 +131,13 @@
      * - 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.
+     *   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()
     {
@@ -148,11 +151,112 @@
             struct rtx_e1000_tx_ring
             {
                 unsigned int                    size;
-                struct rtx_e1000_tx_descriptor  *base;
-                void*   /* dma_addr_t */        dma_base;
+                /* XXX: can't use ${e1000::TxDescriptor} here: */
+                struct rtx_e1000_tx_descriptor  *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(struct rtx_e1000_tx_ring *);
+            static unsigned int rtx_e1000_tx_ring_descriptors_remaining(struct rtx_e1000_tx_ring *);
+            static int          rtx_e1000_tx_ring_tso_cksum_offload(struct rtx_e1000_tx_ring *, struct rtx_socket_skbuff *);
+            static void         rtx_e1000_tx_ring_put(struct rtx_e1000_tx_ring *, struct rtx_socket_skbuff *);
+            static void         rtx_e1000_tx_ring_start_xmit(struct rtx_e1000_tx_ring *, const struct rtx_e1000_ctx *);
+        }
+
+        chunk   LKM::code()
+        {
+            static void         rtx_e1000_tx_ring_clean(struct rtx_e1000_tx_ring *self)
+            {
+                ${e1000::TxDescriptor}  *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(struct rtx_e1000_tx_ring *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(struct rtx_e1000_tx_ring *self, struct rtx_socket_skbuff *skb)
+            {
+                return skb_is_gso(skb->skbuff) || skb->skbuff->ip_summed == CHECKSUM_PARTIAL;
+            }
+
+            static void         rtx_e1000_tx_ring_put(struct rtx_e1000_tx_ring *self, struct rtx_socket_skbuff *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.
+                 */
+                struct rtx_e1000_tx_descriptor *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(skb->skbuff));
+                tx_desc->upper.data = 0;
+                tx_desc->buff_addr = cpu_to_le64(skb->dma_handle);
+                memcpy(&self->skbuffs[self->tail], skb, sizeof(*skb));
+                self->tail = (self->tail + 1) % ${config.tx_ring_size};
+            }
+
+            static void         rtx_e1000_tx_ring_start_xmit(struct rtx_e1000_tx_ring *self, const struct rtx_e1000_ctx *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   clean()
+        {
+            rtx_e1000_tx_ring_clean(${self});
+        }
+
+        chunk   descriptors_remaining()
+        {
+            rtx_e1000_tx_ring_descriptors_remaining(${self});
+        }
+
+        chunk   tso_cksum_offload(Socket::SKBuff skb)
+        {
+        }
+
+        chunk   put(Socket::SKBuff skb)
+        {
+            rtx_e1000_tx_ring_put(${self}, &${skb});
+        }
+
+        chunk   start_xmit(e1000::Context ctx)
+        {
+            rtx_e1000_tx_ring_start_xmit(${self}, ${ctx});
+        }
+
         chunk   ::init()
         {
         }
@@ -334,6 +438,45 @@
         }
     }
 
+    template type   e1000::TxDescriptorFlags()
+    {
+        chunk LKM::includes()
+        {
+            static const ${e1000::TxDescriptorFlags}  force_enum_rtx_e1000_tx_descriptor_flags_decl;
+        }
+
+        chunk ::decl()
+        {
+            enum    rtx_e1000_tx_descriptor_flags
+            {
+                E1000_TXD_DTYP_D        = 0x00100000, /* Data Descriptor */
+                E1000_TXD_DTYP_C        = 0x00000000, /* Context Descriptor */
+                E1000_TXD_POPTS_IXSM    = 0x01,       /* Insert IP checksum */
+                E1000_TXD_POPTS_TXSM    = 0x02,       /* Insert TCP/UDP checksum */
+                E1000_TXD_CMD_EOP       = 0x01000000, /* End of Packet */
+                E1000_TXD_CMD_IFCS      = 0x02000000, /* Insert FCS (Ethernet CRC) */
+                E1000_TXD_CMD_IC        = 0x04000000, /* Insert Checksum */
+                E1000_TXD_CMD_RS        = 0x08000000, /* Report Status */
+                E1000_TXD_CMD_RPS       = 0x10000000, /* Report Packet Sent */
+                E1000_TXD_CMD_DEXT      = 0x20000000, /* Descriptor extension (0 = legacy) */
+                E1000_TXD_CMD_VLE       = 0x40000000, /* Add VLAN tag */
+                E1000_TXD_CMD_IDE       = 0x80000000, /* Enable Tidv register */
+                E1000_TXD_STAT_DD       = 0x00000001, /* Descriptor Done */
+                E1000_TXD_STAT_EC       = 0x00000002, /* Excess Collisions */
+                E1000_TXD_STAT_LC       = 0x00000004, /* Late Collisions */
+                E1000_TXD_STAT_TU       = 0x00000008, /* Transmit underrun */
+                E1000_TXD_CMD_TCP       = 0x01000000, /* TCP packet */
+                E1000_TXD_CMD_IP        = 0x02000000, /* IP packet */
+                E1000_TXD_CMD_TSE       = 0x04000000, /* TCP Seg enable */
+                E1000_TXD_STAT_TC       = 0x00000004, /* Tx Underrun */
+            };
+        }
+
+        map
+        {
+        }
+    }
+
     template sequence   e1000::create_device()
     {
         chunk Ethernet::create_device(PCI::Device pdev, Ethernet::Device rtx_ether_ctx)
@@ -376,8 +519,7 @@
             udelay(10);
 
             /* Now we can load its mac address (thanks minix code) */
-            int i = 0;
-            for (i = 0 /* < this is not generated! (cnorm bug) */; i < 3; ++i)
+            for (int i = 0; i < 3; ++i)
             {
                 rtx_e1000_register_write32(&${rtx_ether_ctx}->hw_ctx, E1000_EEPROM_READ, (i << 8) | 1);
 
@@ -420,6 +562,7 @@
         }
     }
 
+    /* TODO: make that a method of e1000::Context */
     template sequence   e1000::print_status(Ethernet::Device ctx)
     {
         chunk   LKM::prototypes()
@@ -466,17 +609,19 @@
      * ${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)
     {
         chunk   LKM::prototypes()
         {
-            static unsigned int    rtx_e1000_register_read32(struct rtx_e1000_ctx *, unsigned int);
+            static unsigned int    rtx_e1000_register_read32(const struct rtx_e1000_ctx *, unsigned int);
         }
 
         chunk   LKM::code()
         {
-            static unsigned int    rtx_e1000_register_read32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset)
+            static unsigned int    rtx_e1000_register_read32(const struct rtx_e1000_ctx *ctx, unsigned int reg_offset)
             {
                 return ioread32(ctx->ioaddr + reg_offset);
             }
@@ -492,12 +637,12 @@
     {
         chunk   LKM::prototypes()
         {
-            static void rtx_e1000_register_write32(struct rtx_e1000_ctx *, unsigned int, unsigned int);
+            static void rtx_e1000_register_write32(const struct rtx_e1000_ctx *, unsigned int, unsigned int);
         }
 
         chunk   LKM::code()
         {
-            static void rtx_e1000_register_write32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
+            static void rtx_e1000_register_write32(const struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
             {
                 iowrite32(value, ctx->ioaddr + reg_offset);
             }
@@ -513,12 +658,12 @@
     {
         chunk   LKM::prototypes()
         {
-            static void rtx_e1000_register_set32(struct rtx_e1000_ctx *, unsigned int, unsigned int);
+            static void rtx_e1000_register_set32(const struct rtx_e1000_ctx *, unsigned int, unsigned int);
         }
 
         chunk   LKM::code()
         {
-            static void rtx_e1000_register_set32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
+            static void rtx_e1000_register_set32(const struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
             {
                 iowrite32(rtx_e1000_register_read32(ctx, reg_offset) | value, ctx->ioaddr + reg_offset);
             }
@@ -534,12 +679,12 @@
     {
         chunk   LKM::prototypes()
         {
-            static void rtx_e1000_register_unset32(struct rtx_e1000_ctx *, unsigned int, unsigned int);
+            static void rtx_e1000_register_unset32(const struct rtx_e1000_ctx *, unsigned int, unsigned int);
         }
 
         chunk   LKM::code()
         {
-            static void rtx_e1000_register_unset32(struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
+            static void rtx_e1000_register_unset32(const struct rtx_e1000_ctx *ctx, unsigned int reg_offset, unsigned int value)
             {
                 iowrite32(rtx_e1000_register_read32(ctx, reg_offset) & ~value, ctx->ioaddr + reg_offset);
             }
@@ -626,12 +771,18 @@
         }
     }
 
+    /* TODO: refactor
+     *
+     * Split into two method methods:
+     * - e1000::RxRing::init_rx()
+     * - e1000::TxRing::init_tx()
+     *
+     * Also it should use the new methods in Socket::SKbuff.
+     */
     template sequence   set_up_device(Ethernet::Device ctx)
     {
         chunk  ::CALL()
         {
-            typedef unsigned long int   dma_addr_t;
-
             /*
              * This part is documented in the Intel Gigabit Ethernet Controller
              * Software Developper manual. (You can find it in the doc/hardware
@@ -663,6 +814,8 @@
              *   E1000_CRCERRS to E1000_TSCTFC.
              */
 
+            int i;
+
             rtx_e1000_register_set32(hw_ctx, E1000_CTRL,
                                      E1000_CMD_ASDE |
                                      E1000_CMD_SLU);
@@ -676,7 +829,6 @@
             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);
-            int i = 0; /* CNorm workaround, the init part of for isn't generated */
             for (i = 0; i != 64; ++i)
                 rtx_e1000_register_write32(hw_ctx, E1000_CRCERRS + i * 4, 0);
             
@@ -719,7 +871,6 @@
 
             /* 2. Initialize the MTA */
 
-            i = 0; /* CNorm workaround, the init part of for isn't generated */
             for (i = 0; i != 128; ++i)
                 rtx_e1000_register_write32(hw_ctx, E1000_MTA + i * 4, 0);
 
@@ -733,7 +884,7 @@
             hw_ctx->rx_ring.base = dma_alloc_coherent(
                     &${ctx}->pci_dev->dev,
                     hw_ctx->rx_ring.size,
-                    (dma_addr_t *)&hw_ctx->rx_ring.dma_base,
+                    &hw_ctx->rx_ring.dma_base,
                     GFP_KERNEL);
             if (!hw_ctx->rx_ring.base)
             {
@@ -747,41 +898,37 @@
              * Allocate the skbuffs, map them for DMA, and write their address
              * in the corresponding descriptor.
              */
-            i = 0;
             for (i = 0; i != ${config.rx_ring_size}; ++i)
             {
-                hw_ctx->rx_ring.skbuffs[i] = netdev_alloc_skb(
+                hw_ctx->rx_ring.skbuffs[i].skbuff = netdev_alloc_skb(
                         ${ctx}->net_dev,
                         ${config.rx_buffer_len});
-                if (!hw_ctx->rx_ring.skbuffs[i])
+                if (!hw_ctx->rx_ring.skbuffs[i].skbuff)
                 {
                     ${Log::info("cannot allocate a skbuff for the rx ring")};
                     goto err_skbuffs_alloc;
                 }
-                hw_ctx->rx_ring.dma_skbuffs[i] = (void *)dma_map_single(
+                hw_ctx->rx_ring.skbuffs[i].dma_handle = dma_map_single(
                         &${ctx}->pci_dev->dev,
-                        hw_ctx->rx_ring.skbuffs[i]->data,
+                        hw_ctx->rx_ring.skbuffs[i].skbuff->data,
                         ${config.rx_buffer_len},
                         DMA_FROM_DEVICE);
-                /*
-                 * Either this fails because, when compiling with gcc because
-                 * the last argument is not of the correct type (dma_addr_t).
-                 * Or it fails because of the lack of CNorm Unstrict.
-                 */
-                if (dma_mapping_error(&${ctx}->pci_dev->dev, (dma_addr_t)hw_ctx->rx_ring.dma_skbuffs[i]))
+                int dma_error = dma_mapping_error(&${ctx}->pci_dev->dev,
+                        hw_ctx->rx_ring.skbuffs[i].dma_handle);
+                if (dma_error)
                 {
                     ${Log::info("cannot dma-map a skbuff for the rx ring")};
                     goto err_skbuffs_map;
                 }
-                hw_ctx->rx_ring.base[i].buff_addr = (unsigned long int)cpu_to_le64(
-                        hw_ctx->rx_ring.dma_skbuffs[i]);
+                hw_ctx->rx_ring.base[i].buff_addr = cpu_to_le64(
+                        hw_ctx->rx_ring.skbuffs[i].skbuff);
             }
 
             ${Log::info("setup_device: skbuffs allocated")};
 
             /* 5. Save the emplacement and the size of the ring in RDBA/RDLEN */
-            rtx_e1000_register_write32(hw_ctx, E1000_RDBAL, (dma_addr_t)hw_ctx->rx_ring.dma_base & 0xffffffff);
-            rtx_e1000_register_write32(hw_ctx, E1000_RDBAH, (dma_addr_t)hw_ctx->rx_ring.dma_base >> 32);
+            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 */
@@ -820,7 +967,7 @@
             hw_ctx->tx_ring.base = dma_alloc_coherent(
                     &${ctx}->pci_dev->dev,
                     hw_ctx->tx_ring.size,
-                    (dma_addr_t *)&hw_ctx->tx_ring.dma_base,
+                    &hw_ctx->tx_ring.dma_base,
                     GFP_KERNEL);
             if (!hw_ctx->rx_ring.base)
             {
@@ -831,16 +978,18 @@
             ${Log::info("setup_device: tx descriptors allocated")};
 
             /* 2. Save the emplacement and the size of the ring in TDBA/TDLEN */
-            rtx_e1000_register_write32(hw_ctx, E1000_TDBAL, (dma_addr_t)hw_ctx->tx_ring.dma_base & 0xffffffff);
-            rtx_e1000_register_write32(hw_ctx, E1000_TDBAH, (dma_addr_t)hw_ctx->tx_ring.dma_base >> 32);
+            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_PSP);
+            rtx_e1000_register_set32(hw_ctx, E1000_TCTL, E1000_TCTL_PSP|E1000_TCTL_EN);
 
             ${Log::info("transmit registers configured and transmitter enabled")};
 
@@ -860,15 +1009,15 @@
             {
                 dma_unmap_single(
                         &${ctx}->pci_dev->dev,
-                        (dma_addr_t)hw_ctx->rx_ring.dma_skbuffs[i],
+                        hw_ctx->rx_ring.skbuffs[i].dma_handle,
                         ${config.rx_buffer_len},
                         DMA_FROM_DEVICE);
         err_skbuffs_map:
-                dev_kfree_skb(hw_ctx->rx_ring.skbuffs[i]);
+                dev_kfree_skb(hw_ctx->rx_ring.skbuffs[i].skbuff);
             }
 
             dma_free_coherent(&${ctx}->pci_dev->dev, hw_ctx->rx_ring.size,
-                    hw_ctx->rx_ring.base, (dma_addr_t)hw_ctx->rx_ring.dma_base);
+                    hw_ctx->rx_ring.base, hw_ctx->rx_ring.dma_base);
         err_rx_ring_alloc:
             return -ENOMEM;
 
@@ -876,12 +1025,15 @@
         }
     }
 
+    /* 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 ctx)
     {
         chunk   ::CALL()
         {
-            typedef unsigned long int   dma_addr_t;
-
             ${e1000::Context} *hw_ctx;
             hw_ctx = &${ctx}->hw_ctx;
 
@@ -890,18 +1042,17 @@
              * - Unmap and free the skbuffs;
              * - Free the descriptors array.
              */
-            int i = 0;
-            for (i = 0; i != ${config.rx_ring_size}; ++i)
+            for (int i = 0; i != ${config.rx_ring_size}; ++i)
             {
                 dma_unmap_single(
                         &${ctx}->pci_dev->dev,
-                        (dma_addr_t)hw_ctx->rx_ring.dma_skbuffs[i],
+                        (dma_addr_t)hw_ctx->rx_ring.skbuffs[i].dma_handle,
                         ${config.rx_buffer_len},
                         DMA_FROM_DEVICE);
-                dev_kfree_skb(hw_ctx->rx_ring.skbuffs[i]);
+                dev_kfree_skb(hw_ctx->rx_ring.skbuffs[i].skbuff);
             }
             dma_free_coherent(&${ctx}->pci_dev->dev, hw_ctx->rx_ring.size,
-                    hw_ctx->rx_ring.base, (dma_addr_t)hw_ctx->rx_ring.dma_base);
+                    hw_ctx->rx_ring.base, hw_ctx->rx_ring.dma_base);
             ${Log::info("free_rx_tx: rx ring free'ed")};
 
             /*
@@ -909,7 +1060,7 @@
              * - Free the descriptors array.
              */
             dma_free_coherent(&${ctx}->pci_dev->dev, hw_ctx->tx_ring.size,
-                    hw_ctx->tx_ring.base, (dma_addr_t)hw_ctx->tx_ring.dma_base);
+                    hw_ctx->tx_ring.base, hw_ctx->tx_ring.dma_base);
             ${Log::info("free_rx_tx: tx ring free'ed")};
         }
     }
@@ -918,16 +1069,123 @@
     {
         chunk   ::CALL()
         {
-            int intr;
-
-            intr = rtx_e1000_register_read32(&${ctx}->hw_ctx, E1000_ICR);
-            if (intr)
+            unsigned int icr = rtx_e1000_register_read32(&${ctx}->hw_ctx, E1000_ICR);
+            pr_info("%s: interrupt received, ICR: 0x%x", ${config.name}, icr);
+            if (icr)
             {
-                if (intr & E1000_INTR_LSC)
-                    ${Log::info("cable link status changed")};
+                if (icr & E1000_INTR_LSC)
+                {
+                    ${Log::info("handle_interrupt: cable link status changed, dumping card status:")};
+                    ${e1000::print_status(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
+                     * "&${ctx}->hw_ctx.tx_ring" to e1000::TxRing easily?)
+                     */
+                    rtx_e1000_tx_ring_clean(&${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_tso_cksum_offload(Ethernet::Device ctx, Socket::SKBuff skb)
+    {
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   e1000::xmit(Ethernet::Device 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} *hw_ctx;
+            ${e1000::TxRing} *tx_ring;
+            ${Device::AbstractDevice} dev;
+
+            ${local.skb.init(kernel_skb)};
+            hw_ctx = &${ctx}->hw_ctx;
+            tx_ring = &hw_ctx->tx_ring;
+            dev = &${ctx}->pci_dev->dev;
+
+            ${Log::info("xmit: skbuff details:")};
+            /*
+             * skb is 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(local.dev)}; */
+            if (rtx_socket_skbuff_map(&skb, dev, 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, dev, DMA_TO_DEVICE);
+            return NETDEV_TX_OK;
+        }
+    }
 }
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
@@ -31,8 +31,19 @@
     provided type   TxRing
     {
         chunk       LKM::includes();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
         method      decl();
         method      init();
+
+        /* 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);
     }
 
     /*
@@ -51,6 +62,12 @@
         method      decl();
     }
 
+    provided type   TxDescriptorFlags
+    {
+        chunk       LKM::includes();
+        chunk       ::decl();
+    }
+
     provided sequence   create_device()
     {
         provided chunk  Ethernet::create_device(PCI::Device, Ethernet::Device);
@@ -109,6 +126,16 @@
         provided chunk  ::CALL();
     }
 
+    provided sequence   _xmit_tso_cksum_offload(Ethernet::Device, Socket::SKBuff)
+    {
+        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();
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,6 +1,56 @@
 with Ethernet, PCI, LKM, Log
 {
-    template type Ethernet::Net()
+    template type   Ethernet::ProtocolId()
+    {
+        chunk LKM::prototypes()
+        {
+            static const char   *rtx_ethernet_protocol_id_to_str(unsigned short);
+        }
+
+        chunk LKM::data()
+        {
+            static const struct
+            {
+                unsigned short  id;
+                const char      *name;
+            } rtx_ethernet_proto_table[] =
+            {
+                { ETH_P_IP,     "IPv4"  },
+                { ETH_P_IPV6,   "IPv6"  },
+                { ETH_P_ARP,    "ARP"   },
+            };
+        }
+
+        chunk LKM::code()
+        {
+            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]);
+                     i++)
+                    if (proto_id == rtx_ethernet_proto_table[i].id)
+                        return rtx_ethernet_proto_table[i].name;
+
+                return "Unknown";
+            }
+        }
+
+        chunk decl()
+        {
+            typedef unsigned short  rtx_ether_protocol_id;
+        }
+
+        chunk to_str()
+        {
+            rtx_ethernet_protocol_id_to_str(${self});
+        }
+
+        map
+        {
+        }
+    }
+
+    template type   Ethernet::AbstractDevice()
     {
         chunk LKM::includes()
         {
@@ -17,11 +67,6 @@
         }
     }
 
-    /*
-     * Unlike PCI::Device, Ethernet::Device doesn't match the struct net_device
-     * from Linux. Ethernet::Device is the type that we use in the private
-     * field of the struct net_device.
-     */
     template type   Ethernet::Device()
     {
         chunk LKM::includes()
@@ -47,15 +92,15 @@
                 struct pci_dev          *pci_dev;
                 struct net_device       *net_dev;
 
-                /* while waiting on issue #8 */
-                //${e1000::Context}       hw_ctx;
-                // In the long-term, this may disappear for a new concept allowing
-                // to embbed a descriptor defined and manipulated by the front-end
+                /*
+                 * In the long-term, this may disappear for a new concept allowing
+                 * to embbed a descriptor defined and manipulated by the front-end
+                 */
                 ${pointcut Ethernet::SubContext()};
             } *rtx_ethernet_dev_p;
         }
 
-        chunk ::init(Ethernet::Net net_dev, PCI::Device pci_dev)
+        chunk ::init(Ethernet::AbstractDevice net_dev, PCI::Device pci_dev)
         {
             ${self} = netdev_priv(${net_dev});
             /*
@@ -82,9 +127,8 @@
         {
             static int  rtx_ethernet_open(struct net_device *dev)
             {
-                struct rtx_ethernet_dev* rtx_ether_dev = netdev_priv(dev);
+                ${Ethernet::Device} rtx_ether_dev = netdev_priv(dev);
 
-                ${cast local.rtx_ether_dev as Ethernet::Device};
                 ${pointcut ::IMPLEMENTATION(local.rtx_ether_dev)};
 
                 return 0;
@@ -92,7 +136,7 @@
         }
     }
 
-    template sequence   Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    template sequence   Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb)
     {
         chunk LKM::prototypes()
         {
@@ -101,13 +145,11 @@
 
         chunk LKM::code()
         {
-            static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev)
+            static int  rtx_ethernet_xmit(struct sk_buff* kernel_skb, struct net_device *net_dev)
             {
-                ${cast local.dev as Ethernet::Device};
-                ${cast local.skb as Socket::SKBuff};
-                ${pointcut ::IMPLEMENTATION(local.dev, local.skb)};
-
-                return 0;
+                ${Ethernet::Device} rtx_ethernet_dev = netdev_priv(net_dev);
+                ${cast local.kernel_skb as Socket::AbstractSKBuff};
+                ${pointcut ::IMPLEMENTATION(local.rtx_ethernet_dev, local.kernel_skb)};
             }
         }
     }
@@ -123,9 +165,8 @@
         {
             static int  rtx_ethernet_close(struct net_device *dev)
             {
-                struct rtx_ethernet_dev* rtx_ether_dev = netdev_priv(dev);
+                ${Ethernet::Device} rtx_ether_dev = netdev_priv(dev);
 
-                ${cast local.rtx_ether_dev as Ethernet::Device};
                 ${pointcut ::IMPLEMENTATION(local.rtx_ether_dev)};
 
                 return 0;
@@ -148,11 +189,8 @@
         {
             static enum irqreturn   rtx_ethernet_interrupt_handler(int irq, void *dev_id)
             {
-                struct rtx_ethernet_dev* rtx_ether_dev;
-                struct rtx_e1000_ctx* ctx;
+                ${Ethernet::Device} rtx_ether_dev = dev_id;
 
-                rtx_ether_dev = dev_id;
-                ${cast local.rtx_ether_dev as Ethernet::Device};
                 ${pointcut ::IMPLEMENTATION(local.rtx_ether_dev)};
 
                 return IRQ_NONE;
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
@@ -1,16 +1,30 @@
 interface Ethernet : Socket, PCI, LKM
 {
-    provided type   Net
+     provided type   ProtocolId
+     {
+         chunk       LKM::prototypes();
+         chunk       LKM::data();
+         chunk       LKM::code();
+         method      decl();
+         method      to_str();
+     }
+
+    provided type   AbstractDevice
     {
         chunk       LKM::includes();
-        method       decl();
+        method      decl();
     }
 
+    /*
+     * Unlike PCI::Device, Ethernet::Device doesn't match the struct net_device
+     * from Linux. Ethernet::Device is the type that we use in the private
+     * field of the struct net_device.
+     */
     provided type   Device
     {
         chunk       LKM::includes();
         method      decl();
-        method      init(Ethernet::Net, PCI::Device);
+        method      init(Ethernet::AbstractDevice, PCI::Device);
         pointcut    Ethernet::SubContext();
     }
 
@@ -22,7 +36,7 @@
         provided chunk  LKM::code();
     }
 
-    required sequence   send(Ethernet::Device dev, Socket::SKBuff skb)
+    required sequence   send(Ethernet::Device, Socket::AbstractSKBuff)
     {
         provided chunk  LKM::prototypes();
         provided chunk  LKM::code();
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,4 +1,4 @@
-device LKM use LKM, PCI, Ethernet, Log
+device LKM use LKM, PCI, Ethernet, Log, Socket
 {
     Ethernet::open(Ethernet::Device dev)
     {
@@ -43,9 +43,10 @@
         e1000::handle_interrupt(dev);
     }
 
-    Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    Ethernet::send(Ethernet::Device dev, Socket::AbstractSKBuff skb)
     {
         Log::info("we have one packet to transmit!");
+        e1000::xmit(dev, skb);
     }
 
     LKM::init()
@@ -66,8 +67,13 @@
     LKM::description = "Hello World Loadable Kernel Module (LKM)";
     LKM::license = "GPL";
 
+    /*
+     * See section 5.2 of the Intel manual for all the values
+     * - 0x100e is for the 82540EM-A;
+     * - 0x100f, apparently found on vmware by default, is for the 82545EM-A.
+     */
     PCI::vendor_id = 0x8086;
-    PCI::product_id = 0x100e; /* e100f on vmware by default it seems */
+    PCI::product_id = 0x100e;
     PCI::set_master = true;
 
     Ethernet::ifname = "rtx%d";
@@ -79,4 +85,10 @@
      * 4096, 8192 and 16384 bytes:
      */
     e1000::rx_buffer_len = 2048;
+    /*
+     * 4096 bytes maximum per transmit descriptor is used on Linux and FreeBSD,
+     * 2048 on Minix and HelenOS, I can't find why. If I understand the Intel
+     * manual correctly, the maximum should be 16288 (see section 3.3.3).
+     */
+     e1000::tx_max_data_per_desc = 4096;
 }
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
@@ -1,20 +1,152 @@
-with Socket, LKM
+with Socket, LKM, Device, Ethernet
 {
+    template type Socket::AbstractSKBuff()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/skbuff.h>
+
+            static const ${Socket::AbstractSKBuff} force_rtx_socket_kernel_skbuff_decl;
+        }
+
+        chunk ::decl()
+        {
+            typedef struct sk_buff  *rtx_socket_kernel_skbuff_p;
+        }
+
+        map
+        {
+        }
+    }
+
     template type Socket::SKBuff()
     {
         chunk LKM::includes()
         {
-            #include <linux/skbuff.h>
-            static const ${Socket::SKBuff} force_rtx_lnux_skbuf_decl;
+            static const ${Socket::SKBuff} force_rtx_socket_skbuff_decl;
         }
 
         chunk ::decl()
         {
-            struct sk_buff;
+            struct  rtx_socket_skbuff
+            {
+                struct sk_buff  *skbuff;
+                dma_addr_t      dma_handle;
+            };
         }
 
-        chunk ::init()
+        chunk   LKM::prototypes()
         {
+            static void rtx_socket_skbuff_dump_infos(struct rtx_socket_skbuff *);
+            static int  rtx_socket_skbuff_map(struct rtx_socket_skbuff *, struct device *, enum dma_data_direction);
+            static void rtx_socket_skbuff_unmap_and_free(struct rtx_socket_skbuff *, struct device *, enum dma_data_direction);
+        }
+
+        chunk   LKM::code()
+        {
+            static void rtx_socket_skbuff_dump_infos(struct rtx_socket_skbuff *self)
+            {
+                WARN_ON(!self->skbuff);
+
+                /*
+                 * We should use a Rathaxes log abstraction instead of pr_info here,
+                 * but Rathaxes doesn't support functions with a variable number of
+                 * arguments yet.
+                 */
+                ${Ethernet::ProtocolId} ethernet_proto = be16_to_cpu(self->skbuff->protocol);
+                static const char * const ip_summed_values[] = {
+                    "none", "unnecessary", "complete", "partial"
+                };
+                struct skb_shared_info *shinfo = skb_shinfo(self->skbuff);
+
+                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",
+                        ethernet_proto, rtx_ethernet_protocol_id_to_str(ethernet_proto) /* XXX: ${local.ethernet_proto.to_str()} */,
+                        self->skbuff->ip_summed, ip_summed_values[self->skbuff->ip_summed],
+                        self->skbuff->len, self->skbuff->data_len, skb_headlen(self->skbuff),
+                        shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type
+                );
+            }
+
+            static int rtx_socket_skbuff_map(struct rtx_socket_skbuff *self,
+                                             struct device *dev,
+                                             enum dma_data_direction direction)
+            {
+                WARN_ON(!self->skbuff);
+                WARN_ON(!self->skbuff->data);
+                WARN_ON(self->dma_handle);
+
+                self->dma_handle = dma_map_single(
+                        dev,
+                        self->skbuff->data,
+                        skb_headlen(self->skbuff),
+                        direction);
+                int err = dma_mapping_error(dev, self->dma_handle);
+                if (err)
+                {
+                    self->dma_handle = 0;
+                    return err;
+                }
+                return 0;
+            }
+
+            static void rtx_socket_skbuff_unmap_and_free(struct rtx_socket_skbuff *self,
+                                                         struct device *dev,
+                                                         enum dma_data_direction direction)
+            {
+                WARN_ON(!self->skbuff);
+                WARN_ON(!self->skbuff->data);
+
+                if (self->dma_handle)
+                {
+                    dma_unmap_single(dev,
+                            self->dma_handle,
+                            skb_headlen(self->skbuff),
+                            direction);
+                    self->dma_handle = 0;
+                }
+                dev_kfree_skb_any(self->skbuff);
+                self->skbuff = 0;
+            }
+        }
+
+        /*
+         * 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)
+         */
+        chunk ::init(Socket::AbstractSKBuff kernel_skb)
+        {
+            ${self}.skbuff = kernel_skb;
+            ${self}.dma_handle = 0;
+        }
+
+        chunk dump_infos()
+        {
+            rtx_socket_skbuff_dump_infos(${self});
+        }
+
+        chunk   map_to(Device::AbstractDevice dev)
+        {
+            rtx_socket_skbuff_map(${self}, ${dev}, DMA_TO_DEVICE);
+        }
+
+        chunk   map_from(Device::AbstractDevice dev)
+        {
+            rtx_socket_skbuff_map(${self}, ${dev}, DMA_FROM_DEVICE);
+        }
+
+        chunk   unmap_to_and_free(Device::AbstractDevice dev)
+        {
+            rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, DMA_TO_DEVICE);
+        }
+
+        chunk   unmap_from_and_free(Device::AbstractDevice dev)
+        {
+            rtx_socket_skbuff_unmap_and_free(${self}, ${dev}, DMA_FROM_DEVICE);
         }
 
         map
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
@@ -1,8 +1,23 @@
-interface Socket : LKM
+interface Socket : LKM, Device
 {
-    provided type Socket::SKBuff {
-        chunk   LKM::includes();
-        method  decl();
-        method  init();
+    /* The SKBuff type from the kernel */
+    provided type   AbstractSKBuff
+    {
+        chunk       LKM::includes();
+        method      decl();
+    }
+
+    provided type   SKBuff
+    {
+        chunk       LKM::includes();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
+        method      decl();
+        method      init(Socket::AbstractSKBuff);
+        method      dump_infos();
+        method      map_to(Device::AbstractDevice);
+        method      map_from(Device::AbstractDevice);
+        method      unmap_to_and_free(Device::AbstractDevice);
+        method      unmap_from_and_free(Device::AbstractDevice);
     }
 }