blob: 40b9035c197967113e10df8279df8c84ac93cf44 [file] [log] [blame]
// 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 <memory>
#include <string>
#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 kInitalDirectoriesToCreate[] = {
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);
auto device = std::make_unique<GdmDevice>(manager(), device_index,
device_name, AsWeakPtr());
// 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 Network(network_id, network_name, network_type,
network_cinr, network_rssi);
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 (const char* directory : kInitalDirectoriesToCreate) {
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