// SPDX-License-Identifier: GPL-2.0-only // // aw88395_lib.c -- ACF bin parsing and check library file for aw88395 // // Copyright (c) 2022-2023 AWINIC Technology CO., LTD // // Author: Bruce zhao // #include #include #include "aw88395_lib.h" #include "aw88395_device.h" #include "aw88395_reg.h" #define AW88395_CRC8_POLYNOMIAL 0x8C DECLARE_CRC8_TABLE(aw_crc8_table); static char *profile_name[AW88395_PROFILE_MAX] = { "Music", "Voice", "Voip", "Ringtone", "Ringtone_hs", "Lowpower", "Bypass", "Mmi", "Fm", "Notification", "Receiver" }; static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin); static int aw_check_sum(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) { unsigned char *p_check_sum; unsigned int sum_data = 0; unsigned int check_sum; unsigned int i, len; p_check_sum = &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - bin->header_info[bin_num].header_len)]); len = bin->header_info[bin_num].bin_data_len + bin->header_info[bin_num].header_len; check_sum = le32_to_cpup((void *)p_check_sum); for (i = 4; i < len; i++) sum_data += *(p_check_sum + i); dev_dbg(aw_dev->dev, "%s -- check_sum = %p, check_sum = 0x%x, sum_data = 0x%x", __func__, p_check_sum, check_sum, sum_data); if (sum_data != check_sum) { dev_err(aw_dev->dev, "%s. CheckSum Fail.bin_num=%d, CheckSum:0x%x, SumData:0x%x", __func__, bin_num, check_sum, sum_data); return -EINVAL; } return 0; } static int aw_check_data_version(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) { if (bin->header_info[bin_num].bin_data_ver < DATA_VERSION_V1 || bin->header_info[bin_num].bin_data_ver > DATA_VERSION_MAX) { dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin data version\n"); return -EINVAL; } return 0; } static int aw_check_register_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) { struct bin_header_info temp_info = bin->header_info[bin_num]; unsigned int check_register_num, parse_register_num; unsigned char *p_check_sum; p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); parse_register_num = le32_to_cpup((void *)p_check_sum); check_register_num = (bin->header_info[bin_num].bin_data_len - CHECK_REGISTER_NUM_OFFSET) / (bin->header_info[bin_num].reg_byte_len + bin->header_info[bin_num].data_byte_len); dev_dbg(aw_dev->dev, "%s,parse_register_num = 0x%x,check_register_num = 0x%x\n", __func__, parse_register_num, check_register_num); if (parse_register_num != check_register_num) { dev_err(aw_dev->dev, "%s parse_register_num = 0x%x,check_register_num = 0x%x\n", __func__, parse_register_num, check_register_num); return -EINVAL; } bin->header_info[bin_num].reg_num = parse_register_num; bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - VALID_DATA_LEN; bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + VALID_DATA_ADDR; return 0; } static int aw_check_dsp_reg_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) { struct bin_header_info temp_info = bin->header_info[bin_num]; unsigned int check_dsp_reg_num, parse_dsp_reg_num; unsigned char *p_check_sum; p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); parse_dsp_reg_num = le32_to_cpup((void *)(p_check_sum + PARSE_DSP_REG_NUM)); bin->header_info[bin_num].reg_data_byte_len = le32_to_cpup((void *)(p_check_sum + REG_DATA_BYTP_LEN)); check_dsp_reg_num = (bin->header_info[bin_num].bin_data_len - CHECK_DSP_REG_NUM) / bin->header_info[bin_num].reg_data_byte_len; dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", __func__, bin_num, check_dsp_reg_num, check_dsp_reg_num); if (parse_dsp_reg_num != check_dsp_reg_num) { dev_err(aw_dev->dev, "aw_bin_parse check dsp reg num error\n"); dev_err(aw_dev->dev, "%s parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x", __func__, check_dsp_reg_num, check_dsp_reg_num); return -EINVAL; } bin->header_info[bin_num].download_addr = le32_to_cpup((void *)p_check_sum); bin->header_info[bin_num].reg_num = parse_dsp_reg_num; bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - DSP_VALID_DATA_LEN; bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + DSP_VALID_DATA_ADDR; return 0; } static int aw_check_soc_app_num(struct aw_device *aw_dev, struct aw_bin *bin, int bin_num) { struct bin_header_info temp_info = bin->header_info[bin_num]; unsigned int check_soc_app_num, parse_soc_app_num; unsigned char *p_check_sum; p_check_sum = &(bin->info.data[(temp_info.valid_data_addr)]); bin->header_info[bin_num].app_version = le32_to_cpup((void *)p_check_sum); parse_soc_app_num = le32_to_cpup((void *)(p_check_sum + PARSE_SOC_APP_NUM)); check_soc_app_num = bin->header_info[bin_num].bin_data_len - CHECK_SOC_APP_NUM; dev_dbg(aw_dev->dev, "%s bin_num = %d, parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", __func__, bin_num, parse_soc_app_num, check_soc_app_num); if (parse_soc_app_num != check_soc_app_num) { dev_err(aw_dev->dev, "%s parse_soc_app_num=0x%x, check_soc_app_num = 0x%x\n", __func__, parse_soc_app_num, check_soc_app_num); return -EINVAL; } bin->header_info[bin_num].reg_num = parse_soc_app_num; bin->header_info[bin_num].download_addr = le32_to_cpup((void *)(p_check_sum + APP_DOWNLOAD_ADDR)); bin->header_info[bin_num].valid_data_len = temp_info.bin_data_len - APP_VALID_DATA_LEN; bin->header_info[bin_num].valid_data_addr = temp_info.valid_data_addr + APP_VALID_DATA_ADDR; return 0; } static void aw_get_single_bin_header(struct aw_bin *bin) { memcpy((void *)&bin->header_info[bin->all_bin_parse_num], bin->p_addr, DATA_LEN); bin->header_info[bin->all_bin_parse_num].header_len = HEADER_LEN; bin->all_bin_parse_num += 1; } static int aw_parse_one_of_multi_bins(struct aw_device *aw_dev, unsigned int bin_num, int bin_serial_num, struct aw_bin *bin) { struct bin_header_info aw_bin_header_info; unsigned int bin_start_addr; unsigned int valid_data_len; if (bin->info.len < sizeof(struct bin_header_info)) { dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", (int)sizeof(struct bin_header_info), bin->info.len); return -EINVAL; } aw_bin_header_info = bin->header_info[bin->all_bin_parse_num - 1]; if (!bin_serial_num) { bin_start_addr = le32_to_cpup((void *)(bin->p_addr + START_ADDR_OFFSET)); bin->p_addr += (HEADER_LEN + bin_start_addr); bin->header_info[bin->all_bin_parse_num].valid_data_addr = aw_bin_header_info.valid_data_addr + VALID_DATA_ADDR + 8 * bin_num + VALID_DATA_ADDR_OFFSET; } else { valid_data_len = aw_bin_header_info.bin_data_len; bin->p_addr += (HDADER_LEN + valid_data_len); bin->header_info[bin->all_bin_parse_num].valid_data_addr = aw_bin_header_info.valid_data_addr + aw_bin_header_info.bin_data_len + VALID_DATA_ADDR_OFFSET; } return aw_parse_bin_header(aw_dev, bin); } static int aw_get_multi_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) { unsigned int bin_num, i; int ret; bin_num = le32_to_cpup((void *)(bin->p_addr + VALID_DATA_ADDR_OFFSET)); if (bin->multi_bin_parse_num == 1) bin->header_info[bin->all_bin_parse_num].valid_data_addr = VALID_DATA_ADDR_OFFSET; aw_get_single_bin_header(bin); for (i = 0; i < bin_num; i++) { dev_dbg(aw_dev->dev, "aw_bin_parse enter multi bin for is %d\n", i); ret = aw_parse_one_of_multi_bins(aw_dev, bin_num, i, bin); if (ret < 0) return ret; } return 0; } static int aw_parse_bin_header(struct aw_device *aw_dev, struct aw_bin *bin) { unsigned int bin_data_type; if (bin->info.len < sizeof(struct bin_header_info)) { dev_err(aw_dev->dev, "bin_header_info size[%d] overflow file size[%d]\n", (int)sizeof(struct bin_header_info), bin->info.len); return -EINVAL; } bin_data_type = le32_to_cpup((void *)(bin->p_addr + BIN_DATA_TYPE_OFFSET)); dev_dbg(aw_dev->dev, "aw_bin_parse bin_data_type 0x%x\n", bin_data_type); switch (bin_data_type) { case DATA_TYPE_REGISTER: case DATA_TYPE_DSP_REG: case DATA_TYPE_SOC_APP: bin->single_bin_parse_num += 1; dev_dbg(aw_dev->dev, "%s bin->single_bin_parse_num is %d\n", __func__, bin->single_bin_parse_num); if (!bin->multi_bin_parse_num) bin->header_info[bin->all_bin_parse_num].valid_data_addr = VALID_DATA_ADDR_OFFSET; aw_get_single_bin_header(bin); return 0; case DATA_TYPE_MULTI_BINS: bin->multi_bin_parse_num += 1; dev_dbg(aw_dev->dev, "%s bin->multi_bin_parse_num is %d\n", __func__, bin->multi_bin_parse_num); return aw_get_multi_bin_header(aw_dev, bin); default: dev_dbg(aw_dev->dev, "%s There is no corresponding type\n", __func__); return 0; } } static int aw_check_bin_header_version(struct aw_device *aw_dev, struct aw_bin *bin) { unsigned int header_version; header_version = le32_to_cpup((void *)(bin->p_addr + HEADER_VERSION_OFFSET)); dev_dbg(aw_dev->dev, "aw_bin_parse header_version 0x%x\n", header_version); switch (header_version) { case HEADER_VERSION_V1: return aw_parse_bin_header(aw_dev, bin); default: dev_err(aw_dev->dev, "aw_bin_parse Unrecognized this bin header version\n"); return -EINVAL; } } static int aw_parsing_bin_file(struct aw_device *aw_dev, struct aw_bin *bin) { int ret = -EINVAL; int i; if (!bin) { dev_err(aw_dev->dev, "aw_bin_parse bin is NULL\n"); return ret; } bin->p_addr = bin->info.data; bin->all_bin_parse_num = 0; bin->multi_bin_parse_num = 0; bin->single_bin_parse_num = 0; ret = aw_check_bin_header_version(aw_dev, bin); if (ret < 0) { dev_err(aw_dev->dev, "aw_bin_parse check bin header version error\n"); return ret; } for (i = 0; i < bin->all_bin_parse_num; i++) { ret = aw_check_sum(aw_dev, bin, i); if (ret < 0) { dev_err(aw_dev->dev, "aw_bin_parse check sum data error\n"); return ret; } ret = aw_check_data_version(aw_dev, bin, i); if (ret < 0) { dev_err(aw_dev->dev, "aw_bin_parse check data version error\n"); return ret; } if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { switch (bin->header_info[i].bin_data_type) { case DATA_TYPE_REGISTER: ret = aw_check_register_num(aw_dev, bin, i); break; case DATA_TYPE_DSP_REG: ret = aw_check_dsp_reg_num(aw_dev, bin, i); break; case DATA_TYPE_SOC_APP: ret = aw_check_soc_app_num(aw_dev, bin, i); break; default: bin->header_info[i].valid_data_len = bin->header_info[i].bin_data_len; ret = 0; break; } if (ret < 0) return ret; } } return 0; } static int aw_dev_parse_raw_reg(unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data; prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = data_len; prof_desc->prof_st = AW88395_PROFILE_OK; return 0; } static int aw_dev_parse_raw_dsp_cfg(unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { if (data_len & 0x01) return -EINVAL; swab16_array((u16 *)data, data_len >> 1); prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data; prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = data_len; prof_desc->prof_st = AW88395_PROFILE_OK; return 0; } static int aw_dev_parse_raw_dsp_fw(unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { if (data_len & 0x01) return -EINVAL; swab16_array((u16 *)data, data_len >> 1); prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data; prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = data_len; prof_desc->prof_st = AW88395_PROFILE_OK; return 0; } static int aw_dev_prof_parse_multi_bin(struct aw_device *aw_dev, unsigned char *data, unsigned int data_len, struct aw_prof_desc *prof_desc) { struct aw_bin *aw_bin; int ret; int i; aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(struct aw_bin), GFP_KERNEL); if (!aw_bin) return -ENOMEM; aw_bin->info.len = data_len; memcpy(aw_bin->info.data, data, data_len); ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->dev, "parse bin failed"); goto parse_bin_failed; } for (i = 0; i < aw_bin->all_bin_parse_num; i++) { switch (aw_bin->header_info[i].bin_data_type) { case DATA_TYPE_REGISTER: prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = aw_bin->header_info[i].valid_data_len; prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data + aw_bin->header_info[i].valid_data_addr; break; case DATA_TYPE_DSP_REG: if (aw_bin->header_info[i].valid_data_len & 0x01) { ret = -EINVAL; goto parse_bin_failed; } swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].len = aw_bin->header_info[i].valid_data_len; prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_CFG].data = data + aw_bin->header_info[i].valid_data_addr; break; case DATA_TYPE_DSP_FW: case DATA_TYPE_SOC_APP: if (aw_bin->header_info[i].valid_data_len & 0x01) { ret = -EINVAL; goto parse_bin_failed; } swab16_array((u16 *)(data + aw_bin->header_info[i].valid_data_addr), aw_bin->header_info[i].valid_data_len >> 1); prof_desc->fw_ver = aw_bin->header_info[i].app_version; prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].len = aw_bin->header_info[i].valid_data_len; prof_desc->sec_desc[AW88395_DATA_TYPE_DSP_FW].data = data + aw_bin->header_info[i].valid_data_addr; break; default: dev_dbg(aw_dev->dev, "bin_data_type not found"); break; } } prof_desc->prof_st = AW88395_PROFILE_OK; ret = 0; parse_bin_failed: devm_kfree(aw_dev->dev, aw_bin); return ret; } static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev, uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc) { struct aw_bin *aw_bin; int ret; aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL); if (!aw_bin) return -ENOMEM; aw_bin->info.len = data_len; memcpy(aw_bin->info.data, data, data_len); ret = aw_parsing_bin_file(aw_dev, aw_bin); if (ret < 0) { dev_err(aw_dev->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)) { dev_err(aw_dev->dev, "bin num or type error"); goto parse_bin_failed; } if (aw_bin->header_info[0].valid_data_len % 4) { dev_err(aw_dev->dev, "bin data len get error!"); goto parse_bin_failed; } prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data = data + aw_bin->header_info[0].valid_data_addr; prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len = aw_bin->header_info[0].valid_data_len; prof_desc->prof_st = AW88395_PROFILE_OK; devm_kfree(aw_dev->dev, aw_bin); aw_bin = NULL; return 0; parse_bin_failed: devm_kfree(aw_dev->dev, aw_bin); aw_bin = NULL; return ret; } static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr, struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc) { switch (cfg_dde->data_type) { case ACF_SEC_TYPE_REG: return aw_dev_parse_raw_reg((u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); case ACF_SEC_TYPE_DSP_CFG: return aw_dev_parse_raw_dsp_cfg((u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); case ACF_SEC_TYPE_DSP_FW: return aw_dev_parse_raw_dsp_fw( (u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); case ACF_SEC_TYPE_MULTIPLE_BIN: return aw_dev_prof_parse_multi_bin( aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); case ACF_SEC_TYPE_HDR_REG: return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset, cfg_dde->data_size, scene_prof_desc); default: dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type); break; } return 0; } static int aw_dev_parse_dev_type(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) { struct aw_cfg_dde *cfg_dde = (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); int sec_num = 0; int ret, i; for (i = 0; i < prof_hdr->ddt_num; i++) { if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && (cfg_dde[i].type == AW88395_DEV_TYPE_ID) && (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { dev_err(aw_dev->dev, "dev_profile [%d] overflow", cfg_dde[i].dev_profile); return -EINVAL; } ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); if (ret < 0) { dev_err(aw_dev->dev, "parse failed"); return ret; } sec_num++; } } if (sec_num == 0) { dev_dbg(aw_dev->dev, "get dev type num is %d, please use default", sec_num); return AW88395_DEV_TYPE_NONE; } return AW88395_DEV_TYPE_OK; } static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr, struct aw_all_prof_info *all_prof_info) { struct aw_cfg_dde *cfg_dde = (struct aw_cfg_dde *)((char *)prof_hdr + prof_hdr->hdr_offset); int sec_num = 0; int ret, i; for (i = 0; i < prof_hdr->ddt_num; i++) { if ((aw_dev->channel == cfg_dde[i].dev_index) && (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) && (cfg_dde[i].data_type != ACF_SEC_TYPE_MONITOR)) { if (cfg_dde[i].dev_profile >= AW88395_PROFILE_MAX) { dev_err(aw_dev->dev, "dev_profile [%d] overflow", cfg_dde[i].dev_profile); return -EINVAL; } ret = aw_dev_parse_data_by_sec_type(aw_dev, prof_hdr, &cfg_dde[i], &all_prof_info->prof_desc[cfg_dde[i].dev_profile]); if (ret < 0) { dev_err(aw_dev->dev, "parse failed"); return ret; } sec_num++; } } if (sec_num == 0) { dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", sec_num); return -EINVAL; } return 0; } static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; struct aw_prof_info *prof_info = &aw_dev->prof_info; int num = 0; int i; for (i = 0; i < AW88395_PROFILE_MAX; i++) { if (prof_desc[i].prof_st == AW88395_PROFILE_OK) prof_info->count++; } dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); if (!prof_info->count) { dev_err(aw_dev->dev, "no profile data"); return -EPERM; } prof_info->prof_desc = devm_kcalloc(aw_dev->dev, prof_info->count, sizeof(struct aw_prof_desc), GFP_KERNEL); if (!prof_info->prof_desc) return -ENOMEM; for (i = 0; i < AW88395_PROFILE_MAX; i++) { if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { if (num >= prof_info->count) { dev_err(aw_dev->dev, "overflow count[%d]", prof_info->count); return -EINVAL; } prof_info->prof_desc[num] = prof_desc[i]; prof_info->prof_desc[num].id = i; num++; } } return 0; } static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev, struct aw_all_prof_info all_prof_info) { struct aw_prof_desc *prof_desc = all_prof_info.prof_desc; struct aw_prof_info *prof_info = &aw_dev->prof_info; struct aw_sec_data_desc *sec_desc; int num = 0; int i; for (i = 0; i < AW88395_PROFILE_MAX; i++) { if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { sec_desc = prof_desc[i].sec_desc; if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) prof_info->count++; } } dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count); if (!prof_info->count) { dev_err(aw_dev->dev, "no profile data"); return -EPERM; } prof_info->prof_desc = devm_kcalloc(aw_dev->dev, prof_info->count, sizeof(struct aw_prof_desc), GFP_KERNEL); if (!prof_info->prof_desc) return -ENOMEM; for (i = 0; i < AW88395_PROFILE_MAX; i++) { if (prof_desc[i].prof_st == AW88395_PROFILE_OK) { sec_desc = prof_desc[i].sec_desc; if ((sec_desc[AW88395_DATA_TYPE_REG].data != NULL) && (sec_desc[AW88395_DATA_TYPE_REG].len != 0) && (sec_desc[AW88395_DATA_TYPE_DSP_CFG].data != NULL) && (sec_desc[AW88395_DATA_TYPE_DSP_CFG].len != 0) && (sec_desc[AW88395_DATA_TYPE_DSP_FW].data != NULL) && (sec_desc[AW88395_DATA_TYPE_DSP_FW].len != 0)) { if (num >= prof_info->count) { dev_err(aw_dev->dev, "overflow count[%d]", prof_info->count); return -EINVAL; } prof_info->prof_desc[num] = prof_desc[i]; prof_info->prof_desc[num].id = i; num++; } } } return 0; } static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr) { struct aw_all_prof_info *all_prof_info; int ret; all_prof_info = devm_kzalloc(aw_dev->dev, sizeof(struct aw_all_prof_info), GFP_KERNEL); if (!all_prof_info) return -ENOMEM; ret = aw_dev_parse_dev_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) { goto exit; } else if (ret == AW88395_DEV_TYPE_NONE) { dev_dbg(aw_dev->dev, "get dev type num is 0, parse default dev"); ret = aw_dev_parse_dev_default_type(aw_dev, prof_hdr, all_prof_info); if (ret < 0) goto exit; } switch (aw_dev->chip_id) { case AW88395_CHIP_ID: ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); if (ret < 0) goto exit; break; case AW88261_CHIP_ID: ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info); if (ret < 0) goto exit; break; default: dev_err(aw_dev->dev, "valid prof unsupported"); ret = -EINVAL; break; } aw_dev->prof_info.prof_name_list = profile_name; exit: devm_kfree(aw_dev->dev, all_prof_info); return ret; } static int aw_dev_create_prof_name_list_v1(struct aw_device *aw_dev) { struct aw_prof_info *prof_info = &aw_dev->prof_info; struct aw_prof_desc *prof_desc = prof_info->prof_desc; int i; if (!prof_desc) { dev_err(aw_dev->dev, "prof_desc is NULL"); return -EINVAL; } prof_info->prof_name_list = devm_kzalloc(aw_dev->dev, prof_info->count * PROFILE_STR_MAX, GFP_KERNEL); if (!prof_info->prof_name_list) return -ENOMEM; for (i = 0; i < prof_info->count; i++) { prof_desc[i].id = i; prof_info->prof_name_list[i] = prof_desc[i].prf_str; dev_dbg(aw_dev->dev, "prof name is %s", prof_info->prof_name_list[i]); } return 0; } static int aw_get_dde_type_info(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); int default_num = 0; int dev_num = 0; unsigned int i; for (i = 0; i < cfg_hdr->ddt_num; i++) { if (cfg_dde[i].type == AW88395_DEV_TYPE_ID) dev_num++; if (cfg_dde[i].type == AW88395_DEV_DEFAULT_TYPE_ID) default_num++; } if (dev_num != 0) { aw_dev->prof_info.prof_type = AW88395_DEV_TYPE_ID; } else if (default_num != 0) { aw_dev->prof_info.prof_type = AW88395_DEV_DEFAULT_TYPE_ID; } else { dev_err(aw_dev->dev, "can't find scene"); return -EINVAL; } return 0; } static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, unsigned int *scene_num) { struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; int ret; switch (aw_dev->chip_id) { case AW88395_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && (aw_dev->chip_id == cfg_dde[i].chip_id) && (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) (*scene_num)++; } ret = 0; break; case AW88261_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && (aw_dev->chip_id == cfg_dde[i].chip_id) && (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && (aw_dev->i2c->addr == cfg_dde[i].dev_addr)) (*scene_num)++; } ret = 0; break; default: dev_err(aw_dev->dev, "unsupported device"); ret = -EINVAL; break; } return ret; } static int aw_get_default_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, unsigned int *scene_num) { struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset); unsigned int i; int ret; switch (aw_dev->chip_id) { case AW88395_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) && (aw_dev->chip_id == cfg_dde[i].chip_id) && (aw_dev->channel == cfg_dde[i].dev_index)) (*scene_num)++; } ret = 0; break; case AW88261_CHIP_ID: for (i = 0; i < cfg_hdr->ddt_num; ++i) { if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) || (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) && (aw_dev->chip_id == cfg_dde[i].chip_id) && (aw_dev->channel == cfg_dde[i].dev_index)) (*scene_num)++; } ret = 0; break; default: dev_err(aw_dev->dev, "unsupported device"); ret = -EINVAL; break; } return ret; } static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg, unsigned int *count) { int ret; ret = aw_get_dde_type_info(aw_dev, aw_cfg); if (ret < 0) return ret; switch (aw_dev->prof_info.prof_type) { case AW88395_DEV_TYPE_ID: ret = aw_get_dev_scene_count_v1(aw_dev, aw_cfg, count); break; case AW88395_DEV_DEFAULT_TYPE_ID: ret = aw_get_default_scene_count_v1(aw_dev, aw_cfg, count); break; default: dev_err(aw_dev->dev, "unsupported prof_type[%x]", aw_dev->prof_info.prof_type); ret = -EINVAL; break; } return ret; } static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr, struct aw_cfg_dde_v1 *cfg_dde, int *cur_scene_id) { struct aw_prof_info *prof_info = &aw_dev->prof_info; int ret; switch (cfg_dde->data_type) { case ACF_SEC_TYPE_MULTIPLE_BIN: ret = aw_dev_prof_parse_multi_bin(aw_dev, (u8 *)prof_hdr + cfg_dde->data_offset, cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); if (ret < 0) { dev_err(aw_dev->dev, "parse multi bin failed"); return ret; } prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; (*cur_scene_id)++; break; case ACF_SEC_TYPE_HDR_REG: ret = aw_dev_parse_reg_bin_with_hdr(aw_dev, (uint8_t *)prof_hdr + cfg_dde->data_offset, cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]); if (ret < 0) { dev_err(aw_dev->dev, "parse reg bin with hdr failed"); return ret; } prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str; prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile; (*cur_scene_id)++; break; default: dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type); return -EINVAL; } return 0; } static int aw_dev_parse_dev_type_v1(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr) { struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); int cur_scene_id = 0; unsigned int i; int ret; for (i = 0; i < prof_hdr->ddt_num; i++) { if ((aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) && (aw_dev->i2c->addr == cfg_dde[i].dev_addr) && (aw_dev->chip_id == cfg_dde[i].chip_id)) { ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, &cfg_dde[i], &cur_scene_id); if (ret < 0) { dev_err(aw_dev->dev, "parse failed"); return ret; } } } if (cur_scene_id == 0) { dev_err(aw_dev->dev, "get dev type failed, get num [%d]", cur_scene_id); return -EINVAL; } return 0; } static int aw_dev_parse_default_type_v1(struct aw_device *aw_dev, struct aw_cfg_hdr *prof_hdr) { struct aw_cfg_dde_v1 *cfg_dde = (struct aw_cfg_dde_v1 *)((char *)prof_hdr + prof_hdr->hdr_offset); int cur_scene_id = 0; unsigned int i; int ret; for (i = 0; i < prof_hdr->ddt_num; i++) { if ((aw_dev->channel == cfg_dde[i].dev_index) && (aw_dev->chip_id == cfg_dde[i].chip_id)) { ret = aw_dev_parse_data_by_sec_type_v1(aw_dev, prof_hdr, &cfg_dde[i], &cur_scene_id); if (ret < 0) { dev_err(aw_dev->dev, "parse failed"); return ret; } } } if (cur_scene_id == 0) { dev_err(aw_dev->dev, "get dev default type failed, get num[%d]", cur_scene_id); return -EINVAL; } return 0; } static int aw_dev_parse_by_hdr_v1(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr) { int ret; switch (aw_dev->prof_info.prof_type) { case AW88395_DEV_TYPE_ID: ret = aw_dev_parse_dev_type_v1(aw_dev, cfg_hdr); break; case AW88395_DEV_DEFAULT_TYPE_ID: ret = aw_dev_parse_default_type_v1(aw_dev, cfg_hdr); break; default: dev_err(aw_dev->dev, "prof type matched failed, get num[%d]", aw_dev->prof_info.prof_type); ret = -EINVAL; break; } return ret; } static int aw_dev_load_cfg_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; struct aw_prof_info *prof_info = &aw_dev->prof_info; int ret; ret = aw_dev_parse_scene_count_v1(aw_dev, aw_cfg, &prof_info->count); if (ret < 0) { dev_err(aw_dev->dev, "get scene count failed"); return ret; } prof_info->prof_desc = devm_kcalloc(aw_dev->dev, prof_info->count, sizeof(struct aw_prof_desc), GFP_KERNEL); if (!prof_info->prof_desc) return -ENOMEM; ret = aw_dev_parse_by_hdr_v1(aw_dev, cfg_hdr); if (ret < 0) { dev_err(aw_dev->dev, "parse hdr failed"); return ret; } ret = aw_dev_create_prof_name_list_v1(aw_dev); if (ret < 0) { dev_err(aw_dev->dev, "create prof name list failed"); return ret; } return 0; } int aw88395_dev_cfg_load(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr; int ret; cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; switch (cfg_hdr->hdr_version) { case AW88395_CFG_HDR_VER: ret = aw_dev_load_cfg_by_hdr(aw_dev, cfg_hdr); if (ret < 0) { dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", cfg_hdr->hdr_version); return ret; } break; case AW88395_CFG_HDR_VER_V1: ret = aw_dev_load_cfg_by_hdr_v1(aw_dev, aw_cfg); if (ret < 0) { dev_err(aw_dev->dev, "hdr_version[0x%x] parse failed", cfg_hdr->hdr_version); return ret; } break; default: dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); return -EINVAL; } aw_dev->fw_status = AW88395_DEV_FW_OK; return 0; } EXPORT_SYMBOL_GPL(aw88395_dev_cfg_load); static int aw_dev_check_cfg_by_hdr(struct aw_device *aw_dev, struct aw_container *aw_cfg) { unsigned int end_data_offset; struct aw_cfg_hdr *cfg_hdr; struct aw_cfg_dde *cfg_dde; unsigned int act_data = 0; unsigned int hdr_ddt_len; unsigned int i; u8 act_crc8; cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; /* check file type id is awinic acf file */ if (cfg_hdr->id != ACF_FILE_ID) { dev_err(aw_dev->dev, "not acf type file"); return -EINVAL; } hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; if (hdr_ddt_len > aw_cfg->len) { dev_err(aw_dev->dev, "hdr_len with ddt_len [%d] overflow file size[%d]", cfg_hdr->hdr_offset, aw_cfg->len); return -EINVAL; } /* check data size */ cfg_dde = (struct aw_cfg_dde *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); act_data += hdr_ddt_len; for (i = 0; i < cfg_hdr->ddt_num; i++) act_data += cfg_dde[i].data_size; if (act_data != aw_cfg->len) { dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", act_data, aw_cfg->len); return -EINVAL; } for (i = 0; i < cfg_hdr->ddt_num; i++) { /* data check */ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; if (end_data_offset > aw_cfg->len) { dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", i, end_data_offset, aw_cfg->len); return -EINVAL; } /* crc check */ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size, 0); if (act_crc8 != cfg_dde[i].data_crc) { dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc:0x%x", i, (u32)act_crc8, cfg_dde[i].data_crc); return -EINVAL; } } return 0; } static int aw_dev_check_acf_by_hdr_v1(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_dde_v1 *cfg_dde; unsigned int end_data_offset; struct aw_cfg_hdr *cfg_hdr; unsigned int act_data = 0; unsigned int hdr_ddt_len; u8 act_crc8; int i; cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; /* check file type id is awinic acf file */ if (cfg_hdr->id != ACF_FILE_ID) { dev_err(aw_dev->dev, "not acf type file"); return -EINVAL; } hdr_ddt_len = cfg_hdr->hdr_offset + cfg_hdr->ddt_size; if (hdr_ddt_len > aw_cfg->len) { dev_err(aw_dev->dev, "hdrlen with ddt_len [%d] overflow file size[%d]", cfg_hdr->hdr_offset, aw_cfg->len); return -EINVAL; } /* check data size */ cfg_dde = (struct aw_cfg_dde_v1 *)((char *)aw_cfg->data + cfg_hdr->hdr_offset); act_data += hdr_ddt_len; for (i = 0; i < cfg_hdr->ddt_num; i++) act_data += cfg_dde[i].data_size; if (act_data != aw_cfg->len) { dev_err(aw_dev->dev, "act_data[%d] not equal to file size[%d]!", act_data, aw_cfg->len); return -EINVAL; } for (i = 0; i < cfg_hdr->ddt_num; i++) { /* data check */ end_data_offset = cfg_dde[i].data_offset + cfg_dde[i].data_size; if (end_data_offset > aw_cfg->len) { dev_err(aw_dev->dev, "ddt_num[%d] end_data_offset[%d] overflow size[%d]", i, end_data_offset, aw_cfg->len); return -EINVAL; } /* crc check */ act_crc8 = crc8(aw_crc8_table, aw_cfg->data + cfg_dde[i].data_offset, cfg_dde[i].data_size, 0); if (act_crc8 != cfg_dde[i].data_crc) { dev_err(aw_dev->dev, "ddt_num[%d] act_crc8:0x%x != data_crc 0x%x", i, (u32)act_crc8, cfg_dde[i].data_crc); return -EINVAL; } } return 0; } int aw88395_dev_load_acf_check(struct aw_device *aw_dev, struct aw_container *aw_cfg) { struct aw_cfg_hdr *cfg_hdr; if (!aw_cfg) { dev_err(aw_dev->dev, "aw_prof is NULL"); return -EINVAL; } if (aw_cfg->len < sizeof(struct aw_cfg_hdr)) { dev_err(aw_dev->dev, "cfg hdr size[%d] overflow file size[%d]", aw_cfg->len, (int)sizeof(struct aw_cfg_hdr)); return -EINVAL; } crc8_populate_lsb(aw_crc8_table, AW88395_CRC8_POLYNOMIAL); cfg_hdr = (struct aw_cfg_hdr *)aw_cfg->data; switch (cfg_hdr->hdr_version) { case AW88395_CFG_HDR_VER: return aw_dev_check_cfg_by_hdr(aw_dev, aw_cfg); case AW88395_CFG_HDR_VER_V1: return aw_dev_check_acf_by_hdr_v1(aw_dev, aw_cfg); default: dev_err(aw_dev->dev, "unsupported hdr_version [0x%x]", cfg_hdr->hdr_version); return -EINVAL; } return 0; } EXPORT_SYMBOL_GPL(aw88395_dev_load_acf_check); MODULE_DESCRIPTION("AW88395 ACF File Parsing Lib"); MODULE_LICENSE("GPL v2");