static void process_recv()

in intel-ish-hid/ishtp-hid-client.c [66:327]


static void process_recv(struct ishtp_cl *hid_ishtp_cl, void *recv_buf,
			 size_t data_len)
{
	struct hostif_msg *recv_msg;
	unsigned char *payload;
	struct device_info *dev_info;
	int i, j;
	size_t	payload_len, total_len, cur_pos, raw_len;
	int report_type;
	struct report_list *reports_list;
	char *reports;
	size_t report_len;
	struct ishtp_cl_data *client_data = ishtp_get_client_data(hid_ishtp_cl);
	int curr_hid_dev = client_data->cur_hid_dev;
	struct ishtp_hid_data *hid_data = NULL;
	struct hid_device *hid = NULL;

	payload = recv_buf + sizeof(struct hostif_msg_hdr);
	total_len = data_len;
	cur_pos = 0;

	do {
		if (cur_pos + sizeof(struct hostif_msg) > total_len) {
			dev_err(cl_data_to_dev(client_data),
				"[hid-ish]: error, received %u which is less than data header %u\n",
				(unsigned int)data_len,
				(unsigned int)sizeof(struct hostif_msg_hdr));
			++client_data->bad_recv_cnt;
			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
			break;
		}

		recv_msg = (struct hostif_msg *)(recv_buf + cur_pos);
		payload_len = recv_msg->hdr.size;

		/* Sanity checks */
		if (cur_pos + payload_len + sizeof(struct hostif_msg) >
				total_len) {
			++client_data->bad_recv_cnt;
			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
					  payload_len);
			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
			break;
		}

		hid_ishtp_trace(client_data,  "%s %d\n",
				__func__, recv_msg->hdr.command & CMD_MASK);

		switch (recv_msg->hdr.command & CMD_MASK) {
		case HOSTIF_DM_ENUM_DEVICES:
			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
					client_data->init_done)) {
				++client_data->bad_recv_cnt;
				report_bad_packet(hid_ishtp_cl, recv_msg,
						  cur_pos,
						  payload_len);
				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
				break;
			}
			client_data->hid_dev_count = (unsigned int)*payload;
			if (!client_data->hid_devices)
				client_data->hid_devices = devm_kcalloc(
						cl_data_to_dev(client_data),
						client_data->hid_dev_count,
						sizeof(struct device_info),
						GFP_KERNEL);
			if (!client_data->hid_devices) {
				dev_err(cl_data_to_dev(client_data),
				"Mem alloc failed for hid device info\n");
				wake_up_interruptible(&client_data->init_wait);
				break;
			}
			for (i = 0; i < client_data->hid_dev_count; ++i) {
				if (1 + sizeof(struct device_info) * i >=
						payload_len) {
					dev_err(cl_data_to_dev(client_data),
						"[hid-ish]: [ENUM_DEVICES]: content size %zu is bigger than payload_len %zu\n",
						1 + sizeof(struct device_info)
						* i, payload_len);
				}

				if (1 + sizeof(struct device_info) * i >=
						data_len)
					break;

				dev_info = (struct device_info *)(payload + 1 +
					sizeof(struct device_info) * i);
				if (client_data->hid_devices)
					memcpy(client_data->hid_devices + i,
					       dev_info,
					       sizeof(struct device_info));
			}

			client_data->enum_devices_done = true;
			wake_up_interruptible(&client_data->init_wait);

			break;

		case HOSTIF_GET_HID_DESCRIPTOR:
			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
					client_data->init_done)) {
				++client_data->bad_recv_cnt;
				report_bad_packet(hid_ishtp_cl, recv_msg,
						  cur_pos,
						  payload_len);
				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
				break;
			}
			if (!client_data->hid_descr[curr_hid_dev])
				client_data->hid_descr[curr_hid_dev] =
				devm_kmalloc(cl_data_to_dev(client_data),
					     payload_len, GFP_KERNEL);
			if (client_data->hid_descr[curr_hid_dev]) {
				memcpy(client_data->hid_descr[curr_hid_dev],
				       payload, payload_len);
				client_data->hid_descr_size[curr_hid_dev] =
					payload_len;
				client_data->hid_descr_done = true;
			}
			wake_up_interruptible(&client_data->init_wait);

			break;

		case HOSTIF_GET_REPORT_DESCRIPTOR:
			if ((!(recv_msg->hdr.command & ~CMD_MASK) ||
					client_data->init_done)) {
				++client_data->bad_recv_cnt;
				report_bad_packet(hid_ishtp_cl, recv_msg,
						  cur_pos,
						  payload_len);
				ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
				break;
			}
			if (!client_data->report_descr[curr_hid_dev])
				client_data->report_descr[curr_hid_dev] =
				devm_kmalloc(cl_data_to_dev(client_data),
					     payload_len, GFP_KERNEL);
			if (client_data->report_descr[curr_hid_dev])  {
				memcpy(client_data->report_descr[curr_hid_dev],
				       payload,
				       payload_len);
				client_data->report_descr_size[curr_hid_dev] =
					payload_len;
				client_data->report_descr_done = true;
			}
			wake_up_interruptible(&client_data->init_wait);

			break;

		case HOSTIF_GET_FEATURE_REPORT:
			report_type = HID_FEATURE_REPORT;
			goto	do_get_report;

		case HOSTIF_GET_INPUT_REPORT:
			report_type = HID_INPUT_REPORT;
do_get_report:
			/* Get index of device that matches this id */
			for (i = 0; i < client_data->num_hid_devices; ++i) {
				if (recv_msg->hdr.device_id ==
					  client_data->hid_devices[i].dev_id) {
					hid = client_data->hid_sensor_hubs[i];
					if (!hid)
						break;

					hid_data = hid->driver_data;
					if (hid_data->raw_get_req) {
						raw_len =
						  (hid_data->raw_buf_size <
								payload_len) ?
						  hid_data->raw_buf_size :
						  payload_len;

						memcpy(hid_data->raw_buf,
						       payload, raw_len);
					} else {
						hid_input_report
							(hid, report_type,
							 payload, payload_len,
							 0);
					}

					ishtp_hid_wakeup(hid);
					break;
				}
			}
			break;

		case HOSTIF_SET_FEATURE_REPORT:
			/* Get index of device that matches this id */
			for (i = 0; i < client_data->num_hid_devices; ++i) {
				if (recv_msg->hdr.device_id ==
					client_data->hid_devices[i].dev_id)
					if (client_data->hid_sensor_hubs[i]) {
						ishtp_hid_wakeup(
						client_data->hid_sensor_hubs[
							i]);
						break;
					}
			}
			break;

		case HOSTIF_PUBLISH_INPUT_REPORT:
			report_type = HID_INPUT_REPORT;
			for (i = 0; i < client_data->num_hid_devices; ++i)
				if (recv_msg->hdr.device_id ==
					client_data->hid_devices[i].dev_id)
					if (client_data->hid_sensor_hubs[i])
						hid_input_report(
						client_data->hid_sensor_hubs[
									i],
						report_type, payload,
						payload_len, 0);
			break;

		case HOSTIF_PUBLISH_INPUT_REPORT_LIST:
			report_type = HID_INPUT_REPORT;
			reports_list = (struct report_list *)payload;
			reports = (char *)reports_list->reports;

			for (j = 0; j < reports_list->num_of_reports; j++) {
				recv_msg = (struct hostif_msg *)(reports +
					sizeof(uint16_t));
				report_len = *(uint16_t *)reports;
				payload = reports + sizeof(uint16_t) +
					sizeof(struct hostif_msg_hdr);
				payload_len = report_len -
					sizeof(struct hostif_msg_hdr);

				for (i = 0; i < client_data->num_hid_devices;
				     ++i)
					if (recv_msg->hdr.device_id ==
					client_data->hid_devices[i].dev_id &&
					client_data->hid_sensor_hubs[i]) {
						hid_input_report(
						client_data->hid_sensor_hubs[
									i],
						report_type,
						payload, payload_len,
						0);
					}

				reports += sizeof(uint16_t) + report_len;
			}
			break;
		default:
			++client_data->bad_recv_cnt;
			report_bad_packet(hid_ishtp_cl, recv_msg, cur_pos,
					  payload_len);
			ish_hw_reset(ishtp_get_ishtp_device(hid_ishtp_cl));
			break;

		}

		if (!cur_pos && cur_pos + payload_len +
				sizeof(struct hostif_msg) < total_len)
			++client_data->multi_packet_cnt;

		cur_pos += payload_len + sizeof(struct hostif_msg);
		payload += payload_len + sizeof(struct hostif_msg);

	} while (cur_pos < total_len);
}