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

diff --git a/maintainers/CMakeScripts/Templates/MakefileLKM.in b/maintainers/CMakeScripts/Templates/MakefileLKM.in
--- a/maintainers/CMakeScripts/Templates/MakefileLKM.in
+++ b/maintainers/CMakeScripts/Templates/MakefileLKM.in
@@ -1,6 +1,6 @@
 # Disable this "coding-style" warning (seriously, you have to compile with
 # -pedantic to get it...)
-EXTRA_CFLAGS	= -Wno-declaration-after-statement
+EXTRA_CFLAGS	= -Wno-declaration-after-statement -std=gnu99
 
 KDIR		= /lib/modules/$(shell uname -r)/build
 obj-m		:= @LKM_OBJECTS@
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
@@ -964,4 +964,52 @@
             }
         }
     }
+
+    template sequence   e1000::xmit(Ethernet::Device ctx, Socket::SKBuff skb)
+    {
+        chunk   ::CALL()
+        {
+            typedef unsigned long int   dma_addr_t;
+
+            (void)1; // Issue 10
+            /*
+             * Put packets on the TX ring, must return NETDEV_TX_OK or
+             * NETDEV_TX_BUSY.
+             */
+            {
+                ${Log::info("xmit: skbuff details:")};
+                ${skb.dump_infos()};
+            }
+
+            /*
+             * The transmission is going to be several steps:
+             * - 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;
+             * - DMA Map the skbuff data as slices of 4096;
+             * - Signal the hardware that data is available via a tx desc.
+             */
+
+            if (skb_is_gso(${skb}) || ${skb}->ip_summed == CHECKSUM_PARTIAL)
+            {
+                ${Log::info("xmit: the packet needs to be fragmented and/or checksummed but this not implemented yet!")};
+                return NETDEV_TX_OK;
+            }
+
+            /* XXX ${ctx} expands into skb */
+            dma_addr_t buff_addr = dma_map_single(
+                    &${ctx}->pci_dev->dev,
+                    ${skb}->data,
+                    skb_headlen(${skb}),
+                    DMA_TO_DEVICE);
+            if (dma_mapping_error(&${ctx}->pci_dev->dev, buff_addr))
+            {
+                ${Log::info("xmit: can't DMA map a SKBuff")};
+                /* now what, should I free the 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
@@ -109,6 +109,11 @@
         provided chunk  ::CALL();
     }
 
+    provided sequence   xmit(Ethernet::Device, Socket::SKBuff)
+    {
+        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,51 @@
 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 to_str()
+        {
+            rtx_ethernet_protocol_id_to_str(${self});
+        }
+
+        map
+        {
+        }
+    }
+
+    template type   Ethernet::AbstractDevice()
     {
         chunk LKM::includes()
         {
@@ -17,11 +62,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()
@@ -52,7 +92,7 @@
             } *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});
             /*
@@ -100,11 +140,11 @@
         {
             static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev)
             {
-                ${cast local.dev as Ethernet::Device};
+                struct rtx_ethernet_dev* rtx_ethernet_dev = netdev_priv(dev);
+
+                ${cast local.rtx_ethernet_dev as Ethernet::Device};
                 ${cast local.skb as Socket::SKBuff};
-                ${pointcut ::IMPLEMENTATION(local.dev, local.skb)};
-
-                return 0;
+                ${pointcut ::IMPLEMENTATION(local.rtx_ethernet_dev, local.skb)};
             }
         }
     }
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,11 +1,28 @@
 interface Ethernet : Socket, PCI, LKM
 {
-    provided type   Net
+    provided type   ProtocolId
+    {
+        chunk       LKM::prototypes();
+        chunk       LKM::data();
+        chunk       LKM::code();
+        method      ::to_str();
+    }
+
+    /*
+     * This is the abstract type used by the Kernel to represent an ethernet
+     * device.
+     */
+    provided type   AbstractDevice
     {
         chunk       LKM::includes();
         chunk       ::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();
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
@@ -46,6 +46,7 @@
     Ethernet::send(Ethernet::Device dev, Socket::SKBuff 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,4 +1,4 @@
-with Socket, LKM
+with Socket, LKM, Ethernet
 {
     template type Socket::SKBuff()
     {
@@ -17,6 +17,34 @@
         {
         }
 
+        chunk ::dump_infos()
+        {
+            /*
+             * 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.
+             */
+            unsigned short ethernet_proto = be16_to_cpu(${self}->protocol);
+            ${cast local.ethernet_proto as Ethernet::ProtocolId};
+
+            static const char * const ip_summed_values[] = {
+                "none", "unnecessary", "complete", "partial"
+            };
+
+            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\n (%s)",
+                    ethernet_proto, "", // XXX: ${local.ethernet_proto.to_str()},
+                    ${self}->len, ${self}->data_len, skb_headlen(${self}),
+                    skb_shinfo(${self})->nr_frags,
+                    skb_shinfo(${self})->gso_size, skb_shinfo(${self})->gso_segs, skb_shinfo(${self})->gso_type,
+                    ${self}->ip_summed, ip_summed_values[${self}->ip_summed]
+            );
+        }
+
         map
         {
             // some work may have to be done here in order
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,10 @@
 interface Socket : LKM
 {
-    provided type Socket::SKBuff {
-        chunk LKM::includes();
-        chunk ::decl();
-        method ::init();
+    provided type Socket::SKBuff
+    {
+        chunk   LKM::includes();
+        chunk   ::decl();
+        method  ::init();
+        method  ::dump_infos();
     }
 }
