Bug fixes and enhancements for IPMI

This request fixes a number of small bugs, but has some more major
 changes:
 
 Loongson-2K BMC support is added.  This is an MFD device and is
 dependent on the changes coming from that tree.  There is a
 conflict in the MAINTAINERS file and the MFD tree changes must be
 there before this is applied.
 
 The way the driver handles BMCs that have become non-functional
 has been completely redone.  A number of changes in the past have
 attempted to handle various issues around this, but nothing has
 been very good.  After working with some people on this, the
 code has been reworked to disable the driver and fail all
 pending operations if the BMC becomes non functional. It will
 retry the BMC once a second to see if it's back up.
 
 -corey
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmjaykUACgkQYfOMkJGb
 /4G+9g//RTekqxKa4xH/1Q+WxUmYhISYKVdDIx3bhNw6O6o5NWDwve3bQu7V88a3
 6LAazFRkS4jAN7wEWEV88MoO/EscPNSxWmxwZRjuQ/cCmnrqYn614K4b6IQ3io4C
 0bxvHQ7X7kHedWUQvjKMtG6KYmBYMZtT7Ed5aEB4OlgtpFQLLmHDuQCMRIiGQPlr
 9Gp62xngzp4FXotclZCLSAvHATMI7+eIuuBfGEXcrK0hT9PH+h2sImvesN250z8Z
 s4RWmSSrTGYYmCnz1waZAsVUH0dMGvnrJwWYuz2Nz/zxnWiNOhuVs3BrhalVrz51
 tDBKERnOpkPTtktI/imW7+A9JgEdcGPL210X2mEcjkb9jv8EFonqfybbODbkadKr
 k8SV+7fyoKAIsbpRzXeJ6x59WGR2Ts2NmVoU1iGTP7DKhQ7X4xtE3U1J/7uGf+aI
 GldgOnWMAiHXoIT8wyF2okFJjpct0rme6683Tm9ICapseO++BBAgGooz5pTu4nNz
 VsxGvVdD6r9lZ+U7UAaa/1OlAE7jW/INuEnASQTqFWeILY4UkU+CnMnc4vs/YQqJ
 SLoxT1zNOmKC0eDhZWneNL98N/jdKIlGcD6FoRfAwsSPW4MD+oRyWtqKiACiRJjv
 Ka0RGJNKvg71xWdUclZtR6p4FNr0fbtYaNX1e/wTIp0gya7HXZQ=
 =mDVO
 -----END PGP SIGNATURE-----

Merge tag 'for-linus-6.18-1' of https://github.com/cminyard/linux-ipmi

Pull IPMI updates from Corey Minyard:
 "Bug fixes and enhancements for IPMI

  This fixes a number of small bugs, but has some more major changes:

   - Loongson-2K BMC support is added. This is an MFD device and is
     dependent on the changes coming from that tree.

     The way the driver handles BMCs that have become non-functional has
     been completely redone. A number of changes in the past have
     attempted to handle various issues around this, but nothing has
     been very good. After working with some people on this, the code
     has been reworked to disable the driver and fail all pending
     operations if the BMC becomes non functional. It will retry the BMC
     once a second to see if it's back up"

* tag 'for-linus-6.18-1' of https://github.com/cminyard/linux-ipmi:
  ipmi: Add Loongson-2K BMC support
  ipmi:si: Gracefully handle if the BMC is non-functional
  ipmi: Rename "user_data" to "recv_msg" in an SMI message
  ipmi: Allow an SMI sender to return an error
  ipmi:si: Move flags get start to its own function
  ipmi:si: Merge some if statements
  ipmi: Set a timer for maintenance mode
  ipmi: Add a maintenance mode sysfs file
  ipmi: Disable sysfs access and requests in maintenance mode
  ipmi: Differentiate between reset and firmware update in maintenance
  dt-bindings: ipmi: aspeed,ast2400-kcs-bmc: Add missing "clocks" property
  ipmi: Rework user message limit handling
  Revert "ipmi: fix msg stack when IPMI is disconnected"
  ipmi:msghandler:Change seq_lock to a mutex
This commit is contained in:
Linus Torvalds 2025-10-01 13:10:30 -07:00
commit 524c4a5daf
12 changed files with 603 additions and 341 deletions

View File

@ -40,6 +40,9 @@ properties:
- description: ODR register
- description: STR register
clocks:
maxItems: 1
aspeed,lpc-io-reg:
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 1

View File

@ -84,6 +84,13 @@ config IPMI_IPMB
bus, and it also supports direct messaging on the bus using
IPMB direct messages. This module requires I2C support.
config IPMI_LS2K
bool 'Loongson-2K IPMI interface'
depends on LOONGARCH
select MFD_LS2K_BMC_CORE
help
Provides a driver for Loongson-2K IPMI interfaces.
config IPMI_POWERNV
depends on PPC_POWERNV
tristate 'POWERNV (OPAL firmware) IPMI interface'

View File

@ -8,6 +8,7 @@ ipmi_si-y := ipmi_si_intf.o ipmi_kcs_sm.o ipmi_smic_sm.o ipmi_bt_sm.o \
ipmi_si_mem_io.o
ipmi_si-$(CONFIG_HAS_IOPORT) += ipmi_si_port_io.o
ipmi_si-$(CONFIG_PCI) += ipmi_si_pci.o
ipmi_si-$(CONFIG_IPMI_LS2K) += ipmi_si_ls2k.o
ipmi_si-$(CONFIG_PARISC) += ipmi_si_parisc.o
obj-$(CONFIG_IPMI_HANDLER) += ipmi_msghandler.o

View File

@ -404,8 +404,7 @@ static void ipmi_ipmb_shutdown(void *send_info)
ipmi_ipmb_stop_thread(iidev);
}
static void ipmi_ipmb_sender(void *send_info,
struct ipmi_smi_msg *msg)
static int ipmi_ipmb_sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct ipmi_ipmb_dev *iidev = send_info;
unsigned long flags;
@ -417,6 +416,7 @@ static void ipmi_ipmb_sender(void *send_info,
spin_unlock_irqrestore(&iidev->lock, flags);
up(&iidev->wake_thread);
return IPMI_CC_NO_ERROR;
}
static void ipmi_ipmb_request_events(void *send_info)

View File

@ -122,10 +122,10 @@ struct si_sm_data {
unsigned long error0_timeout;
};
static unsigned int init_kcs_data_with_state(struct si_sm_data *kcs,
struct si_sm_io *io, enum kcs_states state)
static unsigned int init_kcs_data(struct si_sm_data *kcs,
struct si_sm_io *io)
{
kcs->state = state;
kcs->state = KCS_IDLE;
kcs->io = io;
kcs->write_pos = 0;
kcs->write_count = 0;
@ -140,12 +140,6 @@ static unsigned int init_kcs_data_with_state(struct si_sm_data *kcs,
return 2;
}
static unsigned int init_kcs_data(struct si_sm_data *kcs,
struct si_sm_io *io)
{
return init_kcs_data_with_state(kcs, io, KCS_IDLE);
}
static inline unsigned char read_status(struct si_sm_data *kcs)
{
return kcs->io->inputb(kcs->io, 1);
@ -276,7 +270,7 @@ static int start_kcs_transaction(struct si_sm_data *kcs, unsigned char *data,
if (size > MAX_KCS_WRITE_SIZE)
return IPMI_REQ_LEN_EXCEEDED_ERR;
if (kcs->state != KCS_IDLE) {
if ((kcs->state != KCS_IDLE) && (kcs->state != KCS_HOSED)) {
dev_warn(kcs->io->dev, "KCS in invalid state %d\n", kcs->state);
return IPMI_NOT_IN_MY_STATE_ERR;
}
@ -501,7 +495,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
}
if (kcs->state == KCS_HOSED) {
init_kcs_data_with_state(kcs, kcs->io, KCS_ERROR0);
init_kcs_data(kcs, kcs->io);
return SI_SM_HOSED;
}

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,7 @@ static void send_error_reply(struct ipmi_smi_powernv *smi,
ipmi_smi_msg_received(smi->intf, msg);
}
static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
static int ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
{
struct ipmi_smi_powernv *smi = send_info;
struct opal_ipmi_msg *opal_msg;
@ -93,18 +93,19 @@ static void ipmi_powernv_send(void *send_info, struct ipmi_smi_msg *msg)
smi->interface_id, opal_msg, size);
rc = opal_ipmi_send(smi->interface_id, opal_msg, size);
pr_devel("%s: -> %d\n", __func__, rc);
if (!rc) {
smi->cur_msg = msg;
spin_unlock_irqrestore(&smi->msg_lock, flags);
return;
if (rc) {
comp = IPMI_ERR_UNSPECIFIED;
goto err_unlock;
}
comp = IPMI_ERR_UNSPECIFIED;
smi->cur_msg = msg;
spin_unlock_irqrestore(&smi->msg_lock, flags);
return IPMI_CC_NO_ERROR;
err_unlock:
spin_unlock_irqrestore(&smi->msg_lock, flags);
err:
send_error_reply(smi, msg, comp);
return comp;
}
static int ipmi_powernv_recv(struct ipmi_smi_powernv *smi)

View File

@ -101,6 +101,13 @@ void ipmi_si_pci_shutdown(void);
static inline void ipmi_si_pci_init(void) { }
static inline void ipmi_si_pci_shutdown(void) { }
#endif
#ifdef CONFIG_IPMI_LS2K
void ipmi_si_ls2k_init(void);
void ipmi_si_ls2k_shutdown(void);
#else
static inline void ipmi_si_ls2k_init(void) { }
static inline void ipmi_si_ls2k_shutdown(void) { }
#endif
#ifdef CONFIG_PARISC
void ipmi_si_parisc_init(void);
void ipmi_si_parisc_shutdown(void);

View File

@ -53,6 +53,7 @@
#define SI_TIMEOUT_JIFFIES (SI_TIMEOUT_TIME_USEC/SI_USEC_PER_JIFFY)
#define SI_SHORT_TIMEOUT_USEC 250 /* .25ms when the SM request a
short timeout */
#define SI_TIMEOUT_HOSED (HZ) /* 1 second when in hosed state. */
enum si_intf_state {
SI_NORMAL,
@ -61,7 +62,8 @@ enum si_intf_state {
SI_CLEARING_FLAGS,
SI_GETTING_MESSAGES,
SI_CHECKING_ENABLES,
SI_SETTING_ENABLES
SI_SETTING_ENABLES,
SI_HOSED
/* FIXME - add watchdog stuff. */
};
@ -313,7 +315,7 @@ static void return_hosed_msg(struct smi_info *smi_info, int cCode)
static enum si_sm_result start_next_msg(struct smi_info *smi_info)
{
int rv;
int rv;
if (!smi_info->waiting_msg) {
smi_info->curr_msg = NULL;
@ -390,6 +392,17 @@ static void start_clear_flags(struct smi_info *smi_info)
smi_info->si_state = SI_CLEARING_FLAGS;
}
static void start_get_flags(struct smi_info *smi_info)
{
unsigned char msg[2];
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_GET_MSG_FLAGS_CMD;
start_new_msg(smi_info, msg, 2);
smi_info->si_state = SI_GETTING_FLAGS;
}
static void start_getting_msg_queue(struct smi_info *smi_info)
{
smi_info->curr_msg->data[0] = (IPMI_NETFN_APP_REQUEST << 2);
@ -742,6 +755,8 @@ static void handle_transaction_done(struct smi_info *smi_info)
}
break;
}
case SI_HOSED: /* Shouldn't happen. */
break;
}
}
@ -756,6 +771,10 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
enum si_sm_result si_sm_result;
restart:
if (smi_info->si_state == SI_HOSED)
/* Just in case, hosed state is only left from the timeout. */
return SI_SM_HOSED;
/*
* There used to be a loop here that waited a little while
* (around 25us) before giving up. That turned out to be
@ -779,18 +798,20 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
/*
* Do the before return_hosed_msg, because that
* releases the lock.
* releases the lock. We just disable operations for
* a while and retry in hosed state.
*/
smi_info->si_state = SI_NORMAL;
smi_info->si_state = SI_HOSED;
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);
return_hosed_msg(smi_info, IPMI_BUS_ERR);
}
goto restart;
smi_mod_timer(smi_info, jiffies + SI_TIMEOUT_HOSED);
goto out;
}
/*
@ -798,8 +819,6 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
* this if there is not yet an upper layer to handle anything.
*/
if (si_sm_result == SI_SM_ATTN || smi_info->got_attn) {
unsigned char msg[2];
if (smi_info->si_state != SI_NORMAL) {
/*
* We got an ATTN, but we are doing something else.
@ -817,11 +836,7 @@ static enum si_sm_result smi_event_handler(struct smi_info *smi_info,
* interrupts work with the SMI, that's not really
* possible.
*/
msg[0] = (IPMI_NETFN_APP_REQUEST << 2);
msg[1] = IPMI_GET_MSG_FLAGS_CMD;
start_new_msg(smi_info, msg, 2);
smi_info->si_state = SI_GETTING_FLAGS;
start_get_flags(smi_info);
goto restart;
}
}
@ -894,27 +909,29 @@ static void flush_messages(void *send_info)
* mode. This means we are single-threaded, no need for locks.
*/
result = smi_event_handler(smi_info, 0);
while (result != SI_SM_IDLE) {
while (result != SI_SM_IDLE && result != SI_SM_HOSED) {
udelay(SI_SHORT_TIMEOUT_USEC);
result = smi_event_handler(smi_info, SI_SHORT_TIMEOUT_USEC);
}
}
static void sender(void *send_info,
struct ipmi_smi_msg *msg)
static int sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct smi_info *smi_info = send_info;
unsigned long flags;
debug_timestamp(smi_info, "Enqueue");
if (smi_info->si_state == SI_HOSED)
return IPMI_BUS_ERR;
if (smi_info->run_to_completion) {
/*
* If we are running to completion, start it. Upper
* layer will call flush_messages to clear it out.
*/
smi_info->waiting_msg = msg;
return;
return IPMI_CC_NO_ERROR;
}
spin_lock_irqsave(&smi_info->si_lock, flags);
@ -929,6 +946,7 @@ static void sender(void *send_info,
smi_info->waiting_msg = msg;
check_start_timer_thread(smi_info);
spin_unlock_irqrestore(&smi_info->si_lock, flags);
return IPMI_CC_NO_ERROR;
}
static void set_run_to_completion(void *send_info, bool i_run_to_completion)
@ -1087,6 +1105,10 @@ static void smi_timeout(struct timer_list *t)
spin_lock_irqsave(&(smi_info->si_lock), flags);
debug_timestamp(smi_info, "Timer");
if (smi_info->si_state == SI_HOSED)
/* Try something to see if the BMC is now operational. */
start_get_flags(smi_info);
jiffies_now = jiffies;
time_diff = (((long)jiffies_now - (long)smi_info->last_timeout_jiffies)
* SI_USEC_PER_JIFFY);
@ -1096,14 +1118,11 @@ static void smi_timeout(struct timer_list *t)
/* 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) {
} else if (smi_result == SI_SM_CALL_WITH_DELAY) {
/*
* If the state machine asks for a short delay, then shorten
* the timer timeout.
*/
smi_inc_stat(smi_info, short_timeouts);
timeout = jiffies + 1;
} else {
@ -1111,7 +1130,6 @@ static void smi_timeout(struct timer_list *t)
timeout = jiffies + SI_TIMEOUT_JIFFIES;
}
do_mod_timer:
if (smi_result != SI_SM_IDLE)
smi_mod_timer(smi_info, timeout);
else
@ -2120,6 +2138,8 @@ static int __init init_ipmi_si(void)
ipmi_si_pci_init();
ipmi_si_ls2k_init();
ipmi_si_parisc_init();
mutex_lock(&smi_infos_lock);
@ -2331,6 +2351,8 @@ static void cleanup_ipmi_si(void)
ipmi_si_pci_shutdown();
ipmi_si_ls2k_shutdown();
ipmi_si_parisc_shutdown();
ipmi_si_platform_shutdown();

View File

@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Driver for Loongson-2K BMC IPMI interface
*
* Copyright (C) 2024-2025 Loongson Technology Corporation Limited.
*
* Authors:
* Chong Qiao <qiaochong@loongson.cn>
* Binbin Zhou <zhoubinbin@loongson.cn>
*/
#include <linux/bitfield.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/types.h>
#include "ipmi_si.h"
#define LS2K_KCS_FIFO_IBFH 0x0
#define LS2K_KCS_FIFO_IBFT 0x1
#define LS2K_KCS_FIFO_OBFH 0x2
#define LS2K_KCS_FIFO_OBFT 0x3
/* KCS registers */
#define LS2K_KCS_REG_STS 0x4
#define LS2K_KCS_REG_DATA_OUT 0x5
#define LS2K_KCS_REG_DATA_IN 0x6
#define LS2K_KCS_REG_CMD 0x8
#define LS2K_KCS_CMD_DATA 0xa
#define LS2K_KCS_VERSION 0xb
#define LS2K_KCS_WR_REQ 0xc
#define LS2K_KCS_WR_ACK 0x10
#define LS2K_KCS_STS_OBF BIT(0)
#define LS2K_KCS_STS_IBF BIT(1)
#define LS2K_KCS_STS_SMS_ATN BIT(2)
#define LS2K_KCS_STS_CMD BIT(3)
#define LS2K_KCS_DATA_MASK (LS2K_KCS_STS_OBF | LS2K_KCS_STS_IBF | LS2K_KCS_STS_CMD)
static bool ls2k_registered;
static unsigned char ls2k_mem_inb_v0(const struct si_sm_io *io, unsigned int offset)
{
void __iomem *addr = io->addr;
int reg_offset;
if (offset & BIT(0)) {
reg_offset = LS2K_KCS_REG_STS;
} else {
writeb(readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_STS_OBF, addr + LS2K_KCS_REG_STS);
reg_offset = LS2K_KCS_REG_DATA_OUT;
}
return readb(addr + reg_offset);
}
static unsigned char ls2k_mem_inb_v1(const struct si_sm_io *io, unsigned int offset)
{
void __iomem *addr = io->addr;
unsigned char inb = 0, cmd;
bool obf, ibf;
obf = readb(addr + LS2K_KCS_FIFO_OBFH) ^ readb(addr + LS2K_KCS_FIFO_OBFT);
ibf = readb(addr + LS2K_KCS_FIFO_IBFH) ^ readb(addr + LS2K_KCS_FIFO_IBFT);
cmd = readb(addr + LS2K_KCS_CMD_DATA);
if (offset & BIT(0)) {
inb = readb(addr + LS2K_KCS_REG_STS) & ~LS2K_KCS_DATA_MASK;
inb |= FIELD_PREP(LS2K_KCS_STS_OBF, obf)
| FIELD_PREP(LS2K_KCS_STS_IBF, ibf)
| FIELD_PREP(LS2K_KCS_STS_CMD, cmd);
} else {
inb = readb(addr + LS2K_KCS_REG_DATA_OUT);
writeb(readb(addr + LS2K_KCS_FIFO_OBFH), addr + LS2K_KCS_FIFO_OBFT);
}
return inb;
}
static void ls2k_mem_outb_v0(const struct si_sm_io *io, unsigned int offset,
unsigned char val)
{
void __iomem *addr = io->addr;
unsigned char sts = readb(addr + LS2K_KCS_REG_STS);
int reg_offset;
if (sts & LS2K_KCS_STS_IBF)
return;
if (offset & BIT(0)) {
reg_offset = LS2K_KCS_REG_CMD;
sts |= LS2K_KCS_STS_CMD;
} else {
reg_offset = LS2K_KCS_REG_DATA_IN;
sts &= ~LS2K_KCS_STS_CMD;
}
writew(val, addr + reg_offset);
writeb(sts | LS2K_KCS_STS_IBF, addr + LS2K_KCS_REG_STS);
writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
}
static void ls2k_mem_outb_v1(const struct si_sm_io *io, unsigned int offset,
unsigned char val)
{
void __iomem *addr = io->addr;
unsigned char ibfh, ibft;
int reg_offset;
ibfh = readb(addr + LS2K_KCS_FIFO_IBFH);
ibft = readb(addr + LS2K_KCS_FIFO_IBFT);
if (ibfh ^ ibft)
return;
reg_offset = (offset & BIT(0)) ? LS2K_KCS_REG_CMD : LS2K_KCS_REG_DATA_IN;
writew(val, addr + reg_offset);
writeb(offset & BIT(0), addr + LS2K_KCS_CMD_DATA);
writeb(!ibft, addr + LS2K_KCS_FIFO_IBFH);
writel(readl(addr + LS2K_KCS_WR_REQ) + 1, addr + LS2K_KCS_WR_REQ);
}
static void ls2k_mem_cleanup(struct si_sm_io *io)
{
if (io->addr)
iounmap(io->addr);
}
static int ipmi_ls2k_mem_setup(struct si_sm_io *io)
{
unsigned char version;
io->addr = ioremap(io->addr_data, io->regspacing);
if (!io->addr)
return -EIO;
version = readb(io->addr + LS2K_KCS_VERSION);
io->inputb = version ? ls2k_mem_inb_v1 : ls2k_mem_inb_v0;
io->outputb = version ? ls2k_mem_outb_v1 : ls2k_mem_outb_v0;
io->io_cleanup = ls2k_mem_cleanup;
return 0;
}
static int ipmi_ls2k_probe(struct platform_device *pdev)
{
struct si_sm_io io;
memset(&io, 0, sizeof(io));
io.si_info = &ipmi_kcs_si_info;
io.io_setup = ipmi_ls2k_mem_setup;
io.addr_data = pdev->resource[0].start;
io.regspacing = resource_size(&pdev->resource[0]);
io.dev = &pdev->dev;
dev_dbg(&pdev->dev, "addr 0x%lx, spacing %d.\n", io.addr_data, io.regspacing);
return ipmi_si_add_smi(&io);
}
static void ipmi_ls2k_remove(struct platform_device *pdev)
{
ipmi_si_remove_by_dev(&pdev->dev);
}
struct platform_driver ipmi_ls2k_platform_driver = {
.driver = {
.name = "ls2k-ipmi-si",
},
.probe = ipmi_ls2k_probe,
.remove = ipmi_ls2k_remove,
};
void ipmi_si_ls2k_init(void)
{
platform_driver_register(&ipmi_ls2k_platform_driver);
ls2k_registered = true;
}
void ipmi_si_ls2k_shutdown(void)
{
if (ls2k_registered)
platform_driver_unregister(&ipmi_ls2k_platform_driver);
}

View File

@ -1068,8 +1068,7 @@ static void start_next_msg(struct ssif_info *ssif_info, unsigned long *flags)
}
}
static void sender(void *send_info,
struct ipmi_smi_msg *msg)
static int sender(void *send_info, struct ipmi_smi_msg *msg)
{
struct ssif_info *ssif_info = send_info;
unsigned long oflags, *flags;
@ -1089,6 +1088,7 @@ static void sender(void *send_info,
msg->data[0], msg->data[1],
(long long)t.tv_sec, (long)t.tv_nsec / NSEC_PER_USEC);
}
return IPMI_CC_NO_ERROR;
}
static int get_smi_info(void *send_info, struct ipmi_smi_info *data)

View File

@ -109,8 +109,9 @@ struct ipmi_smi_msg {
enum ipmi_smi_msg_type type;
long msgid;
void *user_data;
long msgid;
/* Response to this message, will be NULL if not from a user request. */
struct ipmi_recv_msg *recv_msg;
int data_size;
unsigned char data[IPMI_MAX_MSG_LENGTH];
@ -168,9 +169,11 @@ struct ipmi_smi_handlers {
* are held when this is run. Message are delivered one at
* a time by the message handler, a new message will not be
* delivered until the previous message is returned.
*
* This can return an error if the SMI is not in a state where it
* can send a message.
*/
void (*sender)(void *send_info,
struct ipmi_smi_msg *msg);
int (*sender)(void *send_info, struct ipmi_smi_msg *msg);
/*
* Called by the upper layer to request that we try to get