Add sysfs interfaces for gen5 trackpad devices that required in production, including read and update firmware image, report baselines, sensors calibrate, read product id, read firmware version. TEST=test on Chomebooks. Signed-off-by: Du, Dudley <dudl@xxxxxxxxxxx> --- diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index effa9c5..8653ffd 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -205,7 +205,7 @@ config MOUSE_BCM5974 config MOUSE_CYAPA tristate "Cypress APA I2C Trackpad support" - depends on I2C + depends on I2C && CRC_ITU_T help This driver adds support for Cypress All Points Addressable (APA) I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 66cb5cc..63a2c79 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -29,6 +29,8 @@ #include <linux/uaccess.h> #include <linux/unaligned/access_ok.h> #include <linux/pm_runtime.h> +#include <linux/crc-ccitt.h> +#include <linux/crc-itu-t.h> /* APA trackpad firmware generation */ @@ -2364,6 +2366,86 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) return -EAGAIN; } +static int cyapa_gen5_bl_initiate(struct cyapa *cyapa, + const struct firmware *fw) +{ + int ret = 0; + u16 length = 0; + u16 data_len = 0; + u16 meta_data_crc = 0; + u16 cmd_crc = 0; + u8 bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE + 3]; + int bl_gen5_activate_size = 0; + u8 resp_data[11]; + int resp_len; + struct cyapa_tsg_bin_image *image; + int records_num; + u8 *data; + + /* Try to dump all bufferred report data before send any command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + bl_gen5_activate_size = sizeof(bl_gen5_activate); + memset(bl_gen5_activate, 0, bl_gen5_activate_size); + + /* Output Report Register Address[15:0] = 0004h */ + bl_gen5_activate[0] = 0x04; + bl_gen5_activate[1] = 0x00; + + /* totoal command length[15:0] */ + length = bl_gen5_activate_size - 2; + put_unaligned_le16(length, &bl_gen5_activate[2]); + bl_gen5_activate[4] = 0x40; /* Report ID = 40h */ + bl_gen5_activate[5] = 0x00; /* RSVD = 00h */ + + bl_gen5_activate[6] = GEN5_SOP_KEY; /* SOP = 01h */ + bl_gen5_activate[7] = 0x48; /* Command Code = 48h */ + + /* 8 Key bytes and block size */ + data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE; + /* Data Length[15:0] */ + put_unaligned_le16(data_len, &bl_gen5_activate[8]); + bl_gen5_activate[10] = 0xa5; /* Key Byte 0 */ + bl_gen5_activate[11] = 0x01; + bl_gen5_activate[12] = 0x02; /* . */ + bl_gen5_activate[13] = 0x03; /* . */ + bl_gen5_activate[14] = 0xff; /* . */ + bl_gen5_activate[15] = 0xfe; + bl_gen5_activate[16] = 0xfd; + bl_gen5_activate[17] = 0x5a; /* Key Byte 7 */ + + /* copy 60 bytes Meta Data Row Parameters */ + image = (struct cyapa_tsg_bin_image *)fw->data; + records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + /* APP_INTEGRITY row is always the last row block */ + data = image->records[records_num - 1].record_data; + memcpy(&bl_gen5_activate[18], data, CYAPA_TSG_FLASH_MAP_METADATA_SIZE); + + meta_data_crc = crc_itu_t(0xffff, &bl_gen5_activate[18], + CYAPA_TSG_FLASH_MAP_METADATA_SIZE); + /* Meta Data CRC[15:0] */ + put_unaligned_le16(meta_data_crc, + &bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_METADATA_SIZE]); + + cmd_crc = crc_itu_t(0xffff, &bl_gen5_activate[6], 4 + data_len); + put_unaligned_le16(cmd_crc, + &bl_gen5_activate[bl_gen5_activate_size - 3]); /* CRC[15:0] */ + bl_gen5_activate[bl_gen5_activate_size - 1] = GEN5_EOP_KEY; + + resp_len = sizeof(resp_data); + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + bl_gen5_activate, sizeof(bl_gen5_activate), + resp_data, &resp_len, 12000, + cyapa_gen5_sort_tsg_pip_bl_resp_data); + if (ret || resp_len != GEN5_BL_INITIATE_RESP_LEN || + resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return (ret < 0) ? ret : -EAGAIN; + + return 0; +} + bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len) { if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE) @@ -2410,6 +2492,373 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa) return -EAGAIN; } +static int cyapa_gen5_bl_enter(struct cyapa *cyapa) +{ + int ret; + u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 }; + u8 resp_data[2]; + int resp_len; + + if (cyapa->input) { + cyapa_disable_irq(cyapa); + input_unregister_device(cyapa->input); + cyapa->input = NULL; + } + cyapa_enable_irq(cyapa); + + ret = cyapa_poll_state(cyapa, 500); + if (ret < 0) + return ret; + if (cyapa->gen != CYAPA_GEN5) + return -EINVAL; + + /* Already in Gen5 BL. Skipping exit. */ + if (cyapa->state == CYAPA_STATE_GEN5_BL) + return 0; + + if (cyapa->state != CYAPA_STATE_GEN5_APP) + return -EAGAIN; + + /* Try to dump all bufferred report data before send any command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + /* + * send bootloader enter command to trackpad device, + * after enter bootloader, the response data is two bytes of 0x00 0x00. + */ + resp_len = sizeof(resp_data); + memset(resp_data, 0, resp_len); + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 5000, cyapa_gen5_sort_application_launch_data); + if (ret || resp_data[0] != 0x00 || resp_data[1] != 0x00) + return (ret < 0) ? ret : -EAGAIN; + + cyapa->state = CYAPA_STATE_GEN5_BL; + return 0; +} + +static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw) +{ + int i; + struct cyapa_tsg_bin_image *image; + int flash_records_count; + u16 expected_app_crc; + u16 expected_app_integrity_crc; + u16 app_crc = 0; + u16 app_integrity_crc = 0; + u16 row_num; + u8 *data; + u32 app_start; + u16 app_len; + u32 img_start; + u16 img_len; + int record_index; + struct device *dev = &cyapa->client->dev; + + image = (struct cyapa_tsg_bin_image *)fw->data; + flash_records_count = (fw->size - + sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + + /* APP_INTEGRITY row is always the last row block, + * and the row id must be 0x01ff */ + row_num = get_unaligned_be16( + &image->records[flash_records_count - 1].row_number); + if (&image->records[flash_records_count - 1].flash_array_id != 0x00 && + row_num != 0x01ff) { + dev_err(dev, "%s: invaid app_integrity data.\n", __func__); + return -EINVAL; + } + data = image->records[flash_records_count - 1].record_data; + app_start = get_unaligned_le32(&data[4]); + app_len = get_unaligned_le16(&data[8]); + expected_app_crc = get_unaligned_le16(&data[10]); + img_start = get_unaligned_le32(&data[16]); + img_len = get_unaligned_le16(&data[20]); + expected_app_integrity_crc = get_unaligned_le16(&data[60]); + + if ((app_start + app_len + img_start + img_len) % + CYAPA_TSG_FW_ROW_SIZE) { + dev_err(dev, "%s: invaid image alignment.\n", __func__); + return -EINVAL; + } + + /* verify app_integrity crc */ + app_integrity_crc = crc_itu_t(0xffff, data, + CYAPA_TSG_APP_INTEGRITY_SIZE); + if (app_integrity_crc != expected_app_integrity_crc) { + dev_err(dev, "%s: invaid app_integrity crc.\n", __func__); + return -EINVAL; + } + + /* + * verify application image CRC + */ + record_index = app_start / CYAPA_TSG_FW_ROW_SIZE - + CYAPA_TSG_IMG_START_ROW_NUM; + data = (u8 *)&image->records[record_index].record_data; + app_crc = crc_itu_t(0xffff, data, CYAPA_TSG_FW_ROW_SIZE); + for (i = 1; i < (app_len / CYAPA_TSG_FW_ROW_SIZE); i++) { + data = (u8 *)&image->records[++record_index].record_data; + app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE); + } + + if (app_crc != expected_app_crc) { + dev_err(dev, "%s: invaid firmware app crc check.\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int cyapa_gen5_write_fw_block(struct cyapa *cyapa, + struct cyapa_tsg_bin_image_data_record *flash_record) +{ + u8 flash_array_id; + u16 flash_row_id; + u16 record_len; + u8 *record_data; + u8 cmd[144]; /* 13 + 128+ 3 */ + u16 cmd_len; + u16 data_len; + u16 crc; + u8 resp_data[11]; + int resp_len; + int ret; + + flash_array_id = flash_record->flash_array_id; + flash_row_id = get_unaligned_be16(&flash_record->row_number); + record_len = get_unaligned_be16(&flash_record->record_len); + record_data = flash_record->record_data; + + cmd_len = sizeof(cmd) - 2; /* not include 2 bytes regisetr address. */ + memset(cmd, 0, cmd_len + 2); + cmd[0] = 0x04; /* register address */ + cmd[1] = 0x00; + + put_unaligned_le16(cmd_len, &cmd[2]); + cmd[4] = 0x40; /* report id 40h */ + cmd[5] = 0x00; + + cmd[6] = GEN5_SOP_KEY; /* SOP = 01h */ + cmd[7] = 0x39; /* command code = 39h */ + /* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */ + data_len = 3 + record_len; + put_unaligned_le16(data_len, &cmd[8]); + cmd[10] = flash_array_id; /* Flash Array ID = 00h */ + put_unaligned_le16(flash_row_id, &cmd[11]); + + memcpy(&cmd[13], record_data, record_len); + crc = crc_itu_t(0xffff, &cmd[6], 4 + data_len); + put_unaligned_le16(crc, &cmd[2 + cmd_len - 3]); + cmd[2 + cmd_len - 1] = GEN5_EOP_KEY; + + resp_len = sizeof(resp_data); + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_bl_resp_data); + if (ret || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN || + resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return ret < 0 ? ret : -EAGAIN; + + return 0; +} + +static int cyapa_gen5_read_fw_bytes(struct cyapa *cyapa, u16 row_num, u8 *data) +{ + int ret; + u8 cmd[16]; + size_t cmd_len; + u8 resp_data[CYAPA_TSG_FW_ROW_SIZE / 2 + GEN5_MIN_BL_RESP_LENGTH]; + int resp_len; + u16 offset; + u16 cmd_crc; + struct cyapa_tsg_bin_image_data_record *fw_img_record; + + fw_img_record = (struct cyapa_tsg_bin_image_data_record *)data; + + cmd[0] = 0x04; /* register address */ + cmd[1] = 0x00; + cmd[2] = 0x0e; + cmd[3] = 0x00; + cmd[4] = 0x40; /* report id 40h */ + cmd[5] = 0x00; + cmd[6] = GEN5_SOP_KEY; + cmd[7] = 0x3d; /* read application image command code */ + cmd[8] = 0x03; + cmd[9] = 0x00; + offset = row_num * CYAPA_TSG_FW_ROW_SIZE - + CYAPA_TSG_START_OF_APPLICATION; + put_unaligned_le16(offset, &cmd[10]); + cmd[12] = CYAPA_TSG_IMG_READ_SIZE; + cmd_crc = crc_itu_t(0xffff, &cmd[6], 7); + put_unaligned_le16(cmd_crc, &cmd[13]); /* CRC[15:0] */ + cmd[15] = GEN5_EOP_KEY; /* EOP = 17h */ + cmd_len = 16; + + resp_len = CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH; + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, cmd_len, + resp_data, &resp_len, + 50, cyapa_gen5_sort_tsg_pip_bl_resp_data); + if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) || + ret || resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return (ret < 0) ? ret : -EAGAIN; + + /* copy first 64 bytes in the row. */ + memcpy(&fw_img_record->record_data[0], &resp_data[8], + CYAPA_TSG_IMG_READ_SIZE); + + if (row_num == CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM) { + /* last row's rest 64 bytes are bootloader metadata, + * it's not allowed to be read out, will respond with error. */ + memset(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE], + 0, CYAPA_TSG_IMG_READ_SIZE); + goto skip_last_row; + } + + /* read next 64 bytes in the row. */ + offset = offset + CYAPA_TSG_IMG_READ_SIZE; + put_unaligned_le16(offset, &cmd[10]); + cmd_crc = crc_itu_t(0xffff, &cmd[6], 7); + put_unaligned_le16(cmd_crc, &cmd[13]); /* CRC[15:0] */ + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, cmd_len, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_bl_resp_data); + if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) || + ret || resp_data[2] != GEN5_BL_RESP_REPORT_ID || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return (ret < 0) ? ret : -EAGAIN; + + /* copy last 64 bytes in the row. */ + memcpy(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE], + &resp_data[8], CYAPA_TSG_IMG_READ_SIZE); + +skip_last_row: + fw_img_record->flash_array_id = 0; + put_unaligned_be16(row_num, &fw_img_record->row_number); + put_unaligned_be16(CYAPA_TSG_FW_ROW_SIZE, &fw_img_record->record_len); + + return 0; +} + +static int cyapa_gen5_read_fw(struct cyapa *cyapa) +{ + int ret; + int fw_img_head_size; + int fw_img_record_size; + int row_index; + int array_index; + u32 img_start; + u16 img_len; + u16 img_start_row; + u16 img_end_row; + struct cyapa_tsg_bin_image_data_record app_integrity; + u8 *record_data; + + if (cyapa->read_fw_image) + return 0; + + ret = cyapa_gen5_bl_enter(cyapa); + if (ret) + goto err; + + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + fw_img_head_size = sizeof(struct cyapa_tsg_bin_image_head); + fw_img_record_size = sizeof(struct cyapa_tsg_bin_image_data_record); + + /* Read app integrity block data. */ + row_index = CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM; + ret = cyapa_gen5_read_fw_bytes(cyapa, row_index, (u8 *)&app_integrity); + if (ret) + goto err; + img_start = get_unaligned_le32(&app_integrity.record_data[16]); + img_len = get_unaligned_le16(&app_integrity.record_data[20]); + if ((img_start + img_len) % CYAPA_TSG_FW_ROW_SIZE) + goto err; + img_start_row = img_start / CYAPA_TSG_FW_ROW_SIZE; + img_end_row = (img_start + img_len) / CYAPA_TSG_FW_ROW_SIZE - 1; + + /* allocate memory for image. */ + cyapa->read_fw_image_size = fw_img_head_size + + (img_end_row - img_start_row + 2) * fw_img_record_size; + cyapa->read_fw_image = kmalloc(cyapa->read_fw_image_size, GFP_KERNEL); + if (!cyapa->read_fw_image) { + ret = -ENOMEM; + goto err; + } + + /* set image head data. */ + memcpy(cyapa->read_fw_image, &cyapa->fw_img_head, fw_img_head_size); + + /* read image blocks. */ + for (row_index = img_start_row, array_index = 0; + row_index <= img_end_row; + row_index++, array_index++) { + record_data = &cyapa->read_fw_image[fw_img_head_size + + array_index * fw_img_record_size]; + ret = cyapa_gen5_read_fw_bytes(cyapa, row_index, record_data); + if (ret) + goto err; + } + + /* append last app integrity block data. */ + record_data = &cyapa->read_fw_image[fw_img_head_size + + array_index * fw_img_record_size]; + memcpy(record_data, &app_integrity, fw_img_record_size); + +err: + if (ret) { + kfree(cyapa->read_fw_image); + cyapa->read_fw_image = NULL; + cyapa->read_fw_image_size = 0; + } + + cyapa_detect_async(cyapa, 0); + return ret; +} + +static int cyapa_gen5_do_fw_update(struct cyapa *cyapa, + const struct firmware *fw) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_tsg_bin_image *image = + (struct cyapa_tsg_bin_image *)fw->data; + struct cyapa_tsg_bin_image_data_record *flash_record; + int flash_records_count; + int i; + int ret; + + /* Try to dump all bufferred data if exists before send commands. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + flash_records_count = + (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) / + sizeof(struct cyapa_tsg_bin_image_data_record); + /* + * the last flash row 0x01ff has been written through bl_initiate + * command, so DO NOT write flash 0x01ff to trackpad device. + */ + for (i = 0; i < (flash_records_count - 1); i++) { + flash_record = &image->records[i]; + ret = cyapa_gen5_write_fw_block(cyapa, flash_record); + if (ret) { + dev_err(dev, "%s: Gen5 FW update aborted, %d\n", + __func__, ret); + return ret; + } + } + + return 0; +} + static int cyapa_gen5_sleep_time_check(u16 sleep_time) { if (sleep_time > 1000) @@ -2694,6 +3143,763 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, return ret; } +static int cyapa_gen5_resume_scanning(struct cyapa *cyapa) +{ + u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 }; + u8 resp_data[6]; + int resp_len; + int ret; + + /* Try to dump all bufferred data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + resp_len = 6; + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, 7, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_data[2] != GEN5_APP_RESP_REPORT_ID || + resp_data[3] != GEN5_RESP_RSVD_KEY || + GET_GEN5_CMD_CODE(resp_data[4]) != 0x04) + return -EINVAL; + + /* Try to dump all bufferred data when resuming scanning. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + return 0; +} + +static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa) +{ + u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 }; + u8 resp_data[6]; + int resp_len; + int ret; + + /* Try to dump all bufferred data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + resp_len = 6; + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, 7, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_data[2] != GEN5_APP_RESP_REPORT_ID || + resp_data[3] != GEN5_RESP_RSVD_KEY || + GET_GEN5_CMD_CODE(resp_data[4]) != 0x03) + return -EINVAL; + + /* Try to dump all bufferred data when suspending scanning. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + return 0; +} + +static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa, + u8 calibrate_sensing_mode_type) +{ + int ret; + u8 cmd[8]; + u8 resp_data[6]; + int resp_len; + + /* Try to dump all bufferred data before doing command. */ + cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); + + cmd[0] = 0x04; + cmd[1] = 0x00; + cmd[2] = 0x06; + cmd[3] = 0x00; + cmd[4] = GEN5_APP_CMD_REPORT_ID; + cmd[5] = 0x00; + cmd[6] = GEN5_CMD_CALIBRATE; + cmd[7] = calibrate_sensing_mode_type; + resp_len = sizeof(resp_data); + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, sizeof(cmd), + resp_data, &resp_len, + 5000, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_data[2] != GEN5_APP_RESP_REPORT_ID || + GET_GEN5_CMD_CODE(resp_data[4]) != GEN5_CMD_CALIBRATE || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) + return ret < 0 ? ret : -EAGAIN; + + return 0; +} + +static ssize_t cyapa_gen5_do_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int ret, calibrate_ret; + + /* 1. suspend Scanning*/ + ret = cyapa_gen5_suspend_scanning(cyapa); + if (ret) + return ret; + + /* 2. do mutual capacitance fine calibrate. */ + calibrate_ret = cyapa_gen5_calibrate_pwcs(cyapa, + CYAPA_SENSING_MODE_MUTUAL_CAP_FINE); + if (calibrate_ret) + goto resume_scanning; + + /* 3. do self capacitance calibrate. */ + calibrate_ret = cyapa_gen5_calibrate_pwcs(cyapa, + CYAPA_SENSING_MODE_SELF_CAP); + if (calibrate_ret) + goto resume_scanning; + +resume_scanning: + /* 4. resume Scanning*/ + ret = cyapa_gen5_resume_scanning(cyapa); + if (ret || calibrate_ret) + return ret ? ret : calibrate_ret; + + return count; +} + +static s32 two_complement_to_s32(s32 value, int num_bits) +{ + if (value >> (num_bits - 1)) + value |= -1 << num_bits; + return value; +} + +static s32 cyapa_parse_structure_data(u8 data_format, u8 *buf, int buf_len) +{ + int data_size; + bool big_endian; + bool unsigned_type; + s32 value; + + data_size = (data_format & 0x07); + big_endian = ((data_format & 0x10) == 0x00); + unsigned_type = ((data_format & 0x20) == 0x00); + + if (buf_len < data_size) + return 0; + + switch (data_size) { + case 1: + value = buf[0]; + break; + case 2: + if (big_endian) + value = get_unaligned_be16(buf); + else + value = get_unaligned_le16(buf); + break; + case 4: + if (big_endian) + value = get_unaligned_be32(buf); + else + value = get_unaligned_le32(buf); + break; + default: + /* should not happen, just as default case here. */ + value = 0; + break; + } + + if (!unsigned_type) + value = two_complement_to_s32(value, data_size * 8); + + return value; +} + + +/* + * Read all the global mutual or self idac data or mutual or self local PWC + * data based on the @idac_data_type. + * If the input value of @data_size is 0, then means read global mutual or + * self idac data. For read global mutual idac data, @idac_max, @idac_min and + * @idac_ave are in order used to return the max value of global mutual idac + * data, the min value of global mutual idac and the average value of the + * global mutual idac data. For read global self idac data, @idac_max is used + * to return the global self cap idac data in Rx direction, @idac_min is used + * to return the global self cap idac data in Tx direction. @idac_ave is not + * used. + * If the input value of @data_size is not 0, than means read the mutual or + * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to + * return the max, min and average value of the mutual or self local PWC data. + * Note, in order to raed mutual local PWC data, must read invoke this function + * to read the mutual global idac data firstly to set the correct Rx number + * value, otherwise, the read mutual idac and PWC data may not correct. + */ +static int cyapa_gen5_read_idac_data(struct cyapa *cyapa, + u8 cmd_code, u8 idac_data_type, int *data_size, + int *idac_max, int *idac_min, int *idac_ave) +{ + int ret; + int i; + u8 cmd[12]; + u8 resp_data[256]; + int resp_len; + int read_len; + int value; + u16 offset; + int read_elements; + bool read_global_idac; + int sum, count, max_element_cnt; + int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count, tmp_max_elements; + int electrodes_rx; + + if (cmd_code != GEN5_CMD_RETRIEVE_DATA_STRUCTURE || + (idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA && + idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) || + !data_size || !idac_max || !idac_min || !idac_ave) + return -EINVAL; + + *idac_max = INT_MIN; + *idac_min = INT_MAX; + sum = count = tmp_count = 0; + electrodes_rx = 0; + tmp_max_elements = 0; + if (*data_size == 0) { + /* Read global idac values firstly. + * Currently, no idac data exceed 4 bytes. */ + read_global_idac = true; + offset = 0; + *data_size = 4; + + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + if (cyapa->electrodes_rx == 0) { + if (cyapa->electrodes_y > cyapa->electrodes_x) { + electrodes_rx = cyapa->electrodes_y; + tmp_max_elements = cyapa->electrodes_x; + } else { + electrodes_rx = cyapa->electrodes_x; + tmp_max_elements = cyapa->electrodes_y; + } + } else { + electrodes_rx = cyapa->electrodes_rx; + tmp_max_elements = 0; /* disable Rx detect. */ + } + max_element_cnt = ((electrodes_rx + 7) / 8) * 8; + tmp_max = INT_MIN; + tmp_min = INT_MAX; + tmp_ave = tmp_sum = tmp_count = 0; + } else + max_element_cnt = 2; + } else { + read_global_idac = false; + if (*data_size > 4) + *data_size = 4; + /* calculate the start offset in bytes of local PWC data. */ + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + offset = ((cyapa->electrodes_rx + 7) / 8) * 8 + * (*data_size); + if (cyapa->electrodes_rx == cyapa->electrodes_x) + tmp_count = cyapa->electrodes_y; + else + tmp_count = cyapa->electrodes_x; + max_element_cnt = ((cyapa->electrodes_rx + 7) / 8) * + 8 * tmp_count; + } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + offset = 2; + max_element_cnt = cyapa->electrodes_x + + cyapa->electrodes_y; + } + } + + do { + read_elements = (256 - 10) / (*data_size); + read_elements = min(read_elements, max_element_cnt - count); + read_len = read_elements * (*data_size); + + cmd[0] = 0x04; + cmd[1] = 0x00; + cmd[2] = 0x0a; + cmd[3] = 0x00; + cmd[4] = GEN5_APP_CMD_REPORT_ID; + cmd[5] = 0x00; + cmd[6] = cmd_code; + put_unaligned_le16(offset, &cmd[7]); /* Read Offset[15:0] */ + put_unaligned_le16(read_len, &cmd[9]); /* Read Length[15:0] */ + cmd[11] = idac_data_type; + resp_len = 10 + read_len; + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, 12, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_len < 10 || resp_data[2] != + GEN5_APP_RESP_REPORT_ID || + GET_GEN5_CMD_CODE(resp_data[4]) != cmd_code || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + resp_data[6] != idac_data_type) + return (ret < 0) ? ret : -EAGAIN; + read_len = get_unaligned_le16(&resp_data[7]); + if (read_len == 0) + break; + + *data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK); + if (read_len < *data_size) + return -EINVAL; + + if (read_global_idac && + idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) { + /* Rx's self global idac data. */ + *idac_max = cyapa_parse_structure_data( + resp_data[9], &resp_data[10], + *data_size); + /* Tx's self global idac data. */ + *idac_min = cyapa_parse_structure_data( + resp_data[9], + &resp_data[10 + *data_size], + *data_size); + break; + } + + /* read mutual global idac or local mutual/self PWC data. */ + offset += read_len; + for (i = 10; i < (read_len + 10); i += *data_size) { + value = cyapa_parse_structure_data(resp_data[9], + &resp_data[i], *data_size); + *idac_min = min(value, *idac_min); + *idac_max = max(value, *idac_max); + + if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA && + tmp_count < tmp_max_elements && + read_global_idac) { + tmp_min = min(value, tmp_min); + tmp_max = max(value, tmp_max); + tmp_sum += value; + tmp_count++; + } + + sum += value; + count++; + + if (count >= max_element_cnt) + goto out; + } + } while (true); + +out: + *idac_ave = count ? (sum / count) : 0; + + if (read_global_idac && + idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { + if (tmp_count == 0) + return 0; + /* algorithm to detect electrodes_rx value. */ + tmp_ave = tmp_sum / tmp_count; + tmp_count = tmp_ave * 15 / 100; + if (abs(tmp_ave - *idac_ave) > tmp_count || + (abs(tmp_ave - *idac_min) > (tmp_count * 2) && + *idac_min < tmp_min) || + (abs(*idac_max - tmp_ave) > (tmp_count * 2) && + *idac_max > tmp_max)) { + /* overcount the mutual global idac values. */ + cyapa->electrodes_rx = tmp_max_elements; + *idac_min = tmp_min; + *idac_max = tmp_max; + *idac_ave = tmp_ave; + } else + cyapa->electrodes_rx = electrodes_rx; + } + + return 0; +} + +static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa, + int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave, + int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave) +{ + int ret; + int data_size; + + *gidac_mutual_max = *gidac_mutual_min = *gidac_mutual_ave = 0; + *lidac_mutual_max = *lidac_mutual_min = *lidac_mutual_ave = 0; + + data_size = 0; + ret = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_MUTUAL_PWC_DATA, + &data_size, + gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave); + if (ret) + return ret; + + ret = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_MUTUAL_PWC_DATA, + &data_size, + lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave); + return ret; +} + +static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, + int *gidac_self_rx, int *gidac_self_tx, + int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave) +{ + int ret; + int data_size; + + *gidac_self_rx = *gidac_self_tx = 0; + *lidac_self_max = *lidac_self_min = *lidac_self_ave = 0; + + data_size = 0; + ret = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_SELF_CAP_PWC_DATA, + &data_size, + lidac_self_max, lidac_self_min, lidac_self_ave); + if (ret) + return ret; + *gidac_self_rx = *lidac_self_max; + *gidac_self_tx = *lidac_self_min; + + ret = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_SELF_CAP_PWC_DATA, + &data_size, + lidac_self_max, lidac_self_min, lidac_self_ave); + return ret; +} + +static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa) +{ + int ret; + u8 cmd[7]; + u8 resp_data[6]; + int resp_len; + + cmd[0] = 0x04; + cmd[1] = 0x00; + cmd[2] = 0x05; + cmd[3] = 0x00; + cmd[4] = GEN5_APP_CMD_REPORT_ID; + cmd[5] = 0x00; + cmd[6] = GEN5_CMD_EXECUTE_PANEL_SCAN; /* command code */ + resp_len = 6; + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, 7, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_len != 6 || + resp_data[2] != GEN5_APP_RESP_REPORT_ID || + GET_GEN5_CMD_CODE(resp_data[4]) != + GEN5_CMD_EXECUTE_PANEL_SCAN || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5])) { + cyapa_gen5_resume_scanning(cyapa); + return (ret < 0) ? ret : -EAGAIN; + } + + return 0; +} + +static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa, + u8 cmd_code, u8 raw_data_type, int raw_data_max_num, + int *raw_data_max, int *raw_data_min, int *raw_data_ave, + u8 *buffer) +{ + int ret; + int i; + u8 cmd[12]; + u8 resp_data[256]; /* max bytes can transfer one time. */ + int resp_len; + int read_elements; + int read_len; + u16 offset; + s32 value; + int sum, count; + int data_size; + s32 *intp; + + if (cmd_code != GEN5_CMD_RETRIEVE_PANEL_SCAN || + (raw_data_type > GEN5_PANEL_SCAN_SELF_DIFFCOUNT) || + !raw_data_max || !raw_data_min || !raw_data_ave) + return -EINVAL; + + intp = (s32 *)buffer; + *raw_data_max = INT_MIN; + *raw_data_min = INT_MAX; + sum = count = 0; + offset = 0; + read_elements = (256 - 10) / 4; /* currently, max element size is 4. */ + read_len = read_elements * 4; + do { + cmd[0] = 0x04; + cmd[1] = 0x00; + cmd[2] = 0x0a; + cmd[3] = 0x00; + cmd[4] = GEN5_APP_CMD_REPORT_ID; + cmd[5] = 0x00; + cmd[6] = cmd_code; /* command code */ + put_unaligned_le16(offset, &cmd[7]); + put_unaligned_le16(read_elements, &cmd[9]); + cmd[11] = raw_data_type; + resp_len = 10 + read_len; + + ret = cyapa_i2c_pip_cmd_irq_sync(cyapa, + cmd, 12, + resp_data, &resp_len, + 500, cyapa_gen5_sort_tsg_pip_app_resp_data); + if (ret || resp_len < 10 || + resp_data[2] != GEN5_APP_RESP_REPORT_ID || + (resp_data[4] & 0x7f) != cmd_code || + !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) || + resp_data[6] != raw_data_type) + return (ret < 0) ? ret : -EAGAIN; + + read_elements = get_unaligned_le16(&resp_data[7]); + if (read_elements == 0) + break; + + data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK); + offset += read_elements; + if (read_elements) { + for (i = 10; + i < (read_elements * data_size + 10); + i += data_size) { + value = cyapa_parse_structure_data(resp_data[9], + &resp_data[i], data_size); + *raw_data_min = min(value, *raw_data_min); + *raw_data_max = max(value, *raw_data_max); + + if (intp) + put_unaligned_le32(value, &intp[count]); + + sum += value; + count++; + + } + } + + if (count >= raw_data_max_num) + break; + + read_elements = (sizeof(resp_data) - 10) / data_size; + read_len = read_elements * data_size; + } while (true); + + *raw_data_ave = count ? (sum / count) : 0; + + return 0; +} + +static ssize_t cyapa_gen5_show_baseline(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyapa *cyapa = dev_get_drvdata(dev); + int ret, err; + int gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave; + int lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave; + int gidac_self_rx, gidac_self_tx; + int lidac_self_max, lidac_self_min, lidac_self_ave; + int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave; + int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave; + int mutual_diffdata_max, mutual_diffdata_min, mutual_diffdata_ave; + int self_diffdata_max, self_diffdata_min, self_diffdata_ave; + int mutual_baseline_max, mutual_baseline_min, mutual_baseline_ave; + int self_baseline_max, self_baseline_min, self_baseline_ave; + + /* 1. suspend Scanning*/ + ret = cyapa_gen5_suspend_scanning(cyapa); + if (ret) + return ret; + + /* 2. read global and local mutual IDAC data. */ + gidac_self_rx = gidac_self_tx = 0; + err = cyapa_gen5_read_mutual_idac_data(cyapa, + &gidac_mutual_max, &gidac_mutual_min, + &gidac_mutual_ave, &lidac_mutual_max, + &lidac_mutual_min, &lidac_mutual_ave); + if (err) + goto resume_scanning; + + /* 3. read global and local self IDAC data. */ + err = cyapa_gen5_read_self_idac_data(cyapa, + &gidac_self_rx, &gidac_self_tx, + &lidac_self_max, &lidac_self_min, + &lidac_self_ave); + if (err) + goto resume_scanning; + + /* 4. execuate panel scan. It must be executed before read data. */ + err = cyapa_gen5_execute_panel_scan(cyapa); + if (err) + goto resume_scanning; + + /* 5. retrive panel scan, mutual cap raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_RAW_DATA, + cyapa->electrodes_x * cyapa->electrodes_y, + &raw_cap_mutual_max, &raw_cap_mutual_min, + &raw_cap_mutual_ave, + NULL); + if (err) + goto resume_scanning; + + /* 6. retrive panel scan, self cap raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_RAW_DATA, + cyapa->electrodes_x + cyapa->electrodes_y, + &raw_cap_self_max, &raw_cap_self_min, + &raw_cap_self_ave, + NULL); + if (err) + goto resume_scanning; + + /* 7. retrive panel scan, mutual cap diffcount raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT, + cyapa->electrodes_x * cyapa->electrodes_y, + &mutual_diffdata_max, &mutual_diffdata_min, + &mutual_diffdata_ave, + NULL); + if (err) + goto resume_scanning; + + /* 8. retrive panel scan, self cap diffcount raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_DIFFCOUNT, + cyapa->electrodes_x + cyapa->electrodes_y, + &self_diffdata_max, &self_diffdata_min, + &self_diffdata_ave, + NULL); + if (err) + goto resume_scanning; + + /* 9. retrive panel scan, mutual cap baseline raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_BASELINE, + cyapa->electrodes_x * cyapa->electrodes_y, + &mutual_baseline_max, &mutual_baseline_min, + &mutual_baseline_ave, + NULL); + if (err) + goto resume_scanning; + + /* 10. retrive panel scan, self cap baseline raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_BASELINE, + cyapa->electrodes_x + cyapa->electrodes_y, + &self_baseline_max, &self_baseline_min, + &self_baseline_ave, + NULL); + if (err) + goto resume_scanning; + +resume_scanning: + /* 11. resume Scanning*/ + ret = cyapa_gen5_resume_scanning(cyapa); + if (ret || err) + return ret ? ret : err; + + /* 12. output data strings */ + ret = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ", + gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave, + lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave, + gidac_self_rx, gidac_self_tx, + lidac_self_min, lidac_self_max, lidac_self_ave); + err = scnprintf(buf + ret, PAGE_SIZE - ret, + "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave, + raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave, + mutual_diffdata_min, mutual_diffdata_max, mutual_diffdata_ave, + self_diffdata_min, self_diffdata_max, self_diffdata_ave, + mutual_baseline_min, mutual_baseline_max, mutual_baseline_ave, + self_baseline_min, self_baseline_max, self_baseline_ave); + return ret + err; +} + +static int cyapa_gen5_read_raw_data(struct cyapa *cyapa) +{ + int ret, err; + int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave; + int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave; + int offset; + int data_size, max, min, ave; + ktime_t time_mono; + + offset = 0; + if (!cyapa->tp_raw_data) + return -ENOMEM; + + /* 1. suspend Scanning. + * After suspend scanning, the raw data will not be updated, + * so the time of the raw data is before scanning suspended. */ + time_mono = ktime_get(); + ret = cyapa_gen5_suspend_scanning(cyapa); + if (ret) + return ret; + + /* 2. get the correct electrodes_rx number. */ + if (cyapa->electrodes_rx == 0) { + /* Through the read global idac interface to get the Rx number. + * this value is useful to the raw data map.*/ + data_size = 0; + err = cyapa_gen5_read_idac_data(cyapa, + GEN5_CMD_RETRIEVE_DATA_STRUCTURE, + GEN5_RETRIEVE_MUTUAL_PWC_DATA, + &data_size, &max, &min, &ave); + if (err || cyapa->electrodes_rx == 0) + goto resume_scanning; + } + + /* 3. execuate panel scan. It must be executed before read data. */ + err = cyapa_gen5_execute_panel_scan(cyapa); + if (err) + goto resume_scanning; + + /* 4. retrive panel scan, mutual cap raw data. */ + offset = GEN5_RAW_DATA_HEAD_SIZE; + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT, + cyapa->electrodes_x * cyapa->electrodes_y, + &raw_cap_mutual_max, &raw_cap_mutual_min, + &raw_cap_mutual_ave, + cyapa->tp_raw_data + offset); + if (err) + goto resume_scanning; + + offset += sizeof(s32) * cyapa->electrodes_x * cyapa->electrodes_y; + + /* 5. retrive panel scan, self cap raw data. */ + err = cyapa_gen5_read_panel_scan_raw_data(cyapa, + GEN5_CMD_RETRIEVE_PANEL_SCAN, + GEN5_PANEL_SCAN_SELF_DIFFCOUNT, + cyapa->electrodes_x + cyapa->electrodes_y, + &raw_cap_self_max, &raw_cap_self_min, + &raw_cap_self_ave, + cyapa->tp_raw_data + offset); + if (err) + goto resume_scanning; + + offset += sizeof(s32) * (cyapa->electrodes_x + cyapa->electrodes_y); + +resume_scanning: + /* 6. resume Scanning*/ + ret = cyapa_gen5_resume_scanning(cyapa); + if (ret || err) + return ret ? ret : err; + + *((struct timeval *)&cyapa->tp_raw_data[0]) = + ktime_to_timeval(time_mono); + cyapa->tp_raw_data[16] = (u8)cyapa->electrodes_x; + cyapa->tp_raw_data[17] = (u8)cyapa->electrodes_y; + cyapa->tp_raw_data[18] = (u8)cyapa->x_origin; + cyapa->tp_raw_data[19] = (u8)cyapa->y_origin; + cyapa->tp_raw_data[20] = (u8)sizeof(s32); + cyapa->tp_raw_data[21] = (u8)sizeof(s32); + cyapa->tp_raw_data[22] = (u8)cyapa->electrodes_rx; + cyapa->tp_raw_data[23] = 0; /* reserved. */ + + cyapa->tp_raw_data_size = offset; + return 0; +} + static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len) { @@ -3050,19 +4256,19 @@ static int cyapa_check_is_operational(struct cyapa *cyapa) switch (cyapa->gen) { case CYAPA_GEN5: - cyapa->cyapa_check_fw = NULL; - cyapa->cyapa_bl_enter = NULL; + cyapa->cyapa_check_fw = cyapa_gen5_check_fw; + cyapa->cyapa_bl_enter = cyapa_gen5_bl_enter; cyapa->cyapa_bl_activate = NULL; - cyapa->cyapa_bl_initiate = NULL; - cyapa->cyapa_update_fw = NULL; + cyapa->cyapa_bl_initiate = cyapa_gen5_bl_initiate; + cyapa->cyapa_update_fw = cyapa_gen5_do_fw_update; cyapa->cyapa_bl_verify_app_integrity = NULL; cyapa->cyapa_bl_deactivate = NULL; - cyapa->cyapa_show_baseline = NULL; - cyapa->cyapa_calibrate_store = NULL; + cyapa->cyapa_show_baseline = cyapa_gen5_show_baseline; + cyapa->cyapa_calibrate_store = cyapa_gen5_do_calibrate; cyapa->cyapa_irq_handler = cyapa_gen5_irq_handler; cyapa->cyapa_set_power_mode = cyapa_gen5_set_power_mode; - cyapa->cyapa_read_fw = NULL; - cyapa->cyapa_read_raw_data = NULL; + cyapa->cyapa_read_fw = cyapa_gen5_read_fw; + cyapa->cyapa_read_raw_data = cyapa_gen5_read_raw_data; cyapa_enable_irq_save(cyapa); ret = cyapa_gen5_do_operational_check(cyapa); This message and any attachments may contain Cypress (or its subsidiaries) confidential information. If it has been received in error, please advise the sender and immediately delete this message.
<<attachment: winmail.dat>>