/**
    @function: static int __init srecorder_init(void)
    @brief: dump 
    @param: reason
    @return: none
    @note:
**/
static int __init srecorder_init(void)
{
    SRECORDER_PRINTK("Initialization %016lx %016lx.\n", params[0], params[1]);

    srecorder_init_symbols();

    srecorder_init_dev();

    srecorder_ioremap_dts_memory();

    srecorder_init_log_headers();

    srecorder_resigter_notifiers();

#ifdef CONFIG_KPROBES
    srecorder_resigter_jprobes();
#endif

    srecorder_enable_log_category_flags();

    srecorder_enable_log_type_flags();

    SRECORDER_PRINTK("Initialization done.\n");
    
    return 0;
}
/**
    @function: int srecorder_save_log(char *file_path, char *pbuf, int data_len)
    @brief: 在内核中将信息保存到文件中

    @param: file_path 文件路径
    @param: pbuf 存放信息的内存起始地址
    @param: pbuf 要保存的数据长度
    
    @return: 0 - 成功;-1-失败

    @note: 
*/
int srecorder_save_log(char *file_path, char *pbuf, int data_len)
{
    mm_segment_t old_fs;
    loff_t pos;
    struct file *fp;
    
    if (NULL == file_path || NULL == pbuf || data_len <= 0)
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param!\n", __FILE__, __LINE__);
        return -1;
    }

    /*0644文件读写执行权限,所有者:读写、用户组:读、其他:读*/
    fp = filp_open(file_path, O_WRONLY | O_CREAT, 0644); 
    if (NULL != fp)
    {
        old_fs = get_fs();
        set_fs(KERNEL_DS);
        pos = 0;
        vfs_write(fp, pbuf, data_len, &pos);
        /*
        char buf[1024];        
        memset(buf, 0, sizeof(buf));
        pos = 0;
        vfs_read(fp, buf, sizeof(buf) - 1, &pos);*/
        filp_close(fp, NULL);
        set_fs(old_fs);        
    }

    return 0;
}
/**
    @function: int srecorder_get_crash_time(srecorder_reserved_mem_info_for_log_t *pmem_info)
    @brief: 记录死机发生的时间,顺便记录死机的原因。

    @param: pmem_info SRecorder的保留内存信息
    
    @return: 0 - 成功;-1-失败

    @note: 
*/
int srecorder_get_crash_time(srecorder_reserved_mem_info_t *pmem_info)
{
    struct timeval tv;
    /* struct timex txc; */
    struct rtc_time tm;
    int bytes_read = 0;
    char *pbuf = NULL;
    psrecorder_info_header_t pinfo_header = NULL;
        
    if (unlikely(NULL == pmem_info))
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param or kernel symbol addr!\n", __FILE__, __LINE__);
        return -1;
    }

    if (srecorder_get_bit(CRASH_REASON_TIME))
    {
        SRECORDER_PRINTK("Crash reason and time have been dumped successfully!\n");
        return 0;
    }
    
    memset(&tv, 0, sizeof(struct timeval));
    /* memset(&txc, 0, sizeof(struct timex)); */
    memset(&tm, 0, sizeof(struct rtc_time));

    if (0 != srecorder_write_info_header(pmem_info, CRASH_REASON_TIME, &pinfo_header))
    {
        return -1;
    }
    
    do_gettimeofday(&tv);
    tv.tv_sec -= sys_tz.tz_minuteswest * 60; /* 一分钟=60秒 */
    rtc_time_to_tm(tv.tv_sec, &tm);
    pbuf = pmem_info->start_addr + pmem_info->bytes_read;
    bytes_read = SRECORDER_SNPRINTF(pmem_info->start_addr + pmem_info->bytes_read, pmem_info->bytes_left, 
        "Crash reason: %s %s Crash Time: %04d%02d%02d-%02d:%02d:%02d\n", 
        (NULL == pmem_info->crash_reason1) ? ("") : (pmem_info->crash_reason1), 
        (NULL == pmem_info->crash_reason2) ? ("") : (pmem_info->crash_reason2), 
        tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 
        tm.tm_hour, tm.tm_min, tm.tm_sec);
    srecorder_renew_meminfo(pmem_info, bytes_read);
    srecorder_validate_info_header(pinfo_header, pmem_info->bytes_per_type);
    
    return 0;
}
/**
    @function: int srecorder_validate_info_header(srecorder_info_header_t *pheader, unsigned long data_len)
    @brief: 对各种定位信息的头部并进行校验

    @param: pheader 各类信息的校验头
    @param: data_len 每类信息的数据长度,不包括校验头的数据
    
    @return: 0 - 成功;-1-失败

    @note: 
*/
int srecorder_validate_info_header(srecorder_info_header_t *pheader, unsigned long data_len)
{
#ifndef CONFIG_SRECORDER_DUMP_LOG_TO_STORAGECARD
    unsigned long buf[2] = {0, 0};

    if (unlikely(NULL == pheader))
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param!\n", __FILE__, __LINE__);
        return -1;
    }
       
    pheader->data_len = data_len;
    buf[0] = (unsigned long)pheader->type;
    buf[1] = (unsigned long)pheader->data_len;
    pheader->crc32 = srecorder_get_crc32((unsigned char *)buf, sizeof(buf));
#endif

    return 0;
}
/**
    @function:int srecorder_write_info_header(srecorder_reserved_mem_info_for_log_t *pmem_info, 
        srecorder_info_type_e type, 
        psrecorder_info_header_t *pinfo_header)
    @brief: 初始化给类信息的头部数据结构

    @param: pmem_info SRecorder的保留内存信息
    @param: type 信息类型
    @param: pinfo_header 信息头部校验数据结构
    
    @return: 0 - 成功;-1-失败

    @note: 
*/
int srecorder_write_info_header(srecorder_reserved_mem_info_t *pmem_info, 
    srecorder_info_type_e type, 
    psrecorder_info_header_t *pinfo_header)
{
    int bytes_read = 0;
/* 删除此处定义 */
#ifndef CONFIG_SRECORDER_DUMP_LOG_TO_STORAGECARD
    int info_header_size = sizeof(srecorder_info_header_t);
#endif

    if (unlikely(NULL == pmem_info || NULL == pmem_info->start_addr || NULL == pinfo_header 
        || (LOG_TYPE_COUNT <= (int)type || (int)type < 0)))
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param!\n", __FILE__, __LINE__);
        return -1;
    }

#ifndef CONFIG_SRECORDER_DUMP_LOG_TO_STORAGECARD
    /*把校验信息的头部结构留出来*/
    /* 删除此处 */
    if ((pmem_info->bytes_read + info_header_size) > pmem_info->mem_size)
    {
        return -1;
    }

    *pinfo_header = (psrecorder_info_header_t)(pmem_info->start_addr + pmem_info->bytes_read);
    memset(*pinfo_header, 0, info_header_size);
    ((psrecorder_info_header_t)*pinfo_header)->type = type;
    
    pmem_info->bytes_read += (info_header_size);
#endif

    pmem_info->bytes_per_type = 0;

    bytes_read = SRECORDER_SNPRINTF(pmem_info->start_addr + pmem_info->bytes_read, pmem_info->bytes_left, 
        "%s\n", s_srecorder_head_info[type].desciprion);
    srecorder_renew_meminfo(pmem_info, bytes_read);
    
    return 0;
}
/**
    @function: int srecorder_get_modem_log(srecorder_reserved_mem_info_for_log_t *pmem_info)
    @brief: 读取modem死机堆栈和运行日志

    @param: pmem_info SRecorder的保留内存信息
    
    @return: 0 - 成功;-1-失败

    @note: 
*/
int srecorder_get_modem_log(srecorder_reserved_mem_info_t *pmem_info)
{
    psrecorder_info_header_t pinfo_header = NULL;
    
    if (unlikely(NULL == pmem_info))
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param or kernel symbol addr!\n", __FILE__, __LINE__);
        return -1;
    }

    if (srecorder_log_has_been_dumped(MODEM_ERR_BIT7))
    {
        SRECORDER_PRINTK("modem err has been dumped successfully!\n");
    }

    if (srecorder_log_has_been_dumped(MODEM_ERR_F3_BIT8))
    {
        SRECORDER_PRINTK("modem err f3 has been dumped successfully!\n");
    }
    
    if (pmem_info->dump_modem_crash_log_only)
    {
#if defined(CONFIG_DUMP_MODEM_LOG_BY_FIQ)
        if (pmem_info->do_delay_when_dump_modem_log)
        {
            /* do delay about 10ms */
            unsigned long ips = 0;
            
            ips = cpufreq_get_directly(smp_processor_id()) * 1000; /* KHZ = 1000*/
            if (unlikely(0x0 == ips))
            {
                ips = CONFIG_CPU_FREQ_DEFAULT_VALUE;
            }
            
            if (likely(0x0 != CONFIG_DUMP_MODEM_LOG_DELAY_MAX_MS))
            {
                srecorder_do_delay(ips / (1000 / CONFIG_DUMP_MODEM_LOG_DELAY_MAX_MS));
            }
        }
#endif

#if !defined(CONFIG_ARCH_MSM8930)
        if (!srecorder_log_has_been_dumped(MODEM_ERR_BIT7))
        {
            if (0 != srecorder_write_info_header(pmem_info, MODEM_ERR_BIT7, &pinfo_header))
            {
                return -1;
            }
            srecorder_debug_modem_err(pmem_info);
            srecorder_validate_info_header(pinfo_header, pmem_info->bytes_per_type);
        }
#else
#endif
        
#if !defined(CONFIG_ARCH_MSM8930)
        if (!srecorder_log_has_been_dumped(MODEM_ERR_F3_BIT8))
        {
            if (0 != srecorder_write_info_header(pmem_info, MODEM_ERR_F3_BIT8, &pinfo_header))
            {
                return -1;
            }
            srecorder_debug_modem_err_f3(pmem_info);
            srecorder_validate_info_header(pinfo_header, pmem_info->bytes_per_type);
        }
#else
#endif
    }
    
    return 0;
}
/**
    @function: static int srecorder_get_symbol_kallsyms_lookup_name(const char *line, int len)
    @brief: 给s_kernel_symbols_table表中的内核符号找到对应的地址

    @param: line - 在/proc/kallsyms中所读的一行内容
    @param: len - 暂时没用
    
    @return: 0 - 成功;<0-失败

    @note: 
*/
static int srecorder_get_symbol_kallsyms_lookup_name(const char *line, int len)
{
    int ret = 0;
    srec_ksym_addr_t address;    
    char *psymbolname = NULL;
    char *pmodulename = NULL;
    char dummy;

    if (NULL == line)
    {
        SRECORDER_PRINTK("File [%s] line [%d] invalid param!\n", __FILE__, __LINE__);
        return -EINVAL;
    }

    psymbolname = kmalloc(NAME_MAX, GFP_KERNEL);
    if (NULL == psymbolname)
    {
        ret = -ENOMEM;
        goto err_out;
    }

    pmodulename = kmalloc(NAME_MAX, GFP_KERNEL);
    if (NULL == pmodulename)
    {        
        ret = -ENOMEM;
        goto err_out;
    }

    memset(psymbolname, 0, NAME_MAX);
    memset(pmodulename, 0, NAME_MAX);

    /* 
   * 包含kallsyms_lookup_name的line的格式为"[address] [type] [symboleName]" 
   * 遇到"[address] [type] [symboleName] [moduleName]"格式直接返回 
   * /proc/kallsyms中每行一般3个参数
   */
    if (3 != sscanf(line, "%lx %c %s [%s]", &address, &dummy, psymbolname, pmodulename))
    {
        ret = -ENOENT;
        goto err_out;
    }

    /*判断是否是需要的符号kallsyms_lookup_name*/
    if (strncmp(KALLSYMS_LOOKUP_NAME_FUNC, psymbolname, NAME_MAX) != 0)
    {
        ret = -ENOENT;
        goto err_out;
    }

    s_kallsyms_lookup_name =  (kallsyms_lookup_name_func)address;
    ret = 0;

err_out:
    if (NULL != psymbolname)
    {
        kfree(psymbolname);
        psymbolname = NULL;
    }

    if (NULL != pmodulename)
    {
        kfree(pmodulename);
        pmodulename = NULL;
    }

    return ret;
}
/**
    @function: int srecorder_register_external_log(unsigned id, void(*callback)(void*))
    @brief: 
    @param: 
    @return: 
    @note:
**/
int srecorder_register_external_log(unsigned id, void(*callback)(void*))
{
    log_registration_entry_t* tmp_entry = NULL;
    log_registration_entry_t* log_entry = NULL;

    if (external_flag == 0)
    {
        SRECORDER_PRINTK("The dump flag of external log isn't enabled\n");
        return -1;
    }

    if (id == 0 || callback == NULL)
    {
        return -1;
    }

    /* allocate one entry first to safely use spinlock */
    log_entry = kzalloc(sizeof(log_registration_entry_t), GFP_KERNEL);
    if (log_entry == NULL)
    {
        return -1;
    }

    if (spin_trylock(&srecorder_registration_lock) == 0)
    {
        goto out;
    }

    if (spin_trylock(&srecorder_list_lock) == 0)
    {
        goto reg_out;
    }

    tmp_entry = p_log_registration_list;
    while (tmp_entry != NULL)
    {
        if (tmp_entry->info.log_id == id)
        {
            goto list_out;
        }

        tmp_entry = tmp_entry->next;
    }

    log_entry->info.log_id = id;

    log_entry->callback = callback;

    log_entry->next = p_log_registration_list;

    p_log_registration_list = log_entry;

    spin_unlock(&srecorder_list_lock);
    spin_unlock(&srecorder_registration_lock);
    return 0;

list_out:
    spin_unlock(&srecorder_list_lock);
reg_out:
    spin_unlock(&srecorder_registration_lock);
out:
    kfree(log_entry);
    return -1;
}