static int handle_one_recv_msg()

in ipmi/ipmi_msghandler.c [4439:4659]


static int handle_one_recv_msg(struct ipmi_smi *intf,
			       struct ipmi_smi_msg *msg)
{
	int requeue = 0;
	int chan;
	unsigned char cc;
	bool is_cmd = !((msg->rsp[0] >> 2) & 1);

	pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);

	if (msg->rsp_size < 2) {
		/* Message is too small to be correct. */
		dev_warn(intf->si_dev,
			 "BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
			 (msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);

return_unspecified:
		/* Generate an error response for the message. */
		msg->rsp[0] = msg->data[0] | (1 << 2);
		msg->rsp[1] = msg->data[1];
		msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
		msg->rsp_size = 3;
	} else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
		/* commands must have at least 4 bytes, responses 5. */
		if (is_cmd && (msg->rsp_size < 4)) {
			ipmi_inc_stat(intf, invalid_commands);
			goto out;
		}
		if (!is_cmd && (msg->rsp_size < 5)) {
			ipmi_inc_stat(intf, invalid_ipmb_responses);
			/* Construct a valid error response. */
			msg->rsp[0] = msg->data[0] & 0xfc; /* NetFN */
			msg->rsp[0] |= (1 << 2); /* Make it a response */
			msg->rsp[0] |= msg->data[2] & 3; /* rqLUN */
			msg->rsp[1] = msg->data[1]; /* Addr */
			msg->rsp[2] = msg->data[2] & 0xfc; /* rqSeq */
			msg->rsp[2] |= msg->data[0] & 0x3; /* rsLUN */
			msg->rsp[3] = msg->data[3]; /* Cmd */
			msg->rsp[4] = IPMI_ERR_UNSPECIFIED;
			msg->rsp_size = 5;
		}
	} else if ((msg->data_size >= 2)
	    && (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
	    && (msg->data[1] == IPMI_SEND_MSG_CMD)
	    && (msg->user_data == NULL)) {

		if (intf->in_shutdown)
			goto out;

		/*
		 * This is the local response to a command send, start
		 * the timer for these.  The user_data will not be
		 * NULL if this is a response send, and we will let
		 * response sends just go through.
		 */

		/*
		 * Check for errors, if we get certain errors (ones
		 * that mean basically we can try again later), we
		 * ignore them and start the timer.  Otherwise we
		 * report the error immediately.
		 */
		if ((msg->rsp_size >= 3) && (msg->rsp[2] != 0)
		    && (msg->rsp[2] != IPMI_NODE_BUSY_ERR)
		    && (msg->rsp[2] != IPMI_LOST_ARBITRATION_ERR)
		    && (msg->rsp[2] != IPMI_BUS_ERR)
		    && (msg->rsp[2] != IPMI_NAK_ON_WRITE_ERR)) {
			int ch = msg->rsp[3] & 0xf;
			struct ipmi_channel *chans;

			/* Got an error sending the message, handle it. */

			chans = READ_ONCE(intf->channel_list)->c;
			if ((chans[ch].medium == IPMI_CHANNEL_MEDIUM_8023LAN)
			    || (chans[ch].medium == IPMI_CHANNEL_MEDIUM_ASYNC))
				ipmi_inc_stat(intf, sent_lan_command_errs);
			else
				ipmi_inc_stat(intf, sent_ipmb_command_errs);
			intf_err_seq(intf, msg->msgid, msg->rsp[2]);
		} else
			/* The message was sent, start the timer. */
			intf_start_seq_timer(intf, msg->msgid);
	} else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))
		   || (msg->rsp[1] != msg->data[1])) {
		/*
		 * The NetFN and Command in the response is not even
		 * marginally correct.
		 */
		dev_warn(intf->si_dev,
			 "BMC returned incorrect response, expected netfn %x cmd %x, got netfn %x cmd %x\n",
			 (msg->data[0] >> 2) | 1, msg->data[1],
			 msg->rsp[0] >> 2, msg->rsp[1]);

		goto return_unspecified;
	}

	if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
		if ((msg->data[0] >> 2) & 1) {
			/* It's a response to a sent response. */
			chan = 0;
			cc = msg->rsp[4];
			goto process_response_response;
		}
		if (is_cmd)
			requeue = handle_ipmb_direct_rcv_cmd(intf, msg);
		else
			requeue = handle_ipmb_direct_rcv_rsp(intf, msg);
	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
		   && (msg->rsp[1] == IPMI_SEND_MSG_CMD)
		   && (msg->user_data != NULL)) {
		/*
		 * It's a response to a response we sent.  For this we
		 * deliver a send message response to the user.
		 */
		struct ipmi_recv_msg *recv_msg;

		chan = msg->data[2] & 0x0f;
		if (chan >= IPMI_MAX_CHANNELS)
			/* Invalid channel number */
			goto out;
		cc = msg->rsp[2];

process_response_response:
		recv_msg = msg->user_data;

		requeue = 0;
		if (!recv_msg)
			goto out;

		recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
		recv_msg->msg.data = recv_msg->msg_data;
		recv_msg->msg_data[0] = cc;
		recv_msg->msg.data_len = 1;
		deliver_local_response(intf, recv_msg);
	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
		   && (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
		struct ipmi_channel   *chans;

		/* It's from the receive queue. */
		chan = msg->rsp[3] & 0xf;
		if (chan >= IPMI_MAX_CHANNELS) {
			/* Invalid channel number */
			requeue = 0;
			goto out;
		}

		/*
		 * We need to make sure the channels have been initialized.
		 * The channel_handler routine will set the "curr_channel"
		 * equal to or greater than IPMI_MAX_CHANNELS when all the
		 * channels for this interface have been initialized.
		 */
		if (!intf->channels_ready) {
			requeue = 0; /* Throw the message away */
			goto out;
		}

		chans = READ_ONCE(intf->channel_list)->c;

		switch (chans[chan].medium) {
		case IPMI_CHANNEL_MEDIUM_IPMB:
			if (msg->rsp[4] & 0x04) {
				/*
				 * It's a response, so find the
				 * requesting message and send it up.
				 */
				requeue = handle_ipmb_get_msg_rsp(intf, msg);
			} else {
				/*
				 * It's a command to the SMS from some other
				 * entity.  Handle that.
				 */
				requeue = handle_ipmb_get_msg_cmd(intf, msg);
			}
			break;

		case IPMI_CHANNEL_MEDIUM_8023LAN:
		case IPMI_CHANNEL_MEDIUM_ASYNC:
			if (msg->rsp[6] & 0x04) {
				/*
				 * It's a response, so find the
				 * requesting message and send it up.
				 */
				requeue = handle_lan_get_msg_rsp(intf, msg);
			} else {
				/*
				 * It's a command to the SMS from some other
				 * entity.  Handle that.
				 */
				requeue = handle_lan_get_msg_cmd(intf, msg);
			}
			break;

		default:
			/* Check for OEM Channels.  Clients had better
			   register for these commands. */
			if ((chans[chan].medium >= IPMI_CHANNEL_MEDIUM_OEM_MIN)
			    && (chans[chan].medium
				<= IPMI_CHANNEL_MEDIUM_OEM_MAX)) {
				requeue = handle_oem_get_msg_cmd(intf, msg);
			} else {
				/*
				 * We don't handle the channel type, so just
				 * free the message.
				 */
				requeue = 0;
			}
		}

	} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
		   && (msg->rsp[1] == IPMI_READ_EVENT_MSG_BUFFER_CMD)) {
		/* It's an asynchronous event. */
		requeue = handle_read_event_rsp(intf, msg);
	} else {
		/* It's a response from the local BMC. */
		requeue = handle_bmc_rsp(intf, msg);
	}

 out:
	return requeue;
}