patch-2.3.99-pre8 linux/drivers/net/skfp/skfddi.c

Next file: linux/drivers/net/tokenring/ibmtr.c
Previous file: linux/drivers/net/skfp/hwmtm.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre7/linux/drivers/net/skfp/skfddi.c linux/drivers/net/skfp/skfddi.c
@@ -19,6 +19,9 @@
  * Maintainers:
  *   CG    Christoph Goos (cgoos@syskonnect.de)
  *
+ * Contributors:
+ *   DM    David S. Miller
+ *
  * Address all question to:
  *   linux@syskonnect.de
  *
@@ -52,20 +55,20 @@
  *		26-Oct-99	CG	Fixed compilation error on 2.2.13
  *		12-Nov-99	CG	Source code release
  *		22-Nov-99	CG	Included in kernel source.
+ *		07-May-00	DM	64 bit fixes, new dma interface
  *
  * Compilation options (-Dxxx):
  *              DRIVERDEBUG     print lots of messages to log file
  *              DUMPPACKETS     print received/transmitted packets to logfile
  * 
- * Limitations:
- *	I changed the driver to support memory mapped I/O, so it
- *	might run on non-x86 architectures (not tested).
- *	But the hardware module does not yet support 64 bit OS'es.
+ * Tested cpu architectures:
+ *	- i386
+ *	- sparc64
  */
 
 /* Version information string - should be updated prior to */
 /* each new release!!! */
-#define VERSION		"2.05"
+#define VERSION		"2.06"
 
 static const char *boot_msg = 
 	"SysKonnect FDDI PCI Adapter driver v" VERSION " for\n"
@@ -666,18 +669,28 @@
 
 	spin_lock_init(&bp->DriverLock);
 	
+	// Allocate invalid frame
+	bp->LocalRxBuffer = pci_alloc_consistent(&bp->pdev, MAX_FRAME_SIZE, &bp->LocalRxBufferDMA);
+	if (!bp->LocalRxBuffer) {
+		printk("could not allocate mem for ");
+		printk("LocalRxBuffer: %d byte\n", MAX_FRAME_SIZE);
+		goto fail;
+	}
+
 	// Determine the required size of the 'shared' memory area.
 	bp->SharedMemSize = mac_drv_check_space();
 	PRINTK(KERN_INFO "Memory for HWM: %ld\n", bp->SharedMemSize);
 	if (bp->SharedMemSize > 0) {
 		bp->SharedMemSize += 16;	// for descriptor alignment
 
-		bp->SharedMemAddr = kmalloc(bp->SharedMemSize, GFP_KERNEL);
+		bp->SharedMemAddr = pci_alloc_consistent(&bp->pdev,
+							 bp->SharedMemSize,
+							 &bp->SharedMemDMA);
 		if (!bp->SharedMemSize) {
 			printk("could not allocate mem for ");
 			printk("hardware module: %ld byte\n",
 			       bp->SharedMemSize);
-			return (-1);
+			goto fail;
 		}
 		bp->SharedMemHeap = 0;	// Nothing used yet.
 
@@ -693,7 +706,7 @@
 	PRINTK(KERN_INFO "mac_drv_init()..\n");
 	if (mac_drv_init(smc) != 0) {
 		PRINTK(KERN_INFO "mac_drv_init() failed.\n");
-		return (-1);
+		goto fail;
 	}
 	read_address(smc, NULL);
 	PRINTK(KERN_INFO "HW-Addr: %02x %02x %02x %02x %02x %02x\n",
@@ -708,6 +721,21 @@
 	smt_reset_defaults(smc, 0);
 
 	return (0);
+
+fail:
+	if (bp->SharedMemAddr) {
+		pci_free_consistent(&bp->pdev,
+				    bp->SharedMemSize,
+				    bp->SharedMemAddr,
+				    bp->SharedMemDMA);
+		bp->SharedMemAddr = NULL;
+	}
+	if (bp->LocalRxBuffer) {
+		pci_free_consistent(&bp->pdev, MAX_FRAME_SIZE,
+				    bp->LocalRxBuffer, bp->LocalRxBufferDMA);
+		bp->LocalRxBuffer = NULL;
+	}
+	return (-1);
 }				// skfp_driver_init
 
 
@@ -1282,7 +1310,7 @@
  *   is contained in a single physically contiguous buffer
  *   in which the virtual address of the start of packet
  *   (skb->data) can be converted to a physical address
- *   by using virt_to_bus().
+ *   by using pci_map_single().
  *
  *   We have an internal queue for packets we can not send 
  *   immediately. Packets in this queue can be given to the 
@@ -1378,6 +1406,7 @@
 	unsigned char fc;
 	int queue;
 	struct s_smt_fp_txd *txd;	// Current TxD.
+	dma_addr_t dma_address;
 	unsigned long Flags;
 
 	int frame_status;	// HWM tx frame status.
@@ -1442,13 +1471,18 @@
 
 		txd = (struct s_smt_fp_txd *) HWM_GET_CURR_TXD(smc, queue);
 
+		dma_address = pci_map_single(&bp->pdev, skb->data,
+					     skb->len, PCI_DMA_TODEVICE);
 		if (frame_status & LAN_TX) {
-			txd->txd_os.skb = skb;	// save skb
+			txd->txd_os.skb = skb;			// save skb
+			txd->txd_os.dma_addr = dma_address;	// save dma mapping
 		}
-		hwm_tx_frag(smc, skb->data, virt_to_bus(skb->data), skb->len,
+		hwm_tx_frag(smc, skb->data, dma_address, skb->len,
                       frame_status | FIRST_FRAG | LAST_FRAG | EN_IRQ_EOF);
 
 		if (!(frame_status & LAN_TX)) {		// local only frame
+			pci_unmap_single(&bp->pdev, dma_address,
+					 skb->len, PCI_DMA_TODEVICE);
 			dev_kfree_skb_irq(skb);
 		}
 		spin_unlock_irqrestore(&bp->DriverLock, Flags);
@@ -1571,7 +1605,7 @@
 {
 	void *virt;
 
-	PRINTK(KERN_INFO "mac_drv_get_space\n");
+	PRINTK(KERN_INFO "mac_drv_get_space (%d bytes), ", size);
 	virt = (void *) (smc->os.SharedMemAddr + smc->os.SharedMemHeap);
 
 	if ((smc->os.SharedMemHeap + size) > smc->os.SharedMemSize) {
@@ -1581,8 +1615,10 @@
 	smc->os.SharedMemHeap += size;	// Move heap pointer.
 
 	PRINTK(KERN_INFO "mac_drv_get_space end\n");
-	PRINTK(KERN_INFO "virt addr: %08lx\n", (ulong) virt);
-	PRINTK(KERN_INFO "bus  addr: %08lx\n", (ulong) virt_to_bus(virt));
+	PRINTK(KERN_INFO "virt addr: %lx\n", (ulong) virt);
+	PRINTK(KERN_INFO "bus  addr: %lx\n", (ulong)
+	       (smc->os.SharedMemDMA +
+		((char *) virt - (char *)smc->os.SharedMemAddr)));
 	return (virt);
 }				// mac_drv_get_space
 
@@ -1616,7 +1652,7 @@
 
 	virt = mac_drv_get_space(smc, size);
 
-	size = (u_int) ((0 - (unsigned int) virt) & 15);
+	size = (u_int) ((0 - (unsigned long) virt) & 15UL);
 
 	PRINTK("Allocate %u bytes alignment gap ", size);
 	PRINTK("for descriptor memory.\n");
@@ -1644,7 +1680,8 @@
  ************************/
 unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt)
 {
-	return virt_to_bus(virt);
+	return (smc->os.SharedMemDMA +
+		((char *) virt - (char *)smc->os.SharedMemAddr));
 }				// mac_drv_virt2phys
 
 
@@ -1657,7 +1694,8 @@
  *	for the DMA transfer, it should do it in this function.
  *
  *	The hardware module calls this dma_master if it wants to send an SMT
- *	frame.
+ *	frame.  This means that the virt address passed in here is part of
+ *      the 'shared' memory area.
  * Args
  *	smc - A pointer to the SMT context struct.
  *
@@ -1677,7 +1715,8 @@
  ************************/
 u_long dma_master(struct s_smc * smc, void *virt, int len, int flag)
 {
-	return (virt_to_bus(virt));
+	return (smc->os.SharedMemDMA +
+		((char *) virt - (char *)smc->os.SharedMemAddr));
 }				// dma_master
 
 
@@ -1704,7 +1743,31 @@
  ************************/
 void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, int flag)
 {
-	return;
+	/* For TX buffers, there are two cases.  If it is an SMT transmit
+	 * buffer, there is nothing to do since we use consistent memory
+	 * for the 'shared' memory area.  The other case is for normal
+	 * transmit packets given to us by the networking stack, and in
+	 * that case we cleanup the PCI DMA mapping in mac_drv_tx_complete
+	 * below.
+	 *
+	 * For RX buffers, we have to unmap dynamic PCI DMA mappings here
+	 * because the hardware module is about to potentially look at
+	 * the contents of the buffer.  If we did not call the PCI DMA
+	 * unmap first, the hardware module could read inconsistent data.
+	 */
+	if (flag & DMA_WR) {
+		skfddi_priv *bp = (skfddi_priv *) & smc->os;
+		volatile struct s_smt_fp_rxd *r = &descr->r;
+
+		/* If SKB is NULL, we used the local buffer. */
+		if (r->rxd_os.skb && r->rxd_os.dma_addr) {
+			int MaxFrameSize = bp->MaxFrameSize;
+
+			pci_unmap_single(&bp->pdev, r->rxd_os.dma_addr,
+					 MaxFrameSize, PCI_DMA_FROMDEVICE);
+			r->rxd_os.dma_addr = 0;
+		}
+	}
 }				// dma_complete
 
 
@@ -1735,6 +1798,11 @@
 	}
 	txd->txd_os.skb = NULL;
 
+	// release the DMA mapping
+	pci_unmap_single(&smc->os.pdev, txd->txd_os.dma_addr,
+			 skb->len, PCI_DMA_TODEVICE);
+	txd->txd_os.dma_addr = 0;
+
 	smc->os.MacStat.tx_packets++;	// Count transmitted packets.
 	smc->os.MacStat.tx_bytes+=skb->len;	// Count bytes
 
@@ -1823,6 +1891,8 @@
 	}
 	virt = skb->data;
 
+	// The DMA mapping was released in dma_complete above.
+
 	dump_data(skb->data, len);
 
 	/*
@@ -1923,7 +1993,7 @@
 	struct sk_buff *skb;
 	int MaxFrameSize;
 	unsigned char *v_addr;
-	unsigned long b_addr;
+	dma_addr_t b_addr;
 
 	if (frag_count != 1)	// This is not allowed to happen.
 
@@ -1939,25 +2009,34 @@
 		if (skb == NULL) {	// this should not happen
 
 			PRINTK("Requeue with no skb in rxd!\n");
-			skb = alloc_skb(MaxFrameSize, GFP_ATOMIC);
+			skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC);
 			if (skb) {
 				// we got a skb
 				rxd->rxd_os.skb = skb;
+				skb_reserve(skb, 3);
 				skb_put(skb, MaxFrameSize);
 				v_addr = skb->data;
-				b_addr = virt_to_bus(v_addr);
+				b_addr = pci_map_single(&smc->os.pdev,
+							v_addr,
+							MaxFrameSize,
+							PCI_DMA_FROMDEVICE);
+				rxd->rxd_os.dma_addr = b_addr;
 			} else {
 				// no skb available, use local buffer
 				PRINTK("Queueing invalid buffer!\n");
 				rxd->rxd_os.skb = NULL;
 				v_addr = smc->os.LocalRxBuffer;
-				b_addr = virt_to_bus(v_addr);
+				b_addr = smc->os.LocalRxBufferDMA;
 			}
 		} else {
 			// we use skb from old rxd
 			rxd->rxd_os.skb = skb;
 			v_addr = skb->data;
-			b_addr = virt_to_bus(v_addr);
+			b_addr = pci_map_single(&smc->os.pdev,
+						v_addr,
+						MaxFrameSize,
+						PCI_DMA_FROMDEVICE);
+			rxd->rxd_os.dma_addr = b_addr;
 		}
 		hwm_rx_frag(smc, v_addr, b_addr, MaxFrameSize,
 			    FIRST_FRAG | LAST_FRAG);
@@ -2002,12 +2081,17 @@
 		PRINTK(KERN_INFO ".\n");
 
 		rxd = HWM_GET_CURR_RXD(smc);
-		skb = alloc_skb(MaxFrameSize, GFP_ATOMIC);
+		skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC);
 		if (skb) {
 			// we got a skb
+			skb_reserve(skb, 3);
 			skb_put(skb, MaxFrameSize);
 			v_addr = skb->data;
-			b_addr = virt_to_bus(v_addr);
+			b_addr = pci_map_single(&smc->os.pdev,
+						v_addr,
+						MaxFrameSize,
+						PCI_DMA_FROMDEVICE);
+			rxd->rxd_os.dma_addr = b_addr;
 		} else {
 			// no skb available, use local buffer
 			// System has run out of buffer memory, but we want to
@@ -2016,7 +2100,7 @@
 			// so data in it must be considered invalid.
 			PRINTK("Queueing invalid buffer!\n");
 			v_addr = smc->os.LocalRxBuffer;
-			b_addr = virt_to_bus(v_addr);
+			b_addr = smc->os.LocalRxBufferDMA;
 		}
 
 		rxd->rxd_os.skb = skb;
@@ -2060,6 +2144,12 @@
 	for (; frag_count > 0; frag_count--) {
 		skb = rxd->rxd_os.skb;
 		if (skb != NULL) {
+			skfddi_priv *bp = (skfddi_priv *) & smc->os;
+			int MaxFrameSize = bp->MaxFrameSize;
+
+			pci_unmap_single(&bp->pdev, rxd->rxd_os.dma_addr,
+					 MaxFrameSize, PCI_DMA_FROMDEVICE);
+
 			dev_kfree_skb(skb);
 			rxd->rxd_os.skb = NULL;
 		}
@@ -2111,11 +2201,12 @@
 		       len, la_len, (unsigned long) look_ahead);
 		return (0);
 	}
-	skb = alloc_skb(len, GFP_ATOMIC);
+	skb = alloc_skb(len + 3, GFP_ATOMIC);
 	if (!skb) {
 		PRINTK("fddi: Local SMT: skb memory exhausted.\n");
 		return (0);
 	}
+	skb_reserve(skb, 3);
 	skb_put(skb, len);
 	memcpy(skb->data, look_ahead, len);
 
@@ -2162,7 +2253,40 @@
  ************************/
 void ring_status_indication(struct s_smc *smc, u_long status)
 {
-	PRINTK("ring_status_indication(%08lXh)\n", (unsigned long) status);
+	PRINTK("ring_status_indication( ");
+	if (status & RS_RES15)
+		PRINTK("RS_RES15 ");
+	if (status & RS_HARDERROR)
+		PRINTK("RS_HARDERROR ");
+	if (status & RS_SOFTERROR)
+		PRINTK("RS_SOFTERROR ");
+	if (status & RS_BEACON)
+		PRINTK("RS_BEACON ");
+	if (status & RS_PATHTEST)
+		PRINTK("RS_PATHTEST ");
+	if (status & RS_SELFTEST)
+		PRINTK("RS_SELFTEST ");
+	if (status & RS_RES9)
+		PRINTK("RS_RES9 ");
+	if (status & RS_DISCONNECT)
+		PRINTK("RS_DISCONNECT ");
+	if (status & RS_RES7)
+		PRINTK("RS_RES7 ");
+	if (status & RS_DUPADDR)
+		PRINTK("RS_DUPADDR ");
+	if (status & RS_NORINGOP)
+		PRINTK("RS_NORINGOP ");
+	if (status & RS_VERSION)
+		PRINTK("RS_VERSION ");
+	if (status & RS_STUCKBYPASSS)
+		PRINTK("RS_STUCKBYPASSS ");
+	if (status & RS_EVENT)
+		PRINTK("RS_EVENT ");
+	if (status & RS_RINGOPCHANGE)
+		PRINTK("RS_RINGOPCHANGE ");
+	if (status & RS_RES0)
+		PRINTK("RS_RES0 ");
+	PRINTK("]\n");
 }				// ring_status_indication
 
 
@@ -2261,9 +2385,18 @@
 	case SC7_WRAP_S:
 		s = "SC7_WRAP_S";
 		break;
-	default:
-		s = "unknown";
+	case SC9_C_WRAP_A:
+		s = "SC9_C_WRAP_A";
+		break;
+	case SC10_C_WRAP_B:
+		s = "SC10_C_WRAP_B";
+		break;
+	case SC11_C_WRAP_S:
+		s = "SC11_C_WRAP_S";
 		break;
+	default:
+		PRINTK(KERN_INFO "cfm_state_change: unknown %d\n", c_state);
+		return;
 	}
 	PRINTK(KERN_INFO "cfm_state_change: %s\n", s);
 #endif				// DRIVERDEBUG
@@ -2478,7 +2611,18 @@
 		next = lp->os.next_module;
 
 		if (lp->os.SharedMemAddr) {
-			kfree(lp->os.SharedMemAddr);
+			pci_free_consistent(&lp->os.pdev,
+					    lp->os.SharedMemSize,
+					    lp->os.SharedMemAddr,
+					    lp->os.SharedMemDMA);
+			lp->os.SharedMemAddr = NULL;
+		}
+		if (lp->os.LocalRxBuffer) {
+			pci_free_consistent(&lp->os.pdev,
+					    MAX_FRAME_SIZE,
+					    lp->os.LocalRxBuffer,
+					    lp->os.LocalRxBufferDMA);
+			lp->os.LocalRxBuffer = NULL;
 		}
 		release_region(p->base_addr, 
 			(lp->os.bus_type == SK_BUS_TYPE_PCI ? FP_IO_LEN : 0));

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)