]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[netdevice] Add the concept of a network upper-layer driver
authorMichael Brown <mcb30@ipxe.org>
Sat, 4 Sep 2010 23:55:23 +0000 (00:55 +0100)
committerMichael Brown <mcb30@ipxe.org>
Sun, 5 Sep 2010 02:03:38 +0000 (03:03 +0100)
Add the concept of a network upper-layer driver, which can create
arbitrary devices on top of network devices.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/netdevice.h
src/net/netdevice.c

index f829327c54d05691e13435fbd19306f4e5f25b28..80bc1c6e2ce7a862ee5b0cbbb62eb6096321aef5 100644 (file)
@@ -344,6 +344,34 @@ struct net_device {
 /** Declare a network-layer protocol */
 #define __net_protocol __table_entry ( NET_PROTOCOLS, 01 )
 
+/** A network upper-layer driver */
+struct net_driver {
+       /** Name */
+       const char *name;
+       /** Probe device
+        *
+        * @v netdev            Network device
+        * @ret rc              Return status code
+        */
+       int ( * probe ) ( struct net_device *netdev );
+       /** Notify of device or link state change
+        *
+        * @v netdev            Network device
+        */
+       void ( * notify ) ( struct net_device *netdev );
+       /** Remove device
+        *
+        * @v netdev            Network device
+        */
+       void ( * remove ) ( struct net_device *netdev );
+};
+
+/** Network driver table */
+#define NET_DRIVERS __table ( struct net_driver, "net_drivers" )
+
+/** Declare a network driver */
+#define __net_driver __table_entry ( NET_DRIVERS, 01 )
+
 extern struct list_head net_devices;
 extern struct net_device_operations null_netdev_operations;
 extern struct settings_operations netdev_settings_operations;
@@ -451,27 +479,6 @@ netdev_settings_init ( struct net_device *netdev ) {
        netdev->settings.settings.op = &netdev_settings_operations;
 }
 
-/**
- * Mark network device as having link up
- *
- * @v netdev           Network device
- */
-static inline __attribute__ (( always_inline )) void
-netdev_link_up ( struct net_device *netdev ) {
-       netdev->link_rc = 0;
-}
-
-/**
- * Mark network device as having link down due to a specific error
- *
- * @v netdev           Network device
- * @v rc               Link status code
- */
-static inline __attribute__ (( always_inline )) void
-netdev_link_err ( struct net_device *netdev, int rc ) {
-       netdev->link_rc = rc;
-}
-
 /**
  * Check link state of network device
  *
@@ -505,6 +512,7 @@ netdev_irq_enabled ( struct net_device *netdev ) {
        return ( netdev->state & NETDEV_IRQ_ENABLED );
 }
 
+extern void netdev_link_err ( struct net_device *netdev, int rc );
 extern void netdev_link_down ( struct net_device *netdev );
 extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf );
 extern void netdev_tx_complete_err ( struct net_device *netdev,
@@ -554,4 +562,14 @@ static inline void netdev_tx_complete_next ( struct net_device *netdev ) {
        netdev_tx_complete_next_err ( netdev, 0 );
 }
 
+/**
+ * Mark network device as having link up
+ *
+ * @v netdev           Network device
+ */
+static inline __attribute__ (( always_inline )) void
+netdev_link_up ( struct net_device *netdev ) {
+       netdev_link_err ( netdev, 0 );
+}
+
 #endif /* _IPXE_NETDEVICE_H */
index 3fa966600b4a5dacc97d437f6e280f542f5f1269..51bd344b299c7dd50f593d1ea171a3237db3b682 100644 (file)
@@ -56,6 +56,39 @@ struct errortab netdev_errors[] __errortab = {
        __einfo_errortab ( EINFO_EUNKNOWN_LINK_STATUS ),
 };
 
+/**
+ * Notify drivers of network device or link state change
+ *
+ * @v netdev           Network device
+ */
+static void netdev_notify ( struct net_device *netdev ) {
+       struct net_driver *driver;
+
+       for_each_table_entry ( driver, NET_DRIVERS )
+               driver->notify ( netdev );
+}
+
+/**
+ * Mark network device as having a specific link state
+ *
+ * @v netdev           Network device
+ * @v rc               Link status code
+ */
+void netdev_link_err ( struct net_device *netdev, int rc ) {
+
+       /* Record link state */
+       netdev->link_rc = rc;
+       if ( netdev->link_rc == 0 ) {
+               DBGC ( netdev, "NETDEV %p link is up\n", netdev );
+       } else {
+               DBGC ( netdev, "NETDEV %p link is down: %s\n",
+                      netdev, strerror ( netdev->link_rc ) );
+       }
+
+       /* Notify drivers of link state change */
+       netdev_notify ( netdev );
+}
+
 /**
  * Mark network device as having link down
  *
@@ -68,7 +101,7 @@ void netdev_link_down ( struct net_device *netdev ) {
         */
        if ( ( netdev->link_rc == 0 ) ||
             ( netdev->link_rc == -EUNKNOWN_LINK_STATUS ) ) {
-               netdev->link_rc = -ENOTCONN;
+               netdev_link_err ( netdev, -ENOTCONN );
        }
 }
 
@@ -367,6 +400,7 @@ struct net_device * alloc_netdev ( size_t priv_size ) {
  */
 int register_netdev ( struct net_device *netdev ) {
        static unsigned int ifindex = 0;
+       struct net_driver *driver;
        int rc;
 
        /* Create device name */
@@ -376,22 +410,38 @@ int register_netdev ( struct net_device *netdev ) {
        /* Set initial link-layer address */
        netdev->ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
 
+       /* Add to device list */
+       netdev_get ( netdev );
+       list_add_tail ( &netdev->list, &net_devices );
+       DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
+              netdev, netdev->name, netdev->dev->name,
+              netdev_addr ( netdev ) );
+
        /* Register per-netdev configuration settings */
        if ( ( rc = register_settings ( netdev_settings ( netdev ),
                                        NULL ) ) != 0 ) {
                DBGC ( netdev, "NETDEV %p could not register settings: %s\n",
                       netdev, strerror ( rc ) );
-               return rc;
+               goto err_register_settings;
        }
 
-       /* Add to device list */
-       netdev_get ( netdev );
-       list_add_tail ( &netdev->list, &net_devices );
-       DBGC ( netdev, "NETDEV %p registered as %s (phys %s hwaddr %s)\n",
-              netdev, netdev->name, netdev->dev->name,
-              netdev_addr ( netdev ) );
+       /* Probe device */
+       for_each_table_entry ( driver, NET_DRIVERS ) {
+               if ( ( rc = driver->probe ( netdev ) ) != 0 ) {
+                       DBGC ( netdev, "NETDEV %p could not add %s device: "
+                              "%s\n", netdev, driver->name, strerror ( rc ) );
+                       goto err_probe;
+               }
+       }
 
        return 0;
+
+ err_probe:
+       for_each_table_entry_continue_reverse ( driver, NET_DRIVERS )
+               driver->remove ( netdev );
+       unregister_settings ( netdev_settings ( netdev ) );
+ err_register_settings:
+       return rc;
 }
 
 /**
@@ -419,6 +469,9 @@ int netdev_open ( struct net_device *netdev ) {
        /* Add to head of open devices list */
        list_add ( &netdev->open_list, &open_net_devices );
 
+       /* Notify drivers of device state change */
+       netdev_notify ( netdev );
+
        return 0;
 }
 
@@ -435,18 +488,21 @@ void netdev_close ( struct net_device *netdev ) {
 
        DBGC ( netdev, "NETDEV %p closing\n", netdev );
 
+       /* Remove from open devices list */
+       list_del ( &netdev->open_list );
+
+       /* Mark as closed */
+       netdev->state &= ~NETDEV_OPEN;
+
+       /* Notify drivers of device state change */
+       netdev_notify ( netdev );
+
        /* Close the device */
        netdev->op->close ( netdev );
 
        /* Flush TX and RX queues */
        netdev_tx_flush ( netdev );
        netdev_rx_flush ( netdev );
-
-       /* Mark as closed */
-       netdev->state &= ~NETDEV_OPEN;
-
-       /* Remove from open devices list */
-       list_del ( &netdev->open_list );
 }
 
 /**
@@ -457,10 +513,15 @@ void netdev_close ( struct net_device *netdev ) {
  * Removes the network device from the list of network devices.
  */
 void unregister_netdev ( struct net_device *netdev ) {
+       struct net_driver *driver;
 
        /* Ensure device is closed */
        netdev_close ( netdev );
 
+       /* Remove device */
+       for_each_table_entry_reverse ( driver, NET_DRIVERS )
+               driver->remove ( netdev );
+
        /* Unregister per-netdev configuration settings */
        unregister_settings ( netdev_settings ( netdev ) );