static void msg_done_handler()

in ipmi/ipmi_ssif.c [607:880]


static void msg_done_handler(struct ssif_info *ssif_info, int result,
			     unsigned char *data, unsigned int len)
{
	struct ipmi_smi_msg *msg;
	unsigned long oflags, *flags;

	/*
	 * We are single-threaded here, so no need for a lock until we
	 * start messing with driver states or the queues.
	 */

	if (result < 0) {
		ssif_info->retries_left--;
		if (ssif_info->retries_left > 0) {
			ssif_inc_stat(ssif_info, receive_retries);

			flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
			ssif_info->waiting_alert = true;
			ssif_info->rtc_us_timer = SSIF_MSG_USEC;
			if (!ssif_info->stopping)
				mod_timer(&ssif_info->retry_timer,
					  jiffies + SSIF_MSG_JIFFIES);
			ipmi_ssif_unlock_cond(ssif_info, flags);
			return;
		}

		ssif_inc_stat(ssif_info, receive_errors);

		if  (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
			dev_dbg(&ssif_info->client->dev,
				"%s: Error %d\n", __func__, result);
		len = 0;
		goto continue_op;
	}

	if ((len > 1) && (ssif_info->multi_pos == 0)
				&& (data[0] == 0x00) && (data[1] == 0x01)) {
		/* Start of multi-part read.  Start the next transaction. */
		int i;

		ssif_inc_stat(ssif_info, received_message_parts);

		/* Remove the multi-part read marker. */
		len -= 2;
		data += 2;
		for (i = 0; i < len; i++)
			ssif_info->data[i] = data[i];
		ssif_info->multi_len = len;
		ssif_info->multi_pos = 1;

		ssif_i2c_send(ssif_info, msg_done_handler, I2C_SMBUS_READ,
			 SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE,
			 ssif_info->recv, I2C_SMBUS_BLOCK_DATA);
		return;
	} else if (ssif_info->multi_pos) {
		/* Middle of multi-part read.  Start the next transaction. */
		int i;
		unsigned char blocknum;

		if (len == 0) {
			result = -EIO;
			if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
				dev_dbg(&ssif_info->client->dev,
					"Middle message with no data\n");

			goto continue_op;
		}

		blocknum = data[0];
		len--;
		data++;

		if (blocknum != 0xff && len != 31) {
		    /* All blocks but the last must have 31 data bytes. */
			result = -EIO;
			if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
				dev_dbg(&ssif_info->client->dev,
					"Received middle message <31\n");

			goto continue_op;
		}

		if (ssif_info->multi_len + len > IPMI_MAX_MSG_LENGTH) {
			/* Received message too big, abort the operation. */
			result = -E2BIG;
			if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
				dev_dbg(&ssif_info->client->dev,
					"Received message too big\n");

			goto continue_op;
		}

		for (i = 0; i < len; i++)
			ssif_info->data[i + ssif_info->multi_len] = data[i];
		ssif_info->multi_len += len;
		if (blocknum == 0xff) {
			/* End of read */
			len = ssif_info->multi_len;
			data = ssif_info->data;
		} else if (blocknum + 1 != ssif_info->multi_pos) {
			/*
			 * Out of sequence block, just abort.  Block
			 * numbers start at zero for the second block,
			 * but multi_pos starts at one, so the +1.
			 */
			if (ssif_info->ssif_debug & SSIF_DEBUG_MSG)
				dev_dbg(&ssif_info->client->dev,
					"Received message out of sequence, expected %u, got %u\n",
					ssif_info->multi_pos - 1, blocknum);
			result = -EIO;
		} else {
			ssif_inc_stat(ssif_info, received_message_parts);

			ssif_info->multi_pos++;

			ssif_i2c_send(ssif_info, msg_done_handler,
				  I2C_SMBUS_READ,
				  SSIF_IPMI_MULTI_PART_RESPONSE_MIDDLE,
				  ssif_info->recv,
				  I2C_SMBUS_BLOCK_DATA);
			return;
		}
	}

 continue_op:
	if (result < 0) {
		ssif_inc_stat(ssif_info, receive_errors);
	} else {
		ssif_inc_stat(ssif_info, received_messages);
		ssif_inc_stat(ssif_info, received_message_parts);
	}

	if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
		dev_dbg(&ssif_info->client->dev,
			"DONE 1: state = %d, result=%d\n",
			ssif_info->ssif_state, result);

	flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
	msg = ssif_info->curr_msg;
	if (msg) {
		if (data) {
			if (len > IPMI_MAX_MSG_LENGTH)
				len = IPMI_MAX_MSG_LENGTH;
			memcpy(msg->rsp, data, len);
		} else {
			len = 0;
		}
		msg->rsp_size = len;
		ssif_info->curr_msg = NULL;
	}

	switch (ssif_info->ssif_state) {
	case SSIF_NORMAL:
		ipmi_ssif_unlock_cond(ssif_info, flags);
		if (!msg)
			break;

		if (result < 0)
			return_hosed_msg(ssif_info, msg);
		else
			deliver_recv_msg(ssif_info, msg);
		break;

	case SSIF_GETTING_FLAGS:
		/* We got the flags from the SSIF, now handle them. */
		if ((result < 0) || (len < 4) || (data[2] != 0)) {
			/*
			 * Error fetching flags, or invalid length,
			 * just give up for now.
			 */
			ssif_info->ssif_state = SSIF_NORMAL;
			ipmi_ssif_unlock_cond(ssif_info, flags);
			dev_warn(&ssif_info->client->dev,
				 "Error getting flags: %d %d, %x\n",
				 result, len, (len >= 3) ? data[2] : 0);
		} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
			   || data[1] != IPMI_GET_MSG_FLAGS_CMD) {
			/*
			 * Don't abort here, maybe it was a queued
			 * response to a previous command.
			 */
			ipmi_ssif_unlock_cond(ssif_info, flags);
			dev_warn(&ssif_info->client->dev,
				 "Invalid response getting flags: %x %x\n",
				 data[0], data[1]);
		} else {
			ssif_inc_stat(ssif_info, flag_fetches);
			ssif_info->msg_flags = data[3];
			handle_flags(ssif_info, flags);
		}
		break;

	case SSIF_CLEARING_FLAGS:
		/* We cleared the flags. */
		if ((result < 0) || (len < 3) || (data[2] != 0)) {
			/* Error clearing flags */
			dev_warn(&ssif_info->client->dev,
				 "Error clearing flags: %d %d, %x\n",
				 result, len, (len >= 3) ? data[2] : 0);
		} else if (data[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
			   || data[1] != IPMI_CLEAR_MSG_FLAGS_CMD) {
			dev_warn(&ssif_info->client->dev,
				 "Invalid response clearing flags: %x %x\n",
				 data[0], data[1]);
		}
		ssif_info->ssif_state = SSIF_NORMAL;
		ipmi_ssif_unlock_cond(ssif_info, flags);
		break;

	case SSIF_GETTING_EVENTS:
		if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) {
			/* Error getting event, probably done. */
			msg->done(msg);

			/* Take off the event flag. */
			ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
			handle_flags(ssif_info, flags);
		} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
			   || msg->rsp[1] != IPMI_READ_EVENT_MSG_BUFFER_CMD) {
			dev_warn(&ssif_info->client->dev,
				 "Invalid response getting events: %x %x\n",
				 msg->rsp[0], msg->rsp[1]);
			msg->done(msg);
			/* Take off the event flag. */
			ssif_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL;
			handle_flags(ssif_info, flags);
		} else {
			handle_flags(ssif_info, flags);
			ssif_inc_stat(ssif_info, events);
			deliver_recv_msg(ssif_info, msg);
		}
		break;

	case SSIF_GETTING_MESSAGES:
		if ((result < 0) || (len < 3) || (msg->rsp[2] != 0)) {
			/* Error getting event, probably done. */
			msg->done(msg);

			/* Take off the msg flag. */
			ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
			handle_flags(ssif_info, flags);
		} else if (msg->rsp[0] != (IPMI_NETFN_APP_REQUEST | 1) << 2
			   || msg->rsp[1] != IPMI_GET_MSG_CMD) {
			dev_warn(&ssif_info->client->dev,
				 "Invalid response clearing flags: %x %x\n",
				 msg->rsp[0], msg->rsp[1]);
			msg->done(msg);

			/* Take off the msg flag. */
			ssif_info->msg_flags &= ~RECEIVE_MSG_AVAIL;
			handle_flags(ssif_info, flags);
		} else {
			ssif_inc_stat(ssif_info, incoming_messages);
			handle_flags(ssif_info, flags);
			deliver_recv_msg(ssif_info, msg);
		}
		break;
	}

	flags = ipmi_ssif_lock_cond(ssif_info, &oflags);
	if (SSIF_IDLE(ssif_info) && !ssif_info->stopping) {
		if (ssif_info->req_events)
			start_event_fetch(ssif_info, flags);
		else if (ssif_info->req_flags)
			start_flag_fetch(ssif_info, flags);
		else
			start_next_msg(ssif_info, flags);
	} else
		ipmi_ssif_unlock_cond(ssif_info, flags);

	if (ssif_info->ssif_debug & SSIF_DEBUG_STATE)
		dev_dbg(&ssif_info->client->dev,
			"DONE 2: state = %d.\n", ssif_info->ssif_state);
}