From 9206bb37797df1a6448d3f1c336e954b4ddb790d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 4 Apr 2007 09:41:04 +0000 Subject: [PATCH] * Changed ETHER_GET_LINK_STATE ethernet driver interface, added ETHER_SET_LINK_STATE_SEM. * The device interface list now uses class DoublyLinkedList instead of struct list. * Implemented SIOC[SG]IFMEDIA for setting (not supported by any device yet), and retrieving the device media information. * Fixed a locking bug in list_domain_interfaces(). * Added new stack function device_link_changed() that should be called in case the link state (media) changed. * The ethernet device module now spawns a thread and will periodically check the media state of all ethernet devices that support this (if any). * Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20546 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/net/ether_driver.h | 14 +- headers/private/net/net_device.h | 10 +- headers/private/net/net_stack.h | 3 +- .../kernel/drivers/network/bcm440x/b44um.c | 35 +++-- .../kernel/drivers/network/bcm570x/b57um.c | 27 ++-- .../kernel/drivers/network/ipro1000/device.c | 8 +- .../kernel/drivers/network/sis900/device.c | 8 +- .../network/devices/ethernet/ethernet.cpp | 145 +++++++++++++++++- src/add-ons/kernel/network/stack/datalink.cpp | 58 +++++-- src/add-ons/kernel/network/stack/domains.cpp | 50 +++++- src/add-ons/kernel/network/stack/domains.h | 1 + .../kernel/network/stack/interfaces.cpp | 63 ++++---- src/add-ons/kernel/network/stack/interfaces.h | 7 +- src/add-ons/kernel/network/stack/stack.cpp | 1 + 14 files changed, 330 insertions(+), 100 deletions(-) diff --git a/headers/private/net/ether_driver.h b/headers/private/net/ether_driver.h index f988c5aa7a..c3c07d12c3 100644 --- a/headers/private/net/ether_driver.h +++ b/headers/private/net/ether_driver.h @@ -21,21 +21,23 @@ enum { ETHER_REMMULTI, /* remove multicast address */ ETHER_SETPROMISC, /* set promiscuous mode (int *) */ ETHER_GETFRAMESIZE, /* get frame size (required) (int *) */ - ETHER_GETLINKSTATE + ETHER_SET_LINK_STATE_SEM, + /* pass over a semaphore to release on link state changes (sem_id *) */ + ETHER_GET_LINK_STATE /* get line speed, quality, duplex mode, etc. (ether_link_state_t *) */ }; /* ETHER_GETADDR - MAC address */ -typedef struct { - uint8 ebyte[6]; +typedef struct ether_address { + uint8 ebyte[6]; } ether_address_t; /* ETHER_GETLINKSTATE */ typedef struct ether_link_state { - uint32 link_media; /* as specified in net/if_media.h */ - uint32 link_quality; /* in one tenth of a percent */ - uint64 link_speed; /* in Kbit/s */ + uint32 media; /* as specified in net/if_media.h */ + uint32 quality; /* in one tenth of a percent */ + uint64 speed; /* in Kbit/s */ } ether_link_state_t; #endif /* _ETHER_DRIVER_H */ diff --git a/headers/private/net/net_device.h b/headers/private/net/net_device.h index 46903cb3f3..be919861bb 100644 --- a/headers/private/net/net_device.h +++ b/headers/private/net/net_device.h @@ -1,5 +1,5 @@ /* - * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef NET_DEVICE_H @@ -25,6 +25,8 @@ struct net_device { uint32 type; // IFT_ETHER, ... size_t mtu; uint32 media; + uint64 link_speed; + uint32 link_quality; size_t header_length; struct net_hardware_address address; @@ -44,8 +46,10 @@ struct net_device_module_info { status_t (*control)(struct net_device *device, int32 op, void *argument, size_t length); - status_t (*send_data)(struct net_device *device, struct net_buffer *buffer); - status_t (*receive_data)(struct net_device *device, struct net_buffer **_buffer); + status_t (*send_data)(struct net_device *device, + struct net_buffer *buffer); + status_t (*receive_data)(struct net_device *device, + struct net_buffer **_buffer); status_t (*set_mtu)(struct net_device *device, size_t mtu); status_t (*set_promiscuous)(struct net_device *device, bool promiscuous); diff --git a/headers/private/net/net_stack.h b/headers/private/net/net_stack.h index 5fbd693fd6..97662eb4b7 100644 --- a/headers/private/net/net_stack.h +++ b/headers/private/net/net_stack.h @@ -1,5 +1,5 @@ /* - * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef NET_STACK_H @@ -72,6 +72,7 @@ struct net_stack_module_info { status_t (*unregister_device_monitor)(struct net_device *device, net_receive_func receiveFunc, void *cookie); + status_t (*device_link_changed)(struct net_device *device); status_t (*device_removed)(struct net_device *device); // Utility Functions diff --git a/src/add-ons/kernel/drivers/network/bcm440x/b44um.c b/src/add-ons/kernel/drivers/network/bcm440x/b44um.c index 9dcad44325..b8d36a4867 100644 --- a/src/add-ons/kernel/drivers/network/bcm440x/b44um.c +++ b/src/add-ons/kernel/drivers/network/bcm440x/b44um.c @@ -1,5 +1,5 @@ /* - * Copyright 2006, Nathan Whitehorn. + * Copyright 2006-2007, Nathan Whitehorn. * Distributed under the terms of the GPL License. */ @@ -237,40 +237,45 @@ b44_ioctl(void *cookie,uint32 op,void *data,size_t len) case ETHER_REMMULTI: return (b44_LM_MulticastDel(&pUmDevice->lm_dev,(PLM_UINT8)(data)) == LM_STATUS_SUCCESS) ? B_OK : B_ERROR; case ETHER_SETPROMISC: - if (*((uint8 *)(data))) + if (*((uint8 *)(data))) { b44_LM_SetReceiveMask(&pUmDevice->lm_dev, pUmDevice->lm_dev.ReceiveMask | LM_PROMISCUOUS_MODE); - else + } else { b44_LM_SetReceiveMask(&pUmDevice->lm_dev, pUmDevice->lm_dev.ReceiveMask & ~LM_PROMISCUOUS_MODE); + } return B_OK; #ifndef HAIKU_TARGET_PLATFORM_HAIKU - case ETHER_GETLINKSTATE: { + case ETHER_GETLINKSTATE: + { ether_link_state_t *state_buffer = (ether_link_state_t *)(data); state_buffer->link_speed = (pUmDevice->lm_dev.LineSpeed == LM_LINE_SPEED_10MBPS) ? 10 : 100; state_buffer->link_quality = (pUmDevice->lm_dev.LinkStatus == LM_STATUS_LINK_DOWN) ? 0.0 : 1.0; state_buffer->duplex_mode = (pUmDevice->lm_dev.DuplexMode == LM_DUPLEX_MODE_FULL); - } return B_OK; + return B_OK; + } #else - case ETHER_GETLINKSTATE: + case ETHER_GET_LINK_STATE: { ether_link_state_t state; - state.link_media = (pUmDevice->lm_dev.LinkStatus == LM_STATUS_LINK_DOWN) ? IFM_ACTIVE : 0; + state.media = (pUmDevice->lm_dev.LinkStatus + == LM_STATUS_LINK_DOWN) ? IFM_ACTIVE : 0; switch (pUmDevice->lm_dev.LineSpeed) { case LM_LINE_SPEED_10MBPS: - state.link_media |= IFM_10_T; - state.link_speed = 10000; + state.media |= IFM_10_T; + state.speed = 10000; break; case LM_LINE_SPEED_100MBPS: - state.link_media |= IFM_100_TX; - state.link_speed = 100000; + state.media |= IFM_100_TX; + state.speed = 100000; break; default: - state.link_speed = 0; + state.speed = 0; } - state.link_media |= (pUmDevice->lm_dev.DuplexMode == LM_DUPLEX_MODE_FULL ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX); - state.link_quality = 1000; - + state.media |= (pUmDevice->lm_dev.DuplexMode + == LM_DUPLEX_MODE_FULL ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX); + state.quality = 1000; + return user_memcpy(data, &state, sizeof(ether_link_state_t)); } #endif diff --git a/src/add-ons/kernel/drivers/network/bcm570x/b57um.c b/src/add-ons/kernel/drivers/network/bcm570x/b57um.c index f66bbc73b3..6146d99851 100644 --- a/src/add-ons/kernel/drivers/network/bcm570x/b57um.c +++ b/src/add-ons/kernel/drivers/network/bcm570x/b57um.c @@ -482,7 +482,8 @@ b57_ioctl(void *cookie,uint32 op,void *data,size_t len) } return B_OK; #ifndef HAIKU_TARGET_PLATFORM_HAIKU - case ETHER_GETLINKSTATE: { + case ETHER_GETLINKSTATE: + { ether_link_state_t *state_buffer = (ether_link_state_t *)(data); state_buffer->link_speed = pUmDevice->lm_dev.LineSpeed; state_buffer->link_quality = (pUmDevice->lm_dev.LinkStatus == LM_STATUS_LINK_DOWN) ? 0.0 : 1.0; @@ -490,28 +491,30 @@ b57_ioctl(void *cookie,uint32 op,void *data,size_t len) return B_OK; } #else - case ETHER_GETLINKSTATE: + case ETHER_GET_LINK_STATE: { ether_link_state_t state; - state.link_media = (pUmDevice->lm_dev.LinkStatus == LM_STATUS_LINK_DOWN) ? IFM_ACTIVE : 0; + state.media = (pUmDevice->lm_dev.LinkStatus + == LM_STATUS_LINK_DOWN) ? IFM_ACTIVE : 0; switch (pUmDevice->lm_dev.LineSpeed) { case LM_LINE_SPEED_10MBPS: - state.link_media |= IFM_10_T; - state.link_speed = 10000; + state.media |= IFM_10_T; + state.speed = 10000; break; case LM_LINE_SPEED_100MBPS: - state.link_media |= IFM_100_TX; - state.link_speed = 100000; + state.media |= IFM_100_TX; + state.speed = 100000; break; case LM_LINE_SPEED_1000MBPS: - state.link_media |= IFM_1000_T; - state.link_speed = 1000000; + state.media |= IFM_1000_T; + state.speed = 1000000; break; default: - state.link_speed = 0; + state.speed = 0; } - state.link_media |= (pUmDevice->lm_dev.DuplexMode == LM_DUPLEX_MODE_FULL ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX); - state.link_quality = 1000; + state.media |= (pUmDevice->lm_dev.DuplexMode + == LM_DUPLEX_MODE_FULL ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX); + state.quality = 1000; return user_memcpy(data, &state, sizeof(ether_link_state_t)); } diff --git a/src/add-ons/kernel/drivers/network/ipro1000/device.c b/src/add-ons/kernel/drivers/network/ipro1000/device.c index 1df1aeec15..21ec25c967 100644 --- a/src/add-ons/kernel/drivers/network/ipro1000/device.c +++ b/src/add-ons/kernel/drivers/network/ipro1000/device.c @@ -339,15 +339,15 @@ ipro1000_control(void *cookie, uint32 op, void *arg, size_t len) #ifdef HAIKU_TARGET_PLATFORM_HAIKU #if 0 - case ETHER_GETLINKSTATE: + case ETHER_GET_LINK_STATE: { ether_link_state_t state; - state.link_media = (info->link ? IFM_ACTIVE : 0) + state.media = (info->link ? IFM_ACTIVE : 0) | (info->full_duplex ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX) | (info->speed == LINK_SPEED_100_MBIT ? IFM_100_TX : IFM_10_T); - state.link_speed = info->speed == LINK_SPEED_100_MBIT + state.speed = info->speed == LINK_SPEED_100_MBIT ? 100000 : 10000; - state.link_quality = 1000; + state.quality = 1000; return user_memcpy(buffer, &state, sizeof(ether_link_state_t)); } diff --git a/src/add-ons/kernel/drivers/network/sis900/device.c b/src/add-ons/kernel/drivers/network/sis900/device.c index 328b3b9f78..e616435fa2 100644 --- a/src/add-ons/kernel/drivers/network/sis900/device.c +++ b/src/add-ons/kernel/drivers/network/sis900/device.c @@ -387,15 +387,15 @@ device_ioctl(void *data, uint32 msg, void *buffer, size_t bufferLength) break; #ifdef HAIKU_TARGET_PLATFORM_HAIKU - case ETHER_GETLINKSTATE: + case ETHER_GET_LINK_STATE: { ether_link_state_t state; - state.link_media = (info->link ? IFM_ACTIVE : 0) + state.media = (info->link ? IFM_ACTIVE : 0) | (info->full_duplex ? IFM_FULL_DUPLEX : IFM_HALF_DUPLEX) | (info->speed == LINK_SPEED_100_MBIT ? IFM_100_TX : IFM_10_T); - state.link_speed = info->speed == LINK_SPEED_100_MBIT + state.speed = info->speed == LINK_SPEED_100_MBIT ? 100000 : 10000; - state.link_quality = 1000; + state.quality = 1000; return user_memcpy(buffer, &state, sizeof(ether_link_state_t)); } diff --git a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp index 67933a6688..206b2805e0 100644 --- a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp +++ b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp @@ -7,10 +7,16 @@ */ -#include #include + +#include #include #include +#include + +#include +#include +#include #include @@ -24,12 +30,76 @@ #include -struct ethernet_device : net_device { +struct ethernet_device : DoublyLinkedListLinkImpl, net_device { int fd; uint32 frame_size; }; -struct net_buffer_module_info *gBufferModule; +static const bigtime_t kLinkCheckInterval = 1000000; + // 1 second + +net_buffer_module_info *gBufferModule; +static net_stack_module_info *sStackModule; + +static benaphore sListLock; +static DoublyLinkedList sCheckList; +static sem_id sLinkChangeSemaphore; +static thread_id sLinkCheckerThread; + + +static status_t +update_link_state(ethernet_device *device, bool notify = true) +{ + ether_link_state state; + if (ioctl(device->fd, ETHER_GET_LINK_STATE, &state, + sizeof(ether_link_state)) < 0) { + // This device does not support retrieving the link + return EOPNOTSUPP; + } + + if (device->media != state.media + || device->link_quality != state.quality + || device->link_speed != state.speed) { + device->media = state.media; + device->link_quality = state.quality; + device->link_speed = state.speed; + + if (notify) + sStackModule->device_link_changed(device); + } + + return B_OK; +} + + +static status_t +ethernet_link_checker(void *) +{ + while (true) { + status_t status = acquire_sem_etc(sLinkChangeSemaphore, 1, + B_RELATIVE_TIMEOUT, kLinkCheckInterval); + if (status == B_BAD_SEM_ID) + break; + + BenaphoreLocker _(sListLock); + + if (sCheckList.IsEmpty()) + break; + + // check link state of all existing devices + + DoublyLinkedList::Iterator iterator + = sCheckList.GetIterator(); + while (iterator.HasNext()) { + update_link_state(iterator.Next()); + } + } + + return B_OK; +} + + +// #pragma mark - status_t @@ -93,6 +163,27 @@ ethernet_up(net_device *_device) device->frame_size = ETHER_MAX_FRAME_SIZE; } + if (update_link_state(device, false) == B_OK) { + // device supports retrieval of the link state + + // Set the change notification semaphore; doesn't matter + // if this is supported by the device or not + ioctl(device->fd, ETHER_SET_LINK_STATE_SEM, &sLinkChangeSemaphore, + sizeof(sem_id *)); + + BenaphoreLocker _(&sListLock); + + if (sCheckList.IsEmpty()) { + // start thread + sLinkCheckerThread = spawn_kernel_thread(ethernet_link_checker, + "ethernet link state checker", B_LOW_PRIORITY, NULL); + if (sLinkCheckerThread >= B_OK) + resume_thread(sLinkCheckerThread); + } + + sCheckList.Add(device); + } + device->address.length = ETHER_ADDRESS_LENGTH; device->mtu = device->frame_size - device->header_length; return B_OK; @@ -108,6 +199,14 @@ void ethernet_down(net_device *_device) { ethernet_device *device = (ethernet_device *)_device; + + BenaphoreLocker _(sListLock); + + // if the device is still part of the list, remove it + if (device->GetDoublyLinkedListLink()->next != NULL + || device->GetDoublyLinkedListLink()->previous != NULL) + sCheckList.Remove(device); + close(device->fd); } @@ -262,6 +361,7 @@ status_t ethernet_get_multicast_addrs(struct net_device *device, net_hardware_address **addressArray, uint32 count) { + // TODO: see etherpci driver for details return EOPNOTSUPP; } @@ -270,6 +370,7 @@ status_t ethernet_set_multicast_addrs(struct net_device *device, const net_hardware_address **addressArray, uint32 count) { + // TODO: see etherpci driver for details return EOPNOTSUPP; } @@ -279,8 +380,44 @@ ethernet_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: - case B_MODULE_UNINIT: + { + status_t status = get_module(NET_STACK_MODULE_NAME, + (module_info **)&sStackModule); + if (status < B_OK) + return status; + + new (&sCheckList) DoublyLinkedList; + // static C++ objects are not initialized in the module startup + + sLinkCheckerThread = -1; + + sLinkChangeSemaphore = create_sem(0, "ethernet link change"); + if (sLinkChangeSemaphore < B_OK) { + put_module(NET_STACK_MODULE_NAME); + return sLinkChangeSemaphore; + } + + status = benaphore_init(&sListLock, "ethernet devices"); + if (status < B_OK) { + put_module(NET_STACK_MODULE_NAME); + delete_sem(sLinkChangeSemaphore); + return status; + } + return B_OK; + } + + case B_MODULE_UNINIT: + { + delete_sem(sLinkChangeSemaphore); + + status_t status; + wait_for_thread(sLinkCheckerThread, &status); + + benaphore_destroy(&sListLock); + put_module(NET_STACK_MODULE_NAME); + return B_OK; + } default: return B_ERROR; diff --git a/src/add-ons/kernel/network/stack/datalink.cpp b/src/add-ons/kernel/network/stack/datalink.cpp index 9b6e45e262..ce92a1f043 100644 --- a/src/add-ons/kernel/network/stack/datalink.cpp +++ b/src/add-ons/kernel/network/stack/datalink.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -54,7 +55,8 @@ device_reader_thread(void *_interface) // feed device monitors // TODO: locking! - DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator(); + DeviceMonitorList::Iterator iterator + = interface->monitor_funcs.GetIterator(); while (iterator.HasNext()) { net_device_monitor *monitor = iterator.Next(); monitor->func(monitor->cookie, buffer); @@ -64,7 +66,8 @@ device_reader_thread(void *_interface) if (type >= 0) { // find handler for this packet // TODO: locking! - DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator(); + DeviceHandlerList::Iterator iterator + = interface->receive_funcs.GetIterator(); status = B_ERROR; while (iterator.HasNext()) { @@ -135,7 +138,8 @@ remove_default_routes(net_interface_private *interface, int32 option) route.gateway = NULL; route.interface = interface; - if (interface->mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) { + if (interface->mask != NULL + && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) { route.mask = interface->mask; route.flags = 0; remove_route(interface->domain, &route); @@ -157,7 +161,8 @@ add_default_routes(net_interface_private *interface, int32 option) route.gateway = NULL; route.interface = interface; - if (interface->mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) { + if (interface->mask != NULL + && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) { route.mask = interface->mask; route.flags = 0; add_route(interface->domain, &route); @@ -324,16 +329,22 @@ datalink_control(net_domain *_domain, int32 option, void *value, if (interface != NULL) { // filter out bringing the interface up or down if (option == SIOCSIFFLAGS - && ((uint32)request.ifr_flags & IFF_UP) != (interface->flags & IFF_UP)) { + && ((uint32)request.ifr_flags & IFF_UP) + != (interface->flags & IFF_UP)) { if ((interface->flags & IFF_UP) != 0) { // bring the interface down - interface->flags &= ~IFF_UP; - interface->first_info->interface_down(interface->first_protocol); + interface->flags &= ~(IFF_UP | IFF_LINK); + interface->first_info->interface_down( + interface->first_protocol); } else { // bring it up - status = interface->first_info->interface_up(interface->first_protocol); - if (status == B_OK) - interface->flags |= IFF_UP; + status = interface->first_info->interface_up( + interface->first_protocol); + if (status == B_OK) { + interface->flags |= IFF_UP + | (interface->device->media & IFM_ACTIVE + ? IFF_LINK : 0); + } } request.ifr_flags = interface->flags; @@ -341,8 +352,8 @@ datalink_control(net_domain *_domain, int32 option, void *value, if (status == B_OK) { // pass the request into the datalink protocol stack - status = interface->first_info->control(interface->first_protocol, - option, value, *_length); + status = interface->first_info->control( + interface->first_protocol, option, value, *_length); } } else status = B_BAD_VALUE; @@ -704,13 +715,34 @@ interface_protocol_control(net_datalink_protocol *_protocol, return B_BAD_ADDRESS; // check for valid bounds - if (request.ifr_mtu < 100 || (uint32)request.ifr_mtu > interface->device->mtu) + if (request.ifr_mtu < 100 + || (uint32)request.ifr_mtu > interface->device->mtu) return B_BAD_VALUE; interface->mtu = request.ifr_mtu; return B_OK; } + case SIOCSIFMEDIA: + { + // set media + struct ifreq request; + if (user_memcpy(&request, argument, sizeof(struct ifreq)) < B_OK) + return B_BAD_ADDRESS; + + return interface->device_interface->module->set_media( + interface->device, request.ifr_media); + } + case SIOCGIFMEDIA: + { + // get media + struct ifreq request; + request.ifr_media = interface->device->media; + + return user_memcpy(&((struct ifreq *)argument)->ifr_media, + &request.ifr_media, sizeof(request.ifr_media)); + } + case SIOCGIFMETRIC: { // get metric diff --git a/src/add-ons/kernel/network/stack/domains.cpp b/src/add-ons/kernel/network/stack/domains.cpp index 26606e2676..8781bb4d11 100644 --- a/src/add-ons/kernel/network/stack/domains.cpp +++ b/src/add-ons/kernel/network/stack/domains.cpp @@ -11,11 +11,14 @@ #include "interfaces.h" #include "utility.h" -#include +#include #include #include +#include + +#include #include #include @@ -112,6 +115,8 @@ list_domain_interfaces(void *_buffer, size_t *bufferSize) if (domain == NULL) break; + BenaphoreLocker locker(domain->lock); + net_interface *interface = NULL; while (true) { interface = (net_interface *)list_get_next_item(&domain->interfaces, @@ -121,17 +126,18 @@ list_domain_interfaces(void *_buffer, size_t *bufferSize) ifreq request; strlcpy(request.ifr_name, interface->name, IF_NAMESIZE); - if (interface->address != NULL) - memcpy(&request.ifr_addr, interface->address, interface->address->sa_len); - else { + if (interface->address != NULL) { + memcpy(&request.ifr_addr, interface->address, + interface->address->sa_len); + } else { // empty address request.ifr_addr.sa_len = 2; request.ifr_addr.sa_family = AF_UNSPEC; } if (buffer.Copy(&request, IF_NAMESIZE - + request.ifr_addr.sa_len) == NULL) - return buffer.Status(); + + request.ifr_addr.sa_len) == NULL) + return buffer.Status(); } } @@ -187,6 +193,38 @@ remove_interface_from_domain(net_interface *interface) } +void +domain_interfaces_link_changed(net_device *device) +{ + // TODO: notify listeners about this! + + BenaphoreLocker locker(sDomainLock); + + net_domain_private *domain = NULL; + while (true) { + domain = (net_domain_private *)list_get_next_item(&sDomains, domain); + if (domain == NULL) + break; + + BenaphoreLocker locker(domain->lock); + + net_interface *interface = NULL; + while (true) { + interface = (net_interface *)list_get_next_item(&domain->interfaces, + interface); + if (interface == NULL) + break; + + if (interface->device == device) { + // update IFF_LINK flag + interface->flags = (interface->flags & ~IFF_LINK) + | (device->media & IFM_ACTIVE ? IFF_LINK : 0); + } + } + } +} + + status_t register_domain(int family, const char *name, struct net_protocol_module_info *module, diff --git a/src/add-ons/kernel/network/stack/domains.h b/src/add-ons/kernel/network/stack/domains.h index 6b9d43d751..297cc8f201 100644 --- a/src/add-ons/kernel/network/stack/domains.h +++ b/src/add-ons/kernel/network/stack/domains.h @@ -33,6 +33,7 @@ uint32 count_domain_interfaces(); status_t list_domain_interfaces(void *buffer, size_t *_bufferSize); status_t add_interface_to_domain(net_domain *domain, struct ifreq& request); status_t remove_interface_from_domain(net_interface *interface); +void domain_interfaces_link_changed(net_device *device); net_domain *get_domain(int family); status_t register_domain(int family, const char *name, diff --git a/src/add-ons/kernel/network/stack/interfaces.cpp b/src/add-ons/kernel/network/stack/interfaces.cpp index c01d064525..afc8933938 100644 --- a/src/add-ons/kernel/network/stack/interfaces.cpp +++ b/src/add-ons/kernel/network/stack/interfaces.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Copyright 2006-2007, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: @@ -34,7 +34,7 @@ static benaphore sInterfaceLock; -static list sInterfaces; +static DeviceInterfaceList sInterfaces; static uint32 sInterfaceIndex; static uint32 sDeviceIndex; @@ -42,12 +42,10 @@ static uint32 sDeviceIndex; static net_device_interface * find_device_interface(const char *name) { - net_device_interface *interface = NULL; + DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); - while (true) { - interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface); - if (interface == NULL) - break; + while (iterator.HasNext()) { + net_device_interface *interface = iterator.Next(); if (!strcmp(interface->name, name)) return interface; @@ -237,15 +235,11 @@ count_device_interfaces() { BenaphoreLocker locker(sInterfaceLock); - net_device_interface *interface = NULL; + DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); uint32 count = 0; - while (true) { - interface = (net_device_interface *)list_get_next_item(&sInterfaces, - interface); - if (interface == NULL) - break; - + while (iterator.HasNext()) { + iterator.Next(); count++; } @@ -263,22 +257,19 @@ list_device_interfaces(void *_buffer, size_t *bufferSize) { BenaphoreLocker locker(sInterfaceLock); + DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); UserBuffer buffer(_buffer, *bufferSize); - net_device_interface *interface = NULL; - while (true) { - interface = (net_device_interface *)list_get_next_item(&sInterfaces, - interface); - if (interface == NULL) - break; + while (iterator.HasNext()) { + net_device_interface *interface = iterator.Next(); ifreq request; strlcpy(request.ifr_name, interface->name, IF_NAMESIZE); get_device_interface_address(interface, &request.ifr_addr); if (buffer.Copy(&request, IF_NAMESIZE - + request.ifr_addr.sa_len) == NULL) - return buffer.Status(); + + request.ifr_addr.sa_len) == NULL) + return buffer.Status(); } *bufferSize = buffer.ConsumedAmount(); @@ -300,7 +291,7 @@ put_device_interface(struct net_device_interface *interface) { BenaphoreLocker locker(sInterfaceLock); - list_remove_item(&sInterfaces, interface); + sInterfaces.Remove(interface); } interface->module->uninit_device(interface->device); @@ -315,12 +306,10 @@ struct net_device_interface * get_device_interface(uint32 index) { BenaphoreLocker locker(sInterfaceLock); - net_device_interface *interface = NULL; + DeviceInterfaceList::Iterator iterator = sInterfaces.GetIterator(); - while (true) { - interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface); - if (interface == NULL) - break; + while (iterator.HasNext()) { + net_device_interface *interface = iterator.Next(); if (interface->device->index == index) { if (atomic_add(&interface->ref_count, 1) != 0) @@ -380,7 +369,7 @@ get_device_interface(const char *name) device->index = ++sDeviceIndex; device->module = module; - list_add_item(&sInterfaces, interface); + sInterfaces.Add(interface); return interface; } else module->uninit_device(device); @@ -589,6 +578,19 @@ unregister_device_monitor(struct net_device *device, } +/*! + This function is called by device modules in case their link + state changed, ie. if an ethernet cable was plugged in or + removed. +*/ +status_t +device_link_changed(net_device *device) +{ + domain_interfaces_link_changed(device); + return B_OK; +} + + /*! This function is called by device modules once their device got physically removed, ie. a USB networking card is unplugged. @@ -612,7 +614,8 @@ init_interfaces() if (benaphore_init(&sInterfaceLock, "net interfaces") < B_OK) return B_ERROR; - list_init(&sInterfaces); + new (&sInterfaces) DeviceInterfaceList; + // static C++ objects are not initialized in the module startup return B_OK; } diff --git a/src/add-ons/kernel/network/stack/interfaces.h b/src/add-ons/kernel/network/stack/interfaces.h index e801ded8a9..e7571a06e4 100644 --- a/src/add-ons/kernel/network/stack/interfaces.h +++ b/src/add-ons/kernel/network/stack/interfaces.h @@ -29,8 +29,8 @@ struct net_device_monitor : public DoublyLinkedListLinkImpl typedef DoublyLinkedList DeviceHandlerList; typedef DoublyLinkedList DeviceMonitorList; -struct net_device_interface { - struct list_link link; +struct net_device_interface : DoublyLinkedListLinkImpl { + //struct list_link link; const char *name; struct net_device_module_info *module; struct net_device *device; @@ -46,6 +46,8 @@ struct net_device_interface { DeviceHandlerList receive_funcs; }; +typedef DoublyLinkedList DeviceInterfaceList; + struct net_interface_private : net_interface { char base_name[IF_NAMESIZE]; net_device_interface *device_interface; @@ -90,6 +92,7 @@ status_t register_device_monitor(struct net_device *device, net_receive_func receiveFunc, void *cookie); status_t unregister_device_monitor(struct net_device *device, net_receive_func receiveFunc, void *cookie); +status_t device_link_changed(net_device *device); status_t device_removed(net_device *device); #endif // INTERFACES_H diff --git a/src/add-ons/kernel/network/stack/stack.cpp b/src/add-ons/kernel/network/stack/stack.cpp index 1ee6f07350..063f383a22 100644 --- a/src/add-ons/kernel/network/stack/stack.cpp +++ b/src/add-ons/kernel/network/stack/stack.cpp @@ -911,6 +911,7 @@ static net_stack_module_info sNetStackModule = { unregister_device_handler, register_device_monitor, unregister_device_monitor, + device_link_changed, device_removed, notify_socket,