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);
}