/** 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;
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
*
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,
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 */
__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
*
*/
if ( ( netdev->link_rc == 0 ) ||
( netdev->link_rc == -EUNKNOWN_LINK_STATUS ) ) {
- netdev->link_rc = -ENOTCONN;
+ netdev_link_err ( netdev, -ENOTCONN );
}
}
*/
int register_netdev ( struct net_device *netdev ) {
static unsigned int ifindex = 0;
+ struct net_driver *driver;
int rc;
/* Create device name */
/* 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;
}
/**
/* 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;
}
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 );
}
/**
* 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 ) );