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