| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "wimax_manager/gdm_driver.h" |
| |
| #include <utility> |
| |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include <base/strings/stringprintf.h> |
| #include <base/strings/utf_string_conversions.h> |
| |
| #include "wimax_manager/device_dbus_adaptor.h" |
| #include "wimax_manager/gdm_device.h" |
| #include "wimax_manager/network.h" |
| |
| using std::string; |
| using std::vector; |
| using std::wstring; |
| |
| namespace wimax_manager { |
| |
| namespace { |
| |
| const size_t kMaxNumberOfDevices = 256; |
| const size_t kMaxNumberOfProfiles = 8; |
| const size_t kMaxNumberOfNetworks = 16; |
| |
| const char kLogDirectory[] = "/var/log/gct"; |
| const char kNonVolatileDirectory[] = "/var/cache/gct"; |
| const char *const InitalDirectoriesToCreate[] = { |
| kLogDirectory, kNonVolatileDirectory |
| }; |
| |
| string GetDeviceStatusDescription(WIMAX_API_DEVICE_STATUS device_status) { |
| switch (device_status) { |
| case WIMAX_API_DEVICE_STATUS_UnInitialized: |
| return "Uninitialized"; |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW: |
| return "RF off (both H/W and S/W)"; |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_HW: |
| return "RF off (via H/W switch)"; |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: |
| return "RF off (via S/W switch)"; |
| case WIMAX_API_DEVICE_STATUS_Ready: |
| return "Ready"; |
| case WIMAX_API_DEVICE_STATUS_Scanning: |
| return "Scanning"; |
| case WIMAX_API_DEVICE_STATUS_Connecting: |
| return "Connecting"; |
| case WIMAX_API_DEVICE_STATUS_Data_Connected: |
| return "Connected"; |
| default: |
| return "Invalid"; |
| } |
| } |
| |
| DeviceStatus ConvertDeviceStatus(WIMAX_API_DEVICE_STATUS device_status) { |
| switch (device_status) { |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_HW_SW: |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_HW: |
| case WIMAX_API_DEVICE_STATUS_RF_OFF_SW: |
| return kDeviceStatusDisabled; |
| case WIMAX_API_DEVICE_STATUS_Ready: |
| return kDeviceStatusReady; |
| case WIMAX_API_DEVICE_STATUS_Scanning: |
| return kDeviceStatusScanning; |
| case WIMAX_API_DEVICE_STATUS_Connecting: |
| return kDeviceStatusConnecting; |
| case WIMAX_API_DEVICE_STATUS_Data_Connected: |
| return kDeviceStatusConnected; |
| default: |
| return kDeviceStatusUninitialized; |
| } |
| } |
| |
| string GetConnectionProgressDescription( |
| WIMAX_API_CONNECTION_PROGRESS_INFO connection_progress) { |
| switch (connection_progress) { |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Ranging: |
| return "Ranging"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_SBC: |
| return "SBC"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_Device: |
| return "Device authentication via EAP"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_EAP_authentication_User: |
| return "User authentication via EAP"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_3_way_handshake: |
| return "3-way handshake"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registration: |
| return "Registration"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_De_registration: |
| return "De-registration"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registered: |
| return "Registered"; |
| case WIMAX_API_DEVICE_CONNECTION_PROGRESS_Registration_DSX: |
| return "Registration DSX"; |
| default: |
| return "Invalid"; |
| } |
| } |
| |
| string GetNetworkTypeDescription(NetworkType network_type) { |
| switch (network_type) { |
| case kNetworkHome: |
| return "Home"; |
| case kNetworkPartner: |
| return "Partner"; |
| case kNetworkRoamingParnter: |
| return "Roaming partner"; |
| default: |
| return "Unknown"; |
| } |
| } |
| |
| NetworkType ConvertNetworkType(WIMAX_API_NETWORK_TYPE network_type) { |
| switch (network_type) { |
| case WIMAX_API_HOME: |
| return kNetworkHome; |
| case WIMAX_API_PARTNER: |
| return kNetworkPartner; |
| case WIMAX_API_ROAMING_PARTNER: |
| return kNetworkRoamingParnter; |
| default: |
| return kNetworkUnknown; |
| } |
| } |
| |
| template <size_t N> |
| bool ConvertWideCharacterArrayToUTF8String(const wchar_t (&wide_char_array)[N], |
| std::string *utf8_string) { |
| // Check if the wide character array is NULL-terminated. |
| if (wmemchr(wide_char_array, L'\0', N) == nullptr) |
| return false; |
| |
| size_t wide_string_length = wcslen(wide_char_array); |
| CHECK_LT(wide_string_length, N); |
| |
| return base::WideToUTF8(wide_char_array, wide_string_length, utf8_string); |
| } |
| |
| } // namespace |
| |
| GdmDriver::GdmDriver(Manager *manager) |
| : Driver(manager), |
| api_handle_(nullptr) { |
| } |
| |
| GdmDriver::~GdmDriver() { |
| Finalize(); |
| } |
| |
| bool GdmDriver::Initialize() { |
| CHECK(!api_handle_); |
| |
| LOG(INFO) << "Initializing GDM driver"; |
| |
| if (!CreateInitialDirectories()) |
| return false; |
| |
| GCT_WIMAX_API_PARAM api_param; |
| CHECK_LT(snprintf(api_param.nonvolatile_dir, |
| sizeof(api_param.nonvolatile_dir), |
| "%s", |
| kNonVolatileDirectory), |
| static_cast<int>(sizeof(api_param.nonvolatile_dir))); |
| CHECK_LT(snprintf(api_param.log_path, |
| sizeof(api_param.log_path), |
| "%s", |
| kLogDirectory), |
| static_cast<int>(sizeof(api_param.log_path))); |
| api_param.log_level = 1; |
| GCT_API_RET ret = |
| GAPI_Initialize(GCT_WIMAX_SDK_EMBEDDED_EAP_ENABLED, &api_param); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| ret = GAPI_WiMaxAPIOpen(&api_handle_, GCT_WIMAX_API_OPEN_MODE_NORMAL); |
| if (ret != GCT_API_RET_SUCCESS) { |
| GAPI_DeInitialize(); |
| api_handle_ = nullptr; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool GdmDriver::Finalize() { |
| if (!api_handle_) |
| return true; |
| |
| LOG(INFO) << "Finalizing GDM driver"; |
| |
| bool success = true; |
| GAPI_SetDebugLevel(api_handle_, GAPI_LOG_FLUSH_LEVEL, nullptr); |
| GCT_API_RET ret = GAPI_WiMaxAPIClose(api_handle_); |
| api_handle_ = nullptr; |
| |
| if (ret != GCT_API_RET_SUCCESS) |
| success = false; |
| |
| ret = GAPI_DeInitialize(); |
| if (ret != GCT_API_RET_SUCCESS) |
| success = false; |
| |
| return success; |
| } |
| |
| bool GdmDriver::GetDevices(vector<std::unique_ptr<Device>> *devices) { |
| CHECK(devices); |
| |
| WIMAX_API_HW_DEVICE_ID device_list[kMaxNumberOfDevices]; |
| uint32_t num_devices = static_cast<uint32_t>(arraysize(device_list)); |
| GCT_API_RET ret = GAPI_GetListDevice(api_handle_, device_list, &num_devices); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| VLOG(1) << "Number of devices: " << num_devices; |
| for (size_t i = 0; i < num_devices; ++i) { |
| uint8_t device_index = device_list[i].deviceIndex; |
| string device_name; |
| if (!ConvertWideCharacterArrayToUTF8String(device_list[i].deviceName, |
| &device_name)) { |
| LOG(ERROR) << base::StringPrintf( |
| "Ignoring device with index %d due to invalid device name", |
| device_index); |
| continue; |
| } |
| |
| VLOG(1) << base::StringPrintf("Found device '%s': index = %d", |
| device_name.c_str(), |
| device_index); |
| |
| std::unique_ptr<GdmDevice> device(new(std::nothrow) GdmDevice( |
| manager(), device_index, device_name, AsWeakPtr())); |
| CHECK(device); |
| // The WiMAX device changes its MAC address to the actual value after the |
| // firmware is loaded. Opening the device seems to be enough to trigger the |
| // update of the MAC address. So open the device here before |
| // Manager::ScanDevices() creates the device DBus objects. |
| device->Open(); |
| devices->push_back(std::move(device)); |
| } |
| return true; |
| } |
| |
| bool GdmDriver::OpenDevice(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_WiMaxDeviceOpen(&device_id); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| WIMAX_API_DEVICE_INFO device_info; |
| ret = GAPI_GetDeviceInformation(&device_id, &device_info); |
| if (ret != GCT_API_RET_SUCCESS) |
| return CloseDevice(device); |
| |
| ByteIdentifier mac_address(device_info.macAddress, |
| arraysize(device_info.macAddress)); |
| device->SetMACAddress(mac_address); |
| |
| LOG(INFO) << "Opened device '" << device->name() |
| << "': MAC address = " << device->mac_address().GetHexString(); |
| return true; |
| } |
| |
| bool GdmDriver::CloseDevice(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_WiMaxDeviceClose(&device_id); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::GetDeviceStatus(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| WIMAX_API_DEVICE_STATUS device_status; |
| WIMAX_API_CONNECTION_PROGRESS_INFO connection_progress; |
| GCT_API_RET ret = |
| GAPI_GetDeviceStatus(&device_id, &device_status, &connection_progress); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| device->SetStatus(ConvertDeviceStatus(device_status)); |
| device->set_connection_progress(connection_progress); |
| |
| VLOG(1) << "Device '" << device->name() |
| << "': status = '" << GetDeviceStatusDescription(device_status) |
| << "', connection progress = '" |
| << GetConnectionProgressDescription(connection_progress) << "'"; |
| return true; |
| } |
| |
| bool GdmDriver::GetDeviceRFInfo(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RF_INFORM rf_info; |
| GCT_API_RET ret = GAPI_GetRFInform(&device_id, &rf_info); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| ByteIdentifier base_station_id(rf_info.bsId, arraysize(rf_info.bsId)); |
| device->SetBaseStationId(base_station_id); |
| device->set_frequency(rf_info.Frequency); |
| |
| device->set_cinr({Network::DecodeCINR(rf_info.CINR), |
| Network::DecodeCINR(rf_info.CINR2)}); |
| |
| device->set_rssi({Network::DecodeRSSI(rf_info.RSSI), |
| Network::DecodeRSSI(rf_info.RSSI2)}); |
| |
| device->UpdateRFInfo(); |
| return true; |
| } |
| |
| bool GdmDriver::SetDeviceEAPParameters(GdmDevice *device, |
| GCT_API_EAP_PARAM *eap_parameters) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_SetEap(&device_id, eap_parameters); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::AutoSelectProfileForDevice(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| WIMAX_API_PROFILE_INFO profile_list[kMaxNumberOfProfiles]; |
| uint32_t num_profiles = static_cast<uint32_t>(arraysize(profile_list)); |
| GCT_API_RET ret = |
| GAPI_GetSelectProfileList(&device_id, profile_list, &num_profiles); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| LOG(INFO) << "Number of profiles: " << num_profiles; |
| for (size_t i = 0; i < num_profiles; ++i) { |
| string profile_name; |
| if (ConvertWideCharacterArrayToUTF8String(profile_list[i].profileName, |
| &profile_name)) { |
| LOG(INFO) << base::StringPrintf("Found profile '%s': id = %d", |
| profile_name.c_str(), |
| profile_list[i].profileID); |
| } |
| } |
| |
| if (num_profiles == 0) |
| return false; |
| |
| ret = GAPI_SetProfile(&device_id, profile_list[0].profileID); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::PowerOnDeviceRF(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_CmdControlPowerManagement(&device_id, WIMAX_API_RF_ON); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::PowerOffDeviceRF(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = |
| GAPI_CmdControlPowerManagement(&device_id, WIMAX_API_RF_OFF); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::SetScanInterval(GdmDevice *device, uint32_t interval) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_SetScanInterval(&device_id, interval); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::GetNetworksForDevice(GdmDevice *device, |
| vector<NetworkRefPtr> *networks) { |
| CHECK(networks); |
| |
| GDEV_ID device_id = GetDeviceId(device); |
| WIMAX_API_NSP_INFO network_list[kMaxNumberOfNetworks]; |
| uint32_t num_networks = static_cast<uint32_t>(arraysize(network_list)); |
| GCT_API_RET ret = GAPI_GetNetworkList(&device_id, network_list, |
| &num_networks); |
| if (ret != GCT_API_RET_SUCCESS) |
| return false; |
| |
| // After connected to a network, the NSP info returned by GAPI_GetNetworkList |
| // no longer contains updated CINR and RSSI values. The following code works |
| // around the issue by getting the CINR and RSSI values via GAPI_GetRFInform |
| // when the device is in the connected state. |
| bool use_link_info = false; |
| int link_cinr = Network::kMinCINR; |
| int link_rssi = Network::kMinRSSI; |
| WIMAX_API_DEVICE_STATUS device_status; |
| WIMAX_API_CONNECTION_PROGRESS_INFO connection_progress; |
| ret = GAPI_GetDeviceStatus(&device_id, &device_status, &connection_progress); |
| if (ret == GCT_API_RET_SUCCESS && |
| device_status == WIMAX_API_DEVICE_STATUS_Data_Connected) { |
| GCT_API_RF_INFORM rf_info; |
| ret = GAPI_GetRFInform(&device_id, &rf_info); |
| if (ret == GCT_API_RET_SUCCESS) { |
| use_link_info = true; |
| link_cinr = Network::DecodeCINR(rf_info.CINR); |
| link_rssi = Network::DecodeRSSI(rf_info.RSSI); |
| } |
| } |
| |
| VLOG(1) << "Number of networks: " << num_networks; |
| for (size_t i = 0; i < num_networks; ++i) { |
| uint32_t network_id = network_list[i].NSPid; |
| string network_name; |
| if (!ConvertWideCharacterArrayToUTF8String(network_list[i].NSPName, |
| &network_name)) { |
| LOG(ERROR) << base::StringPrintf( |
| "Ignoring network with identifer %08x due to invalid network name", |
| network_id); |
| continue; |
| } |
| |
| wstring network_name_wcs; |
| if (!base::UTF8ToWide(network_name.c_str(), network_name.size(), |
| &network_name_wcs) || |
| (wcscmp(network_name_wcs.c_str(), network_list[i].NSPName) != 0)) { |
| LOG(ERROR) << base::StringPrintf( |
| "Ignoring network with identifer %08x " |
| "due to conversation error of network name", |
| network_id); |
| continue; |
| } |
| |
| NetworkType network_type = ConvertNetworkType(network_list[i].networkType); |
| int network_cinr = |
| use_link_info ? link_cinr : Network::DecodeCINR(network_list[i].CINR); |
| int network_rssi = |
| use_link_info ? link_rssi : Network::DecodeRSSI(network_list[i].RSSI); |
| LOG(INFO) << base::StringPrintf( |
| "Found network '%s': type = '%s', id = %08x, CINR = %d, RSSI = %d", |
| network_name.c_str(), GetNetworkTypeDescription(network_type).c_str(), |
| network_id, network_cinr, network_rssi); |
| |
| NetworkRefPtr network = new(std::nothrow) Network( |
| network_id, network_name, network_type, network_cinr, network_rssi); |
| CHECK(network); |
| networks->push_back(network); |
| } |
| return true; |
| } |
| |
| bool GdmDriver::ConnectDeviceToNetwork(GdmDevice *device, |
| const Network &network) { |
| GDEV_ID device_id = GetDeviceId(device); |
| wstring network_name_wcs; |
| if (!base::UTF8ToWide(network.name().c_str(), network.name().size(), |
| &network_name_wcs)) { |
| return false; |
| } |
| |
| GCT_API_RET ret = |
| GAPI_CmdConnectToNetwork(&device_id, |
| const_cast<wchar_t *>(network_name_wcs.c_str()), |
| 0); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::DisconnectDeviceFromNetwork(GdmDevice *device) { |
| GDEV_ID device_id = GetDeviceId(device); |
| GCT_API_RET ret = GAPI_CmdDisconnectFromNetwork(&device_id); |
| return ret == GCT_API_RET_SUCCESS; |
| } |
| |
| bool GdmDriver::CreateInitialDirectories() const { |
| for (size_t i = 0; i < arraysize(InitalDirectoriesToCreate); ++i) { |
| const char *directory = InitalDirectoriesToCreate[i]; |
| if (!base::CreateDirectory(base::FilePath(directory))) { |
| LOG(ERROR) << "Failed to create directory '" << directory << "'"; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| GDEV_ID GdmDriver::GetDeviceId(const GdmDevice *device) const { |
| CHECK(device); |
| |
| GDEV_ID device_id; |
| device_id.apiHandle = api_handle_; |
| device_id.deviceIndex = device->index(); |
| return device_id; |
| } |
| |
| } // namespace wimax_manager |