📄 ipmi_kcs_intf.c
字号:
/* * ipmi_kcs_intf.c * * The interface to the IPMI driver for the KCS. * * Author: MontaVista Software, Inc. * Corey Minyard <minyard@mvista.com> * source@mvista.com * * Copyright 2002 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. *//* * This file holds the "policy" for the interface to the KCS state * machine. It does the configuration, handles timers and interrupts, * and drives the real KCS state machine. */#include <linux/config.h>#include <linux/module.h>#include <asm/system.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/errno.h>#include <linux/spinlock.h>#include <linux/slab.h>#include <linux/delay.h>#include <linux/list.h>#include <linux/ioport.h>#ifdef CONFIG_HIGH_RES_TIMERS#include <linux/hrtime.h>#endif#include <linux/interrupt.h>#include <linux/ipmi_smi.h>#include <asm/io.h>#include "ipmi_kcs_sm.h"#include <linux/init.h>/* Measure times between events in the driver. */#undef DEBUG_TIMING#ifdef CONFIG_IPMI_KCS/* This forces a dependency to the config file for this option. */#endifenum kcs_intf_state { KCS_NORMAL, KCS_GETTING_FLAGS, KCS_GETTING_EVENTS, KCS_CLEARING_FLAGS, KCS_CLEARING_FLAGS_THEN_SET_IRQ, KCS_GETTING_MESSAGES, KCS_ENABLE_INTERRUPTS1, KCS_ENABLE_INTERRUPTS2 /* FIXME - add watchdog stuff. */};struct kcs_info{ ipmi_smi_t intf; struct kcs_data *kcs_sm; spinlock_t kcs_lock; spinlock_t msg_lock; struct list_head xmit_msgs; struct list_head hp_xmit_msgs; struct ipmi_smi_msg *curr_msg; enum kcs_intf_state kcs_state; /* Flags from the last GET_MSG_FLAGS command, used when an ATTN is set to hold the flags until we are done handling everything from the flags. */#define RECEIVE_MSG_AVAIL 0x01#define EVENT_MSG_BUFFER_FULL 0x02#define WDT_PRE_TIMEOUT_INT 0x08 unsigned char msg_flags; /* If set to true, this will request events the next time the state machine is idle. */ atomic_t req_events; /* If true, run the state machine to completion on every send call. Generally used after a panic to make sure stuff goes out. */ int run_to_completion; /* The I/O port of a KCS interface. */ int port; /* zero if no irq; */ int irq; /* The physical and remapped memory addresses of a KCS interface. */ unsigned long physaddr; unsigned char *addr; /* The timer for this kcs. */ struct timer_list kcs_timer; /* The time (in jiffies) the last timeout occurred at. */ unsigned long last_timeout_jiffies; /* Used to gracefully stop the timer without race conditions. */ volatile int stop_operation; volatile int timer_stopped; /* The driver will disable interrupts when it gets into a situation where it cannot handle messages due to lack of memory. Once that situation clears up, it will re-enable interupts. */ int interrupt_disabled;};static void deliver_recv_msg(struct kcs_info *kcs_info, struct ipmi_smi_msg *msg){ /* Deliver the message to the upper layer with the lock released. */ spin_unlock(&(kcs_info->kcs_lock)); ipmi_smi_msg_received(kcs_info->intf, msg); spin_lock(&(kcs_info->kcs_lock));}static void return_hosed_msg(struct kcs_info *kcs_info){ struct ipmi_smi_msg *msg = kcs_info->curr_msg; /* Make it a reponse */ msg->rsp[0] = msg->data[0] | 4; msg->rsp[1] = msg->data[1]; msg->rsp[2] = 0xFF; /* Unknown error. */ msg->rsp_size = 3; kcs_info->curr_msg = NULL; deliver_recv_msg(kcs_info, msg);}static enum kcs_result start_next_msg(struct kcs_info *kcs_info){ int rv; struct list_head *entry = NULL;#ifdef DEBUG_TIMING struct timeval t;#endif /* No need to save flags, we aleady have interrupts off and we already hold the KCS lock. */ spin_lock(&(kcs_info->msg_lock)); /* Pick the high priority queue first. */ if (! list_empty(&(kcs_info->hp_xmit_msgs))) { entry = kcs_info->hp_xmit_msgs.next; } else if (! list_empty(&(kcs_info->xmit_msgs))) { entry = kcs_info->xmit_msgs.next; } if (!entry) { kcs_info->curr_msg = NULL; rv = KCS_SM_IDLE; } else { int err; list_del(entry); kcs_info->curr_msg = list_entry(entry, struct ipmi_smi_msg, link);#ifdef DEBUG_TIMING do_gettimeofday(&t); printk("**Start2: %d.%9.9d\n", t.tv_sec, t.tv_usec);#endif err = start_kcs_transaction(kcs_info->kcs_sm, kcs_info->curr_msg->data, kcs_info->curr_msg->data_size); if (err) { return_hosed_msg(kcs_info); } rv = KCS_CALL_WITHOUT_DELAY; } spin_unlock(&(kcs_info->msg_lock)); return rv;}static void start_enable_irq(struct kcs_info *kcs_info){ unsigned char msg[2]; /* If we are enabling interrupts, we have to tell the BMC to use them. */ msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_GET_BMC_GLOBAL_ENABLES_CMD; start_kcs_transaction(kcs_info->kcs_sm, msg, 2); kcs_info->kcs_state = KCS_ENABLE_INTERRUPTS1;}static void start_clear_flags(struct kcs_info *kcs_info){ unsigned char msg[3]; /* Make sure the watchdog pre-timeout flag is not set at startup. */ msg[0] = (IPMI_NETFN_APP_REQUEST << 2); msg[1] = IPMI_CLEAR_MSG_FLAGS_CMD; msg[2] = WDT_PRE_TIMEOUT_INT; start_kcs_transaction(kcs_info->kcs_sm, msg, 3); kcs_info->kcs_state = KCS_CLEARING_FLAGS;}/* When we have a situtaion where we run out of memory and cannot allocate messages, we just leave them in the BMC and run the system polled until we can allocate some memory. Once we have some memory, we will re-enable the interrupt. */static inline void disable_kcs_irq(struct kcs_info *kcs_info){ if ((kcs_info->irq) && (!kcs_info->interrupt_disabled)) { disable_irq_nosync(kcs_info->irq); kcs_info->interrupt_disabled = 1; }}static inline void enable_kcs_irq(struct kcs_info *kcs_info){ if ((kcs_info->irq) && (kcs_info->interrupt_disabled)) { enable_irq(kcs_info->irq); kcs_info->interrupt_disabled = 0; }}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; start_kcs_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; start_kcs_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; }}static void handle_transaction_done(struct kcs_info *kcs_info){ struct ipmi_smi_msg *msg;#ifdef DEBUG_TIMING struct timeval t; do_gettimeofday(&t); printk("**Done: %d.%9.9d\n", t.tv_sec, t.tv_usec);#endif switch (kcs_info->kcs_state) { case KCS_NORMAL: kcs_info->curr_msg->rsp_size = kcs_get_result(kcs_info->kcs_sm, kcs_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 = kcs_info->curr_msg; kcs_info->curr_msg = NULL; deliver_recv_msg(kcs_info, msg); break; case KCS_GETTING_FLAGS: { unsigned char msg[4]; unsigned int len; /* We got the flags from the KCS, now handle them. */ len = kcs_get_result(kcs_info->kcs_sm, msg, 4); if (msg[2] != 0) { /* Error fetching flags, just give up for now. */ kcs_info->kcs_state = KCS_NORMAL; } else if (len < 3) { /* Hmm, no flags. That's technically illegal, but don't use uninitialized data. */ kcs_info->kcs_state = KCS_NORMAL; } else { kcs_info->msg_flags = msg[3]; handle_flags(kcs_info); } break; } case KCS_CLEARING_FLAGS: case KCS_CLEARING_FLAGS_THEN_SET_IRQ: { unsigned char msg[3]; /* We cleared the flags. */ kcs_get_result(kcs_info->kcs_sm, msg, 3); if (msg[2] != 0) { /* Error clearing flags */ printk(KERN_WARNING "ipmi_kcs: Error clearing flags: %2.2x\n", msg[2]); } if (kcs_info->kcs_state == KCS_CLEARING_FLAGS_THEN_SET_IRQ) start_enable_irq(kcs_info); else kcs_info->kcs_state = KCS_NORMAL; break; } case KCS_GETTING_EVENTS: { kcs_info->curr_msg->rsp_size = kcs_get_result(kcs_info->kcs_sm, kcs_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 = kcs_info->curr_msg; kcs_info->curr_msg = NULL; if (msg->rsp[2] != 0) { /* Error getting event, probably done. */ msg->done(msg); /* Take off the event flag. */ kcs_info->msg_flags &= ~EVENT_MSG_BUFFER_FULL; } else { deliver_recv_msg(kcs_info, msg); } handle_flags(kcs_info); break; } case KCS_GETTING_MESSAGES: { kcs_info->curr_msg->rsp_size = kcs_get_result(kcs_info->kcs_sm, kcs_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 = kcs_info->curr_msg; kcs_info->curr_msg = NULL; if (msg->rsp[2] != 0) { /* Error getting event, probably done. */ msg->done(msg); /* Take off the msg flag. */ kcs_info->msg_flags &= ~RECEIVE_MSG_AVAIL; } else { deliver_recv_msg(kcs_info, msg); } handle_flags(kcs_info); break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -