static int cyapa_gen5_read_idac_data()

in mouse/cyapa_gen5.c [1933:2120]


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)
{
	struct pip_app_cmd_head *cmd_head;
	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;
	int electrodes_rx, electrodes_tx;
	int i;
	int error;

	if (cmd_code != PIP_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 = electrodes_tx = 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;
		tmp_max = INT_MIN;
		tmp_min = INT_MAX;
		tmp_ave = tmp_sum = tmp_count = 0;

		if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
			if (cyapa->aligned_electrodes_rx == 0) {
				cyapa_gen5_guess_electrodes(cyapa,
					&electrodes_rx, &electrodes_tx);
				cyapa->aligned_electrodes_rx =
					(electrodes_rx + 3) & ~3u;
			}
			max_element_cnt =
				(cyapa->aligned_electrodes_rx + 7) & ~7u;
		} 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->aligned_electrodes_rx * (*data_size);
			if (cyapa->electrodes_rx == cyapa->electrodes_x)
				electrodes_tx = cyapa->electrodes_y;
			else
				electrodes_tx = cyapa->electrodes_x;
			max_element_cnt = ((cyapa->aligned_electrodes_rx + 7) &
						~7u) * electrodes_tx;
		} else {
			offset = 2;
			max_element_cnt = cyapa->electrodes_x +
						cyapa->electrodes_y;
			max_element_cnt = (max_element_cnt + 3) & ~3u;
		}
	}

	memset(cmd, 0, sizeof(cmd));
	cmd_head = (struct pip_app_cmd_head *)cmd;
	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd_head->addr);
	put_unaligned_le16(sizeof(cmd) - 2, &cmd_head->length);
	cmd_head->report_id = PIP_APP_CMD_REPORT_ID;
	cmd_head->cmd_code = cmd_code;
	do {
		read_elements = (256 - GEN5_RESP_DATA_STRUCTURE_OFFSET) /
				(*data_size);
		read_elements = min(read_elements, max_element_cnt - count);
		read_len = read_elements * (*data_size);

		put_unaligned_le16(offset, &cmd_head->parameter_data[0]);
		put_unaligned_le16(read_len, &cmd_head->parameter_data[2]);
		cmd_head->parameter_data[4] = idac_data_type;
		resp_len = GEN5_RESP_DATA_STRUCTURE_OFFSET + read_len;
		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
				cmd, sizeof(cmd),
				resp_data, &resp_len,
				500, cyapa_sort_tsg_pip_app_resp_data,
				true);
		if (error || resp_len < GEN5_RESP_DATA_STRUCTURE_OFFSET ||
				!VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
				!PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
				resp_data[6] != idac_data_type)
			return (error < 0) ? error : -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[GEN5_RESP_DATA_STRUCTURE_OFFSET],
				*data_size);
			/* Tx's self global idac data. */
			*idac_min = cyapa_parse_structure_data(
				resp_data[9],
				&resp_data[GEN5_RESP_DATA_STRUCTURE_OFFSET +
					   *data_size],
				*data_size);
			break;
		}

		/* Read mutual global idac or local mutual/self PWC data. */
		offset += read_len;
		for (i = 10; i < (read_len + GEN5_RESP_DATA_STRUCTURE_OFFSET);
				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 < cyapa->aligned_electrodes_rx &&
				read_global_idac) {
				/*
				 * The value gap between global and local mutual
				 * idac data must bigger than 50%.
				 * Normally, global value bigger than 50,
				 * local values less than 10.
				 */
				if (!tmp_ave || value > tmp_ave / 2) {
					tmp_min = min(value, tmp_min);
					tmp_max = max(value, tmp_max);
					tmp_sum += value;
					tmp_count++;

					tmp_ave = tmp_sum / 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;

		if (tmp_count == cyapa->aligned_electrodes_rx) {
			cyapa->electrodes_rx = cyapa->electrodes_rx ?
				cyapa->electrodes_rx : electrodes_rx;
		} else if (tmp_count == electrodes_rx) {
			cyapa->electrodes_rx = cyapa->electrodes_rx ?
				cyapa->electrodes_rx : electrodes_rx;
			cyapa->aligned_electrodes_rx = electrodes_rx;
		} else {
			cyapa->electrodes_rx = cyapa->electrodes_rx ?
				cyapa->electrodes_rx : electrodes_tx;
			cyapa->aligned_electrodes_rx = tmp_count;
		}

		*idac_min = tmp_min;
		*idac_max = tmp_max;
		*idac_ave = tmp_ave;
	}

	return 0;
}