[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--