diff -r 000000000000 -r c40eb8fe8501 wlan_bearer/wlanengine/wlan_common/wlanengine_common_3.1/src/core_server.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/wlan_bearer/wlanengine/wlan_common/wlanengine_common_3.1/src/core_server.cpp Tue Feb 02 02:03:13 2010 +0200 @@ -0,0 +1,2495 @@ +/* +* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Main class for core server +* +*/ + +/* +* %version: 89 % +*/ + +#include "core_server.h" +#include "abs_core_server_callback.h" +#include "abs_core_driverif.h" +#include "abs_core_timer.h" +#include "abs_core_frame_handler.h" +#include "abs_core_event_handler.h" +#include "core_timer_factory.h" +#include "core_operation_connect.h" +#include "core_operation_update_device_settings.h" +#include "core_operation_scan.h" +#include "core_operation_get_available_iaps.h" +#include "core_operation_release.h" +#include "core_operation_unload_drivers.h" +#include "core_operation_check_rcpi.h" +#include "core_operation_handle_frame.h" +#include "core_operation_null.h" +#include "core_operation_get_rcpi.h" +#include "core_operation_configure_multicast_group.h" +#include "core_operation_update_power_mode.h" +#include "core_operation_update_rxtx_parameters.h" +#include "core_operation_get_statistics.h" +#include "core_operation_set_uapsd_settings.h" +#include "core_operation_set_power_save_settings.h" +#include "core_operation_protected_setup.h" +#include "core_operation_create_ts.h" +#include "core_operation_delete_ts.h" +#include "core_operation_power_save_test.h" +#include "core_operation_set_arp_filter.h" +#include "core_operation_directed_roam.h" +#include "core_frame_ethernet.h" +#include "core_connection_data.h" +#include "core_eapol_handler.h" +#include "core_frame_dot11.h" +#include "core_frame_echo_test.h" +#include "am_debug.h" +#include "core_callback.h" +#include "core_tools.h" +#include "core_tools_parser.h" +#include "core_wpx_adaptation_factory.h" +#include "core_traffic_stream_list_iter.h" + +// ======== MEMBER FUNCTIONS ======== + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_server_c::core_server_c( + abs_core_server_callback_c& callback, + abs_core_driverif_c& drivers, + const core_device_settings_s& settings, + const core_mac_address_s& mac_address, + u32_t features ) : + callback_m( callback ), + drivers_m( drivers ), + device_settings_m( settings ), + queue_m( ), + queue_timer_m( NULL ), + driver_unload_timer_m( NULL ), + core_settings_m( features ), + connection_data_m( NULL ), + own_mac_addr_m( mac_address ), + eapol_handler_m( NULL ), + eapol_m( NULL ), + cm_timer_m( ), + roam_timer_m( NULL ), + operation_timer_m( NULL ), + driver_dhcp_timer_m( NULL ), + frame_handler_m( NULL ), + event_handler_m( NULL ), + scan_list_m( ), + wpx_adaptation_m( NULL ) + { + DEBUG( "core_server_c::core_server_c()" ); + queue_m.clear(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_server_c::~core_server_c() + { + DEBUG( "core_server_c::~core_server_c()" ); + + queue_m.clear(); + delete connection_data_m; + + if ( queue_timer_m ) + { + queue_timer_m->stop(); + core_timer_factory_c::destroy_timer( queue_timer_m ); + queue_timer_m = NULL; + } + if ( driver_unload_timer_m ) + { + driver_unload_timer_m->stop(); + core_timer_factory_c::destroy_timer( driver_unload_timer_m ); + driver_unload_timer_m = NULL; + } + if ( roam_timer_m ) + { + roam_timer_m->stop(); + core_timer_factory_c::destroy_timer( roam_timer_m ); + roam_timer_m = NULL; + } + if ( operation_timer_m ) + { + operation_timer_m->stop(); + core_timer_factory_c::destroy_timer( operation_timer_m ); + operation_timer_m = NULL; + } + if ( driver_dhcp_timer_m ) + { + driver_dhcp_timer_m->stop(); + core_timer_factory_c::destroy_timer( driver_dhcp_timer_m ); + driver_dhcp_timer_m = NULL; + } + if ( eapol_m ) + { + eapol_m->shutdown(); + delete eapol_m; + } + delete wpx_adaptation_m; + delete eapol_handler_m; + frame_handler_m = NULL; + event_handler_m = NULL; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::init() + { + DEBUG( "core_server_c::init()" ); + + drivers_m.init( this ); + + core_callback_c* queue_timer_callback = + new core_callback_c( &(core_server_c::queue_timer_expired), this ); + if( !queue_timer_callback ) + { + DEBUG("ERROR creating callback object"); + return core_error_no_memory; + } + queue_timer_m = core_timer_factory_c::create_timer( queue_timer_callback ); + if( !queue_timer_m ) + { + DEBUG( "ERROR creating timer" ); + delete queue_timer_callback; + return core_error_no_memory; + } + + core_callback_c* unload_timer_callback = + new core_callback_c( &(core_server_c::unload_timer_expired), this ); + if( !unload_timer_callback ) + { + DEBUG("ERROR creating callback object"); + return core_error_no_memory; + } + driver_unload_timer_m = core_timer_factory_c::create_timer( unload_timer_callback ); + if( !driver_unload_timer_m ) + { + DEBUG( "ERROR creating timer" ); + delete unload_timer_callback; + return core_error_no_memory; + } + + core_callback_c* roam_timer_callback = + new core_callback_c( &(core_server_c::roam_timer_expired), this ); + if( !roam_timer_callback ) + { + DEBUG("ERROR creating callback object"); + return core_error_no_memory; + } + roam_timer_m = core_timer_factory_c::create_timer( roam_timer_callback ); + if( !roam_timer_m ) + { + DEBUG( "ERROR creating timer" ); + delete roam_timer_callback; + return core_error_no_memory; + } + + core_callback_c* operation_timer_callback = + new core_callback_c( &(core_server_c::operation_timer_expired), this ); + if( !operation_timer_callback ) + { + DEBUG("ERROR creating callback object"); + return core_error_no_memory; + } + operation_timer_m = core_timer_factory_c::create_timer( operation_timer_callback ); + if( !operation_timer_m ) + { + DEBUG( "ERROR creating timer" ); + delete operation_timer_callback; + return core_error_no_memory; + } + + core_callback_c* dhcp_timer_callback = + new core_callback_c( &(core_server_c::dhcp_timer_expired), this ); + if( !dhcp_timer_callback ) + { + DEBUG("ERROR creating callback object"); + return core_error_no_memory; + } + driver_dhcp_timer_m = core_timer_factory_c::create_timer( dhcp_timer_callback ); + if( !driver_dhcp_timer_m ) + { + DEBUG( "ERROR creating timer" ); + delete dhcp_timer_callback; + return core_error_no_memory; + } + + wpx_adaptation_m = core_wpx_adaptation_factory_c::instance( + this, &drivers_m, &callback_m ); + if ( !wpx_adaptation_m ) + { + DEBUG("ERROR creating WPX adaptation"); + return core_error_no_memory; + } + + eapol_handler_m = new core_eapol_handler_c( + this, &drivers_m, &callback_m ); + if ( !eapol_handler_m ) + { + DEBUG("ERROR creating eapol handler"); + return core_error_no_memory; + } + + core_settings_m.set_rcpi_boundaries( + device_settings_m.rcpi_trigger, + device_settings_m.rcpi_trigger + device_settings_m.rcpi_difference ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_device_settings_s& core_server_c::get_device_settings() + { + return device_settings_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_settings_c& core_server_c::get_core_settings() + { + return core_settings_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_connection_data_c* core_server_c::get_connection_data() + { + return connection_data_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_wlan_eapol_interface_c& core_server_c::get_eapol_instance() + { + return *eapol_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::set_eapol_handler( + abs_wlan_eapol_callback_interface_c* handler ) + { + eapol_handler_m->set_eapol_handler( handler ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_mac_address_s& core_server_c::own_mac_addr() + { + return own_mac_addr_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::queue_int_operation( + core_operation_base_c* operation ) + { + DEBUG( "core_server_c::queue_int_operation()" ); + + if ( !operation ) + { + DEBUG( "core_server_c::queue_int_operation() - no operation" ); + + return core_error_no_memory; + } + else if ( operation->is_flags( core_operation_base_c::core_base_flag_only_one_instance ) && + is_operation_in_queue_with_type( operation->operation_type() ) ) + { + DEBUG( "core_server_c::queue_int_operation() - only one instance allowed in the operation queue" ); + + delete operation; + operation = NULL; + + return core_error_already_exists; + } + else + { + core_error_e ret = queue_m.append( operation ); + if ( ret != core_error_ok ) + { + DEBUG1( "core_server_c::queue_int_operation() - unable to queue the request (%u)", + ret ); + + delete operation; + operation = NULL; + + return ret; + } + } + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::queue_int_operation_and_run_next( + core_operation_base_c* operation ) + { + DEBUG( "core_server_c::queue_int_operation_and_run_next()" ); + + core_error_e ret = queue_int_operation( operation ); + if ( ret != core_error_ok ) + { + return ret; + } + + schedule_operation(); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::init_connection_data( + const core_iap_data_s& iap_data, + const core_device_settings_s& device_settings ) + { + connection_data_m = new core_connection_data_c( iap_data, device_settings ); + + if ( !connection_data_m ) + { + return core_error_no_memory; + } + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::clear_connection_data() + { + DEBUG( "core_server_c::clear_connection_data()" ); + + delete connection_data_m; + connection_data_m = NULL; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_roam_timer( + u32_t delay ) + { + DEBUG1( "core_server_c::schedule_roam_timer() - delay %u", + delay ); + + ASSERT( !roam_timer_m->is_active() ); + + roam_timer_m->start( + delay ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_roam_timer() + { + DEBUG( "core_server_c::cancel_roam_timer()" ); + + roam_timer_m->stop(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::roam_timer_expired( + void* this_ptr ) + { + DEBUG("core_server_c::roam_timer_expired()"); + core_server_c* self = static_cast( this_ptr ); + + // send an indication to adaptation + self->callback_m.notify( + core_notification_rcpi_roam_attempt_started, + 0, + NULL ); + + core_operation_base_c* command = new core_operation_check_rcpi_c( + REQUEST_ID_CORE_INTERNAL, + self, + &(self->drivers_m), + &(self->callback_m), + core_operation_check_rcpi_c::core_rcpi_check_reason_timer ); + + self->queue_int_operation_and_run_next( command ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_operation_timer( + u32_t delay ) + { + DEBUG1( "core_server_c::schedule_operation_timer() - delay %u", + delay ); + + ASSERT( !operation_timer_m->is_active() ); + operation_timer_m->start( + delay ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_operation_timer() + { + DEBUG( "core_server_c::cancel_operation_timer()" ); + operation_timer_m->stop(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::operation_timer_expired( + void* this_ptr ) + { + DEBUG("core_server_c::operation_timer_expired()"); + core_server_c* self = static_cast( this_ptr ); + + core_operation_base_c* command = self->queue_m.first(); + ASSERT( command ); + ASSERT( command->is_executing() ); + self->request_complete( command->request_id(), core_error_ok ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::is_operation_in_queue_with_flags( + u32_t feature_flags ) + { + core_type_list_iterator_c iter( queue_m ); + for( core_operation_base_c* current = iter.first(); current; current = iter.next() ) + { + if ( current->is_flags( feature_flags ) ) + { + return true_t; + } + } + + return false_t; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::is_operation_in_queue_with_type( + u32_t type ) + { + core_type_list_iterator_c iter( queue_m ); + for( core_operation_base_c* current = iter.first(); current; current = iter.next() ) + { + if ( current->operation_type() == type ) + { + return true_t; + } + } + + return false_t; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::is_dhcp_timer_active() + { + return driver_dhcp_timer_m->is_active(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_dhcp_timer( + u32_t delay ) + { + DEBUG1( "core_server_c::schedule_dhcp_timer() - delay %u", + delay ); + + ASSERT( !is_dhcp_timer_active() ); + + driver_dhcp_timer_m->start( + delay ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_dhcp_timer() + { + DEBUG( "core_server_c::cancel_dhcp_timer()" ); + + driver_dhcp_timer_m->stop(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::dhcp_timer_expired( + void* this_ptr ) + { + DEBUG( "core_server_c::dhcp_timer_expired()" ); + + core_server_c* self = static_cast( this_ptr ); + + core_operation_base_c* command = new core_operation_power_save_test_c( + REQUEST_ID_CORE_INTERNAL, + self, + &(self->drivers_m), + &(self->callback_m) ); + + self->queue_int_operation_and_run_next( command ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_unload_timer( + u32_t delay ) + { + DEBUG1( "core_server_c::schedule_unload_timer() - delay %u", + delay ); + + ASSERT( !driver_unload_timer_m->is_active() ); + + driver_unload_timer_m->start( + delay ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_unload_timer() + { + DEBUG( "core_server_c::cancel_unload_timer()" ); + + driver_unload_timer_m->stop(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_operations_with_flags( + u32_t feature_flags ) + { + DEBUG( "core_server_c::cancel_operations_with_flags()" ); + DEBUG1( "core_server_c::cancel_operations_with_flags() - feature_flags %u", + feature_flags ); + + core_operation_base_c* command = queue_m.first(); + while ( command ) + { + if ( !command->is_executing() && + command->is_flags( feature_flags ) ) + { + DEBUG2( "core_server_c::cancel_operations_with_flags() - canceling operation (id %u, type %u)", + command->request_id(), command->operation_type() ); + if ( command->request_id() != REQUEST_ID_CORE_INTERNAL ) + { + DEBUG( "core_server_c::cancel_operations_with_flags() - adaptation request, completing it" ); + callback_m.request_complete( + command->request_id(), + core_error_cancel ); + } + + queue_m.remove( command ); + delete command; + command = queue_m.first(); + } + else + { + command = queue_m.next(); + } + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_operations_with_type( + u32_t type ) + { + DEBUG( "core_server_c::cancel_operation_with_type()" ); + DEBUG1( "core_server_c::cancel_operation_with_type() - type %u", + type ); + + core_operation_base_c* command = queue_m.first(); + while ( command ) + { + if ( !command->is_executing() && + command->operation_type() == type ) + { + DEBUG2( "core_server_c::cancel_operation_with_type() - canceling operation (id %u, type %u)", + command->request_id(), command->operation_type() ); + if ( command->request_id() != REQUEST_ID_CORE_INTERNAL ) + { + DEBUG( "core_server_c::cancel_operation_with_type() - adaptation request, completing it" ); + callback_m.request_complete( + command->request_id(), + core_error_cancel ); + } + + queue_m.remove( command ); + delete command; + command = queue_m.first(); + } + else + { + command = queue_m.next(); + } + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_all_operations( + bool_t is_graceful_cancel ) + { + DEBUG( "core_server_c::cancel_all_operations()" ); + + core_operation_base_c* command = queue_m.first(); + while ( command ) + { + if ( !command->is_executing() ) + { + DEBUG2( "core_server_c::cancel_all_operations() - canceling operation (id %u, type %u)", + command->request_id(), command->operation_type() ); + if ( command->request_id() != REQUEST_ID_CORE_INTERNAL ) + { + DEBUG( "core_server_c::cancel_all_operations() - adaptation request, completing it" ); + callback_m.request_complete( + command->request_id(), + core_error_cancel ); + } + + queue_m.remove( command ); + delete command; + command = queue_m.first(); + } + else + { + command = queue_m.next(); + } + } + + command = queue_m.first(); + if ( command ) + { + if( is_graceful_cancel ) + { + DEBUG2( "core_server_c::cancel_all_operations() - canceling currently executing operation (id %u, type %u) (graceful cancel)", + command->request_id(), command->operation_type() ); + } + else + { + DEBUG2( "core_server_c::cancel_all_operations() - canceling currently executing operation (id %u, type %u) (forced cancel)", + command->request_id(), command->operation_type() ); + } + + ASSERT( command->is_executing() ); + + // Let operation handle it's own cancelling + command->user_cancel_operation( is_graceful_cancel ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::create_eapol_instance( + core_eapol_operating_mode_e mode ) + { + DEBUG( "core_server_c::create_eapol_instance()" ); + + if ( eapol_m && + eapol_m->operating_mode() == mode ) + { + DEBUG( "core_server_c::create_eapol_instance() - an instance already exists" ); + + return true_t; + } + + if ( eapol_m ) + { + DEBUG( "core_server_c::create_eapol_instance() - deleting old core_wlan_eapol_interface_c instance" ); + + eapol_m->shutdown(); + delete eapol_m; + eapol_m = NULL; + } + + DEBUG1( "core_server_c::create_eapol_instance() - creating core_wlan_eapol_interface_c (mode %u)", + mode ); + + eapol_m = new core_wlan_eapol_interface_c( callback_m ); + if ( !eapol_m ) + { + DEBUG( "core_server_c::create_eapol_instance() - unable to create core_wlan_eapol_interface_c" ); + return false_t; + } + core_error_e error = eapol_m->load_eapol( mode, eapol_handler_m ); + if ( error != core_error_ok ) + { + DEBUG1( "core_server_c::create_eapol_instance() - load_eapol failed with %i", error ); + delete eapol_m; + eapol_m = NULL; + return false_t; + } + error = eapol_m->configure(); + if ( error != core_error_ok ) + { + DEBUG1( "core_server_c::create_eapol_instance() - configure failed with %i", error ); + delete eapol_m; + eapol_m = NULL; + return false_t; + } + + return true_t; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +abs_core_frame_handler_c* core_server_c::frame_handler() + { + return frame_handler_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::register_frame_handler( + abs_core_frame_handler_c* handler ) + { + ASSERT( handler ); + ASSERT( handler == frame_handler_m || !frame_handler_m ); + + frame_handler_m = handler; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::unregister_frame_handler( + abs_core_frame_handler_c* handler ) + { + ASSERT( handler ); + + if ( frame_handler_m != handler ) + { + DEBUG1( "core_server_c::unregister_frame_handler() - handler 0x%08X not registered", + handler ); + + return; + } + + frame_handler_m = NULL; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_scan_list_c& core_server_c::get_scan_list() + { + return scan_list_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +abs_core_event_handler_c* core_server_c::event_handler() + { + return event_handler_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::register_event_handler( + abs_core_event_handler_c* handler ) + { + ASSERT( handler ); + ASSERT( handler == event_handler_m || !event_handler_m ); + + event_handler_m = handler; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::unregister_event_handler( + abs_core_event_handler_c* handler ) + { + ASSERT( handler ); + + if ( event_handler_m != handler ) + { + DEBUG1( "core_server_c::unregister_event_handler() - handler 0x%08X not registered", + handler ); + + return; + } + + event_handler_m = NULL; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_roam( + core_operation_handle_bss_lost_c::core_bss_lost_reason_e reason ) + { + if ( !is_operation_in_queue_with_type( core_operation_handle_bss_lost ) ) + { + DEBUG( "core_server_c::schedule_roam() - scheduling a core_operation_handle_bss_lost operation" ); + + core_operation_base_c* command = new core_operation_handle_bss_lost_c( + REQUEST_ID_CORE_INTERNAL, + this, + &drivers_m, + &callback_m, + reason ); + + queue_int_operation_and_run_next( command ); + } + else + { + DEBUG( "core_server_c::schedule_roam() - core_operation_handle_bss_lost already in the queue" ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::send_management_frame( + core_frame_type_e frame_type, + const u16_t frame_length, + const u8_t* const frame_data, + const core_mac_address_s& destination ) + { + /** Management frames can be sent without downgraded user priority. */ + drivers_m.send_frame( + frame_type, + frame_length, + frame_data, + core_tools_c::convert_ac_to_user_priority( core_access_class_voice ), + destination, + true_t ); + + return true_t; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::send_data_frame( + const core_ap_data_c& ap_data, + core_frame_type_e frame_type, + const u16_t frame_length, + const u8_t* const frame_data, + core_access_class_e frame_priority, + const core_mac_address_s& destination, + bool_t send_unencrypted ) + { + DEBUG( "core_server_c::send_data_frame()" ); + + u8_t initial_priority( + core_tools_c::convert_ac_to_user_priority( frame_priority ) ); + u8_t user_priority = initial_priority; + + if ( ap_data.is_wmm_ie_present() ) + { + /** + * Collect a list of active streams per user priority. + */ + bool_t is_ts_for_user_priority[MAX_QOS_USER_PRIORITY]; + core_tools_c::fillz( + &is_ts_for_user_priority[0], + sizeof( is_ts_for_user_priority ) ); + + core_traffic_stream_list_iter_c iter( + get_connection_data()->traffic_stream_list() ); + + core_traffic_stream_c* entry = iter.first(); + while( entry ) + { + if ( entry->status() == core_traffic_stream_status_active ) + { + is_ts_for_user_priority[entry->user_priority()] = true_t; + } + + entry = iter.next(); + } + + /** + * See if downgrade should be done. + */ + bool_t is_send_ok( false_t ); + + while ( user_priority <= initial_priority && !is_send_ok ) + { + core_access_class_e ac( + core_tools_c::convert_user_priority_to_ac( user_priority ) ); + +#if 0 + DEBUG1( "core_server_c::send_data_frame() - user_priority is %u", + user_priority ); + DEBUG1( "core_server_c::send_data_frame() - ac is %u", + ac ); + DEBUG1( "core_server_c::send_data_frame() - is_admission_control_required is %u", + ap_data.is_admission_control_required( ac ) ); + DEBUG1( "core_server_c::send_data_frame() - is_ts_for_user_priority is %u", + is_ts_for_user_priority[user_priority] ); +#endif // 0 + + if ( !ap_data.is_admission_control_required( ac ) || + is_ts_for_user_priority[user_priority] ) + { + is_send_ok = true_t; + } + else + { + user_priority--; + } + } + + if ( !is_send_ok ) + { + DEBUG( "core_server_c::send_data_frame() - unable to send the frame due to admission control settings" ); + return false_t; + } + } + + if ( user_priority == initial_priority ) + { + DEBUG1( "core_server_c::send_data_frame() - no need to downgrade user priority %u", + user_priority ); + } + else + { + DEBUG2( "core_server_c::send_data_frame() - user priority %u downgraded to %u", + initial_priority, user_priority ); + } + + drivers_m.send_frame( + frame_type, + frame_length, + frame_data, + user_priority, + destination, + send_unencrypted ); + + return true_t; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +abs_core_wpx_adaptation_c& core_server_c::get_wpx_adaptation_instance() + { + return *wpx_adaptation_m; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::set_protected_setup_handler( + abs_core_protected_setup_handler_c* handler ) + { + eapol_handler_m->set_protected_setup_handler( handler ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::connect( + u32_t request_id, + const core_iap_data_s& settings, + core_connect_status_e& connect_status, + core_type_list_c* ssid_list ) + { + DEBUG( "core_server_c::connect()" ); + + core_operation_base_c* command = new core_operation_connect_c( + request_id, this, &drivers_m, &callback_m, settings, ssid_list, connect_status ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::release( + u32_t request_id ) + { + DEBUG( "core_server_c::release()" ); + + /** + * Cancel all pending roaming operations. + */ + cancel_operations_with_flags( + core_operation_base_c::core_base_flag_roam_operation ); + + /** + * If there's an executing roam operation, let it handle + * its own cancel. + */ + core_operation_base_c* operation = queue_m.first(); + if( operation && + operation->is_flags( core_operation_base_c::core_base_flag_roam_operation ) ) + { + operation->user_cancel_operation( true_t ); // Using graceful cancel + } + + core_operation_base_c* command = new core_operation_release_c( + request_id, this, &drivers_m, &callback_m, core_release_reason_external_request ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::get_scan_result( + u32_t request_id, + core_scan_mode_e scan_mode, + const core_ssid_s& scan_ssid, + const core_scan_channels_s& scan_channels, + u8_t scan_max_age, + ScanList& scan_data, + bool_t is_current_ap_added ) + { + DEBUG( "core_server_c::get_scan_result()" ); + + core_operation_base_c* command = new core_operation_scan_c( + request_id, this, &drivers_m, &callback_m, + scan_mode, scan_ssid, scan_channels, scan_max_age, scan_data, + true_t, + is_current_ap_added ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::get_available_iaps( + u32_t request_id, + bool_t is_active_scan_allowed, + core_type_list_c& iap_data_list, + core_type_list_c& iap_id_list, + core_type_list_c* iap_ssid_list, + ScanList& scan_data ) + { + DEBUG( "core_server_c::get_available_iaps()" ); + + core_operation_base_c* command = new core_operation_get_available_iaps_c( + request_id, + this, + &drivers_m, + &callback_m, + is_active_scan_allowed, + iap_data_list, + iap_id_list, + iap_ssid_list, + scan_data ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::get_current_rcpi( + u32_t request_id, + u32_t& rcpi ) + { + DEBUG( "core_server_c::get_current_rcpi()" ); + + core_operation_base_c* command = new core_operation_get_rcpi_c( + request_id, this, &drivers_m, &callback_m, rcpi ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::disable_wlan( + u32_t request_id ) + { + DEBUG( "core_server_c::disable_wlan()" ); + + get_core_settings().set_wlan_enabled( false_t ); + + /** + * Schedule an immediate driver unload. + */ + unload_drivers(); + + core_operation_base_c* command = new core_operation_null_c( + request_id, this, &drivers_m, &callback_m, core_error_ok ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::enable_wlan( + u32_t request_id ) + { + DEBUG( "core_server_c::enable_wlan()" ); + + core_settings_m.set_wlan_enabled( true_t ); + + core_operation_base_c* command = new core_operation_null_c( + request_id, this, &drivers_m, &callback_m, core_error_ok ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::unload_drivers() + { + DEBUG( "core_server_c::unload_drivers()" ); + + /** + * If drivers are loaded, schedule an immediate unload. + */ + if( get_core_settings().is_driver_loaded() ) + { + cancel_unload_timer(); + schedule_unload_timer( CORE_TIMER_IMMEDIATELY ); + + return core_error_ok; + } + else + { + return core_error_drivers_not_loaded; + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::get_packet_statistics( + u32_t request_id, + core_packet_statistics_s& statistics ) + { + DEBUG( "core_server_c::get_packet_statistics()" ); + + core_operation_base_c* command = new core_operation_get_statistics_c( + request_id, this, &drivers_m, &callback_m, statistics ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::create_traffic_stream( + u32_t request_id, + u8_t tid, + u8_t user_priority, + bool_t is_automatic_stream, + const core_traffic_stream_params_s& params, + u32_t& stream_id, + core_traffic_stream_status_e& stream_status ) + { + DEBUG( "core_server_c::create_traffic_stream()" ); + + core_operation_base_c* command = new core_operation_create_ts_c( + request_id, + this, + &drivers_m, + &callback_m, + tid, + user_priority, + is_automatic_stream, + params, + stream_id, + stream_status ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::delete_traffic_stream( + u32_t request_id, + u32_t stream_id ) + { + DEBUG( "core_server_c::delete_traffic_stream()" ); + + core_operation_base_c* command = new core_operation_delete_ts_c( + request_id, + this, + &drivers_m, + &callback_m, + stream_id ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::run_protected_setup( + u32_t request_id, + const core_iap_data_s& iap_data, + core_type_list_c& iap_data_list, + core_protected_setup_status_e& protected_setup_status ) + { + DEBUG( "core_server_c::run_protected_setup()" ); + + core_operation_base_c* command = new core_operation_protected_setup_c( + request_id, this, &drivers_m, &callback_m, iap_data, iap_data_list, + protected_setup_status ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::directed_roam( + u32_t request_id, + const core_mac_address_s& bssid ) + { + DEBUG( "core_server_c::directed_roam()" ); + + core_operation_base_c* command = new core_operation_directed_roam_c( + request_id, this, &drivers_m, &callback_m, bssid ); + + queue_ext_operation_and_run_next( command, request_id ); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_bssid( + core_mac_address_s& bssid ) + { + DEBUG( "core_server_c::get_current_bssid()" ); + + bssid = ZERO_MAC_ADDR; + if ( core_settings_m.is_connected() && + connection_data_m && + connection_data_m->current_ap_data() ) + { + bssid = connection_data_m->current_ap_data()->bssid(); + + return core_error_ok; + } + + return core_error_not_connected; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_ssid( + core_ssid_s& ssid ) + { + DEBUG( "core_server_c::get_current_ssid()" ); + + ssid = BROADCAST_SSID; + if ( core_settings_m.is_connected() && + connection_data_m && + connection_data_m->current_ap_data() ) + { + ssid = connection_data_m->ssid(); + + return core_error_ok; + } + + return core_error_not_connected; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_security_mode( + core_connection_security_mode_e& mode ) + { + DEBUG( "core_server_c::get_current_security_mode()" ); + + mode = core_connection_security_mode_open; + if ( core_settings_m.is_connected() && + connection_data_m && + connection_data_m->current_ap_data() ) + { + mode = core_tools_c::security_mode( + connection_data_m->iap_data(), + *connection_data_m->current_ap_data() ); + + return core_error_ok; + } + + return core_error_not_connected; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_connection_state( + core_connection_state_e& state ) + { + DEBUG( "core_server_c::get_current_connection_state()" ); + + state = core_settings_m.connection_state(); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::update_device_settings( + core_device_settings_s& settings ) + { + DEBUG( "core_server_c::update_device_settings()" ); + + device_settings_m = settings; + + core_operation_base_c* command = new core_operation_update_device_settings_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::set_power_save_mode( + const core_power_save_mode_s& mode ) + { + DEBUG( "core_server_c::set_power_save_mode()" ); + + core_settings_m.set_preferred_power_save_mode( mode ); + + core_operation_base_c* command = new core_operation_update_power_mode_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::add_bssid_to_rogue_list( + const core_mac_address_s& bssid ) + { + DEBUG( "core_server_c::add_bssid_to_rogue_list()" ); + + core_settings_m.add_mac_to_permanent_blacklist( + bssid, core_ap_blacklist_reason_external ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::remove_bssid_from_rogue_list( + const core_mac_address_s& bssid ) + { + DEBUG( "core_server_c::remove_bssid_from_rogue_list()" ); + + core_settings_m.remove_mac_from_permanent_blacklist( bssid ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_rogue_list( + core_type_list_c& rogue_list ) + { + DEBUG( "core_server_c::get_rogue_list()" ); + + core_type_list_c& perm_list = core_settings_m.permanent_blacklist(); + DEBUG1( "core_server_c::get_rogue_list() - %d permanent addresses", perm_list.count() ); + + // Loop through permanent blacklist + core_ap_blacklist_entry_s* addr = perm_list.first(); + while ( addr ) + { + // Address must be copied, because rogue_list get ownership for its entries. + core_mac_address_s* copy_addr = new core_mac_address_s; + if ( !copy_addr ) + { + return core_error_no_memory; + } + *copy_addr = addr->bssid; + DEBUG_MAC( copy_addr->addr ); + + core_error_e status = rogue_list.append( copy_addr ); + if ( status != core_error_ok ) + { + return status; + } + + addr = perm_list.next(); + } + + // Check if connection is available. + if ( connection_data_m ) + { + core_type_list_c& temp_list = connection_data_m->temporary_blacklist(); + DEBUG1( "core_server_c::get_rogue_list() - %d temporary addresses", temp_list.count() ); + + // Loop through temporary blacklist + addr = temp_list.first(); + while ( addr ) + { + // Address must be copied, because rogue_list get ownership for its entries. + core_mac_address_s* copy_addr = new core_mac_address_s; + if ( !copy_addr ) + { + return core_error_no_memory; + } + *copy_addr = addr->bssid; + DEBUG_MAC( copy_addr->addr ); + + core_error_e status = rogue_list.append( copy_addr ); + if ( status != core_error_ok ) + { + return status; + } + + addr = temp_list.next(); + } + } + else + { + DEBUG( "core_server_c::get_rogue_list() - connection is not available, temporary blacklist is empty" ); + } + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::set_rcp_level_notification_boundary( + const i32_t rcp_level_boundary, + const i32_t hysteresis ) + { + DEBUG( "core_server_c::set_rcp_level_notification_boundary()" ); + + core_settings_m.set_rcpi_boundaries( + rcp_level_boundary, + rcp_level_boundary + hysteresis ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::clear_packet_statistics() + { + DEBUG( "core_server_c::clear_packet_statistics()" ); + + core_settings_m.clear_connection_statistics(); + core_settings_m.roam_metrics().clear_metrics(); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_uapsd_settings( + core_uapsd_settings_s& settings ) + { + DEBUG( "core_server_c::get_uapsd_settings()" ); + + settings = core_settings_m.uapsd_settings(); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::set_uapsd_settings( + const core_uapsd_settings_s& settings ) + { + DEBUG( "core_server_c::set_uapsd_settings()" ); + + core_operation_base_c* command = new core_operation_set_uapsd_settings_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, settings ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_power_save_settings( + core_power_save_settings_s& settings ) + { + DEBUG( "core_server_c::get_power_save_settings()" ); + + settings = core_settings_m.power_save_settings(); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::set_power_save_settings( + const core_power_save_settings_s& settings ) + { + DEBUG( "core_server_c::set_power_save_settings()" ); + + core_operation_base_c* command = new core_operation_set_power_save_settings_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, settings ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_ap_info( + core_ap_information_s& info ) + { + DEBUG( "core_server_c::get_current_ap_info()" ); + + if ( core_settings_m.is_connected() && + connection_data_m && + connection_data_m->current_ap_data() ) + { + info = core_tools_parser_c::get_ap_info( + connection_data_m->iap_data(), + *connection_data_m->current_ap_data() ); + + return core_error_ok; + } + + return core_error_not_connected; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::get_roam_metrics( + core_roam_metrics_s& roam_metrics ) + { + DEBUG( "core_server_c::get_roam_metrics()" ); + + core_roam_metrics_c& metrics = get_core_settings().roam_metrics(); + + roam_metrics.connection_attempt_total_count = metrics.roam_attempt_count( core_roam_reason_initial_connect ); + roam_metrics.unsuccesfull_connection_attempt_count = + metrics.roam_attempt_failed_count( core_roam_failed_reason_timeout ) + + metrics.roam_attempt_failed_count( core_roam_failed_reason_ap_status_code ) + + metrics.roam_attempt_failed_count( core_roam_failed_reason_eapol_failure ) + + metrics.roam_attempt_failed_count( core_roam_failed_reason_other_failure ); + + roam_metrics.roaming_counter = metrics.roam_success_count(); + roam_metrics.coverage_loss_count = metrics.roam_attempt_count( core_roam_reason_bss_lost ); + + roam_metrics.last_roam_total_duration = metrics.roam_total_delay() / MILLISECONDS_FROM_MICROSECONDS; + roam_metrics.last_roam_data_path_broken_duration = metrics.roam_total_delay() / MILLISECONDS_FROM_MICROSECONDS; + + roam_metrics.last_roam_reason = core_roam_reason_none; + if ( get_connection_data() ) + { + roam_metrics.last_roam_reason = get_connection_data()->last_roam_reason(); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::set_arp_filter( + const core_arp_filter_s& filter ) + { + DEBUG( "core_server_c::set_arp_filter()" ); + + core_operation_base_c* command = new core_operation_set_arp_filter_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, filter ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::configure_multicast_group( + bool_t join_group, + const core_mac_address_s& multicast_addr ) + { + DEBUG( "core_server_c::configure_multicast_group" ); + + core_operation_base_c* command = new core_operation_configure_multicast_group_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, join_group, multicast_addr ); + + queue_int_operation_and_run_next( command ); + + return core_error_ok; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +core_error_e core_server_c::get_current_ac_traffic_info( + core_ac_traffic_information_s& info ) + { + DEBUG( "core_server_c::get_current_ac_traffic_info()" ); + + if ( core_settings_m.is_connected() && + connection_data_m && + connection_data_m->current_ap_data() ) + { + info.status_for_voice = connection_data_m->ac_traffic_status( + core_access_class_voice ); + info.status_for_video = connection_data_m->ac_traffic_status( + core_access_class_video ); + info.status_for_best_effort = connection_data_m->ac_traffic_status( + core_access_class_best_effort ); + info.status_for_background = connection_data_m->ac_traffic_status( + core_access_class_background ); + info.mode_for_voice = connection_data_m->ac_traffic_mode( + core_access_class_voice ); + info.mode_for_video = connection_data_m->ac_traffic_mode( + core_access_class_video ); + info.mode_for_best_effort = connection_data_m->ac_traffic_mode( + core_access_class_best_effort ); + info.mode_for_background = connection_data_m->ac_traffic_mode( + core_access_class_background ); + + return core_error_ok; + } + + return core_error_not_connected; + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::request_complete( + u32_t /* request_id */, + core_error_e status ) + { + DEBUG( "core_server_c::request_complete()" ); + + core_operation_base_c* operation = queue_m.first(); + + ASSERT( operation ); + ASSERT( operation->is_executing() ); + + core_error_e ret = operation->continue_operation( status ); + if ( ret == core_error_request_pending ) + { + DEBUG( "core_server_c::request_complete() - operation is not done yet" ); + return; + } + + DEBUG1( "core_server_c::request_complete() - operation completed with code %u", + ret ); + + if ( operation->request_id() != REQUEST_ID_CORE_INTERNAL ) + { + callback_m.request_complete( + operation->request_id(), + ret ); + } + + queue_m.remove( operation ); + delete operation; + operation = NULL; + + schedule_operation(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::cancel_request( + u32_t request_id ) + { + DEBUG( "core_server_c::cancel_request()" ); + + // Find correct operation + core_operation_base_c* operation = queue_m.first(); + while ( operation ) + { + if ( operation->request_id() == request_id ) + { + break; + } + operation = queue_m.next(); + } + + if ( !operation ) + { + DEBUG( "core_server_c::cancel_request() - operation not found" ); + return; + } + + if ( operation->is_executing() ) + { + // Let operation handle it's own cancelling + operation->user_cancel_operation( true_t ); // Using graceful cancel + } + else + { + // When operation is not yet running, we don't need to ask for it's permissions... + queue_m.remove( operation ); + delete operation; + operation = NULL; + + // Use null operation for completing correctly + operation = new core_operation_null_c( + request_id, this, &drivers_m, &callback_m, core_error_cancel ); + queue_ext_operation_and_run_next( operation, request_id ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::receive_frame( + core_frame_type_e frame_type, + const u16_t frame_length, + const u8_t* const frame_data, + u8_t frame_rcpi ) + { + DEBUG( "core_server_c::receive_frame()" ); + + DEBUG1( "core_server_c::receive_frame() - frame type: %u", frame_type ); + DEBUG1( "core_server_c::receive_frame() - frame (%u bytes): ", + frame_length ); + DEBUG_BUFFER( + frame_length, + frame_data ); + + if ( frame_type == core_frame_type_ethernet ) + { + core_frame_ethernet_c* frame = core_frame_ethernet_c::instance( + frame_length, + frame_data, + false_t ); + if ( frame ) + { + /** + * Currently all Ethernet frames are for EAPOL. + */ + + /** + * If an Ethernet frame is received during a roam, store it + * for later use. + */ + if ( get_connection_data()->is_eapol_connecting() ) + { + DEBUG( "core_server_c::receive_frame() - (re-)association in progress, storing EAPOL frame" ); + + (void)eapol_m->store_frame( *frame ); + } + /** + * Frames are discarded during disconnect. + */ + else if ( get_connection_data()->is_disconnecting() ) + { + DEBUG( "core_server_c::receive_frame() - disconnect in progress, discarding EAPOL frame" ); + } + /** + * Otherwise send it to EAPOL for processing. + */ + else if ( eapol_m ) + { + // Clear stored frame so that it is not sent later. + eapol_m->clear_stored_frame(); + + (void)eapol_m->process_frame( *frame ); + } + else + { + DEBUG( "core_server_c::receive_frame() - EAPOL not instantiated, discarding EAPOL frame" ); + } + + delete frame; + frame = NULL; + } + + return; + } + + /** + * WPX frames are handled separately. + */ + if ( wpx_adaptation_m->handle_wpx_frame( + frame_type, + frame_length, + frame_data ) ) + { + return; + } + + /** + * Offer dot11 and echo test frames to the registered frame handler. + */ + bool_t is_handled( false_t ); + + if ( frame_type == core_frame_type_dot11 && + frame_handler_m ) + { + core_frame_dot11_c* frame = core_frame_dot11_c::instance( + frame_length, + frame_data, + false_t ); + if ( frame && + frame_handler_m->receive_frame( frame, frame_rcpi ) ) + { + DEBUG( "core_server_c::receive_frame() - dot11 frame handled by the operation" ); + + is_handled = true_t; + } + + delete frame; + frame = NULL; + } + else if ( frame_type == core_frame_type_test && + frame_handler_m ) + { + core_frame_ethernet_c* frame = core_frame_ethernet_c::instance( + frame_length, + frame_data, + false_t ); + if ( frame ) + { + core_frame_echo_test_c* test_frame = core_frame_echo_test_c::instance( + *frame ); + if ( test_frame && + frame_handler_m->receive_test_frame( test_frame, frame_rcpi ) ) + { + DEBUG( "core_server_c::receive_frame() - echo test frame handled by the operation" ); + + is_handled = true_t; + } + + delete test_frame; + test_frame = NULL; + + delete frame; + frame = NULL; + } + } + + /** + * All other frames are processed in an operation. + */ + if ( !is_handled ) + { + DEBUG( "core_server_c::receive_frame() - queueing the frame for processing" ); + + core_operation_base_c* command = new core_operation_handle_frame_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, + frame_type, frame_length, frame_data ); + + queue_int_operation_and_run_next( command ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::notify( + core_am_indication_e indication ) + { + DEBUG( "core_server_c::notify()" ); + +#ifdef _DEBUG + switch ( indication ) + { + case core_am_indication_wlan_media_disconnect: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_media_disconnect" ); + break; + case core_am_indication_wlan_hw_failed: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_hw_failed" ); + break; + case core_am_indication_wlan_beacon_lost: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_beacon_lost" ); + break; + case core_am_indication_wlan_power_mode_failure: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_power_mode_failure" ); + break; + case core_am_indication_wlan_tx_fail: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_tx_fail" ); + break; + case core_am_indication_wlan_bss_regained: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_bss_regained" ); + break; + case core_am_indication_wlan_wep_decrypt_failure: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_wep_decrypt_failure" ); + break; + case core_am_indication_wlan_pairwise_key_mic_failure: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_pairwise_key_mic_failure" ); + break; + case core_am_indication_wlan_group_key_mic_failure: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_group_key_mic_failure" ); + break; + case core_am_indication_wlan_scan_complete: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_scan_complete" ); + break; + case core_am_indication_wlan_rcpi_trigger: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_rcpi_trigger" ); + break; + case core_am_indication_wlan_signal_loss_prediction: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_signal_loss_prediction" ); + break; + case core_am_indication_wlan_power_save_test_trigger: + DEBUG( "core_server_c::notify() - core_am_indication_wlan_power_save_test_trigger" ); + break; + case core_am_indication_os_power_standby: + DEBUG( "core_server_c::notify() - core_am_indication_os_power_standby" ); + break; + case core_am_indication_bt_connection_established: + DEBUG( "core_server_c::notify() - core_am_indication_bt_connection_established" ); + break; + case core_am_indication_bt_connection_disconnected: + DEBUG( "core_server_c::notify() - core_am_indication_bt_connection_disconnected" ); + break; + case core_am_indication_voice_call_on: + DEBUG( "core_server_c::notify() - core_am_indication_voice_call_on" ); + break; + case core_am_indication_voice_call_off: + DEBUG( "core_server_c::notify() - core_am_indication_voice_call_off" ); + break; + default: + break; + } +#endif // _DEBUG + + if ( event_handler_m && + event_handler_m->notify( indication ) ) + { + DEBUG( "core_server_c::notify() - indication handled by the operation" ); + + return; + } + + switch ( indication ) + { + case core_am_indication_os_power_standby: + { + /** + * Cancel all operations including the currently executing one. + */ + cancel_all_operations( true_t ); + + /** + * Force an immediate driver unload. + */ + queue_unload_drivers(); + + break; + } + case core_am_indication_wlan_hw_failed: + { + /** + * Cancel all operations including the currently executing one. + */ + cancel_all_operations( false_t ); + + /** + * Force an immediate driver unload. + */ + queue_unload_drivers(); + + break; + } + case core_am_indication_wlan_media_disconnect: + { + /** + * If the current operation is connect, handle_bss_lost or protected_setup, the indication + * is silently ignored. Otherwise this indication is handled like a BSS lost. + */ + core_operation_base_c* op = queue_m.first(); + if( op && + op->is_executing() && + ( op->operation_type() == core_operation_handle_bss_lost || + op->operation_type() == core_operation_connect || + op->operation_type() == core_operation_protected_setup ) ) + { + DEBUG( "core_server_c::notify() - ignoring indication" ); + } + else + { + schedule_roam( + core_operation_handle_bss_lost_c::core_bss_lost_reason_media_disconnect ); + } + + break; + } + case core_am_indication_wlan_beacon_lost: + /** Falls through on purpose. */ + case core_am_indication_wlan_power_mode_failure: + /** Falls through on purpose. */ + case core_am_indication_wlan_tx_fail: + { + // bss_lost is only handled in infrastructure mode + if ( connection_data_m->iap_data().operating_mode() == core_operating_mode_infrastructure ) + { + // send an indication to adaptation + callback_m.notify( + core_notification_bss_lost, + 0, + NULL ); + + schedule_roam( + core_operation_handle_bss_lost_c::core_bss_lost_reason_bss_lost ); + } + + break; + } + case core_am_indication_wlan_pairwise_key_mic_failure: + { + DEBUG( "core_server_c::notify() - a pairwise MIC failure has occured" ); + + mic_failure( false_t ); + + break; + } + case core_am_indication_wlan_group_key_mic_failure: + { + DEBUG( "core_server_c::notify() - a group MIC failure has occured" ); + + mic_failure( true_t ); + + break; + } + case core_am_indication_wlan_bss_regained: + // Do nothing. + break; + case core_am_indication_wlan_wep_decrypt_failure: + // Do nothing. + break; + case core_am_indication_bt_connection_established: + { + core_settings_m.set_bt_connection_established( true_t ); + core_operation_base_c* command = new core_operation_update_rxtx_parameters_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m ); + + queue_int_operation_and_run_next( command ); + + break; + } + case core_am_indication_bt_connection_disconnected: + { + core_settings_m.set_bt_connection_established( false_t ); + core_operation_base_c* command = new core_operation_update_rxtx_parameters_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m ); + + queue_int_operation_and_run_next( command ); + + break; + } + case core_am_indication_wlan_scan_complete: + { + DEBUG( "core_server_c::notify() - scan complete received without pending scan operations" ); + ASSERT( false_t ); + + break; + } + case core_am_indication_wlan_rcpi_trigger: + { + if ( !is_operation_in_queue_with_type( core_operation_check_rcpi ) && + !is_operation_in_queue_with_type( core_operation_handle_bss_lost ) ) + { + // send an indication to adaptation + callback_m.notify( + core_notification_rcpi_roam_attempt_started, + 0, + NULL ); + + // create operation to handle the indication + core_operation_base_c* command = new core_operation_check_rcpi_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, core_operation_check_rcpi_c::core_rcpi_check_reason_rcpi_trigger ); + + queue_int_operation_and_run_next( command ); + } + else + { + DEBUG( "core_server_c::notify() - roaming operation already in queue " ); + } + + break; + } + case core_am_indication_wlan_signal_loss_prediction: + { + if ( !is_operation_in_queue_with_type( core_operation_check_rcpi ) && + !is_operation_in_queue_with_type( core_operation_handle_bss_lost ) ) + { + // send an indication to adaptation + /* + callback_m.notify( + core_notification_rcpi_roam_attempt_started, + 0, + NULL ); + */ + + // create operation to handle the indication + /* + core_operation_base_c* command = new core_operation_check_rcpi_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, core_operation_check_rcpi_c::core_rcpi_check_reason_signal_loss_prediction ); + + queue_int_operation_and_run_next( command, LIST_HIGH_PRIORITY ); + */ + } + else + { + DEBUG( "core_server_c::notify() - roaming operation already in queue " ); + } + + break; + } + case core_am_indication_wlan_power_save_test_trigger: + { + DEBUG( "core_server_c::notify() - scheduling a power save test" ); + + core_operation_base_c* command = new core_operation_power_save_test_c( + REQUEST_ID_CORE_INTERNAL, + this, + &drivers_m, + &callback_m ); + + queue_int_operation_and_run_next( command ); + + break; + } + case core_am_indication_voice_call_on: + { + DEBUG( "core_server_c::notify() - voice call is on" ); + connection_data_m->set_voice_call_state( true_t ); + break; + } + case core_am_indication_voice_call_off: + { + DEBUG( "core_server_c::notify() - voice call is off" ); + connection_data_m->set_voice_call_state( false_t ); + break; + } + case core_am_indication_wlan_ap_ps_mode_error: + // Do nothing. + break; + default: + DEBUG( "core_server_c::notify() - unknown indication" ); + ASSERT( false_t ); + break; + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::schedule_operation() + { + DEBUG( "core_server_c::schedule_operation()" ); + + core_operation_base_c* operation = queue_m.first(); + + DEBUG1( "core_server_c::schedule_operation() - %u operation(s) in the queue", + queue_m.count()); + +#ifdef _DEBUG + if( operation && operation->is_executing() ) + { + DEBUG2("core_server_c::schedule_operation() - operation 0x%08X (type %u) is executing", + operation, operation->operation_type() ); + } +#endif // _DEBUG + + if( !operation && + core_settings_m.connection_state() == core_connection_state_notconnected && + core_settings_m.is_driver_loaded() && + !driver_unload_timer_m->is_active() ) + { + // If 1) no more operations and + // 2) no connection exists and + // 3) drivers are loaded and + // 4) driver not already active, + // start unload timer + DEBUG( "core_server_c::schedule_operation() - starting unload timer" ); + schedule_unload_timer( + device_settings_m.unload_driver_timer * SECONDS_FROM_MICROSECONDS ); + + return; + } + + if ( operation && + !operation->is_executing() && + !queue_timer_m->is_active() ) + { + DEBUG( "core_server_c::schedule_operation() - scheduling a new operation" ); + queue_timer_m->start( CORE_TIMER_IMMEDIATELY ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +bool_t core_server_c::is_cm_active() + { + return !cm_timer_m.is_wpa_allowed(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::queue_ext_operation_and_run_next( + core_operation_base_c* operation, + u32_t request_id ) + { + DEBUG1( "core_server_c::queue_ext_operation_and_run_next() - request id %u", + request_id ); + if( !operation ) + { + DEBUG( "core_server_c::queue_ext_operation_and_run_next() - no operation, completing request." ); + callback_m.request_complete( request_id, core_error_no_memory ); + } + else if ( operation->is_flags( core_operation_base_c::core_base_flag_only_one_instance ) && + is_operation_in_queue_with_type( operation->operation_type() ) ) + { + DEBUG( "core_server_c::queue_ext_operation_and_run_next() - only one instance allowed in the operation queue, completing request" ); + callback_m.request_complete( request_id, core_error_ok ); + + delete operation; + operation = NULL; + } + else + { + core_error_e ret = queue_m.append( operation ); + if ( ret != core_error_ok ) + { + DEBUG1( "core_server_c::queue_ext_operation_and_run_next() - unable to queue the request (%u)", + ret ); + callback_m.request_complete( request_id, ret ); + + delete operation; + operation = NULL; + } + } + + schedule_operation(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::queue_timer_expired( void* this_ptr ) + { + DEBUG( "core_server_c::queue_timer_expired()" ); + core_server_c* self = static_cast( this_ptr ); + + core_operation_base_c* operation = self->queue_m.first(); + if( !operation ) + { + /** + * This shouldn't normally happen but it can happen if we have + * schedule an operation and cancel it before it has a chance to + * start executing. + */ + + return; + } + + ASSERT( operation ); + ASSERT( !operation->is_executing() ); + + // Set operation priority in the queue to maximum + self->queue_m.remove( operation ); + self->queue_m.append( operation, LIST_TOP_PRIORITY ); + + // If operation says it needs drivers, stop driver unload timer + if( operation->is_flags( core_operation_base_c::core_base_flag_drivers_needed ) && + self->driver_unload_timer_m->is_active() ) + { + DEBUG( "core_server_c::queue_timer_expired() - stopping unload timer" ); + self->cancel_unload_timer(); + } + + core_error_e ret = operation->start_operation(); + if( ret == core_error_request_pending ) + { + DEBUG( "core_server_c::queue_timer_expired() - operation is not done yet" ); + return; + } + + DEBUG1( "core_server_c::queue_timer_expired() - operation completed with code %u", + ret ); + + if ( operation->request_id() != REQUEST_ID_CORE_INTERNAL ) + { + self->callback_m.request_complete( + operation->request_id(), + ret ); + } + + self->queue_m.remove( operation ); + delete operation; + operation = NULL; + + self->schedule_operation(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::unload_timer_expired( void* this_ptr ) + { + DEBUG("core_server_c::unload_timer_expired()"); + core_server_c* self = static_cast( this_ptr ); + + /** + * Due to configurable unload timer value and different platform timing issues + * it's possible that a new operation has been added to the operation queue + * AFTER this timer was started but the queue timer has not yet been triggered. + * + * Therefore we have to abort driver unloading if there's an operation in the + * queue that requires drivers. + */ + if( self->is_operation_in_queue_with_flags( + core_operation_base_c::core_base_flag_drivers_needed ) ) + { + DEBUG( "core_server_c::unload_timer_expired() - operation in queue, aborting driver unloading" ); + + return; + } + + self->queue_unload_drivers(); + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::mic_failure( + bool_t is_group_key_fail ) + { + DEBUG( "core_server_c::mic_failure()" ); + + core_connection_security_mode_e mode = core_connection_security_mode_open; + + if( core_error_ok != get_current_security_mode( mode ) ) + { + DEBUG( "core_server_c::mic_failure() - not connected, ignoring mic failure" ); + return; + } + + if( mode != core_connection_security_mode_wpa && + mode != core_connection_security_mode_wpa_psk ) + { + DEBUG( "core_server_c::mic_failure() - security mode not WPA, ignoring mic failure" ); + return; + } + + if( is_group_key_fail ) + { + if( connection_data_m->current_ap_data() != NULL && + connection_data_m->current_ap_data()->best_group_cipher() != core_cipher_suite_tkip ) + { + DEBUG( "core_server_c::mic_failure() - TKIP not the best group cipher, ignoring mic failure" ); + return; + } + } + else + { + if( connection_data_m->current_ap_data() != NULL && + connection_data_m->current_ap_data()->best_pairwise_cipher() != core_cipher_suite_tkip ) + { + DEBUG( "core_server_c::mic_failure() - TKIP not the best pairwise cipher, ignoring mic failure" ); + return; + } + } + + const core_mac_address_s bssid( + connection_data_m->current_ap_data()->bssid() ); + + cm_timer_m.mic_failure(); + + network_id_c network_id( + const_cast( &bssid.addr[0] ), + MAC_ADDR_LEN, + &own_mac_addr_m.addr[0], + MAC_ADDR_LEN, + eapol_m->ethernet_type() ); + + ASSERT( eapol_m ); + (void)eapol_m->tkip_mic_failure( + &network_id, + !cm_timer_m.is_wpa_allowed() ? true_t : false_t, + is_group_key_fail ? wlan_eapol_if_eapol_tkip_mic_failure_type_group_key : + wlan_eapol_if_eapol_tkip_mic_failure_type_pairwise_key ); + + + if ( !cm_timer_m.is_wpa_allowed() ) + { + DEBUG( "core_server_c::mic_failure() - two MIC failures within 60 seconds, disconnecting" ); + + core_operation_base_c* command = new core_operation_release_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m, core_release_reason_tkip_mic_failure ); + + queue_int_operation_and_run_next( command ); + } + } + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// +void core_server_c::queue_unload_drivers() + { + DEBUG( "core_server_c::queue_unload_drivers()" ); + + core_operation_base_c* command = new core_operation_unload_drivers_c( + REQUEST_ID_CORE_INTERNAL, this, &drivers_m, &callback_m ); + + queue_int_operation_and_run_next( command ); + }