OK. So this is my lame try to port this driver to NAPI.
Again only compile tested. ;-)

Oh. And when I was doing this, I learned that this driver should
already have advantages of NAPI (it is disabling interrupts, and
then handling everything).

Maybe someone with more knowledge can comment on this.


Signed-off-by: Domen Puncer <domen@coderock.org>

diff -pru linux-2.6.15/drivers/net/adm5120sw.c linux/drivers/net/adm5120sw.c
--- linux-2.6.15/drivers/net/adm5120sw.c	2006-01-22 12:22:42.000000000 +0100
+++ linux/drivers/net/adm5120sw.c	2006-01-21 19:50:19.000000000 +0100
@@ -88,13 +88,42 @@ static inline void adm5120_rxfixup(struc
 	}
 }
 
-static inline void adm5120_rx(struct adm5120_dma *dma, struct sk_buff **skbl,
-    int *index, int num)
+#define ADM5120_INT_NORX (ADM5120_INT_HFULL | ADM5120_INT_LFULL | ADM5120_INT_TXH | ADM5120_INT_TXL)
+#define ADM5120_INT_RX (ADM5120_INT_RXH | ADM5120_INT_RXL)
+static inline int adm5120_rx(struct adm5120_dma *dma, struct sk_buff **skbl,
+    int *index, int num, int *budget);
+static int adm5120_poll(struct net_device *dev, int *budget)
+{
+	unsigned long intreg;
+
+	intreg = adm5120_get_reg(ADM5120_INT_ST);
+
+	if (intreg & ADM5120_INT_RXH)
+		return adm5120_rx(adm5120_dma_rxh, adm5120_skb_rxh, &adm5120_rxhi,
+			ADM5120_DMA_RXH, budget);
+	if (intreg & ADM5120_INT_RXL)
+		return adm5120_rx(adm5120_dma_rxl, adm5120_skb_rxl, &adm5120_rxli,
+			ADM5120_DMA_RXL, budget);
+
+	/* we shouldn't come here?! */
+	printk(KERN_EMERG "%s: intreg: %lx\n", __func__, intreg);
+	return 0;
+}
+
+static inline int adm5120_rx(struct adm5120_dma *dma, struct sk_buff **skbl,
+    int *index, int num, int *budget)
 {
 	struct sk_buff *skb, *skbn;
 	struct adm5120_sw *priv;
 	struct net_device *dev;
 	int port, vlan, len;
+	int not_d = 0;
+
+	/* somehow ugly... someone rewrite? */
+	for (vlan = 0; vlan < adm5120_nrdevs; vlan++) {
+		struct adm5120_sw *priv = netdev_priv(adm5120_devs[vlan]);
+		priv->rx_work_limit = adm5120_devs[vlan]->quota;
+	}
 
 	while (!(dma[*index].data & ADM5120_DMA_OWN)) {
 		port = (dma[*index].status & ADM5120_DMA_PORTID);
@@ -112,6 +141,12 @@ static inline void adm5120_rx(struct adm
 		len -= ETH_FCS;
 
 		priv = netdev_priv(dev);
+
+		if (--priv->rx_work_limit < 0) {
+			not_d = 1;
+			goto not_done;
+		}
+
 		if (len <= 0 || len > ADM5120_DMA_RXSIZE ||
 		    dma[*index].status & ADM5120_DMA_FCSERR) {
 			priv->stats.rx_errors++;
@@ -128,6 +163,7 @@ static inline void adm5120_rx(struct adm
 				priv->stats.rx_bytes+=len;
 				skb_reserve(skbn, 2);
 				skbl[*index] = skbn;
+				netif_receive_skb(skbn);
 			} else {
 				printk(KERN_INFO "%s recycling!\n", dev->name);
 			}
@@ -144,6 +180,28 @@ static inline void adm5120_rx(struct adm
 		if (skbn)
 			netif_rx(skb);
 	}
+// done:
+ not_done:
+	for (vlan = 0; vlan < adm5120_nrdevs; vlan++) {
+		struct net_device *dev = adm5120_devs[vlan];
+		struct adm5120_sw *priv = netdev_priv(dev);
+		int rx = priv->stats.rx_packets;
+		dev->quota -= rx;
+		*budget -= rx;
+		/* oom? how's memory handled? */
+		netif_rx_complete(dev);
+	}
+	/* done */
+	if (!not_d) {
+		/* enable interrupts */
+		adm5120_set_reg(ADM5120_INT_MASK,
+		    adm5120_get_reg(ADM5120_INT_MASK) & ~ADM5120_INT_RX);
+		return 0;
+	} else {
+		/* not done */
+		return 1;
+	}
+
 }
 
 static inline void adm5120_tx(struct adm5120_dma *dma, struct sk_buff **skbl,
@@ -159,23 +217,40 @@ static inline void adm5120_tx(struct adm
 
 irqreturn_t adm5120_sw_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
+	struct net_device *dev = dev_id;
 	unsigned long intreg;
 
 	adm5120_set_reg(ADM5120_INT_MASK,
-	    adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INTHANDLE);
+	    adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INT_NORX);
 
+	/* i assume this is ack */
+	/* XXX it might be COR or COW, test for that */
 	intreg = adm5120_get_reg(ADM5120_INT_ST);
-	adm5120_set_reg(ADM5120_INT_ST, intreg);
+	printk(KERN_EMERG "%s: intreg: %lx\n", __func__, intreg);
+	intreg = adm5120_get_reg(ADM5120_INT_ST);
+	printk(KERN_EMERG "%s: intreg: %lx\n", __func__, intreg);
+
+	if (intreg & (ADM5120_INT_RXH | ADM5120_INT_RXL)) {
+		if (netif_rx_schedule_prep(dev)) {
+			/* disable ints */
+			adm5120_set_reg(ADM5120_INT_MASK,
+			    adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INT_RX);
+
+			__netif_rx_schedule(dev);
+		} else {
+			printk(KERN_EMERG "interrupt while in poll\n");
+			/* disable ints */
+			adm5120_set_reg(ADM5120_INT_MASK,
+			    adm5120_get_reg(ADM5120_INT_MASK) | ADM5120_INT_RX);
+		}
+	} else {
+		/* ack */
+		adm5120_set_reg(ADM5120_INT_ST, intreg);
+	}
 
-	if (intreg & ADM5120_INT_RXH)
-		adm5120_rx(adm5120_dma_rxh, adm5120_skb_rxh, &adm5120_rxhi,
-		ADM5120_DMA_RXH);
 	if (intreg & ADM5120_INT_HFULL)
 		adm5120_rxfixup(adm5120_dma_rxh, adm5120_skb_rxh,
 		ADM5120_DMA_RXH);
-	if (intreg & ADM5120_INT_RXL)
-		adm5120_rx(adm5120_dma_rxl, adm5120_skb_rxl, &adm5120_rxli,
-		    ADM5120_DMA_RXL);
 	if (intreg & ADM5120_INT_LFULL)
 		adm5120_rxfixup(adm5120_dma_rxl, adm5120_skb_rxl,
 		ADM5120_DMA_RXL);
@@ -187,7 +262,7 @@ irqreturn_t adm5120_sw_irq(int irq, void
 		ADM5120_DMA_TXL);
 
 	adm5120_set_reg(ADM5120_INT_MASK,
-	    adm5120_get_reg(ADM5120_INT_MASK) & ~ADM5120_INTHANDLE);
+	    adm5120_get_reg(ADM5120_INT_MASK) & ~ADM5120_INT_NORX);
 
 	return IRQ_HANDLED;
 }
@@ -444,6 +519,9 @@ static int __init adm5120_sw_init(void)
 		dev->tx_timeout = adm5120_tx_timeout;
 		dev->watchdog_timeo = ETH_TX_TIMEOUT;
 		dev->set_mac_address = adm5120_sw_set_mac_address;
+		/* NAPI */
+		dev->poll = adm5120_poll;
+		dev->weight = 16;
 		/* HACK alert!!!  In the original admtek driver it is asumed
 		   that you can read the MAC addressess from flash, but edimax
 		   decided to leave that space intentionally blank...
Only in linux/drivers/net/: adm5120sw.c.orig
diff -pru linux-2.6.15/drivers/net/adm5120sw.h linux/drivers/net/adm5120sw.h
--- linux-2.6.15/drivers/net/adm5120sw.h	2006-01-22 12:22:42.000000000 +0100
+++ linux/drivers/net/adm5120sw.h	2006-01-21 19:17:49.000000000 +0100
@@ -91,6 +91,7 @@ struct adm5120_dma {
 struct adm5120_sw {
 	int			port;
 	struct net_device_stats	stats;
+	int			rx_work_limit;
 };
 
 #define SIOCSMATRIX	SIOCDEVPRIVATE
