/******************************************************************************/
/* */
/* Copyright (c) 2018 TP-Link Systems Inc. */
/* */
/* File : devmgr_main.c */
/* */
/* Description : doing init process of dev-manager */
/* */
/* Author : libin */
/* */
/******************************************************************************/
/* INCLUDE HEADER FILE */
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <libubox/uloop.h>
#include <libubus-ext.h>
#include <uci_blobmsg.h>
#include "devmgr_global.h"
#include "devmgr_sock.h"
#include "devmgr_dev_ctrl.h"
#include "devmgr_dev_list.h"
#ifdef CFG_DEVMGR_MASTER
#include "devmgr_ubus_master.h"
#include "devmgr_devctl_master.h"
#else
#include <sys/mman.h>
#include <errno.h>
#include "devmgr_ubus_slaver.h"
#include "devmgr_devctl_slaver.h"
#include "devmgr_config_slaver.h"
#endif
/* MACRO DEFINITION */
#define DM_PID_FILE "/var/run/devmgr.pid"
#define DM_FILE_OPEN_FLAG (O_RDWR | O_CREAT)
#define DM_FILE_OPEN_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/*******************************************************************************
Function : file_write_lock
Description : lock a file with write lock from the beginning of the file
Input : fd @ the file descriptor
Output : N/A
Return : 0 @ success
-1 @ failed
*******************************************************************************/
static int file_write_lock(int fd)
{
struct flock lock;
memset(&lock, 0, sizeof(lock));
lock.l_type = F_WRLCK;
lock.l_whence = SEEK_SET;
if (fcntl(fd, F_SETLK, &lock) < 0) {
return -1;
}
return 0;
}
/*******************************************************************************
Function : pid_file_create
Description : create pid file to prevent a second program start to run
Input : N/A
Output : N/A
Return : 0 @ success
-1 @ failed
*******************************************************************************/
static int pid_file_create()
{
int fd;
char buf[16] = {};
fd = open(DM_PID_FILE, DM_FILE_OPEN_FLAG, DM_FILE_OPEN_MODE);
if (fd < 0) {
DBG_NOTICE("open %s failed: %s\n", DM_PID_FILE, strerror(errno));
return -1;
}
if (file_write_lock(fd) < 0) {
if (EACCES == errno || EAGAIN == errno) {
DBG_NOTICE("process are already running\n");
close(fd);
return -1;
}
DBG_NOTICE("cannot lock %s: %s\n", DM_PID_FILE, strerror(errno));
close(fd);
return -1;
}
ftruncate(fd, 0);
snprintf(buf, sizeof(buf), "%d", getpid());
write(fd, buf, strlen(buf) + 1);
return 0;
}
/*******************************************************************************
Function : devmgr_ubus_init
Description : init ubus interface
Input : void
Output : N/A
Return : void
*******************************************************************************/
void devmgr_ubus_init()
{
#ifdef CFG_DEVMGR_MASTER
master_ubus_init();
#else
slaver_ubus_init();
#endif
}
/*******************************************************************************
Function : devmgr_devctl_init
Description : init device control interface
Input : void
Output : N/A
Return : void
*******************************************************************************/
void devmgr_devctl_init()
{
#ifdef CFG_DEVMGR_MASTER
master_devctl_init();
#else
slaver_devctl_init();
#endif
}
/*******************************************************************************
Function : devmgr_config_init
Description : read config and init some global variables
Input : void
Output : N/A
Return : void
*******************************************************************************/
void devmgr_config_init()
{
device_list_init();
#ifdef CFG_DEVMGR_MASTER
devmgr_migrate_init();
device_list_update_timer_init();
device_list_upgrade_dev_timer_init();
device_heartbeart_freq_init();
#else
slaver_hub_ipaddr_init();
slaver_onboarding_step_init();
slaver_start();
#endif
}
#ifdef CFG_DEVMGR_SLAVER
#if defined(DEV_C420) || defined(DEV_C400) || defined(DEV_D230) || defined(DEV_D200)
static int calculate_mem_size()
{
int s = 0, m = 0, c = 0;
char buf[1024] = {0}, *tmp = NULL;
FILE * fp = fopen("/proc/cmdline", "r");
if (fp)
{
fread(buf, sizeof(buf), 1, fp);
fclose(fp);
if ((tmp = strstr(buf, "mem=")) != NULL
&& sscanf(tmp + sizeof("mem=") - 1, "%d", &m) == 1)
{
s += m;
}
if ((tmp = strstr(buf, "rmem=")) != NULL
&& sscanf(tmp + sizeof("rmem=") - 1, "%d", &m) == 1)
{
s += m;
}
}
return s;
}
//x^16 + x^12 + x^5 + x^0
#define CRC16_POLY_LE 0x8408
void devmgr_crc_calculation(uint8_t *crc_code, uint8_t *dat, uint8_t len){
uint8_t i;
uint16_t crc = 0;
crc = ~crc;
while (len--) {
crc ^= *dat++;
for (i = 0; i < 8; i++) {
crc = (crc >> 1) ^ ((crc & 1) ? CRC16_POLY_LE : 0);
}
}
crc = ~crc;
crc_code[0] = crc>>8;
crc_code[1] = (crc & 0x00FF);
}
static uint8_t devmgr_check_read_mcu(unsigned char *fb_data, int base){
unsigned char data[20];
unsigned char crc_code[2];
unsigned char crc_code_calc[2];
#ifdef DOORBELL_FUNCTION_SUPPORT
uint8_t len = 9, i;
for(i=0; i<len; i++){
data[i] = *((unsigned char *)(fb_data + base + i));
}
crc_code[0] = *((unsigned char *)(fb_data + base + 0x09));
crc_code[1] = *((unsigned char *)(fb_data + base + 0x0A));
/*
DBG_NOTICE("devmgr read boot arg: 0x%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", data[0],data[1],data[2],\
data[3],data[4],data[5],data[6],data[7],data[8],crc_code[0],crc_code[1]);
*/
#else
uint8_t len = 8, i;
for(i=0; i<len; i++){
data[i] = *((unsigned char *)(fb_data + base + i));
}
crc_code[0] = *((unsigned char *)(fb_data + base + 0x08));
crc_code[1] = *((unsigned char *)(fb_data + base + 0x09));
/*
DBG_NOTICE("devmgr read boot arg: 0x%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", data[0],data[1],data[2],\
data[3],data[4],data[5],data[6],data[7],crc_code[0],crc_code[1]);
*/
#endif
devmgr_crc_calculation(crc_code_calc, data, len);
//DBG_NOTICE("devmgr crc_code_calc: 0x%02X %02X\n", crc_code_calc[0],crc_code_calc[1]);
if(crc_code_calc[0]==crc_code[0] && crc_code_calc[1]==crc_code[1]){
return 1;
}
return 0;
}
void devmgr_read_fb_mcu()
{
static const int memsize = 0x10000;
unsigned char *fb_data = NULL;
unsigned char event_id = 0;
int fd = -1, base = 0xFE00;
char buf[512] = {0};
uint8_t ret = 0;
if (calculate_mem_size() == 64) {
fd = open("/dev/mcu_info", O_RDWR);
if (fd < 0) {
DBG_ERR("%s:open /dev/mcu_info failed\n", __func__);
return;
}
ioctl(fd, 0, buf);
fb_data = buf;
base = 0;
} else {
fd = open("/dev/mem", O_RDWR);
if (fd < 0) {
DBG_ERR("%s:open /dev/mem failed\n", __func__);
return;
}
fb_data = mmap(0, memsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0x37F0000);
if ((void*)fb_data == MAP_FAILED) {
DBG_ERR("mmap failed with %d %d %d %d %d %d\n", errno, EBADF, EACCES, EINVAL, EAGAIN, ENOMEM);
close(fd);
return;
}
}
event_id = (fb_data + base)[0];
#ifdef DOORBELL_FUNCTION_SUPPORT
ret = devmgr_check_read_mcu(fb_data, base);
if(!ret){
DBG_ERR("[devmgr] boot args crc check error, set event_id to 170\n");
slaver_send_task_invalid_event_id(event_id);
event_id = 170;
}
#endif
if (event_id == 9) {
DBG_NOTICE("recv event id 9 when power on, start to send unbind request to hub\n");
slaver_send_task_unbind();
}
else if (event_id == 12) {
DM_PRINT("event_id: 12, rejoin req.\n");
slaver_send_task_sub1g_rejoin();
}
else if (event_id == 4) {
int onboarding_step = slaver_onboarding_step_get();
if (onboarding_step == ONBOARDING_STEP_TOKEN_GET ||
onboarding_step == ONBOARDING_STEP_SUCCESS) {
slaver_heartbeat_send();
}
}
if (fb_data != buf)
munmap(fb_data, memsize);
close(fd);
}
#endif
#endif
/*******************************************************************************
Function : devmgr_init
Description : dev-manager initiation
Input : void
Output : N/A
Return : void
*******************************************************************************/
void devmgr_init()
{
#ifdef CFG_DEVMGR_SLAVER
#ifdef DEV_KC300
int hibernate_fd = common_hibernate_register("dev-manager");
if (hibernate_fd < 0) {
DBG_WARN("hibernate_sync register failed: #%d %s\n",
-hibernate_fd, strerror(-hibernate_fd));
}
#endif
#endif
devmgr_ubus_init();
#ifdef CFG_DEVMGR_MASTER
master_online_status_init();
#endif
devmgr_devctl_init();
devmgr_socket_init();
#ifdef CFG_DEVMGR_SLAVER
#ifdef DEV_KC300
if (hibernate_fd >= 0) {
common_hibernate_wait_and_close(hibernate_fd);
}
#endif
#endif
devmgr_config_init();
#ifdef CFG_DEVMGR_SLAVER
#if defined(DEV_C420) || defined(DEV_C400) || defined(DEV_D230) || defined(DEV_D200)
devmgr_read_fb_mcu();
#endif
#endif
}
/*******************************************************************************
Function : main
Description : main function
Input : argc @
argv @
Output : N/A
Return : 0
*******************************************************************************/
int main(int argc, char *argv[])
{
tpdbg_set_module_name("dev-manager");
DBG_NOTICE("dev-manager start\n");
if (pid_file_create() < 0) {
exit(1);
}
uloop_init();
devmgr_init();
uloop_run();
uloop_done();
#ifdef CFG_DEVMGR_MASTER
devmgr_battery_statistic_stop();
#endif
DBG_NOTICE("dev-manager exit\n");
return 0;
}