1572 lines
42 KiB
C
1572 lines
42 KiB
C
/*
|
|
* aw_acf_bin.c
|
|
*
|
|
* Copyright (c) 2021 AWINIC Technology CO., LTD
|
|
*
|
|
* Author: Barry <zhaozhongbo@awinic.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <asm/uaccess.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/device.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/vmalloc.h>
|
|
#include "aw87xxx.h"
|
|
#include "aw_acf_bin.h"
|
|
#include "aw_monitor.h"
|
|
#include "aw_log.h"
|
|
#include "aw_bin_parse.h"
|
|
|
|
static char *g_profile_name[] = {"Music", "Voice", "Voip",
|
|
"Ringtone", "Ringtone_hs", "Lowpower", "Bypass", "Mmi",
|
|
"Fm", "Notification", "Receiver", "Off"};
|
|
|
|
static char *g_power_off_name[] = {"Off", "OFF", "off", "oFF", "power_down"};
|
|
|
|
static char *aw_get_prof_name(int profile)
|
|
{
|
|
if (profile < 0 || profile >= AW_PROFILE_MAX)
|
|
return "NULL";
|
|
else
|
|
return g_profile_name[profile];
|
|
}
|
|
|
|
/*************************************************************************
|
|
*
|
|
*acf check
|
|
*
|
|
*************************************************************************/
|
|
static int aw_crc8_check(const unsigned char *data, unsigned int data_size)
|
|
|
|
{
|
|
unsigned char crc_value = 0x00;
|
|
unsigned char *pdata;
|
|
int i;
|
|
unsigned char pdatabuf = 0;
|
|
|
|
pdata = (unsigned char *)data;
|
|
|
|
while (data_size--) {
|
|
pdatabuf = *pdata++;
|
|
for (i = 0; i < 8; i++) {
|
|
if ((crc_value ^ (pdatabuf)) & 0x01) {
|
|
crc_value ^= 0x18;
|
|
crc_value >>= 1;
|
|
crc_value |= 0x80;
|
|
} else {
|
|
crc_value >>= 1;
|
|
}
|
|
pdatabuf >>= 1;
|
|
}
|
|
}
|
|
|
|
return (int)crc_value;
|
|
}
|
|
|
|
static int aw_check_file_id(struct device *dev,
|
|
char *fw_data, int32_t file_id)
|
|
{
|
|
int32_t *acf_file_id = NULL;
|
|
|
|
acf_file_id = (int32_t *)fw_data;
|
|
if (*acf_file_id != file_id) {
|
|
AW_DEV_LOGE(dev, "file id [%x] check failed", *acf_file_id);
|
|
return -ENFILE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_header_size(struct device *dev,
|
|
char *fw_data, size_t fw_size)
|
|
{
|
|
if (fw_size < sizeof(struct aw_acf_hdr)) {
|
|
AW_DEV_LOGE(dev, "acf size check failed,size less-than aw_acf_hdr");
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* V0.0.0.1 version acf check
|
|
**************************************************************************/
|
|
static int aw_check_ddt_size_v_0_0_0_1(struct device *dev, char *fw_data)
|
|
{
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
struct aw_acf_dde *acf_dde = NULL;
|
|
|
|
acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
/* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */
|
|
if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde)) {
|
|
AW_DEV_LOGE(dev, "acf ddt size check failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_data_size_v_0_0_0_1(struct device *dev,
|
|
char *fw_data, size_t fw_size)
|
|
{
|
|
int i = 0;
|
|
size_t data_size = 0;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
struct aw_acf_dde *acf_dde = NULL;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (acf_dde[i].data_size % 2) {
|
|
AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed",
|
|
i, acf_dde[i].data_size, acf_dde[i].dev_name,
|
|
acf_dde[i].data_type);
|
|
return -EINVAL;
|
|
}
|
|
data_size += acf_dde[i].data_size;
|
|
}
|
|
|
|
/* Verify that the file size is equal to the header size plus */
|
|
/* the table size and data size */
|
|
if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) {
|
|
AW_DEV_LOGE(dev, "acf size check failed");
|
|
AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld",
|
|
(u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) +
|
|
acf_hdr->ddt_size));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_data_crc_v_0_0_0_1(struct device *dev, char *fw_data)
|
|
{
|
|
int i = 0;
|
|
size_t crc_val = 0;
|
|
char *data = NULL;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
struct aw_acf_dde *acf_dde = NULL;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
data = fw_data + acf_dde[i].data_offset;
|
|
crc_val = aw_crc8_check(data, acf_dde[i].data_size);
|
|
if (crc_val != acf_dde[i].data_crc) {
|
|
AW_DEV_LOGE(dev, "acf dde_crc check failed");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_profile_id_v_0_0_0_1(struct device *dev, char *fw_data)
|
|
{
|
|
int i = 0;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
struct aw_acf_dde *acf_dde = NULL;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
acf_dde = (struct aw_acf_dde *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (acf_dde[i].data_type == AW_MONITOR)
|
|
continue;
|
|
if (acf_dde[i].dev_profile > AW_PROFILE_MAX) {
|
|
AW_DEV_LOGE(dev, "parse profile_id[%d] failed", acf_dde[i].dev_profile);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static int aw_check_data_v_0_0_0_1(struct device *dev,
|
|
char *fw_data, size_t size)
|
|
{
|
|
int ret = -1;
|
|
|
|
/* check file type id is awinic acf file */
|
|
ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* check ddt_size in header is equal to all ddt aize */
|
|
ret = aw_check_ddt_size_v_0_0_0_1(dev, fw_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Verify that the file size is equal to the header size plus */
|
|
/* the table size and data size */
|
|
ret = aw_check_data_size_v_0_0_0_1(dev, fw_data, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* check crc in is equal to dde data crc */
|
|
ret = aw_check_data_crc_v_0_0_0_1(dev, fw_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* check profile id is in profile_id_max */
|
|
ret = aw_check_profile_id_v_0_0_0_1(dev, fw_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
AW_DEV_LOGI(dev, "acf fimware check succeed");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* V1.0.0.0 version acf chack
|
|
**************************************************************************/
|
|
static int aw_check_ddt_size_v_1_0_0_0(struct device *dev, char *fw_data)
|
|
{
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL;
|
|
|
|
acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
/* check ddt_size in acf_header is aqual to ddt_num multiply by dde_size */
|
|
if (acf_hdr->ddt_size != acf_hdr->dde_num * sizeof(struct aw_acf_dde_v_1_0_0_0)) {
|
|
AW_DEV_LOGE(dev, "acf ddt size check failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_data_size_v_1_0_0_0(struct device *dev,
|
|
char *fw_data, size_t fw_size)
|
|
{
|
|
int i = 0;
|
|
size_t data_size = 0;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (acf_dde[i].data_size % 2) {
|
|
AW_DEV_LOGE(dev, "acf dde[%d].data_size[%d],dev_name[%s],data_type[%d], data_size check failed",
|
|
i, acf_dde[i].data_size, acf_dde[i].dev_name,
|
|
acf_dde[i].data_type);
|
|
return -EINVAL;
|
|
}
|
|
data_size += acf_dde[i].data_size;
|
|
}
|
|
|
|
/* Verify that the file size is equal to the header size plus */
|
|
/* the table size and data size */
|
|
if (fw_size != data_size + sizeof(struct aw_acf_hdr) + acf_hdr->ddt_size) {
|
|
AW_DEV_LOGE(dev, "acf size check failed");
|
|
AW_DEV_LOGE(dev, "fw_size=%ld,hdr_size and ddt size and data size =%ld",
|
|
(u_long)fw_size, (u_long)(data_size + sizeof(struct aw_acf_hdr) +
|
|
acf_hdr->ddt_size));
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_data_crc_v_1_0_0_0(struct device *dev, char *fw_data)
|
|
{
|
|
int i = 0;
|
|
size_t crc_val = 0;
|
|
char *data = NULL;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde = NULL;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
acf_dde = (struct aw_acf_dde_v_1_0_0_0 *)(fw_data + acf_hdr->ddt_offset);
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
data = fw_data + acf_dde[i].data_offset;
|
|
crc_val = aw_crc8_check(data, acf_dde[i].data_size);
|
|
if (crc_val != acf_dde[i].data_crc) {
|
|
AW_DEV_LOGE(dev, "acf dde_crc check failed");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_data_v_1_0_0_0(struct device *dev,
|
|
char *fw_data, size_t size)
|
|
{
|
|
int ret = -1;
|
|
|
|
/* check file type id is awinic acf file */
|
|
ret = aw_check_file_id(dev, fw_data, AW_ACF_FILE_ID);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* check ddt_size in header is equal to all ddt aize */
|
|
ret = aw_check_ddt_size_v_1_0_0_0(dev, fw_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* Verify that the file size is equal to the header size plus */
|
|
/* the table size and data size */
|
|
ret = aw_check_data_size_v_1_0_0_0(dev, fw_data, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
/* check crc in is equal to dde data crc */
|
|
ret = aw_check_data_crc_v_1_0_0_0(dev, fw_data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
AW_DEV_LOGI(dev, "acf fimware check succeed");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* acf chack API
|
|
**************************************************************************/
|
|
static int aw_check_acf_firmware(struct device *dev,
|
|
char *fw_data, size_t size)
|
|
{
|
|
int ret = -1;
|
|
struct aw_acf_hdr *acf_hdr = NULL;
|
|
|
|
if (fw_data == NULL) {
|
|
AW_DEV_LOGE(dev, "fw_data is NULL,fw_data check failed");
|
|
return -ENODATA;
|
|
}
|
|
|
|
/* check file size is less-than header size */
|
|
ret = aw_check_header_size(dev, fw_data, size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
acf_hdr = (struct aw_acf_hdr *)fw_data;
|
|
AW_DEV_LOGI(dev, "project name: [%s]", acf_hdr->project);
|
|
AW_DEV_LOGI(dev, "custom name: [%s]", acf_hdr->custom);
|
|
AW_DEV_LOGI(dev, "version name: [%s]", acf_hdr->version);
|
|
AW_DEV_LOGI(dev, "author_id: [%d]", acf_hdr->author_id);
|
|
|
|
switch (acf_hdr->hdr_version) {
|
|
case AW_ACF_HDR_VER_0_0_0_1:
|
|
return aw_check_data_v_0_0_0_1(dev, fw_data, size);
|
|
case AW_ACF_HDR_VER_1_0_0_0:
|
|
return aw_check_data_v_1_0_0_0(dev, fw_data, size);
|
|
default:
|
|
AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]",
|
|
acf_hdr->hdr_version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
*acf parse
|
|
*
|
|
*************************************************************************/
|
|
static int aw_parse_raw_reg(struct device *dev, uint8_t *data,
|
|
uint32_t data_len, struct aw_prof_desc *prof_desc)
|
|
{
|
|
AW_DEV_LOGD(dev, "data_size:%d enter", data_len);
|
|
|
|
prof_desc->data_container.data = data;
|
|
prof_desc->data_container.len = data_len;
|
|
|
|
prof_desc->prof_st = AW_PROFILE_OK;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_reg_with_hdr(struct device *dev, uint8_t *data,
|
|
uint32_t data_len, struct aw_prof_desc *prof_desc)
|
|
{
|
|
struct aw_bin *aw_bin = NULL;
|
|
int ret = -1;
|
|
|
|
AW_DEV_LOGD(dev, "data_size:%d enter", data_len);
|
|
|
|
aw_bin = kzalloc(data_len + sizeof(struct aw_bin), GFP_KERNEL);
|
|
if (aw_bin == NULL) {
|
|
AW_DEV_LOGE(dev, "devm_kzalloc aw_bin failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
aw_bin->info.len = data_len;
|
|
memcpy(aw_bin->info.data, data, data_len);
|
|
|
|
ret = aw_parsing_bin_file(aw_bin);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse bin failed");
|
|
goto parse_bin_failed;
|
|
}
|
|
|
|
if ((aw_bin->all_bin_parse_num != 1) ||
|
|
(aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
|
|
AW_DEV_LOGE(dev, "bin num or type error");
|
|
goto parse_bin_failed;
|
|
}
|
|
|
|
prof_desc->data_container.data =
|
|
data + aw_bin->header_info[0].valid_data_addr;
|
|
prof_desc->data_container.len = aw_bin->header_info[0].valid_data_len;
|
|
prof_desc->prof_st = AW_PROFILE_OK;
|
|
|
|
kfree(aw_bin);
|
|
aw_bin = NULL;
|
|
|
|
return 0;
|
|
|
|
parse_bin_failed:
|
|
kfree(aw_bin);
|
|
aw_bin = NULL;
|
|
return ret;
|
|
}
|
|
|
|
static int aw_parse_monitor_config(struct device *dev,
|
|
char *monitor_data, uint32_t data_len)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (monitor_data == NULL || data_len == 0) {
|
|
AW_DEV_LOGE(dev, "no data to parse");
|
|
return -EBFONT;
|
|
}
|
|
|
|
ret = aw_monitor_bin_parse(dev, monitor_data, data_len);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "monitor_config parse failed");
|
|
return ret;
|
|
}
|
|
|
|
AW_DEV_LOGI(dev, "monitor_bin parse succeed");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_check_prof_str_is_off(char *profile_name)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < AW_POWER_OFF_NAME_SUPPORT_COUNT; i++) {
|
|
if (strnstr(profile_name, g_power_off_name[i],
|
|
strlen(profile_name) + 1))
|
|
return 0;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* V0.0.0.1 version acf paese
|
|
**************************************************************************/
|
|
static int aw_check_product_name_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_acf_dde *prof_hdr)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < acf_info->product_cnt; i++) {
|
|
if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) {
|
|
AW_DEV_LOGD(dev, "bin_dev_name:%s",
|
|
prof_hdr->dev_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
static int aw_check_data_type_is_monitor_v_0_0_0_1(struct device *dev,
|
|
struct aw_acf_dde *prof_hdr)
|
|
{
|
|
if (prof_hdr->data_type == AW_MONITOR) {
|
|
AW_DEV_LOGD(dev, "bin data is monitor");
|
|
return 0;
|
|
}
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
static int aw_parse_data_by_sec_type_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_acf_dde *prof_hdr,
|
|
struct aw_prof_desc *profile_prof_desc)
|
|
{
|
|
int ret = -1;
|
|
char *cfg_data = acf_info->fw_data + prof_hdr->data_offset;
|
|
|
|
switch (prof_hdr->data_type) {
|
|
case AW_BIN_TYPE_REG:
|
|
snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name),
|
|
"%s", prof_hdr->dev_name);
|
|
profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile);
|
|
AW_DEV_LOGD(dev, "parse reg type data enter,profile=%s",
|
|
aw_get_prof_name(prof_hdr->dev_profile));
|
|
ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size,
|
|
profile_prof_desc);
|
|
break;
|
|
case AW_BIN_TYPE_HDR_REG:
|
|
snprintf(profile_prof_desc->dev_name, sizeof(prof_hdr->dev_name),
|
|
"%s", prof_hdr->dev_name);
|
|
profile_prof_desc->prof_name = aw_get_prof_name(prof_hdr->dev_profile);
|
|
AW_DEV_LOGD(dev, "parse hdr_reg type data enter,profile=%s",
|
|
aw_get_prof_name(prof_hdr->dev_profile));
|
|
ret = aw_parse_reg_with_hdr(dev, cfg_data,
|
|
prof_hdr->data_size,
|
|
profile_prof_desc);
|
|
break;
|
|
case AW_MONITOR:
|
|
AW_DEV_LOGD(dev, "parse monitor type data enter");
|
|
ret = aw_parse_monitor_config(dev, cfg_data,
|
|
prof_hdr->data_size);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw_parse_dev_type_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info)
|
|
{
|
|
int i = 0;
|
|
int ret = -1;
|
|
int sec_num = 0;
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_prof_desc *prof_desc = NULL;
|
|
struct aw_acf_dde *acf_dde =
|
|
(struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset);
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
|
|
for (i = 0; i < acf_info->acf_hdr.dde_num; i++) {
|
|
if ((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) &&
|
|
(acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) &&
|
|
(acf_dde[i].type == AW_DDE_DEV_TYPE_ID)) {
|
|
|
|
ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]);
|
|
if (ret == 0) {
|
|
prof_desc = NULL;
|
|
} else {
|
|
prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile];
|
|
}
|
|
|
|
if (acf_dde[i].dev_profile == AW_PROFILE_OFF && !soft_off_enable) {
|
|
AW_DEV_LOGE(dev, "profile off is not allowed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i],
|
|
prof_desc);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse dev type data failed");
|
|
return ret;
|
|
}
|
|
sec_num++;
|
|
}
|
|
}
|
|
|
|
if (sec_num == 0) {
|
|
AW_DEV_LOGD(dev, "get dev type num is %d, please use default",
|
|
sec_num);
|
|
return AW_DEV_TYPE_NONE;
|
|
}
|
|
|
|
return AW_DEV_TYPE_OK;
|
|
}
|
|
|
|
static int aw_parse_default_type_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info, struct aw_all_prof_info *all_prof_info)
|
|
{
|
|
int i = 0;
|
|
int ret = -1;
|
|
int sec_num = 0;
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_prof_desc *prof_desc = NULL;
|
|
struct aw_acf_dde *acf_dde =
|
|
(struct aw_acf_dde *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset);
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
|
|
for (i = 0; i < acf_info->acf_hdr.dde_num; i++) {
|
|
if ((acf_info->dev_index == acf_dde[i].dev_index) &&
|
|
(acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID)) {
|
|
|
|
ret = aw_check_product_name_v_0_0_0_1(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = aw_check_data_type_is_monitor_v_0_0_0_1(dev, &acf_dde[i]);
|
|
if (ret == 0) {
|
|
prof_desc = NULL;
|
|
} else {
|
|
prof_desc = &all_prof_info->prof_desc[acf_dde[i].dev_profile];
|
|
}
|
|
|
|
if (acf_dde[i].dev_profile == AW_PROFILE_OFF && !soft_off_enable) {
|
|
AW_DEV_LOGE(dev, "profile off is not allowed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = aw_parse_data_by_sec_type_v_0_0_0_1(dev, acf_info, &acf_dde[i],
|
|
prof_desc);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse default type data failed");
|
|
return ret;
|
|
}
|
|
sec_num++;
|
|
}
|
|
}
|
|
|
|
if (sec_num == 0) {
|
|
AW_DEV_LOGE(dev, "get dev default type failed, get num[%d]",
|
|
sec_num);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_get_prof_count_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_all_prof_info *all_prof_info)
|
|
{
|
|
int i = 0;
|
|
int prof_count = 0;
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
|
|
|
|
for (i = 0; i < AW_PROFILE_MAX; i++) {
|
|
if (i == AW_PROFILE_OFF) {
|
|
if (!soft_off_enable && prof_desc[i].prof_st == AW_PROFILE_OK) {
|
|
AW_DEV_LOGE(dev, "profile_off is not allowed");
|
|
return -EINVAL;
|
|
} else if (soft_off_enable && prof_desc[i].prof_st == AW_PROFILE_WAIT) {
|
|
AW_DEV_LOGE(dev, "profile [Off] is necessary,but not found");
|
|
return -EINVAL;
|
|
} else {
|
|
prof_count++;
|
|
}
|
|
} else {
|
|
if (prof_desc[i].prof_st == AW_PROFILE_OK)
|
|
prof_count++;
|
|
}
|
|
}
|
|
|
|
AW_DEV_LOGI(dev, "get profile count=[%d]", prof_count);
|
|
return prof_count;
|
|
}
|
|
|
|
static int aw_set_prof_off_info_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_all_prof_info *all_prof_info,
|
|
int index)
|
|
{
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
if (index >= prof_info->count) {
|
|
AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]",
|
|
index, prof_info->count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (soft_off_enable && prof_desc[AW_PROFILE_OFF].prof_st == AW_PROFILE_OK) {
|
|
prof_info->prof_desc[index] = prof_desc[AW_PROFILE_OFF];
|
|
AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]",
|
|
prof_info->prof_desc[index].dev_name,
|
|
aw_get_prof_name(AW_PROFILE_OFF));
|
|
} else if (!soft_off_enable) {
|
|
memset(&prof_info->prof_desc[index].data_container, 0,
|
|
sizeof(struct aw_data_container));
|
|
prof_info->prof_desc[index].prof_st = AW_PROFILE_WAIT;
|
|
prof_info->prof_desc[index].prof_name = aw_get_prof_name(AW_PROFILE_OFF);
|
|
AW_DEV_LOGI(dev, "set default power_off with no data to profile");
|
|
} else {
|
|
AW_DEV_LOGE(dev, "not init default power_off config");
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int aw_get_vaild_prof_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_all_prof_info *all_prof_info)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
int index = 0;
|
|
struct aw_prof_desc *prof_desc = all_prof_info->prof_desc;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
prof_info->count = 0;
|
|
ret = aw_get_prof_count_v_0_0_0_1(dev, acf_info, all_prof_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
prof_info->count = ret;
|
|
prof_info->prof_desc = devm_kzalloc(dev,
|
|
prof_info->count * sizeof(struct aw_prof_desc),
|
|
GFP_KERNEL);
|
|
if (prof_info->prof_desc == NULL) {
|
|
AW_DEV_LOGE(dev, "prof_desc kzalloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < AW_PROFILE_MAX; i++) {
|
|
if (i != AW_PROFILE_OFF && prof_desc[i].prof_st == AW_PROFILE_OK) {
|
|
if (index >= prof_info->count) {
|
|
AW_DEV_LOGE(dev, "get profile index[%d] overflow count[%d]",
|
|
index, prof_info->count);
|
|
return -ENOMEM;
|
|
}
|
|
prof_info->prof_desc[index] = prof_desc[i];
|
|
AW_DEV_LOGI(dev, "product=[%s]----profile=[%s]",
|
|
prof_info->prof_desc[index].dev_name,
|
|
aw_get_prof_name(i));
|
|
index++;
|
|
}
|
|
}
|
|
|
|
ret = aw_set_prof_off_info_v_0_0_0_1(dev, acf_info, all_prof_info, index);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
AW_DEV_LOGD(dev, "get vaild profile succeed");
|
|
return 0;
|
|
}
|
|
|
|
static int aw_set_prof_name_list_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int i = 0;
|
|
int count = acf_info->prof_info.count;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev,
|
|
count * (AW_PROFILE_STR_MAX), GFP_KERNEL);
|
|
if (prof_info->prof_name_list == NULL) {
|
|
AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s",
|
|
prof_info->prof_desc[i].prof_name);
|
|
AW_DEV_LOGI(dev, "index=[%d], profile_name=[%s]",
|
|
i, prof_info->prof_name_list[i]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_acf_v_0_0_0_1(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
|
|
{
|
|
int ret = 0;
|
|
struct aw_all_prof_info all_prof_info;
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
acf_info->prof_info.status = AW_ACF_WAIT;
|
|
|
|
memset(&all_prof_info, 0, sizeof(struct aw_all_prof_info));
|
|
|
|
ret = aw_parse_dev_type_v_0_0_0_1(dev, acf_info, &all_prof_info);
|
|
if (ret < 0) {
|
|
return ret;
|
|
} else if (ret == AW_DEV_TYPE_NONE) {
|
|
AW_DEV_LOGD(dev, "get dev type num is 0, parse default dev type");
|
|
ret = aw_parse_default_type_v_0_0_0_1(dev, acf_info, &all_prof_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
ret = aw_get_vaild_prof_v_0_0_0_1(dev, acf_info, &all_prof_info);
|
|
if (ret < 0) {
|
|
aw_acf_profile_free(dev, acf_info);
|
|
AW_DEV_LOGE(dev, "hdr_cersion[0x%x] parse failed",
|
|
acf_info->acf_hdr.hdr_version);
|
|
return ret;
|
|
}
|
|
|
|
ret = aw_set_prof_name_list_v_0_0_0_1(dev, acf_info);
|
|
if (ret < 0) {
|
|
aw_acf_profile_free(dev, acf_info);
|
|
AW_DEV_LOGE(dev, "creat prof_id_and_name_list failed");
|
|
return ret;
|
|
}
|
|
|
|
acf_info->prof_info.status = AW_ACF_UPDATE;
|
|
AW_DEV_LOGI(dev, "acf parse success");
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************
|
|
* V1.0.0.0 version acf paese
|
|
**************************************************************************/
|
|
static int aw_check_product_name_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_acf_dde_v_1_0_0_0 *prof_hdr)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < acf_info->product_cnt; i++) {
|
|
if (0 == strcmp(acf_info->product_tab[i], prof_hdr->dev_name)) {
|
|
AW_DEV_LOGI(dev, "bin_dev_name:%s", prof_hdr->dev_name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -ENXIO;
|
|
}
|
|
|
|
static void aw_print_prof_off_name_can_support_v_1_0_0_0(struct device *dev)
|
|
{
|
|
int i = 0;
|
|
|
|
for (i = 0; i < AW_POWER_OFF_NAME_SUPPORT_COUNT; i++)
|
|
AW_DEV_LOGI(dev, "support prof_off_name have string:[%s]", g_power_off_name[i]);
|
|
}
|
|
|
|
static int aw_get_dde_type_info_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int i;
|
|
int dev_num = 0;
|
|
int default_num = 0;
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset);
|
|
|
|
prof_info->prof_type = AW_DEV_NONE_TYPE_ID;
|
|
for (i = 0; i < acf_hdr->dde_num; i++) {
|
|
if (acf_dde[i].type == AW_DDE_DEV_TYPE_ID)
|
|
dev_num++;
|
|
if (acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID)
|
|
default_num++;
|
|
}
|
|
|
|
if (!(dev_num || default_num)) {
|
|
AW_DEV_LOGE(dev, "can't find scene");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (dev_num != 0)
|
|
prof_info->prof_type = AW_DDE_DEV_TYPE_ID;
|
|
else if (default_num != 0)
|
|
prof_info->prof_type = AW_DDE_DEV_DEFAULT_TYPE_ID;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int aw_parse_get_dev_type_prof_count_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset);
|
|
int i = 0;
|
|
int ret = 0;
|
|
int found_off_prof_flag = 0;
|
|
int count = acf_info->prof_info.count;
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) ||
|
|
(acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) &&
|
|
((acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) &&
|
|
(acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr)) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str);
|
|
if (ret == 0) {
|
|
found_off_prof_flag = AW_PROFILE_OK;
|
|
if (soft_off_enable) {
|
|
count++;
|
|
} else {
|
|
AW_DEV_LOGE(dev, "profile_off is not allowed");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count == 0) {
|
|
AW_DEV_LOGE(dev, "can't find profile");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!found_off_prof_flag && soft_off_enable) {
|
|
AW_DEV_LOGE(dev, "profile power off is necessary,but not found");
|
|
aw_print_prof_off_name_can_support_v_1_0_0_0(dev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!found_off_prof_flag && !soft_off_enable) {
|
|
count++;
|
|
AW_DEV_LOGD(dev, "set no config power off profile in count");
|
|
}
|
|
|
|
acf_info->prof_info.count = count;
|
|
AW_DEV_LOGI(dev, "profile dev_type profile count is %d", acf_info->prof_info.count);
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_get_default_type_prof_count_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset);
|
|
int i = 0;
|
|
int ret = 0;
|
|
int found_off_prof_flag = 0;
|
|
int count = acf_info->prof_info.count;
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) ||
|
|
(acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) &&
|
|
(acf_info->dev_index == acf_dde[i].dev_index) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
ret = aw_check_prof_str_is_off(acf_dde[i].dev_profile_str);
|
|
if (ret == 0) {
|
|
found_off_prof_flag = AW_PROFILE_OK;
|
|
if (soft_off_enable) {
|
|
count++;
|
|
} else {
|
|
AW_DEV_LOGE(dev, "profile_off is not allowed");
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count == 0) {
|
|
AW_DEV_LOGE(dev, "can't find profile");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!found_off_prof_flag && soft_off_enable) {
|
|
AW_DEV_LOGE(dev, "profile power off is necessary,but not found");
|
|
aw_print_prof_off_name_can_support_v_1_0_0_0(dev);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!found_off_prof_flag && !soft_off_enable) {
|
|
count++;
|
|
AW_DEV_LOGD(dev, "set no config power off profile in count");
|
|
}
|
|
|
|
acf_info->prof_info.count = count;
|
|
AW_DEV_LOGI(dev, "profile default_type profile count is %d", acf_info->prof_info.count);
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_get_profile_count_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = aw_get_dde_type_info_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) {
|
|
ret = aw_parse_get_dev_type_prof_count_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse dev_type profile count failed");
|
|
return ret;
|
|
}
|
|
} else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) {
|
|
ret = aw_parse_get_default_type_prof_count_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse default_type profile count failed");
|
|
return ret;
|
|
}
|
|
} else {
|
|
AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]",
|
|
acf_info->prof_info.prof_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
AW_DEV_LOGI(dev, "profile count is %d", acf_info->prof_info.count);
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_dev_type_prof_name_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset);
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
int i, ret, list_index = 0;
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) ||
|
|
(acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) &&
|
|
(acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) &&
|
|
(acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
if (list_index > prof_info->count) {
|
|
AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n",
|
|
__func__, list_index,
|
|
acf_dde[i].dev_profile_str);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s",
|
|
acf_dde[i].dev_profile_str);
|
|
AW_DEV_LOGI(dev, "profile_name=[%s]",
|
|
prof_info->prof_name_list[list_index]);
|
|
list_index++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_default_type_prof_name_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
struct aw_acf_hdr *acf_hdr = (struct aw_acf_hdr *)acf_info->fw_data;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_hdr->ddt_offset);
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
int i, ret, list_index = 0;
|
|
|
|
for (i = 0; i < acf_hdr->dde_num; ++i) {
|
|
if (((acf_dde[i].data_type == AW_BIN_TYPE_REG) ||
|
|
(acf_dde[i].data_type == AW_BIN_TYPE_HDR_REG)) &&
|
|
(acf_info->dev_index == acf_dde[i].dev_index) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
if (list_index > prof_info->count) {
|
|
AW_DEV_LOGE(dev, "%s:Alrealdy set list_index [%d], redundant profile [%s]exist\n",
|
|
__func__, list_index,
|
|
acf_dde[i].dev_profile_str);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
snprintf(prof_info->prof_name_list[list_index], AW_PROFILE_STR_MAX, "%s",
|
|
acf_dde[i].dev_profile_str);
|
|
AW_DEV_LOGI(dev, "profile_name=[%s]",
|
|
prof_info->prof_name_list[list_index]);
|
|
list_index++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_parse_prof_name_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int ret = 0;
|
|
int count = acf_info->prof_info.count;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
prof_info->prof_name_list = (char (*)[AW_PROFILE_STR_MAX])devm_kzalloc(dev,
|
|
count * (AW_PROFILE_STR_MAX), GFP_KERNEL);
|
|
if (prof_info->prof_name_list == NULL) {
|
|
AW_DEV_LOGE(dev, "prof_name_list devm_kzalloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) {
|
|
ret = aw_parse_dev_type_prof_name_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse dev_type profile count failed");
|
|
return ret;
|
|
}
|
|
} else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) {
|
|
ret = aw_parse_default_type_prof_name_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse default_type profile count failed");
|
|
return ret;
|
|
}
|
|
} else {
|
|
AW_DEV_LOGE(dev, "unsupport prof_type[0x%x]",
|
|
acf_info->prof_info.prof_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
AW_DEV_LOGI(dev, "profile name parse succeed");
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int aw_search_prof_index_from_list_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_prof_desc **prof_desc,
|
|
struct aw_acf_dde_v_1_0_0_0 *prof_hdr)
|
|
{
|
|
int i = 0;
|
|
int count = acf_info->prof_info.count;
|
|
char (*prof_name_list)[AW_PROFILE_STR_MAX] = acf_info->prof_info.prof_name_list;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (!strncmp(prof_name_list[i], prof_hdr->dev_profile_str, AW_PROFILE_STR_MAX)) {
|
|
*prof_desc = &(acf_info->prof_info.prof_desc[i]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (i == count)
|
|
AW_DEV_LOGE(dev, "not find prof_id and prof_name in list");
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int aw_parse_data_by_sec_type_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info,
|
|
struct aw_acf_dde_v_1_0_0_0 *prof_hdr)
|
|
{
|
|
int ret = -1;
|
|
char *cfg_data = acf_info->fw_data + prof_hdr->data_offset;
|
|
struct aw_prof_desc *prof_desc = NULL;
|
|
|
|
ret = aw_search_prof_index_from_list_v_1_0_0_0(dev, acf_info, &prof_desc, prof_hdr);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
switch (prof_hdr->data_type) {
|
|
case AW_BIN_TYPE_REG:
|
|
snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name),
|
|
"%s", prof_hdr->dev_name);
|
|
AW_DEV_LOGI(dev, "parse reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]",
|
|
prof_hdr->dev_name, prof_hdr->dev_profile,
|
|
prof_hdr->dev_profile_str);
|
|
prof_desc->prof_name = prof_hdr->dev_profile_str;
|
|
ret = aw_parse_raw_reg(dev, cfg_data, prof_hdr->data_size,
|
|
prof_desc);
|
|
break;
|
|
case AW_BIN_TYPE_HDR_REG:
|
|
snprintf(prof_desc->dev_name, sizeof(prof_hdr->dev_name),
|
|
"%s", prof_hdr->dev_name);
|
|
AW_DEV_LOGI(dev, "parse hdr_reg type data enter,product=[%s],prof_id=[%d],prof_name=[%s]",
|
|
prof_hdr->dev_name, prof_hdr->dev_profile,
|
|
prof_hdr->dev_profile_str);
|
|
prof_desc->prof_name = prof_hdr->dev_profile_str;
|
|
ret = aw_parse_reg_with_hdr(dev, cfg_data,
|
|
prof_hdr->data_size, prof_desc);
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int aw_parse_dev_type_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int i = 0;
|
|
int ret;
|
|
int parse_prof_count = 0;
|
|
char *cfg_data = NULL;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset);
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
|
|
for (i = 0; i < acf_info->acf_hdr.dde_num; i++) {
|
|
if ((acf_dde[i].type == AW_DDE_DEV_TYPE_ID) &&
|
|
(acf_info->aw_dev->i2c_bus == acf_dde[i].dev_bus) &&
|
|
(acf_info->aw_dev->i2c_addr == acf_dde[i].dev_addr) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
if (acf_dde[i].data_type == AW_MONITOR) {
|
|
cfg_data = acf_info->fw_data + acf_dde[i].data_offset;
|
|
AW_DEV_LOGD(dev, "parse monitor type data enter");
|
|
ret = aw_parse_monitor_config(dev, cfg_data,
|
|
acf_dde[i].data_size);
|
|
} else {
|
|
ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info,
|
|
&acf_dde[i]);
|
|
if (ret < 0)
|
|
AW_DEV_LOGE(dev, "parse dev type data failed");
|
|
else
|
|
parse_prof_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parse_prof_count == 0) {
|
|
AW_DEV_LOGE(dev, "get dev type num is %d, parse failed", parse_prof_count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return AW_DEV_TYPE_OK;
|
|
}
|
|
|
|
static int aw_parse_default_type_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int i = 0;
|
|
int ret;
|
|
int parse_prof_count = 0;
|
|
char *cfg_data = NULL;
|
|
struct aw_acf_dde_v_1_0_0_0 *acf_dde =
|
|
(struct aw_acf_dde_v_1_0_0_0 *)(acf_info->fw_data + acf_info->acf_hdr.ddt_offset);
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
|
|
for (i = 0; i < acf_info->acf_hdr.dde_num; i++) {
|
|
if ((acf_dde[i].type == AW_DDE_DEV_DEFAULT_TYPE_ID) &&
|
|
(acf_info->dev_index == acf_dde[i].dev_index) &&
|
|
(acf_info->aw_dev->chipid == acf_dde[i].chip_id)) {
|
|
ret = aw_check_product_name_v_1_0_0_0(dev, acf_info, &acf_dde[i]);
|
|
if (ret < 0)
|
|
continue;
|
|
|
|
if (acf_dde[i].data_type == AW_MONITOR) {
|
|
cfg_data = acf_info->fw_data + acf_dde[i].data_offset;
|
|
AW_DEV_LOGD(dev, "parse monitor type data enter");
|
|
ret = aw_parse_monitor_config(dev, cfg_data,
|
|
acf_dde[i].data_size);
|
|
} else {
|
|
ret = aw_parse_data_by_sec_type_v_1_0_0_0(dev, acf_info,
|
|
&acf_dde[i]);
|
|
if (ret < 0)
|
|
AW_DEV_LOGE(dev, "parse default type data failed");
|
|
else
|
|
parse_prof_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (parse_prof_count == 0) {
|
|
AW_DEV_LOGE(dev, "get default type num is %d,parse failed", parse_prof_count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return AW_DEV_TYPE_OK;
|
|
}
|
|
|
|
static int aw_parse_by_hdr_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int ret;
|
|
|
|
if (acf_info->prof_info.prof_type == AW_DDE_DEV_TYPE_ID) {
|
|
ret = aw_parse_dev_type_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
} else if (acf_info->prof_info.prof_type == AW_DDE_DEV_DEFAULT_TYPE_ID) {
|
|
ret = aw_parse_default_type_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int aw_set_prof_off_info_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
uint8_t soft_off_enable = acf_info->aw_dev->soft_off_enable;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
int i = 0;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < prof_info->count; ++i) {
|
|
if (!(prof_info->prof_desc[i].prof_st) && !soft_off_enable) {
|
|
snprintf(prof_info->prof_name_list[i], AW_PROFILE_STR_MAX, "%s",
|
|
g_power_off_name[0]);
|
|
prof_info->prof_desc[i].prof_name = prof_info->prof_name_list[i];
|
|
prof_info->prof_desc[i].prof_st = AW_PROFILE_WAIT;
|
|
memset(&prof_info->prof_desc[i].data_container, 0,
|
|
sizeof(struct aw_data_container));
|
|
return 0;
|
|
}
|
|
|
|
ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]);
|
|
if (ret == 0) {
|
|
AW_DEV_LOGD(dev, "found profile off,data_len=[%d]",
|
|
prof_info->prof_desc[i].data_container.len);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
AW_DEV_LOGE(dev, "index[%d] is out of table,profile count[%d]",
|
|
i, prof_info->count);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int aw_parse_acf_v_1_0_0_0(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
|
|
{
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
int ret;
|
|
|
|
ret = aw_parse_get_profile_count_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "get profile count failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = aw_parse_prof_name_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "get profile count failed");
|
|
return ret;
|
|
}
|
|
|
|
acf_info->prof_info.prof_desc = devm_kzalloc(dev,
|
|
prof_info->count * sizeof(struct aw_prof_desc), GFP_KERNEL);
|
|
if (acf_info->prof_info.prof_desc == NULL) {
|
|
AW_DEV_LOGE(dev, "prof_desc devm_kzalloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = aw_parse_by_hdr_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "parse data failed");
|
|
return ret;
|
|
}
|
|
|
|
ret = aw_set_prof_off_info_v_1_0_0_0(dev, acf_info);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "set profile off info failed");
|
|
return ret;
|
|
}
|
|
|
|
prof_info->status = AW_ACF_UPDATE;
|
|
AW_DEV_LOGI(dev, "acf paese succeed");
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
*
|
|
*acf parse API
|
|
*
|
|
*************************************************************************/
|
|
void aw_acf_profile_free(struct device *dev, struct acf_bin_info *acf_info)
|
|
{
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
prof_info->count = 0;
|
|
prof_info->status = AW_ACF_WAIT;
|
|
memset(&acf_info->acf_hdr, 0, sizeof(struct aw_acf_hdr));
|
|
|
|
if (prof_info->prof_desc) {
|
|
devm_kfree(dev, prof_info->prof_desc);
|
|
prof_info->prof_desc = NULL;
|
|
}
|
|
|
|
if (prof_info->prof_name_list) {
|
|
devm_kfree(dev, prof_info->prof_name_list);
|
|
prof_info->prof_name_list = NULL;
|
|
}
|
|
|
|
if (acf_info->fw_data) {
|
|
vfree(acf_info->fw_data);
|
|
acf_info->fw_data = NULL;
|
|
}
|
|
}
|
|
|
|
int aw_acf_parse(struct device *dev, struct acf_bin_info *acf_info)
|
|
{
|
|
int ret = 0;
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
acf_info->prof_info.status = AW_ACF_WAIT;
|
|
ret = aw_check_acf_firmware(dev, acf_info->fw_data,
|
|
acf_info->fw_size);
|
|
if (ret < 0) {
|
|
AW_DEV_LOGE(dev, "load firmware check failed");
|
|
return -EINVAL;
|
|
}
|
|
|
|
memcpy(&acf_info->acf_hdr, acf_info->fw_data,
|
|
sizeof(struct aw_acf_hdr));
|
|
|
|
switch (acf_info->acf_hdr.hdr_version) {
|
|
case AW_ACF_HDR_VER_0_0_0_1:
|
|
return aw_parse_acf_v_0_0_0_1(dev, acf_info);
|
|
case AW_ACF_HDR_VER_1_0_0_0:
|
|
return aw_parse_acf_v_1_0_0_0(dev, acf_info);
|
|
default:
|
|
AW_DEV_LOGE(dev, "unsupported hdr_version [0x%x]",
|
|
acf_info->acf_hdr.hdr_version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct aw_prof_desc *aw_acf_get_prof_desc_form_name(struct device *dev,
|
|
struct acf_bin_info *acf_info, char *profile_name)
|
|
{
|
|
int i = 0;
|
|
struct aw_prof_desc *prof_desc = NULL;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
AW_DEV_LOGD(dev, "enter");
|
|
|
|
if (!acf_info->prof_info.status) {
|
|
AW_DEV_LOGE(dev, "profile_cfg not load");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < prof_info->count; i++) {
|
|
if (!strncmp(profile_name, prof_info->prof_desc[i].prof_name,
|
|
AW_PROFILE_STR_MAX)) {
|
|
prof_desc = &prof_info->prof_desc[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == prof_info->count) {
|
|
AW_DEV_LOGE(dev, "profile not found");
|
|
return NULL;
|
|
}
|
|
|
|
AW_DEV_LOGI(dev, "get prof desc down");
|
|
return prof_desc;
|
|
}
|
|
|
|
int aw_acf_get_prof_index_form_name(struct device *dev,
|
|
struct acf_bin_info *acf_info, char *profile_name)
|
|
{
|
|
int i = 0;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
if (!acf_info->prof_info.status) {
|
|
AW_DEV_LOGE(dev, "profile_cfg not load");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < prof_info->count; i++) {
|
|
if (!strncmp(profile_name, prof_info->prof_name_list[i],
|
|
AW_PROFILE_STR_MAX)) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
AW_DEV_LOGE(dev, "profile_index not found");
|
|
return -EINVAL;
|
|
}
|
|
|
|
char *aw_acf_get_prof_name_form_index(struct device *dev,
|
|
struct acf_bin_info *acf_info, int index)
|
|
{
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
if (!acf_info->prof_info.status) {
|
|
AW_DEV_LOGE(dev, "profile_cfg not load");
|
|
return NULL;
|
|
}
|
|
|
|
if (index >= prof_info->count || index < 0) {
|
|
AW_DEV_LOGE(dev, "profile_index out of table");
|
|
return NULL;
|
|
}
|
|
|
|
return prof_info->prof_desc[index].prof_name;
|
|
}
|
|
|
|
|
|
int aw_acf_get_profile_count(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
if (!acf_info->prof_info.status) {
|
|
AW_DEV_LOGE(dev, "profile_cfg not load");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (prof_info->count > 0) {
|
|
return prof_info->count;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
char *aw_acf_get_prof_off_name(struct device *dev,
|
|
struct acf_bin_info *acf_info)
|
|
{
|
|
int i = 0;
|
|
int ret = 0;
|
|
struct aw_prof_info *prof_info = &acf_info->prof_info;
|
|
|
|
if (!acf_info->prof_info.status) {
|
|
AW_DEV_LOGE(dev, "profile_cfg not load");
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < prof_info->count; i++) {
|
|
ret = aw_check_prof_str_is_off(prof_info->prof_name_list[i]);
|
|
if (ret == 0)
|
|
return prof_info->prof_name_list[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void aw_acf_init(struct aw_device *aw_dev, struct acf_bin_info *acf_info, int index)
|
|
{
|
|
|
|
acf_info->load_count = 0;
|
|
acf_info->prof_info.status = AW_ACF_WAIT;
|
|
acf_info->dev_index = index;
|
|
acf_info->aw_dev = aw_dev;
|
|
acf_info->product_cnt = aw_dev->product_cnt;
|
|
acf_info->product_tab = aw_dev->product_tab;
|
|
acf_info->prof_info.prof_desc = NULL;
|
|
acf_info->fw_data = NULL;
|
|
acf_info->fw_size = 0;
|
|
}
|
|
|