[Prism54-devel] Using MF Bit In Transmit Fragments
Feyd
feyd@seznam.cz
Thu, 11 Mar 2004 08:37:52 +0100
This is a multi-part message in MIME format.
--Multipart=_Thu__11_Mar_2004_08_37_52_+0100_Vz_ziwoBf=djI+Un
Content-Type: text/plain; charset=US-ASCII
Content-Transfer-Encoding: 7bit
On Wed, 10 Mar 2004 14:17:37 -0500
"Kirk M. Sigel" <kmsigel@ksx.com> wrote:
>
> Hello,
>
> I am writing a driver for the Prism 3880/3890 MAC using firmware version
> 1.0.4.3. Everything is working fine except transmitting an Ethernet frame
> using two transmit fragments.
It was present in the earlier releases in a form that has no advantage (it
didnt do the zerocopy transmit), but it works. The attached patch reverts
to that version.
Feyd
--Multipart=_Thu__11_Mar_2004_08_37_52_+0100_Vz_ziwoBf=djI+Un
Content-Type: text/plain;
name="tx-frag.diff"
Content-Disposition: attachment;
filename="tx-frag.diff"
Content-Transfer-Encoding: 7bit
--- islpci_eth.c
+++ islpci_eth.c
@@ -77,19 +77,15 @@
islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev)
{
islpci_private *priv = ndev->priv;
- isl38xx_control_block *cb = priv->control_block;
- u32 index;
- dma_addr_t pci_map_address;
+ isl38xx_control_block *control_block = priv->control_block;
+ u32 index, counter, pci_map_address;
+ int fragments;
int frame_size;
- isl38xx_fragment *fragment;
int offset;
struct sk_buff *newskb;
int newskb_offset;
unsigned long flags;
unsigned char wds_mac[6];
- u32 curr_frag;
- int err = 0;
-
#if VERBOSE > SHOW_ERROR_MESSAGES
DEBUG(SHOW_FUNCTION_CALLS, "islpci_eth_transmit \n");
#endif
@@ -120,21 +116,30 @@
frame_size = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len;
if (init_wds)
frame_size += 6;
+ fragments = frame_size / MAX_FRAGMENT_SIZE;
+ fragments += frame_size % MAX_FRAGMENT_SIZE == 0 ? 0 : 1;
+
+#if VERBOSE > SHOW_ERROR_MESSAGES
+ DEBUG(SHOW_TRACING, "Fragments needed for frame %i\n", fragments);
+#endif
/* check whether the destination queue has enough fragments for the frame */
- curr_frag = le32_to_cpu(cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ]);
- if (curr_frag - priv->free_data_tx >= ISL38XX_CB_TX_QSIZE) {
- printk(KERN_ERR "%s: transmit device queue full when awake\n",
- ndev->name);
- netif_stop_queue(ndev);
+ if (ISL38XX_CB_TX_QSIZE - isl38xx_in_queue(control_block,
+ ISL38XX_CB_TX_DATA_LQ) <
+ fragments) {
+ /* Error, cannot add the frame because the queue is full */
+ DEBUG(SHOW_ERROR_MESSAGES,
+ "Error: Queue [%i] not enough room\n",
+ ISL38XX_CB_TX_DATA_LQ);
- /* trigger the device */
+ /* trigger the device by setting the Update bit in the Device Int reg */
isl38xx_w32_flush(priv->device_base, ISL38XX_DEV_INT_UPDATE,
ISL38XX_DEV_INT_REG);
udelay(ISL38XX_WRITEIO_DELAY);
- err = -EBUSY;
- goto drop_free;
+ /* unlock the driver code */
+ spin_unlock_irqrestore(&priv->slock, flags);
+ return -EBUSY;
}
/* Check alignment and WDS frame formatting. The start of the packet should
* be aligned on a 4-byte boundary. If WDS is enabled add another 6 bytes
@@ -210,60 +215,86 @@
pci_map_address = pci_map_single(priv->pdev,
(void *) skb->data, skb->len,
PCI_DMA_TODEVICE);
- if (pci_map_address == 0) {
- printk(KERN_WARNING "%s: cannot map buffer to PCI\n",
- ndev->name);
+ if (pci_map_address == (dma_addr_t) NULL) {
+ /* error mapping the buffer to device accessable memory address */
+ DEBUG(SHOW_ERROR_MESSAGES, "Error map DMA addr, data lost\n");
+
+ /* free the skbuf structure before aborting */
+ dev_kfree_skb(skb);
- err = -EIO;
- goto drop_free;
+ /* unlock the driver code */
+ spin_unlock_irqrestore(&priv->slock, flags);
+ return -EIO;
}
- /* Place the fragment in the control block structure. */
- index = curr_frag % ISL38XX_CB_TX_QSIZE;
- fragment = &cb->tx_data_low[index];
-
- priv->pci_map_tx_address[index] = pci_map_address;
- /* store the skb address for future freeing */
- priv->data_low_tx[index] = skb;
- /* set the proper fragment start address and size information */
- fragment->size = cpu_to_le16(frame_size);
- fragment->flags = cpu_to_le16(0); /* set to 1 if more fragments */
- fragment->address = cpu_to_le32(pci_map_address);
- curr_frag++;
+ /* place each fragment in the control block structure and store in the last
+ * needed fragment entry of the pci_map_tx_address and data_low_tx arrays
+ * the skb frame information */
+ for (counter = 0; counter < fragments; counter++) {
+ isl38xx_fragment *fragment;
+
+ /* get a pointer to the target control block fragment */
+ index =
+ (counter +
+ le32_to_cpu(control_block->
+ driver_curr_frag[ISL38XX_CB_TX_DATA_LQ])) %
+ ISL38XX_CB_TX_QSIZE;
+
+ fragment = &control_block->tx_data_low[index];
+
+ /* check whether this frame fragment is the last one */
+ if (counter == fragments - 1) {
+ /* the fragment is the last one, add the streaming DMA mapping for
+ * proper PCI bus operation */
+ priv->pci_map_tx_address[index] =
+ (dma_addr_t) pci_map_address;
- /* The fragment address in the control block must have been
- * written before announcing the frame buffer to device. */
+ /* store the skb address for future freeing */
+ priv->data_low_tx[index] = skb;
+ } else {
+ /* the fragment will be followed by more fragments
+ * clear the pci_map_tx_address and data_low_tx entries */
+ priv->pci_map_tx_address[index] = (dma_addr_t) NULL;
+ priv->data_low_tx[index] = NULL;
+ }
+
+ /* set the proper fragment start address and size information */
+ fragment->address = cpu_to_le32(pci_map_address +
+ counter * MAX_FRAGMENT_SIZE);
+ fragment->size = cpu_to_le16(frame_size > MAX_FRAGMENT_SIZE ?
+ MAX_FRAGMENT_SIZE : frame_size);
+ fragment->flags =
+ cpu_to_le16(frame_size > MAX_FRAGMENT_SIZE ? 1 : 0);
+ frame_size -= MAX_FRAGMENT_SIZE;
+ }
+
+ /* ready loading all fragements to the control block and setup pci mapping
+ * update the control block interface information */
wmb();
- cb->driver_curr_frag[ISL38XX_CB_TX_DATA_LQ] = cpu_to_le32(curr_frag);
+ add_le32p((u32 *) & control_block->
+ driver_curr_frag[ISL38XX_CB_TX_DATA_LQ], fragments);
- if (curr_frag - priv->free_data_tx + ISL38XX_MIN_QTHRESHOLD
- > ISL38XX_CB_TX_QSIZE) {
- /* stop sends from upper layers */
+ /* check whether the data tx queue is full now */
+ if (ISL38XX_CB_TX_QSIZE - isl38xx_in_queue(control_block,
+ ISL38XX_CB_TX_DATA_LQ) <
+ ISL38XX_MIN_QTHRESHOLD) {
+ /* stop the transmission of network frames to the driver */
netif_stop_queue(ndev);
- /* set the full flag for the transmission queue */
+ /* set the full flag for the data low transmission queue */
priv->data_low_tx_full = 1;
}
/* trigger the device */
islpci_trigger(priv);
- /* unlock the driver code */
- spin_unlock_irqrestore(&priv->slock, flags);
-
/* set the transmission time */
ndev->trans_start = jiffies;
priv->statistics.tx_packets++;
priv->statistics.tx_bytes += skb->len;
- return 0;
-
- drop_free:
- /* free the skbuf structure before aborting */
- dev_kfree_skb(skb);
-
- priv->statistics.tx_dropped++;
+ /* unlock the driver code */
spin_unlock_irqrestore(&priv->slock, flags);
- return err;
+ return 0;
}
int
--Multipart=_Thu__11_Mar_2004_08_37_52_+0100_Vz_ziwoBf=djI+Un--