IPMI和BMC 通信的过程

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;
}


你可能感兴趣的:(Linux,源码分析)