blob: c86907e53a445f3e479c17cf1ac57d22ac92b63a [file] [log] [blame]
// Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <fcntl.h>
#include <assert.h>
#include <errno.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <dirent.h>
#include <stdarg.h>
#include <wchar.h>
#include <linux/version.h>
#include <signal.h>
#include "cm.h"
#include "exitcb.h"
#include "profilestring.h"
#include "dhclient.h"
extern void reg_indications(APIHAND *api_handle);
extern void kill_dhclient(int dev_idx);
extern int setup_device_conf(GDEV_ID *ID, const unsigned char *mac);
extern void load_device_conf(dev_conf_t *conf, const char *mac);
extern dev_conf_t default_dev_conf;
int cm_init(int read_only);
int cm_deinit(void);
APIHAND cm_api_handle;
char cm_odev_list[MAX_DEV];
dev_conf_t cm_dev_conf[MAX_DEV];
int cm_odev_cnt;
cm_common_conf_t cm_common_conf;
int get_first_odev(void)
{
int i;
if (cm_odev_cnt) {
for (i = 0; i < MAX_DEV; i++) {
if (cm_odev_list[i])
return i;
}
}
return 0;
}
void add_odev_list(int dev_idx)
{
assert(!cm_odev_list[dev_idx]);
cm_odev_list[dev_idx] = 1;
cm_odev_cnt++;
}
int del_odev_list(int dev_idx)
{
if (cm_odev_list[dev_idx]) {
cm_odev_list[dev_idx] = 0;
cm_odev_cnt--;
return 1;
}
return 0;
}
int cm_get_filesize(const char *file)
{
int fd, len;
if ((fd = open(file, O_RDONLY)) < 0) {
cm_printf("open(%s) fail. %s(%d)\n", file, strerror(errno), errno);
return -1;
}
len = lseek(fd, 0, SEEK_END);
close(fd);
return len;
}
int cm_read_file(const char *file, char *buf, int buf_size)
{
int fd, len, total = 0;
if ((fd = open(file, O_RDONLY)) < 0) {
cm_printf("open(%s) fail. %s(%d)\n", file, strerror(errno), errno);
return -1;
}
while ((len = read(fd, &buf[total], buf_size-total)) > 0) {
total += len;
if (buf_size-total == 0)
break;
}
if (len < 0)
cm_printf("read(%s) fail. %s(%d)\n", file, strerror(errno), errno);
close(fd);
return total;
}
int cm_write_file(const char *file, char *buf, int buf_size, int flags)
{
int fd, len = 0, total = 0;
mode_t mode = 0;
if (flags & O_CREAT)
mode = 0644;
if ((fd = open(file, flags, mode)) < 0) {
cm_printf("open(%s) fail. %s(%d)\n", file, strerror(errno), errno);
return -1;
}
if (buf_size) {
while ((len = write(fd, &buf[total], buf_size-total)) > 0) {
total += len;
if (buf_size-total == 0)
break;
}
}
if (len < 0)
cm_printf("write(%s) fail\n", file);
close(fd);
return total;
}
int cm_cmp_file(const char *file, const char *file2)
{
int fd = -1, fd2 = -1;
int ret = -1;
int len, len2;
char buf[4096], buf2[4096];
if ((fd = open(file, O_RDONLY)) < 0) {
cm_printf("open(%s) fail. %s(%d)\n", file, strerror(errno), errno);
goto out;
}
if ((fd2 = open(file2, O_RDONLY)) < 0) {
cm_printf("open(%s) fail. %s(%d)\n", file2, strerror(errno), errno);
goto out;
}
do {
if ((len = read(fd, buf, sizeof(buf))) < 0) {
cm_printf("read(%d:%s) fail. %s(%d)\n", fd, file, strerror(errno), errno);
break;
}
if ((len2 = read(fd2, buf2, sizeof(buf2))) < 0) {
cm_printf("read(%d:%s) fail. %s(%d)\n", fd2, file2, strerror(errno), errno);
break;
}
if (len != len2)
break;
if (len < 0 || len2 < 0)
break;
if (len == 0 && len2 == 0) {
ret = 0;
break;
}
if (memcmp(buf, buf2, len))
break;
} while (1);
out:
if (fd > 0)
close(fd);
if (fd2 > 0)
close(fd2);
return ret;
}
int cm_get_dir_entry(const char *dir, int index, char *filepath, int size)
{
DIR *dirp;
struct dirent *dp;
int idx = 0, ret = -1;
char *file;
dirp = opendir(dir);
while ((dp = readdir(dirp)) != NULL) {
file = dp->d_name;
if (!strcmp(file, ".") || !strcmp(file, ".."))
continue;
if (idx++ == index) {
strncpy(filepath, file, size);
ret = 0;
break;
}
}
closedir(dirp);
return ret;
}
int cm_load_file_lines(const char *file, int lines, ...)
{
char line[1024];
FILE *fp;
va_list list;
char *p;
int i, len;
if (!(fp = fopen(file, "rt"))) {
cm_eprintf("fopen(%s) error\n", file);
return -1;
}
va_start(list, lines);
for (i = 0; i < lines; i++) {
p = va_arg(list, char *);
if (p) *p = 0;
if (!fgets(line, sizeof(line), fp))
break;
len = strlen(line);
if (len >= 2 && line[len-2] == '\r') line[len-2] = 0;
if (len >= 1 && line[len-1] == '\n') line[len-1] = 0;
//cm_printf("[%s]\n", line);
if (p) strcpy(p, line);
}
va_end(list);
fclose(fp);
return i;
}
int cm_store_file_lines(const char *file, int lines, ...)
{
FILE *fp;
va_list list;
char *p;
int i;
if (!(fp = fopen(file, "wt"))) {
cm_eprintf("fopen(%s) error\n", file);
return -1;
}
va_start(list, lines);
for (i = 0; i < lines; i++) {
p = va_arg(list, char *);
if (fprintf(fp, "%s\n", p ? p : "") < 0) {
cm_eprintf("fprintf(%s) error\n", file);
break;
}
//cm_printf("[%s]\n", p);
}
va_end(list);
fclose(fp);
return i;
}
char *cm_get_dev_idx2name(int dev_idx)
{
static char name_buf[MAX_DEV][8];
char *name = name_buf[dev_idx];
sprintf(name, GCT_WM_PREFIX "%d", dev_idx-1);
return name;
}
int cm_get_dhcp_ip(int dev_idx, char *ip)
{
#define sizeof_sa_sin_port 2
#define p_inaddrr(x) (&ifr.x[sizeof_sa_sin_port])
struct ifreq ifr;
struct in_addr inaddr;
int fd = 0;
int ret = 0;
fd = socket(PF_PACKET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0) {
cm_eprintf("socket\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, cm_get_dev_idx2name(dev_idx), IFNAMSIZ);
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {
//cm_dprintf("ioctl SIOCGIFFLAGS(%d)\n", fd);
ret = -1;
goto out;
}
memcpy(&inaddr, p_inaddrr(ifr_addr.sa_data), sizeof(inaddr));
if (ip)
strcpy(ip, inet_ntoa(inaddr));
out:
if (fd > 0)
close(fd);
return ret;
}
static void extract_fw_version(const char *fw_ver, unsigned *app_ver, unsigned *mac_ver)
{
const char *fw_ver_fmt = "FW(%d.%d.%d.%d : %d.%d.%d.%d)";
int v[8];
sscanf(fw_ver, fw_ver_fmt, &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6], &v[7]);
*app_ver = ((char)v[0] << 24) | ((char)v[1] << 16) | ((char)v[2] << 8) | (char)v[3];
*mac_ver = ((char)v[4] << 24) | ((char)v[5] << 16) | ((char)v[6] << 8) | (char)v[7];
}
static void extract_fw_w_version(const wchar_t *fw_ver, unsigned *app_ver, unsigned *mac_ver)
{
char ch_fw_ver[128];
wcstombs((char *)ch_fw_ver, fw_ver, wcslen(fw_ver)+1);
extract_fw_version(ch_fw_ver, app_ver, mac_ver);
}
static void setup_fw_version(int dev_idx, wchar_t *fw_ver)
{
dev_conf_t *conf = &cm_dev_conf[dev_idx];
extract_fw_w_version(fw_ver, &conf->fw_app_version, &conf->fw_mac_version);
#if 0
cm_dprintf("fw_app_version=0x%08x\n", conf->fw_app_version);
cm_dprintf("fw_mac_version=0x%08x\n", conf->fw_mac_version);
#endif
}
static int send_print_log_on_eap(GDEV_ID_P pID)
{
#define STR_LOG_ON_EAP "log on eap\n"
char *buf = STR_LOG_ON_EAP;
int len = strlen(STR_LOG_ON_EAP);
char hci_buf[HCI_HEADER_SIZE + 32];
hci_t *hci = (hci_t *) hci_buf;
hci->cmd_evt = _H2B(WIMAX_CLI_CMD);
hci->length = _H2B(len);
memcpy(hci->data, buf, len);
len += HCI_HEADER_SIZE;
if (GAPI_WriteHCIPacket(pID, hci_buf, len) != GCT_API_RET_SUCCESS) {
cm_printf("Write HCI failure\n");
return -1;
}
return 0;
}
int device_open(GDEV_ID_P pID)
{
cm_common_conf_t *pconf = &cm_common_conf;
WIMAX_API_DEVICE_INFO info;
char bl_ver[64];
if (GAPI_WiMaxDeviceOpen(pID) == GCT_API_RET_SUCCESS) {
add_odev_list(pID->deviceIndex);
GAPI_GetBootloaderVersion(pID, bl_ver, sizeof(bl_ver));
cm_printf("BL version: %s\n", bl_ver);
if (GAPI_GetDeviceInformation(pID, &info) != GCT_API_RET_SUCCESS)
return -1;
setup_fw_version(pID->deviceIndex, (wchar_t *)info.swVersion.version);
cm_printf("HW version: %S\n", (wchar_t *)info.hwVersion.version);
cm_printf("SW version: %S\n", (wchar_t *)info.swVersion.version);
setup_device_conf(pID, info.macAddress);
if (pconf->eap_log_enable && CM_USE_EEAP && CAP_EEAP_ENABLED(pID->deviceIndex))
send_print_log_on_eap(pID);
if (pconf->api_mode != GCT_WIMAX_API_PRIVILEGE_READ_ONLY) {
if (pconf->auto_connect_enable)
cm_request_auto_connection(pID->deviceIndex);
#if defined(CONFIG_DM_INTERFACE)
else
cm_profiling_rf_up(pID->deviceIndex);
#endif
}
return 0;
}
else {
cm_printf("open fail device[%d]\n", pID->deviceIndex);
return -1;
}
}
int device_close(GDEV_ID_P pID)
{
cm_common_conf_t *pconf = &cm_common_conf;
if (del_odev_list(pID->deviceIndex)) {
if (pconf->api_mode != GCT_WIMAX_API_PRIVILEGE_READ_ONLY)
dh_stop_dhclient(pID->deviceIndex);
if (GAPI_WiMaxDeviceClose(pID) == GCT_API_RET_SUCCESS)
return 0;
else {
cm_printf("close fail device[%d]\n", pID->deviceIndex);
return -1;
}
}
cm_printf("Not found device[%d]\n", pID->deviceIndex);
return -1;
}
int open_dev_list(void)
{
WIMAX_API_HW_DEVICE_ID list[256];
UINT32 cnt = 256;
GDEV_ID ID;
int i;
ID.apiHandle = cm_api_handle;
if (GAPI_GetListDevice(cm_api_handle, list, &cnt) == GCT_API_RET_SUCCESS) {
for (i = 0; i < cnt; i++) {
ID.deviceIndex = list[i].deviceIndex;
if (device_open(&ID) < 0)
return -1;
}
return 0;
}
else
return -1;
}
void close_dev_list(void)
{
GDEV_ID ID;
int i;
ID.apiHandle = cm_api_handle;
for (i = 0; i < sizeof(cm_odev_list)/sizeof(cm_odev_list[0]); i++) {
if (cm_odev_list[i]) {
ID.deviceIndex = i;
device_close(&ID);
}
}
}
static void cb_cm_deinit(int signal/*, void *info*/)
{
cm_deinit();
}
#define KEY_log_path "log_path"
#define KEY_log_level "log_level"
#define KEY_eap_log_enable "eap_log_enable"
#define KEY_embedded_eap_enable "embedded_eap_enable"
#define KEY_oma_dm_enable "oma_dm_enable"
#define KEY_nonvolatile_dir "nonvolatile_dir"
#define KEY_run_script_file "run_script_file"
#define KEY_auto_connect_enable "auto_connect_enable"
#define KEY_auto_connect_retry_count "auto_connect_retry_count"
#define KEY_auto_select_profile_index "auto_select_profile_index"
#define KEY_unknown_net_auto_connect_enable "unknown_net_auto_connect_enable"
#define KEY_ip_allocation_timeout_sec "ip_allocation_timeout_sec"
#define KEY_disconnct_on_ip_failure "disconnct_on_ip_failure"
static void cm_load_common(cm_common_conf_t *pconf)
{
char *section = "common";
char str[256], *p;
int len;
p = pconf->log_path;
len = sizeof(pconf->log_path);
get_profile_string(section, KEY_log_path, "./sdklog", p, len, CONF_FILE);
get_profile_string(section, KEY_eap_log_enable, "n", str, sizeof(str), CONF_FILE);
if (!strcasecmp(str, "y"))
pconf->eap_log_enable = 1;
else
pconf->eap_log_enable = 0;
get_profile_string(section, KEY_embedded_eap_enable, "n", str, sizeof(str), CONF_FILE);
if (!strcasecmp(str, "y"))
pconf->embedded_eap_enable = 1;
else
pconf->embedded_eap_enable = 0;
pconf->oma_dm_enable = 0;
p = pconf->nonvolatile_dir;
len = sizeof(pconf->nonvolatile_dir);
get_profile_string(section, KEY_nonvolatile_dir, "./", p, len, CONF_FILE);
len = strlen(pconf->nonvolatile_dir);
if (pconf->nonvolatile_dir[len-1] == '/' || pconf->nonvolatile_dir[len-1] == '\\')
pconf->nonvolatile_dir[len-1] = 0;
p = pconf->run_script_file;
len = sizeof(pconf->run_script_file);
get_profile_string(section, KEY_run_script_file, "", p, len, CONF_FILE);
len = strlen(pconf->run_script_file);
if (pconf->run_script_file[len-1] == '/' || pconf->run_script_file[len-1] == '\\')
pconf->run_script_file[len-1] = 0;
get_profile_string(section, KEY_log_level, "1", str, sizeof(str), CONF_FILE);
pconf->log_level = strtoul(str, NULL, 0);
get_profile_string(section, KEY_auto_connect_enable, "n", str, sizeof(str), CONF_FILE);
if (!strcasecmp(str, "y"))
pconf->auto_connect_enable = 1;
else
pconf->auto_connect_enable = 0;
get_profile_string(section, KEY_auto_connect_retry_count, "0", str, sizeof(str), CONF_FILE);
pconf->auto_connect_retry_count = strtoul(str, NULL, 0);
get_profile_string(section, KEY_auto_select_profile_index, "0", str, sizeof(str), CONF_FILE);
pconf->auto_select_profile_index = strtoul(str, NULL, 0);
get_profile_string(section, KEY_unknown_net_auto_connect_enable, "n", str, sizeof(str), CONF_FILE);
if (!strcasecmp(str, "y"))
pconf->unknown_net_auto_connect_enable = 1;
else
pconf->unknown_net_auto_connect_enable = 0;
get_profile_string(section, KEY_ip_allocation_timeout_sec, "30", str, sizeof(str), CONF_FILE);
pconf->ip_allocation_timeout_sec = strtoul(str, NULL, 0);
get_profile_string(section, KEY_disconnct_on_ip_failure, "n", str, sizeof(str), CONF_FILE);
if (!strcasecmp(str, "y"))
pconf->disconnct_on_ip_failure = 1;
else
pconf->disconnct_on_ip_failure = 0;
}
static void *cm_auto_connect_thread(void *data)
{
cm_common_conf_t *pconf = &cm_common_conf;
cm_msg_cb_t *msg_cb;
int dev_idx;
msg_cb = &pconf->auto_conn_msg;
cm_msg_init(msg_cb);
cm_printf("Auto connection is ready!\n");
while (1) {
dev_idx = DEFAULT_DEVICE;
if (cm_msg_recv(msg_cb, &dev_idx, NULL) < 0) {
cm_eprintf("auto connector cm_msg_recv error\n");
break;
}
if (pconf->auto_connect_enable)
cm_auto_connect(dev_idx);
}
cm_msg_deinit(msg_cb);
pconf->auto_conn_thr = (pthread_t) NULL;
cm_printf("Auto connection finished!\n");
return NULL;
}
void cm_request_auto_connection(int dev_idx)
{
cm_common_conf_t *pconf = &cm_common_conf;
dev_conf_t *conf = &cm_dev_conf[dev_idx];
if (pconf->api_mode == GCT_WIMAX_API_PRIVILEGE_READ_ONLY)
return;
conf->auto_conn_retry_cnt = 0;
cm_printf("Request auto connection device(%d)\n", dev_idx);
if (dev_idx > 0)
cm_msg_send(&pconf->auto_conn_msg, dev_idx, NULL);
}
void cm_retry_auto_connection(int dev_idx)
{
cm_common_conf_t *pconf = &cm_common_conf;
dev_conf_t *conf = &cm_dev_conf[dev_idx];
if (pconf->api_mode == GCT_WIMAX_API_PRIVILEGE_READ_ONLY)
return;
if (++conf->auto_conn_retry_cnt == pconf->auto_connect_retry_count) {
cm_printf("Too many retries(%u), auto_connect_retry_count in cm.conf is %u\n",
conf->auto_conn_retry_cnt, pconf->auto_connect_retry_count);
}
else if (conf->auto_conn_retry_cnt < pconf->auto_connect_retry_count) {
cm_printf("Re-request(%u/%u) auto connection device(%d)\n",
conf->auto_conn_retry_cnt, pconf->auto_connect_retry_count, dev_idx);
if (dev_idx > 0)
cm_msg_send(&pconf->auto_conn_msg, dev_idx, NULL);
}
}
static void cm_create_auto_connector(void)
{
cm_common_conf_t *pconf = &cm_common_conf;
if (!pconf->auto_connect_enable)
cm_printf("Auto connection flag was disabled!\n");
if (pconf->auto_conn_thr) {
cm_eprintf("Auto connector has been started already!\n");
return;
}
pthread_create(&pconf->auto_conn_thr, NULL, cm_auto_connect_thread, NULL);
}
static void cm_delete_auto_connector(void)
{
cm_common_conf_t *pconf = &cm_common_conf;
pthread_t thread;
if ((thread = pconf->auto_conn_thr)) {
pconf->auto_conn_thr = (pthread_t) NULL;
pthread_cancel(thread);
pthread_join(thread, NULL);
cm_printf("Auto connector deleted!\n");
}
pconf->auto_connect_enable = 0;
}
int cm_init(int read_only)
{
cm_common_conf_t *pconf = &cm_common_conf;
GCT_WIMAX_SDK_MODE sdk_mode = 0;
GCT_WIMAX_API_PARAM param;
GCT_API_RET ret;
if (read_only) {
pconf->api_mode = GCT_WIMAX_API_PRIVILEGE_READ_ONLY;
pconf->eap_log_enable = 0;
}
else
pconf->api_mode = GCT_WIMAX_API_OPEN_MODE_NORMAL;
cm_timer_module_init();
cm_load_common(pconf);
if (!read_only) {
cm_create_auto_connector();
dh_create_dhclient();
}
load_device_conf(&default_dev_conf, NULL);
#if defined(CONFIG_DM_INTERFACE)
if (!read_only) {
if (dmif_init() < 0)
return -1;
pconf->dm_interface_enable = 1;
}
#endif
if (mkdir(pconf->log_path, 0644) < 0 && errno != EEXIST) {
cm_eprintf("Access directory(%s) failed %s(%d)\n",
pconf->log_path, strerror(errno), errno);
return -1;
}
if (pconf->embedded_eap_enable)
sdk_mode |= GCT_WIMAX_SDK_EMBEDDED_EAP_ENABLED;
if (pconf->oma_dm_enable)
sdk_mode |= GCT_WIMAX_SDK_OMA_DM_ENABLED;
strcpy(param.nonvolatile_dir, pconf->nonvolatile_dir);
strcpy(param.log_path, pconf->log_path);
param.log_level = pconf->log_level;
ret = GAPI_Initialize(sdk_mode, &param);
if (ret != GCT_API_RET_SUCCESS) {
cm_eprintf("GAPI_Initialize failed(%d)\n", ret);
return -1;
}
register_exit_cb(cb_cm_deinit);
ret = GAPI_WiMaxAPIOpen(&cm_api_handle, pconf->api_mode);
if (ret != GCT_API_RET_SUCCESS) {
cm_eprintf("GAPI_WiMaxAPIOpen failed(%d)\n", ret);
return -1;
}
reg_indications(cm_api_handle);
if (open_dev_list() < 0)
return -1;
return 0;
}
int cm_deinit(void)
{
APIHAND apihand = cm_api_handle;
GCT_API_RET ret;
cm_delete_auto_connector();
unregister_exit_cb(cb_cm_deinit);
close_dev_list();
dh_delete_dhclient();
#if (CONFIG_LOG_FILE_BUF_SIZE > 0)
GAPI_SetDebugLevel(apihand, GAPI_LOG_FLUSH_LEVEL, NULL);
#endif
cm_api_handle = NULL;
GAPI_WiMaxAPIClose(apihand);
ret = GAPI_DeInitialize();
if (ret != GCT_API_RET_SUCCESS)
return -1;
cm_timer_module_deinit();
return 0;
}