Use net_device_operations structure and netdev_nullify() to allow for
authorMichael Brown <mcb30@etherboot.org>
Sat, 7 Jul 2007 15:43:39 +0000 (16:43 +0100)
committerMichael Brown <mcb30@etherboot.org>
Sat, 7 Jul 2007 15:43:39 +0000 (16:43 +0100)
safe dropping of the netdev ref by the driver while other refs still
exist.

Add netdev_irq() method.  Net device open()/close() methods should no
longer enable or disable IRQs.

Remove rx_quota; it wasn't used anywhere and added too much complexity
to implementing correct interrupt-masking behaviour in pxe_undi.c.

src/arch/i386/drivers/net/undinet.c
src/drivers/net/legacy.c
src/drivers/net/pnic.c
src/drivers/net/rtl8139.c
src/include/gpxe/netdevice.h
src/interface/pxe/pxe_undi.c
src/net/netdevice.c
src/net/nullnet.c [new file with mode: 0644]

index 46a759c..3ed0ed6 100644 (file)
@@ -394,7 +394,6 @@ static int undinet_transmit ( struct net_device *netdev,
  * Poll for received packets
  *
  * @v netdev           Network device
- * @v rx_quota         Maximum number of packets to receive
  *
  * Fun, fun, fun.  UNDI drivers don't use polling; they use
  * interrupts.  We therefore cheat and pretend that an interrupt has
@@ -412,7 +411,7 @@ static int undinet_transmit ( struct net_device *netdev,
  * of installing a genuine interrupt service routine and dealing with
  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
  */
-static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
+static void undinet_poll ( struct net_device *netdev ) {
        struct undi_nic *undinic = netdev->priv;
        struct s_PXENV_UNDI_ISR undi_isr;
        struct io_buffer *iobuf = NULL;
@@ -454,7 +453,7 @@ static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
        }
 
        /* Run through the ISR loop */
-       while ( rx_quota ) {
+       while ( 1 ) {
                if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
                                           sizeof ( undi_isr ) ) ) != 0 )
                        break;
@@ -487,7 +486,6 @@ static void undinet_poll ( struct net_device *netdev, unsigned int rx_quota ) {
                                /* Whole packet received; deliver it */
                                netdev_rx ( netdev, iobuf );
                                iobuf = NULL;
-                               --rx_quota;
                                /* Etherboot 5.4 fails to return all packets
                                 * under mild load; pretend it retriggered.
                                 */
@@ -599,6 +597,29 @@ static void undinet_close ( struct net_device *netdev ) {
        DBGC ( undinic, "UNDINIC %p closed\n", undinic );
 }
 
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev           Net device
+ * @v enable           Interrupts should be enabled
+ */
+static void undinet_irq ( struct net_device *netdev, int enable ) {
+       struct undi_nic *undinic = netdev->priv;
+
+       /* Cannot support interrupts yet */
+       DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
+              undinic, ( enable ? "enable" : "disable" ) );
+}
+
+/** UNDI network device operations */
+static struct net_device_operations undinet_operations = {
+       .open           = undinet_open,
+       .close          = undinet_close,
+       .transmit       = undinet_transmit,
+       .poll           = undinet_poll,
+       .irq            = undinet_irq,
+};
+
 /**
  * Probe UNDI device
  *
@@ -622,6 +643,7 @@ int undinet_probe ( struct undi_device *undi ) {
        netdev = alloc_etherdev ( sizeof ( *undinic ) );
        if ( ! netdev )
                return -ENOMEM;
+       netdev_init ( netdev, &undinet_operations );
        undinic = netdev->priv;
        undi_set_drvdata ( undi, netdev );
        netdev->dev = &undi->dev;
@@ -685,12 +707,6 @@ int undinet_probe ( struct undi_device *undi ) {
                undinic->hacks |= UNDI_HACK_EB54;
        }
 
-       /* Point to NIC specific routines */
-       netdev->open     = undinet_open;
-       netdev->close    = undinet_close;
-       netdev->transmit = undinet_transmit;
-       netdev->poll     = undinet_poll;
-
        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register;
@@ -716,6 +732,7 @@ int undinet_probe ( struct undi_device *undi ) {
        undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
                       sizeof ( stop_undi ) );
  err_start_undi:
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
        undi_set_drvdata ( undi, NULL );
        return rc;
@@ -751,6 +768,7 @@ void undinet_remove ( struct undi_device *undi ) {
        undi->flags &= ~UNDI_FL_STARTED;
 
        /* Free network device */
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
 
        DBGC ( undinic, "UNDINIC %p removed\n", undinic );
index 2d4633d..6ae2fbe 100644 (file)
@@ -36,13 +36,10 @@ static int legacy_transmit ( struct net_device *netdev, struct io_buffer *iobuf
        return 0;
 }
 
-static void legacy_poll ( struct net_device *netdev, unsigned int rx_quota ) {
+static void legacy_poll ( struct net_device *netdev ) {
        struct nic *nic = netdev->priv;
        struct io_buffer *iobuf;
 
-       if ( ! rx_quota )
-               return;
-
        iobuf = alloc_iob ( ETH_FRAME_LEN );
        if ( ! iobuf )
                return;
@@ -57,19 +54,29 @@ static void legacy_poll ( struct net_device *netdev, unsigned int rx_quota ) {
        }
 }
 
-static int legacy_open ( struct net_device *netdev ) {
-       struct nic *nic = netdev->priv;
-
-       nic->nic_op->irq ( nic, ENABLE );
+static int legacy_open ( struct net_device *netdev __unused ) {
+       /* Nothing to do */
        return 0;
 }
 
-static void legacy_close ( struct net_device *netdev ) {
+static void legacy_close ( struct net_device *netdev __unused ) {
+       /* Nothing to do */
+}
+
+static void legacy_irq ( struct net_device *netdev __unused, int enable ) {
        struct nic *nic = netdev->priv;
 
-       nic->nic_op->irq ( nic, DISABLE );
+       nic->nic_op->irq ( nic, ( enable ? ENABLE : DISABLE ) );
 }
 
+static struct net_device_operations legacy_operations = {
+       .open           = legacy_open,
+       .close          = legacy_close,
+       .transmit       = legacy_transmit,
+       .poll           = legacy_poll,
+       .irq            = legacy_irq,
+};
+
 int legacy_probe ( void *hwdev,
                   void ( * set_drvdata ) ( void *hwdev, void *priv ),
                   struct device *dev,
@@ -84,27 +91,21 @@ int legacy_probe ( void *hwdev,
        netdev = alloc_etherdev ( 0 );
        if ( ! netdev )
                return -ENOMEM;
+       netdev_init ( netdev, &legacy_operations );
        netdev->priv = &nic;
        memset ( &nic, 0, sizeof ( nic ) );
        set_drvdata ( hwdev, netdev );
        netdev->dev = dev;
 
-       netdev->open = legacy_open;
-       netdev->close = legacy_close;
-       netdev->transmit = legacy_transmit;
-       netdev->poll = legacy_poll;
        nic.node_addr = netdev->ll_addr;
 
        if ( ! probe ( &nic, hwdev ) ) {
-               netdev_put ( netdev );
-               return -ENODEV;
+               rc = -ENODEV;
+               goto err_probe;
        }
 
-       if ( ( rc = register_netdev ( netdev ) ) != 0 ) {
-               disable ( &nic, hwdev );
-               netdev_put ( netdev );
-               return rc;
-       }
+       if ( ( rc = register_netdev ( netdev ) ) != 0 )
+               goto err_register;
 
        /* Do not remove this message */
        printf ( "WARNING: Using legacy NIC wrapper on %s\n",
@@ -112,6 +113,13 @@ int legacy_probe ( void *hwdev,
 
        legacy_registered = 1;
        return 0;
+
+ err_register:
+       disable ( &nic, hwdev );
+ err_probe:
+       netdev_nullify ( netdev );
+       netdev_put ( netdev );
+       return rc;
 }
 
 void legacy_remove ( void *hwdev,
@@ -122,6 +130,7 @@ void legacy_remove ( void *hwdev,
 
        unregister_netdev ( netdev );
        disable ( nic, hwdev );
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
        legacy_registered = 0;
 }
index 38b4af6..b431ec5 100644 (file)
@@ -112,35 +112,34 @@ static int pnic_api_check ( uint16_t api_version ) {
 /**************************************************************************
 POLL - Wait for a frame
 ***************************************************************************/
-static void pnic_poll ( struct net_device *netdev, unsigned int rx_quota ) {
+static void pnic_poll ( struct net_device *netdev ) {
        struct pnic *pnic = netdev->priv;
        struct io_buffer *iobuf;
        uint16_t length;
        uint16_t qlen;
 
        /* Fetch all available packets */
-       while ( rx_quota ) {
+       while ( 1 ) {
                if ( pnic_command ( pnic, PNIC_CMD_RECV_QLEN, NULL, 0,
                                    &qlen, sizeof ( qlen ), NULL )
                     != PNIC_STATUS_OK )
-                       break;
+                       return;
                if ( qlen == 0 )
-                       break;
+                       return;
                iobuf = alloc_iob ( ETH_FRAME_LEN );
                if ( ! iobuf ) {
                        DBG ( "could not allocate buffer\n" );
                        netdev_rx_err ( netdev, NULL, -ENOMEM );
-                       break;
+                       return;
                }
                if ( pnic_command ( pnic, PNIC_CMD_RECV, NULL, 0,
                                    iobuf->data, ETH_FRAME_LEN, &length )
                     != PNIC_STATUS_OK ) {
                        netdev_rx_err ( netdev, iobuf, -EIO );
-                       break;
+                       return;
                }
                iob_put ( iobuf, length );
                netdev_rx ( netdev, iobuf );
-               --rx_quota;
        }
 }
 
@@ -164,29 +163,40 @@ static int pnic_transmit ( struct net_device *netdev, struct io_buffer *iobuf )
 /**************************************************************************
 OPEN - Open network device
 ***************************************************************************/
-static int pnic_open ( struct net_device *netdev ) {
-       struct pnic *pnic = netdev->priv;
-       static const uint8_t enable = 1;
-
-       /* Enable interrupts */
-       pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &enable,
-                      sizeof ( enable ), NULL, 0, NULL );
-       
+static int pnic_open ( struct net_device *netdev __unused ) {
+       /* Nothing to do */
        return 0;
 }
 
 /**************************************************************************
 CLOSE - Close network device
 ***************************************************************************/
-static void pnic_close ( struct net_device *netdev ) {
-       struct pnic *pnic = netdev->priv;
-       static const uint8_t disable = 0;
+static void pnic_close ( struct net_device *netdev __unused ) {
+       /* Nothing to do */
+}
 
-       /* Disable interrupts */
-       pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &disable,
-                      sizeof ( disable ), NULL, 0, NULL );
+/**************************************************************************
+IRQ - Enable/disable interrupts
+***************************************************************************/
+static void pnic_irq ( struct net_device *netdev, int enable ) {
+       struct pnic *pnic = netdev->priv;
+       uint8_t mask = ( enable ? 1 : 0 );
+       
+       pnic_command ( pnic, PNIC_CMD_MASK_IRQ, &mask, sizeof ( mask ),
+                      NULL, 0, NULL );
 }
 
+/**************************************************************************
+OPERATIONS TABLE
+***************************************************************************/
+static struct net_device_operations pnic_operations = {
+       .open           = pnic_open,
+       .close          = pnic_close,
+       .transmit       = pnic_transmit,
+       .poll           = pnic_poll,
+       .irq            = pnic_irq,
+};
+
 /**************************************************************************
 DISABLE - Turn off ethernet interface
 ***************************************************************************/
@@ -196,6 +206,7 @@ static void pnic_remove ( struct pci_device *pci ) {
 
        unregister_netdev ( netdev );
        pnic_command ( pnic, PNIC_CMD_RESET, NULL, 0, NULL, 0, NULL );
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
 }
 
@@ -214,6 +225,7 @@ static int pnic_probe ( struct pci_device *pci,
        netdev = alloc_etherdev ( sizeof ( *pnic ) );
        if ( ! netdev )
                return -ENOMEM;
+       netdev_init ( netdev, &pnic_operations );
        pnic = netdev->priv;
        pci_set_drvdata ( pci, netdev );
        netdev->dev = &pci->dev;
@@ -238,12 +250,6 @@ static int pnic_probe ( struct pci_device *pci,
        status = pnic_command ( pnic, PNIC_CMD_READ_MAC, NULL, 0,
                                netdev->ll_addr, ETH_ALEN, NULL );
 
-       /* Point to NIC specific routines */
-       netdev->open     = pnic_open;
-       netdev->close    = pnic_close;
-       netdev->poll     = pnic_poll;
-       netdev->transmit = pnic_transmit;
-
        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err;
@@ -252,6 +258,7 @@ static int pnic_probe ( struct pci_device *pci,
 
  err:
        /* Free net device */
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
        return rc;
 }
index 54bda07..8b40918 100644 (file)
@@ -302,12 +302,6 @@ static struct nvo_fragment rtl_nvo_fragments[] = {
  */
 static void rtl_reset ( struct rtl8139_nic *rtl ) {
 
-       /* Disable interrupts.  May not be necessary, but datasheet
-        * doesn't say that the reset command also resets the
-        * interrupt mask.
-        */
-       outw ( 0, rtl->ioaddr + IntrMask );
-
        /* Reset chip */
        outb ( CmdReset, rtl->ioaddr + ChipCmd );
        mdelay ( 10 );
@@ -346,9 +340,6 @@ static int rtl_open ( struct net_device *netdev ) {
        outl ( ( ( TX_DMA_BURST << 8 ) | ( TX_IPG << 24 ) ),
               rtl->ioaddr + TxConfig );
 
-       /* Enable interrupts */
-       outw ( ( ROK | RER | TOK | TER ), rtl->ioaddr + IntrMask );
-
        return 0;
 }
 
@@ -400,13 +391,12 @@ static int rtl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
        return 0;
 }
 
-/** 
+/**
  * Poll for received packets
  *
  * @v netdev   Network device
- * @v rx_quota Maximum number of packets to receive
  */
-static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
+static void rtl_poll ( struct net_device *netdev ) {
        struct rtl8139_nic *rtl = netdev->priv;
        unsigned int status;
        unsigned int tsad;
@@ -433,7 +423,7 @@ static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
        }
 
        /* Handle received packets */
-       while ( rx_quota && ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ){
+       while ( ! ( inw ( rtl->ioaddr + ChipCmd ) & RxBufEmpty ) ) {
                rx_status = * ( ( uint16_t * )
                                ( rtl->rx.ring + rtl->rx.offset ) );
                rx_len = * ( ( uint16_t * )
@@ -461,7 +451,6 @@ static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
                                 rtl->rx.ring, wrapped_len );
 
                        netdev_rx ( netdev, rx_iob );
-                       rx_quota--;
                } else {
                        DBG ( "RX bad packet (status %#04x len %d)\n",
                              rx_status, rx_len );
@@ -473,6 +462,28 @@ static void rtl_poll ( struct net_device *netdev, unsigned int rx_quota ) {
        }
 }
 
+/**
+ * Enable/disable interrupts
+ *
+ * @v netdev   Network device
+ * @v enable   Interrupts should be enabled
+ */
+static void rtl_irq ( struct net_device *netdev, int enable ) {
+       struct rtl8139_nic *rtl = netdev->priv;
+       
+       outw ( ( enable ? ( ROK | RER | TOK | TER ) : 0 ),
+              rtl->ioaddr + IntrMask );
+}
+
+/** RTL8139 net device operations */
+static struct net_device_operations rtl_operations = {
+       .open           = rtl_open,
+       .close          = rtl_close,
+       .transmit       = rtl_transmit,
+       .poll           = rtl_poll,
+       .irq            = rtl_irq,
+};
+
 /**
  * Probe PCI device
  *
@@ -490,6 +501,7 @@ static int rtl_probe ( struct pci_device *pci,
        netdev = alloc_etherdev ( sizeof ( *rtl ) );
        if ( ! netdev )
                return -ENOMEM;
+       netdev_init ( netdev, &rtl_operations );
        rtl = netdev->priv;
        pci_set_drvdata ( pci, netdev );
        netdev->dev = &pci->dev;
@@ -504,12 +516,6 @@ static int rtl_probe ( struct pci_device *pci,
        rtl_init_eeprom ( rtl );
        nvs_read ( &rtl->eeprom.nvs, EE_MAC, netdev->ll_addr, ETH_ALEN );
        
-       /* Point to NIC specific routines */
-       netdev->open     = rtl_open;
-       netdev->close    = rtl_close;
-       netdev->transmit = rtl_transmit;
-       netdev->poll     = rtl_poll;
-
        /* Register network device */
        if ( ( rc = register_netdev ( netdev ) ) != 0 )
                goto err_register_netdev;
@@ -526,6 +532,7 @@ static int rtl_probe ( struct pci_device *pci,
        unregister_netdev ( netdev );
  err_register_netdev:
        rtl_reset ( rtl );
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
        return rc;
 }
@@ -543,6 +550,7 @@ static void rtl_remove ( struct pci_device *pci ) {
                nvo_unregister ( &rtl->nvo );
        unregister_netdev ( netdev );
        rtl_reset ( rtl );
+       netdev_nullify ( netdev );
        netdev_put ( netdev );
 }
 
index d6b9a1b..d82c6d8 100644 (file)
@@ -128,41 +128,8 @@ struct ll_protocol {
        const uint8_t *ll_broadcast;
 };
 
-/**
- * Network device statistics
- *
- */
-struct net_device_stats {
-       /** Count of successfully completed transmissions */
-       unsigned int tx_ok;
-       /** Count of transmission errors */
-       unsigned int tx_err;
-       /** Count of successfully received packets */
-       unsigned int rx_ok;
-       /** Count of reception errors */
-       unsigned int rx_err;
-};
-
-/**
- * A network device
- *
- * This structure represents a piece of networking hardware.  It has
- * properties such as a link-layer address and methods for
- * transmitting and receiving raw packets.
- *
- * Note that this structure must represent a generic network device,
- * not just an Ethernet device.
- */
-struct net_device {
-       /** Reference counter */
-       struct refcnt refcnt;
-       /** List of network devices */
-       struct list_head list;
-       /** Name of this network device */
-       char name[8];
-       /** Underlying hardware device */
-       struct device *dev;
-
+/** Network device operations */
+struct net_device_operations {
        /** Open network device
         *
         * @v netdev    Network device
@@ -199,20 +166,62 @@ struct net_device {
         * This method is guaranteed to be called only when the device
         * is open.
         */
-       int ( * transmit ) ( struct net_device *netdev, struct io_buffer *iobuf );
-       /** Poll for received packet
+       int ( * transmit ) ( struct net_device *netdev,
+                            struct io_buffer *iobuf );
+       /** Poll for completed and received packets
         *
         * @v netdev    Network device
-        * @v rx_quota  Maximum number of packets to receive
         *
-        * This method should cause the hardware to check for received
-        * packets.  Any received packets should be delivered via
-        * netdev_rx(), up to a maximum of @c rx_quota packets.
+        * This method should cause the hardware to check for
+        * completed transmissions and received packets.  Any received
+        * packets should be delivered via netdev_rx().
         *
         * This method is guaranteed to be called only when the device
         * is open.
         */
-       void ( * poll ) ( struct net_device *netdev, unsigned int rx_quota );
+       void ( * poll ) ( struct net_device *netdev );
+       /** Enable or disable interrupts
+        *
+        * @v netdev    Network device
+        * @v enable    Interrupts should be enabled
+        */
+       void ( * irq ) ( struct net_device *netdev, int enable );
+};
+
+/** Network device statistics */
+struct net_device_stats {
+       /** Count of successfully completed transmissions */
+       unsigned int tx_ok;
+       /** Count of transmission errors */
+       unsigned int tx_err;
+       /** Count of successfully received packets */
+       unsigned int rx_ok;
+       /** Count of reception errors */
+       unsigned int rx_err;
+};
+
+/**
+ * A network device
+ *
+ * This structure represents a piece of networking hardware.  It has
+ * properties such as a link-layer address and methods for
+ * transmitting and receiving raw packets.
+ *
+ * Note that this structure must represent a generic network device,
+ * not just an Ethernet device.
+ */
+struct net_device {
+       /** Reference counter */
+       struct refcnt refcnt;
+       /** List of network devices */
+       struct list_head list;
+       /** Name of this network device */
+       char name[8];
+       /** Underlying hardware device */
+       struct device *dev;
+
+       /** Network device operations */
+       struct net_device_operations *op;
 
        /** Link-layer protocol */
        struct ll_protocol *ll_protocol;
@@ -248,6 +257,30 @@ struct net_device {
 #define __net_protocol __table ( struct net_protocol, net_protocols, 01 )
 
 extern struct list_head net_devices;
+extern struct net_device_operations null_netdev_operations;
+
+/**
+ * Initialise a network device
+ *
+ * @v netdev           Network device
+ * @v op               Network device operations
+ */
+static inline void netdev_init ( struct net_device *netdev,
+                                struct net_device_operations *op ) {
+       netdev->op = op;
+}
+
+/**
+ * Stop using a network device
+ *
+ * @v netdev           Network device
+ *
+ * Drivers should call this method immediately before the final call
+ * to netdev_put().
+ */
+static inline void netdev_nullify ( struct net_device *netdev ) {
+       netdev->op = &null_netdev_operations;
+}
 
 /**
  * Get printable network device hardware address
@@ -300,13 +333,14 @@ extern void netdev_tx_complete_next_err ( struct net_device *netdev, int rc );
 extern void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf );
 extern void netdev_rx_err ( struct net_device *netdev,
                            struct io_buffer *iobuf, int rc );
-extern int netdev_poll ( struct net_device *netdev, unsigned int rx_quota );
+extern void netdev_poll ( struct net_device *netdev );
 extern struct io_buffer * netdev_rx_dequeue ( struct net_device *netdev );
 extern struct net_device * alloc_netdev ( size_t priv_size );
 extern int register_netdev ( struct net_device *netdev );
 extern int netdev_open ( struct net_device *netdev );
 extern void netdev_close ( struct net_device *netdev );
 extern void unregister_netdev ( struct net_device *netdev );
+extern void netdev_irq ( struct net_device *netdev, int enable );
 extern struct net_device * find_netdev ( const char *name );
 extern struct net_device * find_netdev_by_location ( unsigned int bus_type,
                                                     unsigned int location );
index cc23de7..f19dfcc 100644 (file)
@@ -69,7 +69,13 @@ void pxe_set_netdev ( struct net_device *netdev ) {
  * @ret rc             Return status code
  */
 static int pxe_netdev_open ( void ) {
-       return netdev_open ( pxe_netdev );
+       int rc;
+
+       if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
+               return rc;
+
+       netdev_irq ( pxe_netdev, 1 );
+       return 0;
 }
 
 /**
@@ -77,6 +83,7 @@ static int pxe_netdev_open ( void ) {
  *
  */
 static void pxe_netdev_close ( void ) {
+       netdev_irq ( pxe_netdev, 0 );
        netdev_close ( pxe_netdev );
        undi_tx_count = 0;
 }
@@ -543,14 +550,13 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
                /* Call poll().  This should acknowledge the device
                 * interrupt and queue up any received packet.
                 */
-               if ( netdev_poll ( pxe_netdev, -1U ) ) {
-                       /* Packet waiting in queue */
-                       DBG ( " OURS" );
-                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
-               } else {
-                       DBG ( " NOT_OURS" );
-                       undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
-               }
+               netdev_poll ( pxe_netdev );
+
+               /* Disable interrupts to avoid interrupt storm */
+               netdev_irq ( pxe_netdev, 0 );
+
+               /* Always say it was ours for the sake of simplicity */
+               undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
                break;
        case PXENV_UNDI_ISR_IN_PROCESS :
        case PXENV_UNDI_ISR_IN_GET_NEXT :
@@ -570,6 +576,8 @@ PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
                if ( ! iobuf ) {
                        /* No more packets remaining */
                        undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
+                       /* Re-enable interrupts */
+                       netdev_irq ( pxe_netdev, 1 );
                        break;
                }
 
index d72392c..460de89 100644 (file)
@@ -68,7 +68,7 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) {
                goto err;
        }
                
-       if ( ( rc = netdev->transmit ( netdev, iobuf ) ) != 0 )
+       if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 )
                goto err;
 
        return 0;
@@ -187,22 +187,18 @@ void netdev_rx_err ( struct net_device *netdev,
 }
 
 /**
- * Poll for packet on network device
+ * Poll for completed and received packets on network device
  *
  * @v netdev           Network device
- * @v rx_quota         Maximum number of packets to receive
- * @ret True           There are packets present in the receive queue
- * @ret False          There are no packets present in the receive queue
  *
- * Polls the network device for received packets.  Any received
- * packets will be added to the RX packet queue via netdev_rx().
+ * Polls the network device for completed transmissions and received
+ * packets.  Any received packets will be added to the RX packet queue
+ * via netdev_rx().
  */
-int netdev_poll ( struct net_device *netdev, unsigned int rx_quota ) {
+void netdev_poll ( struct net_device *netdev ) {
 
        if ( netdev->state & NETDEV_OPEN )
-               netdev->poll ( netdev, rx_quota );
-
-       return ( ! list_empty ( &netdev->rx_queue ) );
+               netdev->op->poll ( netdev );
 }
 
 /**
@@ -317,7 +313,7 @@ int netdev_open ( struct net_device *netdev ) {
        DBGC ( netdev, "NETDEV %p opening\n", netdev );
 
        /* Open the device */
-       if ( ( rc = netdev->open ( netdev ) ) != 0 )
+       if ( ( rc = netdev->op->open ( netdev ) ) != 0 )
                return rc;
 
        /* Mark as opened */
@@ -339,7 +335,7 @@ void netdev_close ( struct net_device *netdev ) {
        DBGC ( netdev, "NETDEV %p closing\n", netdev );
 
        /* Close the device */
-       netdev->close ( netdev );
+       netdev->op->close ( netdev );
 
        /* Flush TX and RX queues */
        netdev_tx_flush ( netdev );
@@ -367,6 +363,15 @@ void unregister_netdev ( struct net_device *netdev ) {
        DBGC ( netdev, "NETDEV %p unregistered\n", netdev );
 }
 
+/** Enable or disable interrupts
+ *
+ * @v netdev           Network device
+ * @v enable           Interrupts should be enabled
+ */
+void netdev_irq ( struct net_device *netdev, int enable ) {
+       netdev->op->irq ( netdev, enable );
+}
+
 /**
  * Get network device by name
  *
@@ -462,7 +467,7 @@ static void net_step ( struct process *process __unused ) {
        list_for_each_entry ( netdev, &net_devices, list ) {
 
                /* Poll for new packets */
-               netdev_poll ( netdev, -1U );
+               netdev_poll ( netdev );
 
                /* Process at most one received packet.  Give priority
                 * to getting packets out of the NIC over processing
diff --git a/src/net/nullnet.c b/src/net/nullnet.c
new file mode 100644 (file)
index 0000000..4b6ba4c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/netdevice.h>
+
+/** @file
+ *
+ * Null network device
+ *
+ */
+
+static int null_open ( struct net_device *netdev __unused ) {
+       return -ENODEV;
+};
+
+static void null_close ( struct net_device *netdev __unused ) {
+       /* Do nothing */
+};
+
+static int null_transmit ( struct net_device *netdev __unused,
+                          struct io_buffer *iobuf __unused ) {
+       return -ENODEV;
+};
+
+static void null_poll ( struct net_device *netdev __unused ) {
+       /* Do nothing */
+}
+
+static void null_irq ( struct net_device *netdev __unused,
+                      int enable __unused ) {
+       /* Do nothing */
+}
+
+struct net_device_operations null_netdev_operations = {
+       .open           = null_open,
+       .close          = null_close,
+       .transmit       = null_transmit,
+       .poll           = null_poll,
+       .irq            = null_irq,
+};