int mei_irq_read_handler()

in mei/interrupt.c [327:467]


int mei_irq_read_handler(struct mei_device *dev,
			 struct list_head *cmpl_list, s32 *slots)
{
	struct mei_msg_hdr *mei_hdr;
	struct mei_ext_meta_hdr *meta_hdr = NULL;
	struct mei_cl *cl;
	int ret;
	u32 hdr_size_left;
	u32 hdr_size_ext;
	int i;
	int ext_hdr_end;

	if (!dev->rd_msg_hdr[0]) {
		dev->rd_msg_hdr[0] = mei_read_hdr(dev);
		dev->rd_msg_hdr_count = 1;
		(*slots)--;
		dev_dbg(dev->dev, "slots =%08x.\n", *slots);

		ret = hdr_is_valid(dev->rd_msg_hdr[0]);
		if (ret) {
			dev_err(dev->dev, "corrupted message header 0x%08X\n",
				dev->rd_msg_hdr[0]);
			goto end;
		}
	}

	mei_hdr = (struct mei_msg_hdr *)dev->rd_msg_hdr;
	dev_dbg(dev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));

	if (mei_slots2data(*slots) < mei_hdr->length) {
		dev_err(dev->dev, "less data available than length=%08x.\n",
				*slots);
		/* we can't read the message */
		ret = -ENODATA;
		goto end;
	}

	ext_hdr_end = 1;
	hdr_size_left = mei_hdr->length;

	if (mei_hdr->extended) {
		if (!dev->rd_msg_hdr[1]) {
			dev->rd_msg_hdr[1] = mei_read_hdr(dev);
			dev->rd_msg_hdr_count++;
			(*slots)--;
			dev_dbg(dev->dev, "extended header is %08x\n", dev->rd_msg_hdr[1]);
		}
		meta_hdr = ((struct mei_ext_meta_hdr *)&dev->rd_msg_hdr[1]);
		if (check_add_overflow((u32)sizeof(*meta_hdr),
				       mei_slots2data(meta_hdr->size),
				       &hdr_size_ext)) {
			dev_err(dev->dev, "extended message size too big %d\n",
				meta_hdr->size);
			return -EBADMSG;
		}
		if (hdr_size_left < hdr_size_ext) {
			dev_err(dev->dev, "corrupted message header len %d\n",
				mei_hdr->length);
			return -EBADMSG;
		}
		hdr_size_left -= hdr_size_ext;

		ext_hdr_end = meta_hdr->size + 2;
		for (i = dev->rd_msg_hdr_count; i < ext_hdr_end; i++) {
			dev->rd_msg_hdr[i] = mei_read_hdr(dev);
			dev_dbg(dev->dev, "extended header %d is %08x\n", i,
				dev->rd_msg_hdr[i]);
			dev->rd_msg_hdr_count++;
			(*slots)--;
		}
	}

	if (mei_hdr->dma_ring) {
		if (hdr_size_left != sizeof(dev->rd_msg_hdr[ext_hdr_end])) {
			dev_err(dev->dev, "corrupted message header len %d\n",
				mei_hdr->length);
			return -EBADMSG;
		}

		dev->rd_msg_hdr[ext_hdr_end] = mei_read_hdr(dev);
		dev->rd_msg_hdr_count++;
		(*slots)--;
		mei_hdr->length -= sizeof(dev->rd_msg_hdr[ext_hdr_end]);
	}

	/*  HBM message */
	if (hdr_is_hbm(mei_hdr)) {
		ret = mei_hbm_dispatch(dev, mei_hdr);
		if (ret) {
			dev_dbg(dev->dev, "mei_hbm_dispatch failed ret = %d\n",
					ret);
			goto end;
		}
		goto reset_slots;
	}

	/* find recipient cl */
	list_for_each_entry(cl, &dev->file_list, link) {
		if (mei_cl_hbm_equal(cl, mei_hdr)) {
			cl_dbg(dev, cl, "got a message\n");
			break;
		}
	}

	/* if no recipient cl was found we assume corrupted header */
	if (&cl->link == &dev->file_list) {
		/* A message for not connected fixed address clients
		 * should be silently discarded
		 * On power down client may be force cleaned,
		 * silently discard such messages
		 */
		if (hdr_is_fixed(mei_hdr) ||
		    dev->dev_state == MEI_DEV_POWER_DOWN) {
			mei_irq_discard_msg(dev, mei_hdr, mei_hdr->length);
			ret = 0;
			goto reset_slots;
		}
		dev_err(dev->dev, "no destination client found 0x%08X\n",
				dev->rd_msg_hdr[0]);
		ret = -EBADMSG;
		goto end;
	}

	ret = mei_cl_irq_read_msg(cl, mei_hdr, meta_hdr, cmpl_list);


reset_slots:
	/* reset the number of slots and header */
	memset(dev->rd_msg_hdr, 0, sizeof(dev->rd_msg_hdr));
	dev->rd_msg_hdr_count = 0;
	*slots = mei_count_full_read_slots(dev);
	if (*slots == -EOVERFLOW) {
		/* overflow - reset */
		dev_err(dev->dev, "resetting due to slots overflow.\n");
		/* set the event since message has been read */
		ret = -ERANGE;
		goto end;
	}
end:
	return ret;
}