view e1000_implement_the_frame_transmission_chunk.patch @ 87:e9736ab70995

Add a couple of patches to the build system and WIP on the e1000 code architecture to implement the tranmission
author Louis Opter <louis@lse.epitech.net>
date Mon, 24 Sep 2012 09:13:15 +0200
parents c99e69966dd3
children 8ffcdd6aa410
line wrap: on
line source

# HG changeset patch
# Parent 87ba2a19a59fb7be346ad40a57439b6b752b152e
rathaxes: start to queue up packets in the TX ring on the e1000 sample

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::Device()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/device.h>
+
+            static const ${Device::Device}  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   Device
+    {
+        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
@@ -110,9 +110,9 @@
             {
                 unsigned int                    size;
                 struct rtx_e1000_rx_descriptor  *base;
-                void*   /* dma_addr_t */        dma_base;
+                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_skbuffs[256 /* ${config.rx_ring_size} */];
             };
         }
 
@@ -148,11 +148,59 @@
             struct rtx_e1000_tx_ring
             {
                 unsigned int                    size;
+                /* We should probably use ${e1000::TxDescriptor} here: */
                 struct rtx_e1000_tx_descriptor  *base;
-                void*   /* dma_addr_t */        dma_base;
+                dma_addr_t                      dma_base;
+                /* indexes on base */
+                unsigned int                    head;
+                unsigned int                    tail;
             };
         }
 
+        chunk   LKM::prototypes()
+        {
+            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 int          rtx_e1000_tx_ring_put(struct rtx_e1000_tx_ring *, struct rtx_socket_skbuff *);
+        }
+
+        chunk   LKM::code()
+        {
+            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 int          rtx_e1000_tx_ring_put(struct rtx_e1000_tx_ring *self, struct rtx_socket_skbuff *skb)
+            {
+                return NETDEV_TX_OK;
+            }
+        }
+
+        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   ::init()
         {
         }
@@ -183,7 +231,7 @@
             struct rtx_e1000_ctx
             {
                 int                         bars;
-                unsigned char /* __iomem */ *ioaddr;
+                unsigned char __iomem       *ioaddr;
                 int                         irq;
 
                 /* we can't use the Rathaxes type here (#8) */
@@ -334,6 +382,30 @@
         }
     }
 
+    template type   e1000::TxFlags()
+    {
+        chunk LKM::includes()
+        {
+            static const ${e1000::TxFlags}  force_enum_rtx_e1000_tx_flags_decl;
+        }
+
+        chunk ::decl()
+        {
+            enum    rtx_e1000_tx_flags
+            {
+                E1000_TX_FLAGS_CSUM     = 0x00000001,
+                E1000_TX_FLAGS_VLAN     = 0x00000002,
+                E1000_TX_FLAGS_TSO      = 0x00000004,
+                E1000_TX_FLAGS_IPV4     = 0x00000008,
+                E1000_TX_FLAGS_NO_FCS   = 0x00000010,
+            };
+        }
+
+        map
+        {
+        }
+    }
+
     template sequence   e1000::create_device()
     {
         chunk Ethernet::create_device(PCI::Device pdev, Ethernet::Device rtx_ether_ctx)
@@ -358,8 +430,8 @@
                 ${Log::info("e1000::create: pci_enable_device_mem failed")};
             if (pci_request_selected_regions(${pdev}, ${rtx_ether_ctx}->hw_ctx.bars, ${config.name}))
                 ${Log::info("e1000::create: pci_request_selected_regions failed")};
-            if (${config.set_master})
-                pci_set_master(${pdev});
+// XXX      if (${config.set_master})
+// XXX          pci_set_master(${pdev});
 
             /* 0 here is for BAR_0: */
             ${rtx_ether_ctx}->hw_ctx.ioaddr = pci_ioremap_bar(${pdev}, 0);
@@ -630,8 +702,6 @@
     {
         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
@@ -733,7 +803,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)
             {
@@ -758,7 +828,7 @@
                     ${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.dma_skbuffs[i] = dma_map_single(
                         &${ctx}->pci_dev->dev,
                         hw_ctx->rx_ring.skbuffs[i]->data,
                         ${config.rx_buffer_len},
@@ -820,7 +890,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)
             {
@@ -838,6 +908,8 @@
             /* 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);
@@ -930,4 +1002,81 @@
             }
         }
     }
+
+    template sequence   e1000::_xmit_tso_cksum_offload(Ethernet::Device ctx, Socket::SKBuff skb)
+    {
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   e1000::xmit(Ethernet::Device ctx, Socket::KernelSKBuff kernel_skb)
+    {
+        chunk   ::CALL()
+        {
+            /*
+             * Put packets on the TX ring, must return NETDEV_TX_OK or
+             * NETDEV_TX_BUSY.
+             */
+
+            /*
+             * XXX: This leaves a placeholder if I cast local.tx_ring as
+             * e1000::TxRing below.
+             */
+            ${Socket::SKBuff} skb;
+            ${local.skb.init(kernel_skb)};
+
+            /*
+             * XXX: can't write ${e1000::TxRing} * (the placeholder isn't
+             * resolved).
+             */
+            struct rtx_e1000_tx_ring *tx_ring = &${ctx}->hw_ctx.tx_ring;
+            //${cast local.tx_ring as e1000::TxRing};
+
+            ${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 contex
+             *    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!")};
+                return NETDEV_TX_OK;
+            }
+
+            /* 2. Map the data */
+            ${Device::Device} dev = &${ctx}->pci_dev->dev;
+            /* XXX: ${local.skb.dma_map(local.dev)}; */
+            rtx_socket_dma_map(&skb, dev);
+
+            /* 3. Update the TX Ring */
+            /* XXX: ${local.tx_ring.put(skb)}; */
+            rtx_e1000_tx_ring_put(tx_ring, &skb);
+
+            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,14 @@
     provided type   TxRing
     {
         chunk       LKM::includes();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
         method      decl();
         method      init();
+
+        method      descriptors_remaining();
+        method      tso_cksum_offload(Socket::SKBuff);
+        method      put(Socket::SKBuff);
     }
 
     /*
@@ -51,6 +57,12 @@
         method      decl();
     }
 
+    provided type   TxFlags
+    {
+        chunk       LKM::includes();
+        chunk       ::decl();
+    }
+
     provided sequence   create_device()
     {
         provided chunk  Ethernet::create_device(PCI::Device, Ethernet::Device);
@@ -109,6 +121,16 @@
         provided chunk  ::CALL();
     }
 
+    provided sequence   _xmit_tso_cksum_offload(Ethernet::Device, Socket::SKBuff)
+    {
+        provided chunk  ::CALL();
+    }
+
+    provided sequence   xmit(Ethernet::Device, Socket::KernelSKBuff)
+    {
+        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
+            {
+                const 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[0]) / sizeof(rtx_ethernet_proto_table);
+                     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::KernelSKBuff 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::KernelSKBuff};
+                ${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::KernelSKBuff)
     {
         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::KernelSKBuff skb)
     {
         Log::info("we have one packet to transmit!");
+        e1000::xmit(dev, skb);
     }
 
     LKM::init()
@@ -79,4 +80,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
+     * man 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,104 @@
-with Socket, LKM
+with Socket, LKM, Device, Ethernet
 {
+    template type Socket::KernelSKBuff()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/skbuff.h>
+
+            static const ${Socket::KernelSKBuff} 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_dma_map(struct rtx_socket_skbuff *, struct device *);
+        }
+
+        chunk   LKM::code()
+        {
+            static void rtx_socket_skbuff_dump_infos(struct rtx_socket_skbuff *self)
+            {
+                /*
+                 * 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)\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\n"
+                        "\tip_summed = %d (%s)",
+                        ethernet_proto, rtx_ethernet_protocol_id_to_str(ethernet_proto) /* XXX: ${local.ethernet_proto.to_str()} */,
+                        self->skbuff->len, self->skbuff->data_len, skb_headlen(self->skbuff),
+                        shinfo->nr_frags, shinfo->gso_size, shinfo->gso_segs, shinfo->gso_type,
+                        self->skbuff->ip_summed, ip_summed_values[self->skbuff->ip_summed]
+                );
+            }
+
+            static int rtx_socket_dma_map(struct rtx_socket_skbuff *self, struct device *dev)
+            {
+                self->dma_handle = dma_map_single(
+                        dev,
+                        self->skbuff->data,
+                        skb_headlen(self->skbuff),
+                        DMA_TO_DEVICE);
+                return dma_mapping_error(dev, self->dma_handle);
+            }
+        }
+
+        /*
+         * 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::KernelSKBuff kernel_skb)
+        {
+            ${self}.skbuff = kernel_skb;
+            ${self}.dma_handle = 0;
+        }
+
+        chunk dump_infos()
+        {
+            rtx_socket_skbuff_dump_infos(${self});
+        }
+
+        chunk   dma_map(Device::Device dev)
+        {
+            rtx_socket_dma_map(${self}, ${dev});
         }
 
         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,19 @@
-interface Socket : LKM
+interface Socket : LKM, Device
 {
-    provided type Socket::SKBuff {
-        chunk   LKM::includes();
-        method  decl();
-        method  init();
+    provided type   KernelSKBuff
+    {
+        chunk       LKM::includes();
+        method      decl();
+    }
+
+    provided type   SKBuff
+    {
+        chunk       LKM::includes();
+        chunk       LKM::prototypes();
+        chunk       LKM::code();
+        method      decl();
+        method      init(Socket::KernelSKBuff);
+        method      dump_infos();
+        method      dma_map(Device::Device);
     }
 }