ipmi和bmc通讯可以从try_get_dev_id中看出
static int try_get_dev_id(struct smi_info *smi_info)
{
unsigned char msg[2];
unsigned char *resp;
unsigned long resp_len;
int rv = 0;
resp = kmalloc(IPMI_MAX_MSG_LENGTH, GFP_KERNEL);
if (!resp)
return -ENOMEM;
/*
* Do a Get Device ID command, since it comes back with some
* useful info.
*/
//这两个是要发送的命令
msg[0] = IPMI_NETFN_APP_REQUEST << 2;
msg[1] = IPMI_GET_DEVICE_ID_CMD;
//开始发送
smi_info->handlers->start_transaction(smi_info->si_sm, msg, 2);
//等待完成
rv = wait_for_msg_done(smi_info);
if (rv)
goto out;
//获取bmc发回的结果
resp_len = smi_info->handlers->get_result(smi_info->si_sm,
resp, IPMI_MAX_MSG_LENGTH);
//前面四步是和BMC通信的完成过程
/* Check and record info from the get device id, in case we need it. */
rv = ipmi_demangle_device_id(resp, resp_len, &smi_info->device_id);
out:
kfree(resp);
return rv;
}
smi_info->handlers有三种实现,这里以bt为例,bt是Block Transfer的意思
const struct si_sm_handlers bt_smi_handlers = {
.init_data = bt_init_data,
.start_transaction = bt_start_transaction,
.get_result = bt_get_result,
.event = bt_event,
.detect = bt_detect,
.cleanup = bt_cleanup,
.size = bt_size,
};
先看bt_start_transaction
static int bt_start_transaction(struct si_sm_data *bt,
unsigned char *data,
unsigned int size)
{
unsigned int i;
if (size < 2)
return IPMI_REQ_LEN_INVALID_ERR;
if (size > IPMI_MAX_MSG_LENGTH)
return IPMI_REQ_LEN_EXCEEDED_ERR;
if (bt->state == BT_STATE_LONG_BUSY)
return IPMI_NODE_BUSY_ERR;
if (bt->state != BT_STATE_IDLE)
return IPMI_NOT_IN_MY_STATE_ERR;
if (bt_debug & BT_DEBUG_MSG) {
printk(KERN_WARNING "BT: +++++++++++++++++ New command\n");
printk(KERN_WARNING "BT: NetFn/LUN CMD [%d data]:", size - 2);
for (i = 0; i < size; i ++)
printk(" %02x", data[i]);
printk("\n");
}
bt->write_data[0] = size + 1; /* all data plus seq byte */
bt->write_data[1] = *data; /* NetFn/LUN */
bt->write_data[2] = bt->seq++;
memcpy(bt->write_data + 3, data + 1, size - 1);
bt->write_count = size + 2;
bt->error_retries = 0;
bt->nonzero_status = 0;
bt->truncated = 0;
bt->state = BT_STATE_XACTION_START;
bt->timeout = bt->BT_CAP_req2rsp;
force_result(bt, IPMI_ERR_UNSPECIFIED);
return 0;
}
基本就是对si_sm_data *bt 赋值的过程。
static int wait_for_msg_done(struct smi_info *smi_info)
{
enum si_sm_result smi_result;
smi_result = smi_info->handlers->event(smi_info->si_sm, 0);
//看起来event会最多会被调用两次,如果第一次调用event返回的结果是SI_SM_CALL_WITH_DELAY/SI_SM_CALL_WITH_TICK_DELAY/SI_SM_CALL_WITHOUT_DELAY,则再次调用event.
for (;;) {
if (smi_result == SI_SM_CALL_WITH_DELAY ||
smi_result == SI_SM_CALL_WITH_TICK_DELAY) {
schedule_timeout_uninterruptible(1);
smi_result = smi_info->handlers->event(
smi_info->si_sm, jiffies_to_usecs(1));
} else if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
smi_result = smi_info->handlers->event(
smi_info->si_sm, 0);
} else
break;
}
if (smi_result == SI_SM_HOSED)
/*
* We couldn't get the state machine to run, so whatever's at
* the port is probably not an IPMI SMI interface.
*/
return -ENODEV;
return 0;
}
原来wait_for_msg_done 中是通过event 来启动传送和接受bmc发回的结果啊,在bt_event中主要是处理各种命令。注意在bt_start_transaction中发送的是BT_STATE_XACTION_START命令
static enum si_sm_result bt_event(struct si_sm_data *bt, long time)
{
unsigned char status, BT_CAP[8];
static enum bt_states last_printed = BT_STATE_PRINTME;
int i;
status = BT_STATUS;
bt->nonzero_status |= status;
case BT_STATE_XACTION_START:
if (status & (BT_B_BUSY | BT_H2B_ATN))
BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY);
if (BT_STATUS & BT_H_BUSY)
BT_CONTROL(BT_H_BUSY); /* force clear */
BT_STATE_CHANGE(BT_STATE_WRITE_BYTES,
SI_SM_CALL_WITHOUT_DELAY);
//这里的返回值就对应wait_for_msg_done 中第二次需要处理的返回值
return SI_SM_CALL_WITH_DELAY;
}
最终在bt_event中是通过BT_CONTROL来发送命令
#define BT_CONTROL(x) bt->io->outputb(bt->io, 0, x)
这里的io是在port_setup中赋值
static int port_setup(struct smi_info *info)
{
unsigned int addr = info->io.addr_data;
int idx;
if (!addr)
return -ENODEV;
info->io_cleanup = port_cleanup;
/*
* Figure out the actual inb/inw/inl/etc routine to use based
* upon the register size.
*/
switch (info->io.regsize) {
case 1:
info->io.inputb = port_inb;
info->io.outputb = port_outb;
break;
case 2:
info->io.inputb = port_inw;
info->io.outputb = port_outw;
break;
case 4:
info->io.inputb = port_inl;
info->io.outputb = port_outl;
break;
default:
dev_warn(info->dev, "Invalid register size: %d\n",
info->io.regsize);
return -EINVAL;
}
}
这里以port_outb为例
static void port_outb(const struct si_sm_io *io, unsigned int offset,
unsigned char b)
{
unsigned int addr = io->addr_data;
outb(b, addr + (offset * io->regspacing));
}
static inline void outb(u8 value, unsigned long addr)
{
writeb(value, PCI_IOBASE + addr);
}
原来就是直接写寄存器
最后通过bt_get_result 得到BMC返回的数值,可以看到主要是从si_sm_data *bt中的read_data的到其返回的值.
static int bt_get_result(struct si_sm_data *bt,
unsigned char *data,
unsigned int length)
{
int i, msg_len;
msg_len = bt->read_count - 2; /* account for length & seq */
if (msg_len < 3 || msg_len > IPMI_MAX_MSG_LENGTH) {
force_result(bt, IPMI_ERR_UNSPECIFIED);
msg_len = 3;
}
data[0] = bt->read_data[1];
data[1] = bt->read_data[3];
if (length < msg_len || bt->truncated) {
data[2] = IPMI_ERR_MSG_TRUNCATED;
msg_len = 3;
} else
memcpy(data + 2, bt->read_data + 4, msg_len - 2);
if (bt_debug & BT_DEBUG_MSG) {
printk(KERN_WARNING "BT: result %d bytes:", msg_len);
for (i = 0; i < msg_len; i++)
printk(" %02x", data[i]);
printk("\n");
}
return msg_len;
}