在acpi_ipmi_probe 中可以发现ipmi支持两种形式的中断
/* If _GPE exists, use it; otherwise use standard interrupts */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
if (ACPI_SUCCESS(status)) {
info->irq = tmp;
info->irq_setup = acpi_gpe_irq_setup;
} else {
int irq = platform_get_irq(dev, 0);
if (irq > 0) {
info->irq = irq;
info->irq_setup = std_irq_setup;
}
}
一种是acpi的GPE中断,这个中断是需要bios支持的,一种是正常的中断
首先看看GPE中断
static int acpi_gpe_irq_setup(struct smi_info *info)
{
acpi_status status;
if (!info->irq)
return 0;
status = acpi_install_gpe_handler(NULL,
info->irq,
ACPI_GPE_LEVEL_TRIGGERED,
&ipmi_acpi_gpe,
info);
if (status != AE_OK) {
dev_warn(info->dev, "%s unable to claim ACPI GPE %d,"
" running polled\n", DEVICE_NAME, info->irq);
info->irq = 0;
return -EINVAL;
} else {
info->irq_cleanup = acpi_gpe_irq_cleanup;
dev_info(info->dev, "Using ACPI GPE %d\n", info->irq);
return 0;
}
}
基本就是调用acpi_install_gpe_handler 来注册handler,对应的type是ACPI_GPE_LEVEL_TRIGGERED,如果发生中断的话,就调用ipmi_acpi_gpe
static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
u32 gpe_number, void *context)
{
struct smi_info *smi_info = context;
unsigned long flags;
spin_lock_irqsave(&(smi_info->si_lock), flags);
smi_inc_stat(smi_info, interrupts);
debug_timestamp("ACPI_GPE");
smi_event_handler(smi_info, 0);
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
return ACPI_INTERRUPT_HANDLED;
}
在ipmi_acpi_gpe 中就直接调用smi_event_handler
回到acpi_ipmi_probe 中标准的中断注册是调用std_irq_setup
static int std_irq_setup(struct smi_info *info)
{
int rv;
if (!info->irq)
return 0;
if (info->si_type == SI_BT) {
rv = request_irq(info->irq,
si_bt_irq_handler,
IRQF_SHARED,
DEVICE_NAME,
info);
if (!rv)
/* Enable the interrupt in the BT interface. */
info->io.outputb(&info->io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
} else
rv = request_irq(info->irq,
si_irq_handler,
IRQF_SHARED,
DEVICE_NAME,
info);
if (rv) {
dev_warn(info->dev, "%s unable to claim interrupt %d,"
" running polled\n",
DEVICE_NAME, info->irq);
info->irq = 0;
} else {
info->irq_cleanup = std_irq_cleanup;
dev_info(info->dev, "Using irq %d\n", info->irq);
}
return rv;
}
可见bt和其他的中断处理函数有所不同si_bt_irq_handler->
static irqreturn_t si_bt_irq_handler(int irq, void *data)
{
struct smi_info *smi_info = data;
/* We need to clear the IRQ flag for the BT interface. */
smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_CLEAR_IRQ_BIT
| IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
return si_irq_handler(irq, data);
}
i_bt_irq_handler->si_irq_handler—>smi_event_handler.可见最终都是调用smi_event_handler。只是bt比其的case要多写一个flag
static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
int time)
{
enum si_sm_result si_sm_result;
restart:
/*
* There used to be a loop here that waited a little while
* (around 25us) before giving up. That turned out to be
* pointless, the minimum delays I was seeing were in the 300us
* range, which is far too long to wait in an interrupt. So
* we just run until the state machine tells us something
* happened or it needs a delay.
*/
//通过event想bmc发送命令后面si_sm_result就是bmc返回的结果
si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);
time = 0;
while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)
si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);
if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) {
smi_inc_stat(smi_info, complete_transactions);
handle_transaction_done(smi_info);
goto restart;
} else if (si_sm_result == SI_SM_HOSED) {
smi_inc_stat(smi_info, hosed_count);
/*
* Do the before return_hosed_msg, because that
* releases the lock.
*/
smi_info->si_state = SI_NORMAL;
if (smi_info->curr_msg != NULL) {
/*
* If we were handling a user message, format
* a response to send to the upper layer to
* tell it about the error.
*/
return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
}
goto restart;
}
if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
/* Ok it if fails, the timer will just go off. */
if (del_timer(&smi_info->si_timer))
smi_info->timer_running = false;
}
out:
return si_sm_result;
}
这里需要注意的是如果si_sm_result == SI_SM_IDLE,则说明当前已经没有要处理的命令了,就把smi_info->si_timer 停掉
这个timer是在smi_start_processing 中赋值的。
static int smi_start_processing(void *send_info,
ipmi_smi_t intf)
{
struct smi_info *new_smi = send_info;
int enable = 0;
new_smi->intf = intf;
/* Set up the timer that drives the interface. */
setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
}
每隔SI_TIMEOUT_JIFFIES 就会调用smi_timeout
static void smi_timeout(unsigned long data)
{
struct smi_info *smi_info = (struct smi_info *) data;
enum si_sm_result smi_result;
unsigned long flags;
unsigned long jiffies_now;
long time_diff;
long timeout;
spin_lock_irqsave(&(smi_info->si_lock), flags);
debug_timestamp("Timer");
jiffies_now = jiffies;
time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
* SI_USEC_PER_JIFFY);
smi_result = smi_event_handler(smi_info, time_diff);
if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
/* Running with interrupts, only do long timeouts. */
timeout = jiffies + SI_TIMEOUT_JIFFIES;
smi_inc_stat(smi_info, long_timeouts);
goto do_mod_timer;
}
/*
* If the state machine asks for a short delay, then shorten
* the timer timeout.
*/
if (smi_result == SI_SM_CALL_WITH_DELAY) {
smi_inc_stat(smi_info, short_timeouts);
timeout = jiffies + 1;
} else {
smi_inc_stat(smi_info, long_timeouts);
timeout = jiffies + SI_TIMEOUT_JIFFIES;
}
do_mod_timer:
if (smi_result != SI_SM_IDLE)
smi_mod_timer(smi_info, timeout);
else
smi_info->timer_running = false;
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
可见在smi_timeout 中依然是调用smi_event_handler 来处理IPMI发给bmc的命令。
/* If _GPE exists, use it; otherwise use standard interrupts */
status = acpi_evaluate_integer(handle, "_GPE", NULL, &tmp);
if (ACPI_SUCCESS(status)) {
info->irq = tmp;
info->irq_setup = acpi_gpe_irq_setup;
} else {
int irq = platform_get_irq(dev, 0);
if (irq > 0) {
info->irq = irq;
info->irq_setup = std_irq_setup;
}
}
一种是acpi的GPE中断,这个中断是需要bios支持的,一种是正常的中断
首先看看GPE中断
static int acpi_gpe_irq_setup(struct smi_info *info)
{
acpi_status status;
if (!info->irq)
return 0;
status = acpi_install_gpe_handler(NULL,
info->irq,
ACPI_GPE_LEVEL_TRIGGERED,
&ipmi_acpi_gpe,
info);
if (status != AE_OK) {
dev_warn(info->dev, "%s unable to claim ACPI GPE %d,"
" running polled\n", DEVICE_NAME, info->irq);
info->irq = 0;
return -EINVAL;
} else {
info->irq_cleanup = acpi_gpe_irq_cleanup;
dev_info(info->dev, "Using ACPI GPE %d\n", info->irq);
return 0;
}
}
基本就是调用acpi_install_gpe_handler 来注册handler,对应的type是ACPI_GPE_LEVEL_TRIGGERED,如果发生中断的话,就调用ipmi_acpi_gpe
static u32 ipmi_acpi_gpe(acpi_handle gpe_device,
u32 gpe_number, void *context)
{
struct smi_info *smi_info = context;
unsigned long flags;
spin_lock_irqsave(&(smi_info->si_lock), flags);
smi_inc_stat(smi_info, interrupts);
debug_timestamp("ACPI_GPE");
smi_event_handler(smi_info, 0);
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
return ACPI_INTERRUPT_HANDLED;
}
在ipmi_acpi_gpe 中就直接调用smi_event_handler
回到acpi_ipmi_probe 中标准的中断注册是调用std_irq_setup
static int std_irq_setup(struct smi_info *info)
{
int rv;
if (!info->irq)
return 0;
if (info->si_type == SI_BT) {
rv = request_irq(info->irq,
si_bt_irq_handler,
IRQF_SHARED,
DEVICE_NAME,
info);
if (!rv)
/* Enable the interrupt in the BT interface. */
info->io.outputb(&info->io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
} else
rv = request_irq(info->irq,
si_irq_handler,
IRQF_SHARED,
DEVICE_NAME,
info);
if (rv) {
dev_warn(info->dev, "%s unable to claim interrupt %d,"
" running polled\n",
DEVICE_NAME, info->irq);
info->irq = 0;
} else {
info->irq_cleanup = std_irq_cleanup;
dev_info(info->dev, "Using irq %d\n", info->irq);
}
return rv;
}
可见bt和其他的中断处理函数有所不同si_bt_irq_handler->
static irqreturn_t si_bt_irq_handler(int irq, void *data)
{
struct smi_info *smi_info = data;
/* We need to clear the IRQ flag for the BT interface. */
smi_info->io.outputb(&smi_info->io, IPMI_BT_INTMASK_REG,
IPMI_BT_INTMASK_CLEAR_IRQ_BIT
| IPMI_BT_INTMASK_ENABLE_IRQ_BIT);
return si_irq_handler(irq, data);
}
i_bt_irq_handler->si_irq_handler—>smi_event_handler.可见最终都是调用smi_event_handler。只是bt比其的case要多写一个flag
static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
int time)
{
enum si_sm_result si_sm_result;
restart:
/*
* There used to be a loop here that waited a little while
* (around 25us) before giving up. That turned out to be
* pointless, the minimum delays I was seeing were in the 300us
* range, which is far too long to wait in an interrupt. So
* we just run until the state machine tells us something
* happened or it needs a delay.
*/
//通过event想bmc发送命令后面si_sm_result就是bmc返回的结果
si_sm_result = smi_info->handlers->event(smi_info->si_sm, time);
time = 0;
while (si_sm_result == SI_SM_CALL_WITHOUT_DELAY)
si_sm_result = smi_info->handlers->event(smi_info->si_sm, 0);
if (si_sm_result == SI_SM_TRANSACTION_COMPLETE) {
smi_inc_stat(smi_info, complete_transactions);
handle_transaction_done(smi_info);
goto restart;
} else if (si_sm_result == SI_SM_HOSED) {
smi_inc_stat(smi_info, hosed_count);
/*
* Do the before return_hosed_msg, because that
* releases the lock.
*/
smi_info->si_state = SI_NORMAL;
if (smi_info->curr_msg != NULL) {
/*
* If we were handling a user message, format
* a response to send to the upper layer to
* tell it about the error.
*/
return_hosed_msg(smi_info, IPMI_ERR_UNSPECIFIED);
}
goto restart;
}
if (si_sm_result == SI_SM_IDLE && smi_info->timer_running) {
/* Ok it if fails, the timer will just go off. */
if (del_timer(&smi_info->si_timer))
smi_info->timer_running = false;
}
out:
return si_sm_result;
}
这里需要注意的是如果si_sm_result == SI_SM_IDLE,则说明当前已经没有要处理的命令了,就把smi_info->si_timer 停掉
这个timer是在smi_start_processing 中赋值的。
static int smi_start_processing(void *send_info,
ipmi_smi_t intf)
{
struct smi_info *new_smi = send_info;
int enable = 0;
new_smi->intf = intf;
/* Set up the timer that drives the interface. */
setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
smi_mod_timer(new_smi, jiffies + SI_TIMEOUT_JIFFIES);
}
每隔SI_TIMEOUT_JIFFIES 就会调用smi_timeout
static void smi_timeout(unsigned long data)
{
struct smi_info *smi_info = (struct smi_info *) data;
enum si_sm_result smi_result;
unsigned long flags;
unsigned long jiffies_now;
long time_diff;
long timeout;
spin_lock_irqsave(&(smi_info->si_lock), flags);
debug_timestamp("Timer");
jiffies_now = jiffies;
time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
* SI_USEC_PER_JIFFY);
smi_result = smi_event_handler(smi_info, time_diff);
if ((smi_info->irq) && (!smi_info->interrupt_disabled)) {
/* Running with interrupts, only do long timeouts. */
timeout = jiffies + SI_TIMEOUT_JIFFIES;
smi_inc_stat(smi_info, long_timeouts);
goto do_mod_timer;
}
/*
* If the state machine asks for a short delay, then shorten
* the timer timeout.
*/
if (smi_result == SI_SM_CALL_WITH_DELAY) {
smi_inc_stat(smi_info, short_timeouts);
timeout = jiffies + 1;
} else {
smi_inc_stat(smi_info, long_timeouts);
timeout = jiffies + SI_TIMEOUT_JIFFIES;
}
do_mod_timer:
if (smi_result != SI_SM_IDLE)
smi_mod_timer(smi_info, timeout);
else
smi_info->timer_running = false;
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
可见在smi_timeout 中依然是调用smi_event_handler 来处理IPMI发给bmc的命令。