static void handle_flags(struct kcs_info *kcs_info) { if (kcs_info->msg_flags & WDT_PRE_TIMEOUT_INT) { /* Watchdog pre-timeout */ start_clear_flags(kcs_info); kcs_info->msg_flags &= ~WDT_PRE_TIMEOUT_INT; spin_unlock(&(kcs_info->kcs_lock)); ipmi_smi_watchdog_pretimeout(kcs_info->intf); spin_lock(&(kcs_info->kcs_lock)); } else if (kcs_info->msg_flags & RECEIVE_MSG_AVAIL) { /* Messages available. */ kcs_info->curr_msg = ipmi_alloc_smi_msg(); if (!kcs_info->curr_msg) { disable_kcs_irq(kcs_info); kcs_info->kcs_state = KCS_NORMAL; return; } enable_kcs_irq(kcs_info); kcs_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); kcs_info->curr_msg->data[1] = IPMI_GET_MSG_CMD; kcs_info->curr_msg->data_size = 2; kcs_smi_handlers.start_transaction( kcs_info->kcs_sm, kcs_info->curr_msg->data, kcs_info->curr_msg->data_size); kcs_info->kcs_state = KCS_GETTING_MESSAGES; } else if (kcs_info->msg_flags & EVENT_MSG_BUFFER_FULL) { /* Events available. */ kcs_info->curr_msg = ipmi_alloc_smi_msg(); if (!kcs_info->curr_msg) { disable_kcs_irq(kcs_info); kcs_info->kcs_state = KCS_NORMAL; return; } enable_kcs_irq(kcs_info); kcs_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2); kcs_info->curr_msg->data[1] = IPMI_READ_EVENT_MSG_BUFFER_CMD; kcs_info->curr_msg->data_size = 2; kcs_smi_handlers.start_transaction( kcs_info->kcs_sm, kcs_info->curr_msg->data, kcs_info->curr_msg->data_size); kcs_info->kcs_state = KCS_GETTING_EVENTS; } else { kcs_info->kcs_state = KCS_NORMAL; } }
/* * Allocate a message. If unable to allocate, start the interrupt * disable process and return NULL. If able to allocate but * interrupts are disabled, free the message and return NULL after * starting the interrupt enable process. */ static struct ipmi_smi_msg *alloc_msg_handle_irq(struct smi_info *smi_info) { struct ipmi_smi_msg *msg; msg = ipmi_alloc_smi_msg(); if (!msg) { if (!disable_si_irq(smi_info)) smi_info->si_state = SI_NORMAL; } else if (enable_si_irq(smi_info)) { ipmi_free_smi_msg(msg); msg = NULL; } return msg; }
static void handle_transaction_done(struct smi_info *smi_info) { struct ipmi_smi_msg *msg; debug_timestamp("Done"); switch (smi_info->si_state) { case SI_NORMAL: if (!smi_info->curr_msg) break; smi_info->curr_msg->rsp_size = smi_info->handlers->get_result( smi_info->si_sm, smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); /* * Do this here becase deliver_recv_msg() releases the * lock, and a new message can be put in during the * time the lock is released. */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; deliver_recv_msg(smi_info, msg); break; case SI_GETTING_FLAGS: { unsigned char msg[4]; unsigned int len; /* We got the flags from the SMI, now handle them. */ len = smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { /* Error fetching flags, just give up for now. */ smi_info->si_state = SI_NORMAL; } else if (len < 4) { /* * Hmm, no flags. That's technically illegal, but * don't use uninitialized data. */ smi_info->si_state = SI_NORMAL; } else { smi_info->msg_flags = msg[3]; handle_flags(smi_info); } break; } case SI_CLEARING_FLAGS: { unsigned char msg[3]; /* We cleared the flags. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 3); if (msg[2] != 0) { /* Error clearing flags */ dev_warn(smi_info->dev, "Error clearing flags: %2.2x\n", msg[2]); } smi_info->si_state = SI_NORMAL; break; } case SI_GETTING_EVENTS: { smi_info->curr_msg->rsp_size = smi_info->handlers->get_result( smi_info->si_sm, smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); /* * Do this here becase deliver_recv_msg() releases the * lock, and a new message can be put in during the * time the lock is released. */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; if (msg->rsp[2] != 0) { /* Error getting event, probably done. */ msg->done(msg); /* Take off the event flag. */ smi_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; handle_flags(smi_info); } else { smi_inc_stat(smi_info, events); /* * Do this before we deliver the message * because delivering the message releases the * lock and something else can mess with the * state. */ handle_flags(smi_info); deliver_recv_msg(smi_info, msg); } break; } case SI_GETTING_MESSAGES: { smi_info->curr_msg->rsp_size = smi_info->handlers->get_result( smi_info->si_sm, smi_info->curr_msg->rsp, IPMI_MAX_MSG_LENGTH); /* * Do this here becase deliver_recv_msg() releases the * lock, and a new message can be put in during the * time the lock is released. */ msg = smi_info->curr_msg; smi_info->curr_msg = NULL; if (msg->rsp[2] != 0) { /* Error getting event, probably done. */ msg->done(msg); /* Take off the msg flag. */ smi_info->msg_flags &= ~RECEIVE_MSG_AVAIL; handle_flags(smi_info); } else { smi_inc_stat(smi_info, incoming_messages); /* * Do this before we deliver the message * because delivering the message releases the * lock and something else can mess with the * state. */ handle_flags(smi_info); deliver_recv_msg(smi_info, msg); } break; } case SI_CHECKING_ENABLES: { unsigned char msg[4]; u8 enables; bool irq_on; /* We got the flags from the SMI, now handle them. */ smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) { dev_warn(smi_info->dev, "Couldn't get irq info: %x.\n", msg[2]); dev_warn(smi_info->dev, "Maybe ok, but ipmi might run very slowly.\n"); smi_info->si_state = SI_NORMAL; break; } enables = current_global_enables(smi_info, 0, &irq_on); if (smi_info->si_type == SI_BT) /* BT has its own interrupt enable bit. */ check_bt_irq(smi_info, irq_on); if (enables != (msg[3] & GLOBAL_ENABLES_MASK)) { /* Enables are not correct, fix them. */ msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_SET_BMC_GLOBAL_ENABLES_CMD; msg[2] = enables | (msg[3] & ~GLOBAL_ENABLES_MASK); smi_info->handlers->start_transaction( smi_info->si_sm, msg, 3); smi_info->si_state = SI_SETTING_ENABLES; } else if (smi_info->supports_event_msg_buff) { smi_info->curr_msg = ipmi_alloc_smi_msg(); if (!smi_info->curr_msg) { smi_info->si_state = SI_NORMAL; break; } start_getting_msg_queue(smi_info); } else { smi_info->si_state = SI_NORMAL; } break; } case SI_SETTING_ENABLES: { unsigned char msg[4]; smi_info->handlers->get_result(smi_info->si_sm, msg, 4); if (msg[2] != 0) dev_warn(smi_info->dev, "Could not set the global enables: 0x%x.\n", msg[2]); if (smi_info->supports_event_msg_buff) { smi_info->curr_msg = ipmi_alloc_smi_msg(); if (!smi_info->curr_msg) { smi_info->si_state = SI_NORMAL; break; } start_getting_msg_queue(smi_info); } else { smi_info->si_state = SI_NORMAL; } break; } } }