Mercurial > archived > louis > epitech > mq > rathaxes
view rathaxes_sample_e1000_rewrite_device_dependent_code.patch @ 139:ab3a2aa22f95
Wip on cleaning up the existing code, now starting to build with gcc
author | Louis Opter <kalessin@kalessin.fr> |
---|---|
date | Sun, 19 Jan 2014 18:13:17 -0800 |
parents | 6527e078252c |
children | a291b41efb69 |
line wrap: on
line source
# 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; } }