diff --git a/src/hci/strerror.c b/src/hci/strerror.c index 4b16608c..216b1e57 100644 --- a/src/hci/strerror.c +++ b/src/hci/strerror.c @@ -115,6 +115,7 @@ struct errortab common_errors[] __errortab = { { ENOEXEC, "Not an executable image" }, { ENOMEM, "Out of memory" }, { ENOSPC, "No space left on device" }, + { ENOTCONN, "Not connected" }, { ENOTSUP, "Not supported" }, { EPERM, "Operation not permitted" }, { ERANGE, "Out of range" }, diff --git a/src/include/gpxe/netdevice.h b/src/include/gpxe/netdevice.h index 43c5aa31..70644af1 100644 --- a/src/include/gpxe/netdevice.h +++ b/src/include/gpxe/netdevice.h @@ -267,6 +267,12 @@ struct net_device { * This is the bitwise-OR of zero or more NETDEV_XXX constants. */ unsigned int state; + /** Link status code + * + * Zero indicates that the link is up; any other value + * indicates the error preventing link-up. + */ + int link_rc; /** Maximum packet length * * This length includes any link-layer headers. @@ -291,9 +297,6 @@ struct net_device { /** Network device is open */ #define NETDEV_OPEN 0x0001 -/** Network device has link */ -#define NETDEV_LINK_UP 0x0002 - /** Link-layer protocol table */ #define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" ) @@ -420,17 +423,18 @@ netdev_settings_init ( struct net_device *netdev ) { */ static inline __attribute__ (( always_inline )) void netdev_link_up ( struct net_device *netdev ) { - netdev->state |= NETDEV_LINK_UP; + netdev->link_rc = 0; } /** - * Mark network device as having link down + * 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_down ( struct net_device *netdev ) { - netdev->state &= ~NETDEV_LINK_UP; +netdev_link_err ( struct net_device *netdev, int rc ) { + netdev->link_rc = rc; } /** @@ -441,9 +445,10 @@ netdev_link_down ( struct net_device *netdev ) { */ static inline __attribute__ (( always_inline )) int netdev_link_ok ( struct net_device *netdev ) { - return ( netdev->state & NETDEV_LINK_UP ); + return ( netdev->link_rc == 0 ); } +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, struct io_buffer *iobuf, int rc ); diff --git a/src/net/netdevice.c b/src/net/netdevice.c index c3551ea4..e16ebaa0 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include /** @file @@ -44,6 +45,34 @@ struct list_head net_devices = LIST_HEAD_INIT ( net_devices ); /** List of open network devices, in reverse order of opening */ struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices ); +/** Default link status code */ +#define EUNKNOWN_LINK_STATUS EINPROGRESS + +/** Human-readable message for the default link status */ +struct errortab netdev_errors[] __errortab = { + { EUNKNOWN_LINK_STATUS, "Unknown" }, +}; + +/** + * Mark network device as having link down + * + * @v netdev Network device + */ +void netdev_link_down ( struct net_device *netdev ) { + + switch ( netdev->link_rc ) { + case 0: + case -EUNKNOWN_LINK_STATUS: + netdev->link_rc = -ENOTCONN; + break; + default: + /* Avoid clobbering a more detailed link status code, + * if one is already set. + */ + break; + } +} + /** * Record network device statistic * @@ -302,6 +331,7 @@ struct net_device * alloc_netdev ( size_t priv_size ) { netdev = zalloc ( total_len ); if ( netdev ) { netdev->refcnt.free = free_netdev; + netdev->link_rc = -EUNKNOWN_LINK_STATUS; INIT_LIST_HEAD ( &netdev->tx_queue ); INIT_LIST_HEAD ( &netdev->rx_queue ); netdev_settings_init ( netdev ); diff --git a/src/usr/ifmgmt.c b/src/usr/ifmgmt.c index 9c82503a..2c4b3d24 100644 --- a/src/usr/ifmgmt.c +++ b/src/usr/ifmgmt.c @@ -94,6 +94,10 @@ void ifstat ( struct net_device *netdev ) { ( netdev_link_ok ( netdev ) ? "up" : "down" ), netdev->tx_stats.good, netdev->tx_stats.bad, netdev->rx_stats.good, netdev->rx_stats.bad ); + if ( ! netdev_link_ok ( netdev ) ) { + printf ( " [Link status: %s]\n", + strerror ( netdev->link_rc ) ); + } ifstat_errors ( &netdev->tx_stats, "TXE" ); ifstat_errors ( &netdev->rx_stats, "RXE" ); }