Updates for the IPMI driver
A new type of low-level IPMI driver is added for direct communication over the IPMI message bus without a BMC between the driver and the bus. Other than that, lots of little bug fixes and enhancements. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAmF/5YwACgkQYfOMkJGb /4GV5A/8CSXagtu6JJTY88+7CNxXSca4fIGcpO8RZUA1b+Hkw3+y/Qlv/djK6bk1 lt16OpMxSYggTXtkfH6f8zRnf5jAbXDIrcdYAZdCdIt/u4zgXksa8XycUgYDcVS8 Tb07qO55HsNwjjjPglIUXqohkpyuilJLpBDhWW+AWNDs2niAwFFpFoBTDnDbZEe1 6ide1f/IBhibwEx4T4nGs45VKUB+jHsORSj8+fB2suREgs0GTGf51E9NyR91CYyk uSkqpeHVN85KzTWAeAV5nCfRab/rBVS/EL8tEQEt1LniXZpJs3bJHLJFhgaQWesB 6b2hVdL8FoG5qO1Xcf2ofbLKdrY+AOWW8gBWfsZmsK2dQYex+CHO7bm2BkRt037i YJdg5+qXz093uDCotYZYvGjm+TTmrTeFVhEJD6+CW0AyVeSAqBB3TBxqDpBn3LfN p2sTUS/1BZuChe0VXPXcDkkUFsVs3J+Z4lFl0f/WzTFUxWZEC/ELh/3Nuz7J2eYy gEXWqvox8BAU8byYucmLNb7xelFztjXpga+WSDk7kEIIStM6aeIVKenpYI/KJCr/ jZh0M/zYUk4QVMf54nsWEfWOiKi7D4UyE+C3Ijta2UordLTetLeY40HVZqyPwbPE AMDJH2MhmIr5soIw1yMVP0cp+6CWatBgnrHcXSbBc8ZpJEJzCNU= =WxcC -----END PGP SIGNATURE----- Merge tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi Pull IPMI driver updates from Corey Minyard: "A new type of low-level IPMI driver is added for direct communication over the IPMI message bus without a BMC between the driver and the bus. Other than that, lots of little bug fixes and enhancements" * tag 'for-linus-5.16-1' of https://github.com/cminyard/linux-ipmi: ipmi: kcs_bmc: Fix a memory leak in the error handling path of 'kcs_bmc_serio_add_device()' char: ipmi: replace snprintf in show functions with sysfs_emit ipmi: ipmb: fix dependencies to eliminate build error ipmi:ipmb: Add OF support ipmi: bt: Add ast2600 compatible string ipmi: bt-bmc: Use registers directly ipmi: ipmb: Fix off-by-one size check on rcvlen ipmi:ssif: Use depends on, not select, for I2C ipmi: Add docs for the IPMI IPMB driver ipmi: Add docs for IPMB direct addressing ipmi:ipmb: Add initial support for IPMI over IPMB ipmi: Add support for IPMB direct messages ipmi: Export ipmb_checksum() ipmi: Fix a typo ipmi: Check error code before processing BMC response ipmi:devintf: Return a proper error when recv buffer too small ipmi: Disable some operations during a panic ipmi:watchdog: Set panic count to proper value on a panic
This commit is contained in:
commit
316b7eaa93
|
@ -9,6 +9,7 @@ Required properties:
|
|||
- compatible : should be one of
|
||||
"aspeed,ast2400-ibt-bmc"
|
||||
"aspeed,ast2500-ibt-bmc"
|
||||
"aspeed,ast2600-ibt-bmc"
|
||||
- reg: physical address and size of the registers
|
||||
|
||||
Optional properties:
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/ipmi/ipmi-ipmb.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: IPMI IPMB device bindings
|
||||
|
||||
description: IPMI IPMB device bindings
|
||||
|
||||
maintainers:
|
||||
- Corey Minyard <cminyard@mvista.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ipmi-ipmb
|
||||
|
||||
device_type:
|
||||
items:
|
||||
- const: "ipmi"
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
bmcaddr:
|
||||
$ref: /schemas/types.yaml#/definitions/uint8
|
||||
description: The address of the BMC on the IPMB bus. Defaults to 0x20.
|
||||
|
||||
retry-time:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: |
|
||||
Time between retries of sends, in milliseconds. Defaults to 250.
|
||||
|
||||
max-retries:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description: Number of retries before a failure is declared. Defaults to 1.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
ipmi-ipmb@40 {
|
||||
compatible = "ipmi-ipmb";
|
||||
device_type = "ipmi";
|
||||
reg = <0x40>;
|
||||
bmcaddr = /bits/ 8 <0x20>;
|
||||
retry-time = <250>;
|
||||
max-retries = <1>;
|
||||
};
|
||||
};
|
|
@ -166,8 +166,8 @@ and the type is IPMI_SYSTEM_INTERFACE_ADDR_TYPE. This is used for talking
|
|||
straight to the BMC on the current card. The channel must be
|
||||
IPMI_BMC_CHANNEL.
|
||||
|
||||
Messages that are destined to go out on the IPMB bus use the
|
||||
IPMI_IPMB_ADDR_TYPE address type. The format is::
|
||||
Messages that are destined to go out on the IPMB bus going through the
|
||||
BMC use the IPMI_IPMB_ADDR_TYPE address type. The format is::
|
||||
|
||||
struct ipmi_ipmb_addr
|
||||
{
|
||||
|
@ -181,6 +181,23 @@ The "channel" here is generally zero, but some devices support more
|
|||
than one channel, it corresponds to the channel as defined in the IPMI
|
||||
spec.
|
||||
|
||||
There is also an IPMB direct address for a situation where the sender
|
||||
is directly on an IPMB bus and doesn't have to go through the BMC.
|
||||
You can send messages to a specific management controller (MC) on the
|
||||
IPMB using the IPMI_IPMB_DIRECT_ADDR_TYPE with the following format::
|
||||
|
||||
struct ipmi_ipmb_direct_addr
|
||||
{
|
||||
int addr_type;
|
||||
short channel;
|
||||
unsigned char slave_addr;
|
||||
unsigned char rq_lun;
|
||||
unsigned char rs_lun;
|
||||
};
|
||||
|
||||
The channel is always zero. You can also receive commands from other
|
||||
MCs that you have registered to handle and respond to them, so you can
|
||||
use this to implement a management controller on a bus..
|
||||
|
||||
Messages
|
||||
--------
|
||||
|
@ -348,6 +365,10 @@ user may be registered for each netfn/cmd/channel, but different users
|
|||
may register for different commands, or the same command if the
|
||||
channel bitmasks do not overlap.
|
||||
|
||||
To respond to a received command, set the response bit in the returned
|
||||
netfn, use the address from the received message, and use the same
|
||||
msgid that you got in the receive message.
|
||||
|
||||
From userland, equivalent IOCTLs are provided to do these functions.
|
||||
|
||||
|
||||
|
@ -570,6 +591,45 @@ web page.
|
|||
The driver supports a hot add and remove of interfaces through the I2C
|
||||
sysfs interface.
|
||||
|
||||
The IPMI IPMB Driver
|
||||
--------------------
|
||||
|
||||
This driver is for supporting a system that sits on an IPMB bus; it
|
||||
allows the interface to look like a normal IPMI interface. Sending
|
||||
system interface addressed messages to it will cause the message to go
|
||||
to the registered BMC on the system (default at IPMI address 0x20).
|
||||
|
||||
It also allows you to directly address other MCs on the bus using the
|
||||
ipmb direct addressing. You can receive commands from other MCs on
|
||||
the bus and they will be handled through the normal received command
|
||||
mechanism described above.
|
||||
|
||||
Parameters are::
|
||||
|
||||
ipmi_ipmb.bmcaddr=<address to use for system interface addresses messages>
|
||||
ipmi_ipmb.retry_time_ms=<Time between retries on IPMB>
|
||||
ipmi_ipmb.max_retries=<Number of times to retry a message>
|
||||
|
||||
Loading the module will not result in the driver automatcially
|
||||
starting unless there is device tree information setting it up. If
|
||||
you want to instantiate one of these by hand, do::
|
||||
|
||||
echo ipmi-ipmb <addr> > /sys/class/i2c-dev/i2c-<n>/device/new_device
|
||||
|
||||
Note that the address you give here is the I2C address, not the IPMI
|
||||
address. So if you want your MC address to be 0x60, you put 0x30
|
||||
here. See the I2C driver info for more details.
|
||||
|
||||
Command bridging to other IPMB busses through this interface does not
|
||||
work. The receive message queue is not implemented, by design. There
|
||||
is only one receive message queue on a BMC, and that is meant for the
|
||||
host drivers, not something on the IPMB bus.
|
||||
|
||||
A BMC may have multiple IPMB busses, which bus your device sits on
|
||||
depends on how the system is wired. You can fetch the channels with
|
||||
"ipmitool channel info <n>" where <n> is the channel, with the
|
||||
channels being 0-7 and try the IPMB channels.
|
||||
|
||||
Other Pieces
|
||||
------------
|
||||
|
||||
|
|
|
@ -69,12 +69,21 @@ config IPMI_SI
|
|||
|
||||
config IPMI_SSIF
|
||||
tristate 'IPMI SMBus handler (SSIF)'
|
||||
select I2C
|
||||
depends on I2C
|
||||
help
|
||||
Provides a driver for a SMBus interface to a BMC, meaning that you
|
||||
have a driver that must be accessed over an I2C bus instead of a
|
||||
standard interface. This module requires I2C support.
|
||||
|
||||
config IPMI_IPMB
|
||||
tristate 'IPMI IPMB interface'
|
||||
depends on I2C && I2C_SLAVE
|
||||
help
|
||||
Provides a driver for a system running right on the IPMB bus.
|
||||
It supports normal system interface messages to a BMC on the IPMB
|
||||
bus, and it also supports direct messaging on the bus using
|
||||
IPMB direct messages. This module requires I2C support.
|
||||
|
||||
config IPMI_POWERNV
|
||||
depends on PPC_POWERNV
|
||||
tristate 'POWERNV (OPAL firmware) IPMI interface'
|
||||
|
|
|
@ -19,6 +19,7 @@ obj-$(CONFIG_IPMI_SI) += ipmi_si.o
|
|||
obj-$(CONFIG_IPMI_DMI_DECODE) += ipmi_dmi.o
|
||||
obj-$(CONFIG_IPMI_PLAT_DATA) += ipmi_plat_data.o
|
||||
obj-$(CONFIG_IPMI_SSIF) += ipmi_ssif.o
|
||||
obj-$(CONFIG_IPMI_IPMB) += ipmi_ipmb.o
|
||||
obj-$(CONFIG_IPMI_POWERNV) += ipmi_powernv.o
|
||||
obj-$(CONFIG_IPMI_WATCHDOG) += ipmi_watchdog.o
|
||||
obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
|
||||
|
|
|
@ -8,13 +8,11 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
|
@ -59,8 +57,7 @@
|
|||
struct bt_bmc {
|
||||
struct device dev;
|
||||
struct miscdevice miscdev;
|
||||
struct regmap *map;
|
||||
int offset;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
wait_queue_head_t queue;
|
||||
struct timer_list poll_timer;
|
||||
|
@ -69,29 +66,14 @@ struct bt_bmc {
|
|||
|
||||
static atomic_t open_count = ATOMIC_INIT(0);
|
||||
|
||||
static const struct regmap_config bt_regmap_cfg = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
};
|
||||
|
||||
static u8 bt_inb(struct bt_bmc *bt_bmc, int reg)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
int rc;
|
||||
|
||||
rc = regmap_read(bt_bmc->map, bt_bmc->offset + reg, &val);
|
||||
WARN(rc != 0, "regmap_read() failed: %d\n", rc);
|
||||
|
||||
return rc == 0 ? (u8) val : 0;
|
||||
return readb(bt_bmc->base + reg);
|
||||
}
|
||||
|
||||
static void bt_outb(struct bt_bmc *bt_bmc, u8 data, int reg)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = regmap_write(bt_bmc->map, bt_bmc->offset + reg, data);
|
||||
WARN(rc != 0, "regmap_write() failed: %d\n", rc);
|
||||
writeb(data, bt_bmc->base + reg);
|
||||
}
|
||||
|
||||
static void clr_rd_ptr(struct bt_bmc *bt_bmc)
|
||||
|
@ -376,18 +358,15 @@ static irqreturn_t bt_bmc_irq(int irq, void *arg)
|
|||
{
|
||||
struct bt_bmc *bt_bmc = arg;
|
||||
u32 reg;
|
||||
int rc;
|
||||
|
||||
rc = regmap_read(bt_bmc->map, bt_bmc->offset + BT_CR2, ®);
|
||||
if (rc)
|
||||
return IRQ_NONE;
|
||||
reg = readl(bt_bmc->base + BT_CR2);
|
||||
|
||||
reg &= BT_CR2_IRQ_H2B | BT_CR2_IRQ_HBUSY;
|
||||
if (!reg)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* ack pending IRQs */
|
||||
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR2, reg);
|
||||
writel(reg, bt_bmc->base + BT_CR2);
|
||||
|
||||
wake_up(&bt_bmc->queue);
|
||||
return IRQ_HANDLED;
|
||||
|
@ -398,6 +377,7 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
|
|||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
int rc;
|
||||
u32 reg;
|
||||
|
||||
bt_bmc->irq = platform_get_irq_optional(pdev, 0);
|
||||
if (bt_bmc->irq < 0)
|
||||
|
@ -417,11 +397,11 @@ static int bt_bmc_config_irq(struct bt_bmc *bt_bmc,
|
|||
* will be cleared (along with B2H) when we can write the next
|
||||
* message to the BT buffer
|
||||
*/
|
||||
rc = regmap_update_bits(bt_bmc->map, bt_bmc->offset + BT_CR1,
|
||||
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY),
|
||||
(BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY));
|
||||
reg = readl(bt_bmc->base + BT_CR1);
|
||||
reg |= BT_CR1_IRQ_H2B | BT_CR1_IRQ_HBUSY;
|
||||
writel(reg, bt_bmc->base + BT_CR1);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bt_bmc_probe(struct platform_device *pdev)
|
||||
|
@ -439,25 +419,9 @@ static int bt_bmc_probe(struct platform_device *pdev)
|
|||
|
||||
dev_set_drvdata(&pdev->dev, bt_bmc);
|
||||
|
||||
bt_bmc->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
|
||||
if (IS_ERR(bt_bmc->map)) {
|
||||
void __iomem *base;
|
||||
|
||||
/*
|
||||
* Assume it's not the MFD-based devicetree description, in
|
||||
* which case generate a regmap ourselves
|
||||
*/
|
||||
base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
bt_bmc->map = devm_regmap_init_mmio(dev, base, &bt_regmap_cfg);
|
||||
bt_bmc->offset = 0;
|
||||
} else {
|
||||
rc = of_property_read_u32(dev->of_node, "reg", &bt_bmc->offset);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
bt_bmc->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(bt_bmc->base))
|
||||
return PTR_ERR(bt_bmc->base);
|
||||
|
||||
mutex_init(&bt_bmc->mutex);
|
||||
init_waitqueue_head(&bt_bmc->queue);
|
||||
|
@ -483,12 +447,12 @@ static int bt_bmc_probe(struct platform_device *pdev)
|
|||
add_timer(&bt_bmc->poll_timer);
|
||||
}
|
||||
|
||||
regmap_write(bt_bmc->map, bt_bmc->offset + BT_CR0,
|
||||
(BT_IO_BASE << BT_CR0_IO_BASE) |
|
||||
writel((BT_IO_BASE << BT_CR0_IO_BASE) |
|
||||
(BT_IRQ << BT_CR0_IRQ) |
|
||||
BT_CR0_EN_CLR_SLV_RDP |
|
||||
BT_CR0_EN_CLR_SLV_WRP |
|
||||
BT_CR0_ENABLE_IBT);
|
||||
BT_CR0_ENABLE_IBT,
|
||||
bt_bmc->base + BT_CR0);
|
||||
|
||||
clr_b_busy(bt_bmc);
|
||||
|
||||
|
@ -508,6 +472,7 @@ static int bt_bmc_remove(struct platform_device *pdev)
|
|||
static const struct of_device_id bt_bmc_match[] = {
|
||||
{ .compatible = "aspeed,ast2400-ibt-bmc" },
|
||||
{ .compatible = "aspeed,ast2500-ibt-bmc" },
|
||||
{ .compatible = "aspeed,ast2600-ibt-bmc" },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
@ -247,11 +247,13 @@ static int handle_recv(struct ipmi_file_private *priv,
|
|||
|
||||
if (msg->msg.data_len > 0) {
|
||||
if (rsp->msg.data_len < msg->msg.data_len) {
|
||||
rv2 = -EMSGSIZE;
|
||||
if (trunc)
|
||||
if (trunc) {
|
||||
rv2 = -EMSGSIZE;
|
||||
msg->msg.data_len = rsp->msg.data_len;
|
||||
else
|
||||
} else {
|
||||
rv = -EMSGSIZE;
|
||||
goto recv_putback_on_err;
|
||||
}
|
||||
}
|
||||
|
||||
if (copy_to_user(rsp->msg.data,
|
||||
|
|
|
@ -0,0 +1,539 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* Driver to talk to a remote management controller on IPMB.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/ipmi_msgdefs.h>
|
||||
#include <linux/ipmi_smi.h>
|
||||
|
||||
#define DEVICE_NAME "ipmi-ipmb"
|
||||
|
||||
static int bmcaddr = 0x20;
|
||||
module_param(bmcaddr, int, 0644);
|
||||
MODULE_PARM_DESC(bmcaddr, "Address to use for BMC.");
|
||||
|
||||
static unsigned int retry_time_ms = 250;
|
||||
module_param(retry_time_ms, uint, 0644);
|
||||
MODULE_PARM_DESC(max_retries, "Timeout time between retries, in milliseconds.");
|
||||
|
||||
static unsigned int max_retries = 1;
|
||||
module_param(max_retries, uint, 0644);
|
||||
MODULE_PARM_DESC(max_retries, "Max resends of a command before timing out.");
|
||||
|
||||
/* Add room for the two slave addresses, two checksums, and rqSeq. */
|
||||
#define IPMB_MAX_MSG_LEN (IPMI_MAX_MSG_LENGTH + 5)
|
||||
|
||||
struct ipmi_ipmb_dev {
|
||||
struct ipmi_smi *intf;
|
||||
struct i2c_client *client;
|
||||
|
||||
struct ipmi_smi_handlers handlers;
|
||||
|
||||
bool ready;
|
||||
|
||||
u8 curr_seq;
|
||||
|
||||
u8 bmcaddr;
|
||||
u32 retry_time_ms;
|
||||
u32 max_retries;
|
||||
|
||||
struct ipmi_smi_msg *next_msg;
|
||||
struct ipmi_smi_msg *working_msg;
|
||||
|
||||
/* Transmit thread. */
|
||||
struct task_struct *thread;
|
||||
struct semaphore wake_thread;
|
||||
struct semaphore got_rsp;
|
||||
spinlock_t lock;
|
||||
bool stopping;
|
||||
|
||||
u8 xmitmsg[IPMB_MAX_MSG_LEN];
|
||||
unsigned int xmitlen;
|
||||
|
||||
u8 rcvmsg[IPMB_MAX_MSG_LEN];
|
||||
unsigned int rcvlen;
|
||||
bool overrun;
|
||||
};
|
||||
|
||||
static bool valid_ipmb(struct ipmi_ipmb_dev *iidev)
|
||||
{
|
||||
u8 *msg = iidev->rcvmsg;
|
||||
u8 netfn;
|
||||
|
||||
if (iidev->overrun)
|
||||
return false;
|
||||
|
||||
/* Minimum message size. */
|
||||
if (iidev->rcvlen < 7)
|
||||
return false;
|
||||
|
||||
/* Is it a response? */
|
||||
netfn = msg[1] >> 2;
|
||||
if (netfn & 1) {
|
||||
/* Response messages have an added completion code. */
|
||||
if (iidev->rcvlen < 8)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ipmb_checksum(msg, 3) != 0)
|
||||
return false;
|
||||
if (ipmb_checksum(msg + 3, iidev->rcvlen - 3) != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_check_msg_done(struct ipmi_ipmb_dev *iidev)
|
||||
{
|
||||
struct ipmi_smi_msg *imsg = NULL;
|
||||
u8 *msg = iidev->rcvmsg;
|
||||
bool is_cmd;
|
||||
unsigned long flags;
|
||||
|
||||
if (iidev->rcvlen == 0)
|
||||
return;
|
||||
if (!valid_ipmb(iidev))
|
||||
goto done;
|
||||
|
||||
is_cmd = ((msg[1] >> 2) & 1) == 0;
|
||||
|
||||
if (is_cmd) {
|
||||
/* Ignore commands until we are up. */
|
||||
if (!iidev->ready)
|
||||
goto done;
|
||||
|
||||
/* It's a command, allocate a message for it. */
|
||||
imsg = ipmi_alloc_smi_msg();
|
||||
if (!imsg)
|
||||
goto done;
|
||||
imsg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT;
|
||||
imsg->data_size = 0;
|
||||
} else {
|
||||
spin_lock_irqsave(&iidev->lock, flags);
|
||||
if (iidev->working_msg) {
|
||||
u8 seq = msg[4] >> 2;
|
||||
bool xmit_rsp = (iidev->working_msg->data[0] >> 2) & 1;
|
||||
|
||||
/*
|
||||
* Responses should carry the sequence we sent
|
||||
* them with. If it's a transmitted response,
|
||||
* ignore it. And if the message hasn't been
|
||||
* transmitted, ignore it.
|
||||
*/
|
||||
if (!xmit_rsp && seq == iidev->curr_seq) {
|
||||
iidev->curr_seq = (iidev->curr_seq + 1) & 0x3f;
|
||||
|
||||
imsg = iidev->working_msg;
|
||||
iidev->working_msg = NULL;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&iidev->lock, flags);
|
||||
}
|
||||
|
||||
if (!imsg)
|
||||
goto done;
|
||||
|
||||
if (imsg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
imsg->rsp[0] = msg[1]; /* NetFn/LUN */
|
||||
/*
|
||||
* Keep the source address, rqSeq. Drop the trailing
|
||||
* checksum.
|
||||
*/
|
||||
memcpy(imsg->rsp + 1, msg + 3, iidev->rcvlen - 4);
|
||||
imsg->rsp_size = iidev->rcvlen - 3;
|
||||
} else {
|
||||
imsg->rsp[0] = msg[1]; /* NetFn/LUN */
|
||||
/*
|
||||
* Skip the source address, rqSeq. Drop the trailing
|
||||
* checksum.
|
||||
*/
|
||||
memcpy(imsg->rsp + 1, msg + 5, iidev->rcvlen - 6);
|
||||
imsg->rsp_size = iidev->rcvlen - 5;
|
||||
}
|
||||
ipmi_smi_msg_received(iidev->intf, imsg);
|
||||
if (!is_cmd)
|
||||
up(&iidev->got_rsp);
|
||||
|
||||
done:
|
||||
iidev->overrun = false;
|
||||
iidev->rcvlen = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The IPMB protocol only supports i2c writes so there is no need to
|
||||
* support I2C_SLAVE_READ* events, except to know if the other end has
|
||||
* issued a read without going to stop mode.
|
||||
*/
|
||||
static int ipmi_ipmb_slave_cb(struct i2c_client *client,
|
||||
enum i2c_slave_event event, u8 *val)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
|
||||
|
||||
switch (event) {
|
||||
case I2C_SLAVE_WRITE_REQUESTED:
|
||||
ipmi_ipmb_check_msg_done(iidev);
|
||||
/*
|
||||
* First byte is the slave address, to ease the checksum
|
||||
* calculation.
|
||||
*/
|
||||
iidev->rcvmsg[0] = client->addr << 1;
|
||||
iidev->rcvlen = 1;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_WRITE_RECEIVED:
|
||||
if (iidev->rcvlen >= sizeof(iidev->rcvmsg))
|
||||
iidev->overrun = true;
|
||||
else
|
||||
iidev->rcvmsg[iidev->rcvlen++] = *val;
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_REQUESTED:
|
||||
case I2C_SLAVE_STOP:
|
||||
ipmi_ipmb_check_msg_done(iidev);
|
||||
break;
|
||||
|
||||
case I2C_SLAVE_READ_PROCESSED:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_send_response(struct ipmi_ipmb_dev *iidev,
|
||||
struct ipmi_smi_msg *msg, u8 cc)
|
||||
{
|
||||
if ((msg->data[0] >> 2) & 1) {
|
||||
/*
|
||||
* It's a response being sent, we needto return a
|
||||
* response response. Fake a send msg command
|
||||
* response with channel 0. This will always be ipmb
|
||||
* direct.
|
||||
*/
|
||||
msg->data[0] = (IPMI_NETFN_APP_REQUEST | 1) << 2;
|
||||
msg->data[3] = IPMI_SEND_MSG_CMD;
|
||||
msg->data[4] = cc;
|
||||
msg->data_size = 5;
|
||||
}
|
||||
msg->rsp[0] = msg->data[0] | (1 << 2);
|
||||
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
msg->rsp[1] = msg->data[1];
|
||||
msg->rsp[2] = msg->data[2];
|
||||
msg->rsp[3] = msg->data[3];
|
||||
msg->rsp[4] = cc;
|
||||
msg->rsp_size = 5;
|
||||
} else {
|
||||
msg->rsp[1] = msg->data[1];
|
||||
msg->rsp[2] = cc;
|
||||
msg->rsp_size = 3;
|
||||
}
|
||||
ipmi_smi_msg_received(iidev->intf, msg);
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_format_for_xmit(struct ipmi_ipmb_dev *iidev,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
iidev->xmitmsg[0] = msg->data[1];
|
||||
iidev->xmitmsg[1] = msg->data[0];
|
||||
memcpy(iidev->xmitmsg + 4, msg->data + 2, msg->data_size - 2);
|
||||
iidev->xmitlen = msg->data_size + 2;
|
||||
} else {
|
||||
iidev->xmitmsg[0] = iidev->bmcaddr;
|
||||
iidev->xmitmsg[1] = msg->data[0];
|
||||
iidev->xmitmsg[4] = 0;
|
||||
memcpy(iidev->xmitmsg + 5, msg->data + 1, msg->data_size - 1);
|
||||
iidev->xmitlen = msg->data_size + 4;
|
||||
}
|
||||
iidev->xmitmsg[3] = iidev->client->addr << 1;
|
||||
if (((msg->data[0] >> 2) & 1) == 0)
|
||||
/* If it's a command, put in our own sequence number. */
|
||||
iidev->xmitmsg[4] = ((iidev->xmitmsg[4] & 0x03) |
|
||||
(iidev->curr_seq << 2));
|
||||
|
||||
/* Now add on the final checksums. */
|
||||
iidev->xmitmsg[2] = ipmb_checksum(iidev->xmitmsg, 2);
|
||||
iidev->xmitmsg[iidev->xmitlen] =
|
||||
ipmb_checksum(iidev->xmitmsg + 3, iidev->xmitlen - 3);
|
||||
iidev->xmitlen++;
|
||||
}
|
||||
|
||||
static int ipmi_ipmb_thread(void *data)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = data;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
long ret;
|
||||
struct i2c_msg i2c_msg;
|
||||
struct ipmi_smi_msg *msg = NULL;
|
||||
unsigned long flags;
|
||||
unsigned int retries = 0;
|
||||
|
||||
/* Wait for a message to send */
|
||||
ret = down_interruptible(&iidev->wake_thread);
|
||||
if (iidev->stopping)
|
||||
break;
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
spin_lock_irqsave(&iidev->lock, flags);
|
||||
if (iidev->next_msg) {
|
||||
msg = iidev->next_msg;
|
||||
iidev->next_msg = NULL;
|
||||
}
|
||||
spin_unlock_irqrestore(&iidev->lock, flags);
|
||||
if (!msg)
|
||||
continue;
|
||||
|
||||
ipmi_ipmb_format_for_xmit(iidev, msg);
|
||||
|
||||
retry:
|
||||
i2c_msg.len = iidev->xmitlen - 1;
|
||||
if (i2c_msg.len > 32) {
|
||||
ipmi_ipmb_send_response(iidev, msg,
|
||||
IPMI_REQ_LEN_EXCEEDED_ERR);
|
||||
continue;
|
||||
}
|
||||
|
||||
i2c_msg.addr = iidev->xmitmsg[0] >> 1;
|
||||
i2c_msg.flags = 0;
|
||||
i2c_msg.buf = iidev->xmitmsg + 1;
|
||||
|
||||
/* Rely on i2c_transfer for a barrier. */
|
||||
iidev->working_msg = msg;
|
||||
|
||||
ret = i2c_transfer(iidev->client->adapter, &i2c_msg, 1);
|
||||
|
||||
if ((msg->data[0] >> 2) & 1) {
|
||||
/*
|
||||
* It's a response, nothing will be returned
|
||||
* by the other end.
|
||||
*/
|
||||
|
||||
iidev->working_msg = NULL;
|
||||
ipmi_ipmb_send_response(iidev, msg,
|
||||
ret < 0 ? IPMI_BUS_ERR : 0);
|
||||
continue;
|
||||
}
|
||||
if (ret < 0) {
|
||||
iidev->working_msg = NULL;
|
||||
ipmi_ipmb_send_response(iidev, msg, IPMI_BUS_ERR);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* A command was sent, wait for its response. */
|
||||
ret = down_timeout(&iidev->got_rsp,
|
||||
msecs_to_jiffies(iidev->retry_time_ms));
|
||||
|
||||
/*
|
||||
* Grab the message if we can. If the handler hasn't
|
||||
* already handled it, the message will still be there.
|
||||
*/
|
||||
spin_lock_irqsave(&iidev->lock, flags);
|
||||
msg = iidev->working_msg;
|
||||
iidev->working_msg = NULL;
|
||||
spin_unlock_irqrestore(&iidev->lock, flags);
|
||||
|
||||
if (!msg && ret) {
|
||||
/*
|
||||
* If working_msg is not set and we timed out,
|
||||
* that means the message grabbed by
|
||||
* check_msg_done before we could grab it
|
||||
* here. Wait again for check_msg_done to up
|
||||
* the semaphore.
|
||||
*/
|
||||
down(&iidev->got_rsp);
|
||||
} else if (msg && ++retries <= iidev->max_retries) {
|
||||
spin_lock_irqsave(&iidev->lock, flags);
|
||||
iidev->working_msg = msg;
|
||||
spin_unlock_irqrestore(&iidev->lock, flags);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
if (msg)
|
||||
ipmi_ipmb_send_response(iidev, msg, IPMI_TIMEOUT_ERR);
|
||||
}
|
||||
|
||||
if (iidev->next_msg)
|
||||
/* Return an unspecified error. */
|
||||
ipmi_ipmb_send_response(iidev, iidev->next_msg, 0xff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipmi_ipmb_start_processing(void *send_info,
|
||||
struct ipmi_smi *new_intf)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = send_info;
|
||||
|
||||
iidev->intf = new_intf;
|
||||
iidev->ready = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_stop_thread(struct ipmi_ipmb_dev *iidev)
|
||||
{
|
||||
if (iidev->thread) {
|
||||
struct task_struct *t = iidev->thread;
|
||||
|
||||
iidev->thread = NULL;
|
||||
iidev->stopping = true;
|
||||
up(&iidev->wake_thread);
|
||||
up(&iidev->got_rsp);
|
||||
kthread_stop(t);
|
||||
}
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_shutdown(void *send_info)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = send_info;
|
||||
|
||||
ipmi_ipmb_stop_thread(iidev);
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_sender(void *send_info,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = send_info;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&iidev->lock, flags);
|
||||
BUG_ON(iidev->next_msg);
|
||||
|
||||
iidev->next_msg = msg;
|
||||
spin_unlock_irqrestore(&iidev->lock, flags);
|
||||
|
||||
up(&iidev->wake_thread);
|
||||
}
|
||||
|
||||
static void ipmi_ipmb_request_events(void *send_info)
|
||||
{
|
||||
/* We don't fetch events here. */
|
||||
}
|
||||
|
||||
static int ipmi_ipmb_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ipmi_ipmb_dev *iidev = i2c_get_clientdata(client);
|
||||
|
||||
if (iidev->client) {
|
||||
iidev->client = NULL;
|
||||
i2c_slave_unregister(client);
|
||||
}
|
||||
ipmi_ipmb_stop_thread(iidev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipmi_ipmb_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct ipmi_ipmb_dev *iidev;
|
||||
int rv;
|
||||
|
||||
iidev = devm_kzalloc(&client->dev, sizeof(*iidev), GFP_KERNEL);
|
||||
if (!iidev)
|
||||
return -ENOMEM;
|
||||
|
||||
if (of_property_read_u8(dev->of_node, "bmcaddr", &iidev->bmcaddr) != 0)
|
||||
iidev->bmcaddr = bmcaddr;
|
||||
if (iidev->bmcaddr == 0 || iidev->bmcaddr & 1) {
|
||||
/* Can't have the write bit set. */
|
||||
dev_notice(&client->dev,
|
||||
"Invalid bmc address value %2.2x\n", iidev->bmcaddr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "retry-time",
|
||||
&iidev->retry_time_ms) != 0)
|
||||
iidev->retry_time_ms = retry_time_ms;
|
||||
|
||||
if (of_property_read_u32(dev->of_node, "max-retries",
|
||||
&iidev->max_retries) != 0)
|
||||
iidev->max_retries = max_retries;
|
||||
|
||||
i2c_set_clientdata(client, iidev);
|
||||
client->flags |= I2C_CLIENT_SLAVE;
|
||||
|
||||
rv = i2c_slave_register(client, ipmi_ipmb_slave_cb);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
iidev->client = client;
|
||||
|
||||
iidev->handlers.flags = IPMI_SMI_CAN_HANDLE_IPMB_DIRECT;
|
||||
iidev->handlers.start_processing = ipmi_ipmb_start_processing;
|
||||
iidev->handlers.shutdown = ipmi_ipmb_shutdown;
|
||||
iidev->handlers.sender = ipmi_ipmb_sender;
|
||||
iidev->handlers.request_events = ipmi_ipmb_request_events;
|
||||
|
||||
spin_lock_init(&iidev->lock);
|
||||
sema_init(&iidev->wake_thread, 0);
|
||||
sema_init(&iidev->got_rsp, 0);
|
||||
|
||||
iidev->thread = kthread_run(ipmi_ipmb_thread, iidev,
|
||||
"kipmb%4.4x", client->addr);
|
||||
if (IS_ERR(iidev->thread)) {
|
||||
rv = PTR_ERR(iidev->thread);
|
||||
dev_notice(&client->dev,
|
||||
"Could not start kernel thread: error %d\n", rv);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
rv = ipmi_register_smi(&iidev->handlers,
|
||||
iidev,
|
||||
&client->dev,
|
||||
iidev->bmcaddr);
|
||||
if (rv)
|
||||
goto out_err;
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
ipmi_ipmb_remove(client);
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id of_ipmi_ipmb_match[] = {
|
||||
{ .type = "ipmi", .compatible = DEVICE_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, of_ipmi_ipmb_match);
|
||||
#else
|
||||
#define of_ipmi_ipmb_match NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id ipmi_ipmb_id[] = {
|
||||
{ DEVICE_NAME, 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ipmi_ipmb_id);
|
||||
|
||||
static struct i2c_driver ipmi_ipmb_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.of_match_table = of_ipmi_ipmb_match,
|
||||
},
|
||||
.probe = ipmi_ipmb_probe,
|
||||
.remove = ipmi_ipmb_remove,
|
||||
.id_table = ipmi_ipmb_id,
|
||||
};
|
||||
module_i2c_driver(ipmi_ipmb_driver);
|
||||
|
||||
MODULE_AUTHOR("Corey Minyard");
|
||||
MODULE_DESCRIPTION("IPMI IPMB driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -653,6 +653,11 @@ static int is_ipmb_bcast_addr(struct ipmi_addr *addr)
|
|||
return addr->addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE;
|
||||
}
|
||||
|
||||
static int is_ipmb_direct_addr(struct ipmi_addr *addr)
|
||||
{
|
||||
return addr->addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE;
|
||||
}
|
||||
|
||||
static void free_recv_msg_list(struct list_head *q)
|
||||
{
|
||||
struct ipmi_recv_msg *msg, *msg2;
|
||||
|
@ -805,6 +810,17 @@ ipmi_addr_equal(struct ipmi_addr *addr1, struct ipmi_addr *addr2)
|
|||
&& (ipmb_addr1->lun == ipmb_addr2->lun));
|
||||
}
|
||||
|
||||
if (is_ipmb_direct_addr(addr1)) {
|
||||
struct ipmi_ipmb_direct_addr *daddr1
|
||||
= (struct ipmi_ipmb_direct_addr *) addr1;
|
||||
struct ipmi_ipmb_direct_addr *daddr2
|
||||
= (struct ipmi_ipmb_direct_addr *) addr2;
|
||||
|
||||
return daddr1->slave_addr == daddr2->slave_addr &&
|
||||
daddr1->rq_lun == daddr2->rq_lun &&
|
||||
daddr1->rs_lun == daddr2->rs_lun;
|
||||
}
|
||||
|
||||
if (is_lan_addr(addr1)) {
|
||||
struct ipmi_lan_addr *lan_addr1
|
||||
= (struct ipmi_lan_addr *) addr1;
|
||||
|
@ -843,6 +859,23 @@ int ipmi_validate_addr(struct ipmi_addr *addr, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (is_ipmb_direct_addr(addr)) {
|
||||
struct ipmi_ipmb_direct_addr *daddr = (void *) addr;
|
||||
|
||||
if (addr->channel != 0)
|
||||
return -EINVAL;
|
||||
if (len < sizeof(struct ipmi_ipmb_direct_addr))
|
||||
return -EINVAL;
|
||||
|
||||
if (daddr->slave_addr & 0x01)
|
||||
return -EINVAL;
|
||||
if (daddr->rq_lun >= 4)
|
||||
return -EINVAL;
|
||||
if (daddr->rs_lun >= 4)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_lan_addr(addr)) {
|
||||
if (len < sizeof(struct ipmi_lan_addr))
|
||||
return -EINVAL;
|
||||
|
@ -862,6 +895,9 @@ unsigned int ipmi_addr_length(int addr_type)
|
|||
|| (addr_type == IPMI_IPMB_BROADCAST_ADDR_TYPE))
|
||||
return sizeof(struct ipmi_ipmb_addr);
|
||||
|
||||
if (addr_type == IPMI_IPMB_DIRECT_ADDR_TYPE)
|
||||
return sizeof(struct ipmi_ipmb_direct_addr);
|
||||
|
||||
if (addr_type == IPMI_LAN_ADDR_TYPE)
|
||||
return sizeof(struct ipmi_lan_addr);
|
||||
|
||||
|
@ -1710,7 +1746,7 @@ int ipmi_unregister_for_cmd(struct ipmi_user *user,
|
|||
}
|
||||
EXPORT_SYMBOL(ipmi_unregister_for_cmd);
|
||||
|
||||
static unsigned char
|
||||
unsigned char
|
||||
ipmb_checksum(unsigned char *data, int size)
|
||||
{
|
||||
unsigned char csum = 0;
|
||||
|
@ -1720,6 +1756,7 @@ ipmb_checksum(unsigned char *data, int size)
|
|||
|
||||
return -csum;
|
||||
}
|
||||
EXPORT_SYMBOL(ipmb_checksum);
|
||||
|
||||
static inline void format_ipmb_msg(struct ipmi_smi_msg *smi_msg,
|
||||
struct kernel_ipmi_msg *msg,
|
||||
|
@ -2051,6 +2088,58 @@ out_err:
|
|||
return rv;
|
||||
}
|
||||
|
||||
static int i_ipmi_req_ipmb_direct(struct ipmi_smi *intf,
|
||||
struct ipmi_addr *addr,
|
||||
long msgid,
|
||||
struct kernel_ipmi_msg *msg,
|
||||
struct ipmi_smi_msg *smi_msg,
|
||||
struct ipmi_recv_msg *recv_msg,
|
||||
unsigned char source_lun)
|
||||
{
|
||||
struct ipmi_ipmb_direct_addr *daddr;
|
||||
bool is_cmd = !(recv_msg->msg.netfn & 0x1);
|
||||
|
||||
if (!(intf->handlers->flags & IPMI_SMI_CAN_HANDLE_IPMB_DIRECT))
|
||||
return -EAFNOSUPPORT;
|
||||
|
||||
/* Responses must have a completion code. */
|
||||
if (!is_cmd && msg->data_len < 1) {
|
||||
ipmi_inc_stat(intf, sent_invalid_commands);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((msg->data_len + 4) > IPMI_MAX_MSG_LENGTH) {
|
||||
ipmi_inc_stat(intf, sent_invalid_commands);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
daddr = (struct ipmi_ipmb_direct_addr *) addr;
|
||||
if (daddr->rq_lun > 3 || daddr->rs_lun > 3) {
|
||||
ipmi_inc_stat(intf, sent_invalid_commands);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
smi_msg->type = IPMI_SMI_MSG_TYPE_IPMB_DIRECT;
|
||||
smi_msg->msgid = msgid;
|
||||
|
||||
if (is_cmd) {
|
||||
smi_msg->data[0] = msg->netfn << 2 | daddr->rs_lun;
|
||||
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rq_lun;
|
||||
} else {
|
||||
smi_msg->data[0] = msg->netfn << 2 | daddr->rq_lun;
|
||||
smi_msg->data[2] = recv_msg->msgid << 2 | daddr->rs_lun;
|
||||
}
|
||||
smi_msg->data[1] = daddr->slave_addr;
|
||||
smi_msg->data[3] = msg->cmd;
|
||||
|
||||
memcpy(smi_msg->data + 4, msg->data, msg->data_len);
|
||||
smi_msg->data_size = msg->data_len + 4;
|
||||
|
||||
smi_msg->user_data = recv_msg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i_ipmi_req_lan(struct ipmi_smi *intf,
|
||||
struct ipmi_addr *addr,
|
||||
long msgid,
|
||||
|
@ -2240,6 +2329,9 @@ static int i_ipmi_request(struct ipmi_user *user,
|
|||
rv = i_ipmi_req_ipmb(intf, addr, msgid, msg, smi_msg, recv_msg,
|
||||
source_address, source_lun,
|
||||
retries, retry_time_ms);
|
||||
} else if (is_ipmb_direct_addr(addr)) {
|
||||
rv = i_ipmi_req_ipmb_direct(intf, addr, msgid, msg, smi_msg,
|
||||
recv_msg, source_lun);
|
||||
} else if (is_lan_addr(addr)) {
|
||||
rv = i_ipmi_req_lan(intf, addr, msgid, msg, smi_msg, recv_msg,
|
||||
source_lun, retries, retry_time_ms);
|
||||
|
@ -2369,6 +2461,13 @@ static void bmc_device_id_handler(struct ipmi_smi *intf,
|
|||
return;
|
||||
}
|
||||
|
||||
if (msg->msg.data[0]) {
|
||||
dev_warn(intf->si_dev, "device id fetch failed: 0x%2.2x\n",
|
||||
msg->msg.data[0]);
|
||||
intf->bmc->dyn_id_set = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = ipmi_demangle_device_id(msg->msg.netfn, msg->msg.cmd,
|
||||
msg->msg.data, msg->msg.data_len, &intf->bmc->fetch_id);
|
||||
if (rv) {
|
||||
|
@ -2384,7 +2483,7 @@ static void bmc_device_id_handler(struct ipmi_smi *intf,
|
|||
smp_wmb();
|
||||
intf->bmc->dyn_id_set = 1;
|
||||
}
|
||||
|
||||
out:
|
||||
wake_up(&intf->waitq);
|
||||
}
|
||||
|
||||
|
@ -2617,7 +2716,7 @@ static ssize_t device_id_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 10, "%u\n", id.device_id);
|
||||
return sysfs_emit(buf, "%u\n", id.device_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(device_id);
|
||||
|
||||
|
@ -2633,7 +2732,7 @@ static ssize_t provides_device_sdrs_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 10, "%u\n", (id.device_revision & 0x80) >> 7);
|
||||
return sysfs_emit(buf, "%u\n", (id.device_revision & 0x80) >> 7);
|
||||
}
|
||||
static DEVICE_ATTR_RO(provides_device_sdrs);
|
||||
|
||||
|
@ -2648,7 +2747,7 @@ static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 20, "%u\n", id.device_revision & 0x0F);
|
||||
return sysfs_emit(buf, "%u\n", id.device_revision & 0x0F);
|
||||
}
|
||||
static DEVICE_ATTR_RO(revision);
|
||||
|
||||
|
@ -2664,7 +2763,7 @@ static ssize_t firmware_revision_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 20, "%u.%x\n", id.firmware_revision_1,
|
||||
return sysfs_emit(buf, "%u.%x\n", id.firmware_revision_1,
|
||||
id.firmware_revision_2);
|
||||
}
|
||||
static DEVICE_ATTR_RO(firmware_revision);
|
||||
|
@ -2681,7 +2780,7 @@ static ssize_t ipmi_version_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 20, "%u.%u\n",
|
||||
return sysfs_emit(buf, "%u.%u\n",
|
||||
ipmi_version_major(&id),
|
||||
ipmi_version_minor(&id));
|
||||
}
|
||||
|
@ -2699,7 +2798,7 @@ static ssize_t add_dev_support_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 10, "0x%02x\n", id.additional_device_support);
|
||||
return sysfs_emit(buf, "0x%02x\n", id.additional_device_support);
|
||||
}
|
||||
static DEVICE_ATTR(additional_device_support, S_IRUGO, add_dev_support_show,
|
||||
NULL);
|
||||
|
@ -2716,7 +2815,7 @@ static ssize_t manufacturer_id_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 20, "0x%6.6x\n", id.manufacturer_id);
|
||||
return sysfs_emit(buf, "0x%6.6x\n", id.manufacturer_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(manufacturer_id);
|
||||
|
||||
|
@ -2732,7 +2831,7 @@ static ssize_t product_id_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 10, "0x%4.4x\n", id.product_id);
|
||||
return sysfs_emit(buf, "0x%4.4x\n", id.product_id);
|
||||
}
|
||||
static DEVICE_ATTR_RO(product_id);
|
||||
|
||||
|
@ -2748,7 +2847,7 @@ static ssize_t aux_firmware_rev_show(struct device *dev,
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
return snprintf(buf, 21, "0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||
return sysfs_emit(buf, "0x%02x 0x%02x 0x%02x 0x%02x\n",
|
||||
id.aux_firmware_revision[3],
|
||||
id.aux_firmware_revision[2],
|
||||
id.aux_firmware_revision[1],
|
||||
|
@ -2770,7 +2869,7 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr,
|
|||
if (!guid_set)
|
||||
return -ENOENT;
|
||||
|
||||
return snprintf(buf, UUID_STRING_LEN + 1 + 1, "%pUl\n", &guid);
|
||||
return sysfs_emit(buf, "%pUl\n", &guid);
|
||||
}
|
||||
static DEVICE_ATTR_RO(guid);
|
||||
|
||||
|
@ -3794,6 +3893,123 @@ static int handle_ipmb_get_msg_cmd(struct ipmi_smi *intf,
|
|||
return rv;
|
||||
}
|
||||
|
||||
static int handle_ipmb_direct_rcv_cmd(struct ipmi_smi *intf,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
struct cmd_rcvr *rcvr;
|
||||
int rv = 0;
|
||||
struct ipmi_user *user = NULL;
|
||||
struct ipmi_ipmb_direct_addr *daddr;
|
||||
struct ipmi_recv_msg *recv_msg;
|
||||
unsigned char netfn = msg->rsp[0] >> 2;
|
||||
unsigned char cmd = msg->rsp[3];
|
||||
|
||||
rcu_read_lock();
|
||||
/* We always use channel 0 for direct messages. */
|
||||
rcvr = find_cmd_rcvr(intf, netfn, cmd, 0);
|
||||
if (rcvr) {
|
||||
user = rcvr->user;
|
||||
kref_get(&user->refcount);
|
||||
} else
|
||||
user = NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
if (user == NULL) {
|
||||
/* We didn't find a user, deliver an error response. */
|
||||
ipmi_inc_stat(intf, unhandled_commands);
|
||||
|
||||
msg->data[0] = ((netfn + 1) << 2) | (msg->rsp[4] & 0x3);
|
||||
msg->data[1] = msg->rsp[2];
|
||||
msg->data[2] = msg->rsp[4] & ~0x3;
|
||||
msg->data[3] = cmd;
|
||||
msg->data[4] = IPMI_INVALID_CMD_COMPLETION_CODE;
|
||||
msg->data_size = 5;
|
||||
|
||||
rcu_read_lock();
|
||||
if (!intf->in_shutdown) {
|
||||
smi_send(intf, intf->handlers, msg, 0);
|
||||
/*
|
||||
* We used the message, so return the value
|
||||
* that causes it to not be freed or
|
||||
* queued.
|
||||
*/
|
||||
rv = -1;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
recv_msg = ipmi_alloc_recv_msg();
|
||||
if (!recv_msg) {
|
||||
/*
|
||||
* We couldn't allocate memory for the
|
||||
* message, so requeue it for handling
|
||||
* later.
|
||||
*/
|
||||
rv = 1;
|
||||
kref_put(&user->refcount, free_user);
|
||||
} else {
|
||||
/* Extract the source address from the data. */
|
||||
daddr = (struct ipmi_ipmb_direct_addr *)&recv_msg->addr;
|
||||
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
|
||||
daddr->channel = 0;
|
||||
daddr->slave_addr = msg->rsp[1];
|
||||
daddr->rs_lun = msg->rsp[0] & 3;
|
||||
daddr->rq_lun = msg->rsp[2] & 3;
|
||||
|
||||
/*
|
||||
* Extract the rest of the message information
|
||||
* from the IPMB header.
|
||||
*/
|
||||
recv_msg->user = user;
|
||||
recv_msg->recv_type = IPMI_CMD_RECV_TYPE;
|
||||
recv_msg->msgid = (msg->rsp[2] >> 2);
|
||||
recv_msg->msg.netfn = msg->rsp[0] >> 2;
|
||||
recv_msg->msg.cmd = msg->rsp[3];
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
|
||||
recv_msg->msg.data_len = msg->rsp_size - 4;
|
||||
memcpy(recv_msg->msg_data, msg->rsp + 4,
|
||||
msg->rsp_size - 4);
|
||||
if (deliver_response(intf, recv_msg))
|
||||
ipmi_inc_stat(intf, unhandled_commands);
|
||||
else
|
||||
ipmi_inc_stat(intf, handled_commands);
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int handle_ipmb_direct_rcv_rsp(struct ipmi_smi *intf,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
struct ipmi_recv_msg *recv_msg;
|
||||
struct ipmi_ipmb_direct_addr *daddr;
|
||||
|
||||
recv_msg = (struct ipmi_recv_msg *) msg->user_data;
|
||||
if (recv_msg == NULL) {
|
||||
dev_warn(intf->si_dev,
|
||||
"IPMI message received with no owner. This could be because of a malformed message, or because of a hardware error. Contact your hardware vendor for assistance.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
recv_msg->recv_type = IPMI_RESPONSE_RECV_TYPE;
|
||||
recv_msg->msgid = msg->msgid;
|
||||
daddr = (struct ipmi_ipmb_direct_addr *) &recv_msg->addr;
|
||||
daddr->addr_type = IPMI_IPMB_DIRECT_ADDR_TYPE;
|
||||
daddr->channel = 0;
|
||||
daddr->slave_addr = msg->rsp[1];
|
||||
daddr->rq_lun = msg->rsp[0] & 3;
|
||||
daddr->rs_lun = msg->rsp[2] & 3;
|
||||
recv_msg->msg.netfn = msg->rsp[0] >> 2;
|
||||
recv_msg->msg.cmd = msg->rsp[3];
|
||||
memcpy(recv_msg->msg_data, &msg->rsp[4], msg->rsp_size - 4);
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
recv_msg->msg.data_len = msg->rsp_size - 4;
|
||||
deliver_local_response(intf, recv_msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_lan_get_msg_rsp(struct ipmi_smi *intf,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
|
@ -4219,18 +4435,40 @@ static int handle_bmc_rsp(struct ipmi_smi *intf,
|
|||
static int handle_one_recv_msg(struct ipmi_smi *intf,
|
||||
struct ipmi_smi_msg *msg)
|
||||
{
|
||||
int requeue;
|
||||
int requeue = 0;
|
||||
int chan;
|
||||
unsigned char cc;
|
||||
bool is_cmd = !((msg->rsp[0] >> 2) & 1);
|
||||
|
||||
pr_debug("Recv: %*ph\n", msg->rsp_size, msg->rsp);
|
||||
|
||||
if ((msg->data_size >= 2)
|
||||
if (msg->rsp_size < 2) {
|
||||
/* Message is too small to be correct. */
|
||||
dev_warn(intf->si_dev,
|
||||
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
|
||||
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
|
||||
|
||||
return_unspecified:
|
||||
/* Generate an error response for the message. */
|
||||
msg->rsp[0] = msg->data[0] | (1 << 2);
|
||||
msg->rsp[1] = msg->data[1];
|
||||
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
|
||||
msg->rsp_size = 3;
|
||||
} else if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
/* commands must have at least 3 bytes, responses 4. */
|
||||
if (is_cmd && (msg->rsp_size < 3)) {
|
||||
ipmi_inc_stat(intf, invalid_commands);
|
||||
goto out;
|
||||
}
|
||||
if (!is_cmd && (msg->rsp_size < 4))
|
||||
goto return_unspecified;
|
||||
} else if ((msg->data_size >= 2)
|
||||
&& (msg->data[0] == (IPMI_NETFN_APP_REQUEST << 2))
|
||||
&& (msg->data[1] == IPMI_SEND_MSG_CMD)
|
||||
&& (msg->user_data == NULL)) {
|
||||
|
||||
if (intf->in_shutdown)
|
||||
goto free_msg;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* This is the local response to a command send, start
|
||||
|
@ -4265,21 +4503,6 @@ static int handle_one_recv_msg(struct ipmi_smi *intf,
|
|||
} else
|
||||
/* The message was sent, start the timer. */
|
||||
intf_start_seq_timer(intf, msg->msgid);
|
||||
free_msg:
|
||||
requeue = 0;
|
||||
goto out;
|
||||
|
||||
} else if (msg->rsp_size < 2) {
|
||||
/* Message is too small to be correct. */
|
||||
dev_warn(intf->si_dev,
|
||||
"BMC returned too small a message for netfn %x cmd %x, got %d bytes\n",
|
||||
(msg->data[0] >> 2) | 1, msg->data[1], msg->rsp_size);
|
||||
|
||||
/* Generate an error response for the message. */
|
||||
msg->rsp[0] = msg->data[0] | (1 << 2);
|
||||
msg->rsp[1] = msg->data[1];
|
||||
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
|
||||
msg->rsp_size = 3;
|
||||
} else if (((msg->rsp[0] >> 2) != ((msg->data[0] >> 2) | 1))
|
||||
|| (msg->rsp[1] != msg->data[1])) {
|
||||
/*
|
||||
|
@ -4291,39 +4514,46 @@ free_msg:
|
|||
(msg->data[0] >> 2) | 1, msg->data[1],
|
||||
msg->rsp[0] >> 2, msg->rsp[1]);
|
||||
|
||||
/* Generate an error response for the message. */
|
||||
msg->rsp[0] = msg->data[0] | (1 << 2);
|
||||
msg->rsp[1] = msg->data[1];
|
||||
msg->rsp[2] = IPMI_ERR_UNSPECIFIED;
|
||||
msg->rsp_size = 3;
|
||||
goto return_unspecified;
|
||||
}
|
||||
|
||||
if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
|
||||
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
|
||||
&& (msg->user_data != NULL)) {
|
||||
if (msg->type == IPMI_SMI_MSG_TYPE_IPMB_DIRECT) {
|
||||
if ((msg->data[0] >> 2) & 1) {
|
||||
/* It's a response to a sent response. */
|
||||
chan = 0;
|
||||
cc = msg->rsp[4];
|
||||
goto process_response_response;
|
||||
}
|
||||
if (is_cmd)
|
||||
requeue = handle_ipmb_direct_rcv_cmd(intf, msg);
|
||||
else
|
||||
requeue = handle_ipmb_direct_rcv_rsp(intf, msg);
|
||||
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
|
||||
&& (msg->rsp[1] == IPMI_SEND_MSG_CMD)
|
||||
&& (msg->user_data != NULL)) {
|
||||
/*
|
||||
* It's a response to a response we sent. For this we
|
||||
* deliver a send message response to the user.
|
||||
*/
|
||||
struct ipmi_recv_msg *recv_msg = msg->user_data;
|
||||
|
||||
requeue = 0;
|
||||
if (msg->rsp_size < 2)
|
||||
/* Message is too small to be correct. */
|
||||
goto out;
|
||||
struct ipmi_recv_msg *recv_msg;
|
||||
|
||||
chan = msg->data[2] & 0x0f;
|
||||
if (chan >= IPMI_MAX_CHANNELS)
|
||||
/* Invalid channel number */
|
||||
goto out;
|
||||
cc = msg->rsp[2];
|
||||
|
||||
process_response_response:
|
||||
recv_msg = msg->user_data;
|
||||
|
||||
requeue = 0;
|
||||
if (!recv_msg)
|
||||
goto out;
|
||||
|
||||
recv_msg->recv_type = IPMI_RESPONSE_RESPONSE_TYPE;
|
||||
recv_msg->msg.data = recv_msg->msg_data;
|
||||
recv_msg->msg_data[0] = cc;
|
||||
recv_msg->msg.data_len = 1;
|
||||
recv_msg->msg_data[0] = msg->rsp[2];
|
||||
deliver_local_response(intf, recv_msg);
|
||||
} else if ((msg->rsp[0] == ((IPMI_NETFN_APP_REQUEST|1) << 2))
|
||||
&& (msg->rsp[1] == IPMI_GET_MSG_CMD)) {
|
||||
|
@ -4789,7 +5019,9 @@ static atomic_t recv_msg_inuse_count = ATOMIC_INIT(0);
|
|||
static void free_smi_msg(struct ipmi_smi_msg *msg)
|
||||
{
|
||||
atomic_dec(&smi_msg_inuse_count);
|
||||
kfree(msg);
|
||||
/* Try to keep as much stuff out of the panic path as possible. */
|
||||
if (!oops_in_progress)
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
struct ipmi_smi_msg *ipmi_alloc_smi_msg(void)
|
||||
|
@ -4808,7 +5040,9 @@ EXPORT_SYMBOL(ipmi_alloc_smi_msg);
|
|||
static void free_recv_msg(struct ipmi_recv_msg *msg)
|
||||
{
|
||||
atomic_dec(&recv_msg_inuse_count);
|
||||
kfree(msg);
|
||||
/* Try to keep as much stuff out of the panic path as possible. */
|
||||
if (!oops_in_progress)
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
|
||||
|
@ -4826,7 +5060,7 @@ static struct ipmi_recv_msg *ipmi_alloc_recv_msg(void)
|
|||
|
||||
void ipmi_free_recv_msg(struct ipmi_recv_msg *msg)
|
||||
{
|
||||
if (msg->user)
|
||||
if (msg->user && !oops_in_progress)
|
||||
kref_put(&msg->user->refcount, free_user);
|
||||
msg->done(msg);
|
||||
}
|
||||
|
|
|
@ -1603,7 +1603,7 @@ static ssize_t name##_show(struct device *dev, \
|
|||
{ \
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev); \
|
||||
\
|
||||
return snprintf(buf, 10, "%u\n", smi_get_stat(smi_info, name)); \
|
||||
return sysfs_emit(buf, "%u\n", smi_get_stat(smi_info, name)); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(name)
|
||||
|
||||
|
@ -1613,7 +1613,7 @@ static ssize_t type_show(struct device *dev,
|
|||
{
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, 10, "%s\n", si_to_str[smi_info->io.si_type]);
|
||||
return sysfs_emit(buf, "%s\n", si_to_str[smi_info->io.si_type]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(type);
|
||||
|
||||
|
@ -1624,7 +1624,7 @@ static ssize_t interrupts_enabled_show(struct device *dev,
|
|||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
int enabled = smi_info->io.irq && !smi_info->interrupt_disabled;
|
||||
|
||||
return snprintf(buf, 10, "%d\n", enabled);
|
||||
return sysfs_emit(buf, "%d\n", enabled);
|
||||
}
|
||||
static DEVICE_ATTR_RO(interrupts_enabled);
|
||||
|
||||
|
@ -1646,7 +1646,7 @@ static ssize_t params_show(struct device *dev,
|
|||
{
|
||||
struct smi_info *smi_info = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, 200,
|
||||
return sysfs_emit(buf,
|
||||
"%s,%s,0x%lx,rsp=%d,rsi=%d,rsh=%d,irq=%d,ipmb=%d\n",
|
||||
si_to_str[smi_info->io.si_type],
|
||||
addr_space_to_str[smi_info->io.addr_space],
|
||||
|
|
|
@ -1190,7 +1190,7 @@ static ssize_t ipmi_##name##_show(struct device *dev, \
|
|||
{ \
|
||||
struct ssif_info *ssif_info = dev_get_drvdata(dev); \
|
||||
\
|
||||
return snprintf(buf, 10, "%u\n", ssif_get_stat(ssif_info, name));\
|
||||
return sysfs_emit(buf, "%u\n", ssif_get_stat(ssif_info, name));\
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, ipmi_##name##_show, NULL)
|
||||
|
||||
|
@ -1198,7 +1198,7 @@ static ssize_t ipmi_type_show(struct device *dev,
|
|||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, 10, "ssif\n");
|
||||
return sysfs_emit(buf, "ssif\n");
|
||||
}
|
||||
static DEVICE_ATTR(type, S_IRUGO, ipmi_type_show, NULL);
|
||||
|
||||
|
|
|
@ -342,13 +342,17 @@ static atomic_t msg_tofree = ATOMIC_INIT(0);
|
|||
static DECLARE_COMPLETION(msg_wait);
|
||||
static void msg_free_smi(struct ipmi_smi_msg *msg)
|
||||
{
|
||||
if (atomic_dec_and_test(&msg_tofree))
|
||||
complete(&msg_wait);
|
||||
if (atomic_dec_and_test(&msg_tofree)) {
|
||||
if (!oops_in_progress)
|
||||
complete(&msg_wait);
|
||||
}
|
||||
}
|
||||
static void msg_free_recv(struct ipmi_recv_msg *msg)
|
||||
{
|
||||
if (atomic_dec_and_test(&msg_tofree))
|
||||
complete(&msg_wait);
|
||||
if (atomic_dec_and_test(&msg_tofree)) {
|
||||
if (!oops_in_progress)
|
||||
complete(&msg_wait);
|
||||
}
|
||||
}
|
||||
static struct ipmi_smi_msg smi_msg = {
|
||||
.done = msg_free_smi
|
||||
|
@ -434,8 +438,10 @@ static int _ipmi_set_timeout(int do_heartbeat)
|
|||
rv = __ipmi_set_timeout(&smi_msg,
|
||||
&recv_msg,
|
||||
&send_heartbeat_now);
|
||||
if (rv)
|
||||
if (rv) {
|
||||
atomic_set(&msg_tofree, 0);
|
||||
return rv;
|
||||
}
|
||||
|
||||
wait_for_completion(&msg_wait);
|
||||
|
||||
|
@ -497,7 +503,7 @@ static void panic_halt_ipmi_heartbeat(void)
|
|||
msg.cmd = IPMI_WDOG_RESET_TIMER;
|
||||
msg.data = NULL;
|
||||
msg.data_len = 0;
|
||||
atomic_inc(&panic_done_count);
|
||||
atomic_add(2, &panic_done_count);
|
||||
rv = ipmi_request_supply_msgs(watchdog_user,
|
||||
(struct ipmi_addr *) &addr,
|
||||
0,
|
||||
|
@ -507,7 +513,7 @@ static void panic_halt_ipmi_heartbeat(void)
|
|||
&panic_halt_heartbeat_recv_msg,
|
||||
1);
|
||||
if (rv)
|
||||
atomic_dec(&panic_done_count);
|
||||
atomic_sub(2, &panic_done_count);
|
||||
}
|
||||
|
||||
static struct ipmi_smi_msg panic_halt_smi_msg = {
|
||||
|
@ -531,12 +537,12 @@ static void panic_halt_ipmi_set_timeout(void)
|
|||
/* Wait for the messages to be free. */
|
||||
while (atomic_read(&panic_done_count) != 0)
|
||||
ipmi_poll_interface(watchdog_user);
|
||||
atomic_inc(&panic_done_count);
|
||||
atomic_add(2, &panic_done_count);
|
||||
rv = __ipmi_set_timeout(&panic_halt_smi_msg,
|
||||
&panic_halt_recv_msg,
|
||||
&send_heartbeat_now);
|
||||
if (rv) {
|
||||
atomic_dec(&panic_done_count);
|
||||
atomic_sub(2, &panic_done_count);
|
||||
pr_warn("Unable to extend the watchdog timeout\n");
|
||||
} else {
|
||||
if (send_heartbeat_now)
|
||||
|
@ -580,6 +586,7 @@ restart:
|
|||
&recv_msg,
|
||||
1);
|
||||
if (rv) {
|
||||
atomic_set(&msg_tofree, 0);
|
||||
pr_warn("heartbeat send failure: %d\n", rv);
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -73,10 +73,12 @@ static int kcs_bmc_serio_add_device(struct kcs_bmc_device *kcs_bmc)
|
|||
struct serio *port;
|
||||
|
||||
priv = devm_kzalloc(kcs_bmc->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Use kzalloc() as the allocation is cleaned up with kfree() via serio_unregister_port() */
|
||||
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!(priv && port))
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->id.type = SERIO_8042;
|
||||
|
|
|
@ -335,4 +335,7 @@ extern int ipmi_get_smi_info(int if_num, struct ipmi_smi_info *data);
|
|||
|
||||
#define GET_DEVICE_ID_MAX_RETRY 5
|
||||
|
||||
/* Helper function for computing the IPMB checksum of some data. */
|
||||
unsigned char ipmb_checksum(unsigned char *data, int size);
|
||||
|
||||
#endif /* __LINUX_IPMI_H */
|
||||
|
|
|
@ -38,6 +38,59 @@ struct ipmi_smi;
|
|||
#define IPMI_WATCH_MASK_CHECK_WATCHDOG (1 << 1)
|
||||
#define IPMI_WATCH_MASK_CHECK_COMMANDS (1 << 2)
|
||||
|
||||
/*
|
||||
* SMI messages
|
||||
*
|
||||
* When communicating with an SMI, messages come in two formats:
|
||||
*
|
||||
* * Normal (to a BMC over a BMC interface)
|
||||
*
|
||||
* * IPMB (over a IPMB to another MC)
|
||||
*
|
||||
* When normal, commands are sent using the format defined by a
|
||||
* standard message over KCS (NetFn must be even):
|
||||
*
|
||||
* +-----------+-----+------+
|
||||
* | NetFn/LUN | Cmd | Data |
|
||||
* +-----------+-----+------+
|
||||
*
|
||||
* And responses, similarly, with an completion code added (NetFn must
|
||||
* be odd):
|
||||
*
|
||||
* +-----------+-----+------+------+
|
||||
* | NetFn/LUN | Cmd | CC | Data |
|
||||
* +-----------+-----+------+------+
|
||||
*
|
||||
* With normal messages, only commands are sent and only responses are
|
||||
* received.
|
||||
*
|
||||
* In IPMB mode, we are acting as an IPMB device. Commands will be in
|
||||
* the following format (NetFn must be even):
|
||||
*
|
||||
* +-------------+------+-------------+-----+------+
|
||||
* | NetFn/rsLUN | Addr | rqSeq/rqLUN | Cmd | Data |
|
||||
* +-------------+------+-------------+-----+------+
|
||||
*
|
||||
* Responses will using the following format:
|
||||
*
|
||||
* +-------------+------+-------------+-----+------+------+
|
||||
* | NetFn/rqLUN | Addr | rqSeq/rsLUN | Cmd | CC | Data |
|
||||
* +-------------+------+-------------+-----+------+------+
|
||||
*
|
||||
* This is similar to the format defined in the IPMB manual section
|
||||
* 2.11.1 with the checksums and the first address removed. Also, the
|
||||
* address is always the remote address.
|
||||
*
|
||||
* IPMB messages can be commands and responses in both directions.
|
||||
* Received commands are handled as received commands from the message
|
||||
* queue.
|
||||
*/
|
||||
|
||||
enum ipmi_smi_msg_type {
|
||||
IPMI_SMI_MSG_TYPE_NORMAL = 0,
|
||||
IPMI_SMI_MSG_TYPE_IPMB_DIRECT
|
||||
};
|
||||
|
||||
/*
|
||||
* Messages to/from the lower layer. The smi interface will take one
|
||||
* of these to send. After the send has occurred and a response has
|
||||
|
@ -54,6 +107,8 @@ struct ipmi_smi;
|
|||
struct ipmi_smi_msg {
|
||||
struct list_head link;
|
||||
|
||||
enum ipmi_smi_msg_type type;
|
||||
|
||||
long msgid;
|
||||
void *user_data;
|
||||
|
||||
|
@ -73,6 +128,10 @@ struct ipmi_smi_msg {
|
|||
struct ipmi_smi_handlers {
|
||||
struct module *owner;
|
||||
|
||||
/* Capabilities of the SMI. */
|
||||
#define IPMI_SMI_CAN_HANDLE_IPMB_DIRECT (1 << 0)
|
||||
unsigned int flags;
|
||||
|
||||
/*
|
||||
* The low-level interface cannot start sending messages to
|
||||
* the upper layer until this function is called. This may
|
||||
|
|
|
@ -80,6 +80,20 @@ struct ipmi_ipmb_addr {
|
|||
unsigned char lun;
|
||||
};
|
||||
|
||||
/*
|
||||
* Used for messages received directly from an IPMB that have not gone
|
||||
* through a MC. This is for systems that sit right on an IPMB so
|
||||
* they can receive commands and respond to them.
|
||||
*/
|
||||
#define IPMI_IPMB_DIRECT_ADDR_TYPE 0x81
|
||||
struct ipmi_ipmb_direct_addr {
|
||||
int addr_type;
|
||||
short channel;
|
||||
unsigned char slave_addr;
|
||||
unsigned char rs_lun;
|
||||
unsigned char rq_lun;
|
||||
};
|
||||
|
||||
/*
|
||||
* A LAN Address. This is an address to/from a LAN interface bridged
|
||||
* by the BMC, not an address actually out on the LAN.
|
||||
|
@ -158,7 +172,7 @@ struct kernel_ipmi_msg {
|
|||
* is used for the receive in-kernel interface and in the receive
|
||||
* IOCTL.
|
||||
*
|
||||
* The "IPMI_RESPONSE_RESPNOSE_TYPE" is a little strange sounding, but
|
||||
* The "IPMI_RESPONSE_RESPONSE_TYPE" is a little strange sounding, but
|
||||
* it allows you to get the message results when you send a response
|
||||
* message.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue