in iqs62x.c [220:338]
static int iqs62x_firmware_parse(struct iqs62x_core *iqs62x,
const struct firmware *fw)
{
struct i2c_client *client = iqs62x->client;
struct iqs62x_fw_rec *fw_rec;
struct iqs62x_fw_blk *fw_blk;
unsigned int val;
size_t pos = 0;
int ret = 0;
u8 mask, len, *data;
u8 hall_cal_index = 0;
while (pos < fw->size) {
if (pos + sizeof(*fw_rec) > fw->size) {
ret = -EINVAL;
break;
}
fw_rec = (struct iqs62x_fw_rec *)(fw->data + pos);
pos += sizeof(*fw_rec);
if (pos + fw_rec->len - 1 > fw->size) {
ret = -EINVAL;
break;
}
pos += fw_rec->len - 1;
switch (fw_rec->type) {
case IQS62X_FW_REC_TYPE_INFO:
continue;
case IQS62X_FW_REC_TYPE_PROD:
if (fw_rec->data == iqs62x->dev_desc->prod_num)
continue;
dev_err(&client->dev,
"Incompatible product number: 0x%02X\n",
fw_rec->data);
ret = -EINVAL;
break;
case IQS62X_FW_REC_TYPE_HALL:
if (!hall_cal_index) {
ret = regmap_write(iqs62x->regmap,
IQS62X_OTP_CMD,
IQS62X_OTP_CMD_FG3);
if (ret)
break;
ret = regmap_read(iqs62x->regmap,
IQS62X_OTP_DATA, &val);
if (ret)
break;
hall_cal_index = val & IQS62X_HALL_CAL_MASK;
if (!hall_cal_index) {
dev_err(&client->dev,
"Uncalibrated device\n");
ret = -ENODATA;
break;
}
}
if (hall_cal_index > fw_rec->len) {
ret = -EINVAL;
break;
}
mask = 0;
data = &fw_rec->data + hall_cal_index - 1;
len = sizeof(*data);
break;
case IQS62X_FW_REC_TYPE_MASK:
if (fw_rec->len < (sizeof(mask) + sizeof(*data))) {
ret = -EINVAL;
break;
}
mask = fw_rec->data;
data = &fw_rec->data + sizeof(mask);
len = sizeof(*data);
break;
case IQS62X_FW_REC_TYPE_DATA:
mask = 0;
data = &fw_rec->data;
len = fw_rec->len;
break;
default:
dev_err(&client->dev,
"Unrecognized record type: 0x%02X\n",
fw_rec->type);
ret = -EINVAL;
}
if (ret)
break;
fw_blk = devm_kzalloc(&client->dev,
struct_size(fw_blk, data, len),
GFP_KERNEL);
if (!fw_blk) {
ret = -ENOMEM;
break;
}
fw_blk->addr = fw_rec->addr;
fw_blk->mask = mask;
fw_blk->len = len;
memcpy(fw_blk->data, data, len);
list_add(&fw_blk->list, &iqs62x->fw_blk_head);
}
release_firmware(fw);
return ret;
}