📄 ipmi_bt_sm.c
字号:
if (bt_debug & BT_DEBUG_MSG) { printk(KERN_WARNING "BT: write %d bytes seq=0x%02X", bt->write_count, bt->seq); for (i = 0; i < bt->write_count; i++) printk (" %02x", bt->write_data[i]); printk ("\n"); } for (i = 0; i < bt->write_count; i++) HOST2BMC(bt->write_data[i]);}static inline int read_all_bytes(struct si_sm_data *bt){ unsigned char i; bt->read_data[0] = BMC2HOST; bt->read_count = bt->read_data[0]; if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: read %d bytes:", bt->read_count); /* minimum: length, NetFn, Seq, Cmd, cCode == 5 total, or 4 more following the length byte. */ if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) { if (bt_debug & BT_DEBUG_MSG) printk("bad length %d\n", bt->read_count); bt->truncated = 1; return 1; /* let next XACTION START clean it up */ } for (i = 1; i <= bt->read_count; i++) bt->read_data[i] = BMC2HOST; bt->read_count++; /* account for the length byte */ if (bt_debug & BT_DEBUG_MSG) { for (i = 0; i < bt->read_count; i++) printk (" %02x", bt->read_data[i]); printk ("\n"); } if (bt->seq != bt->write_data[2]) /* idiot check */ printk(KERN_DEBUG "BT: internal error: sequence mismatch\n"); /* per the spec, the (NetFn, Seq, Cmd) tuples should match */ if ((bt->read_data[3] == bt->write_data[3]) && /* Cmd */ (bt->read_data[2] == bt->write_data[2]) && /* Sequence */ ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) return 1; if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: bad packet: " "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n", bt->write_data[1], bt->write_data[2], bt->write_data[3], bt->read_data[1], bt->read_data[2], bt->read_data[3]); return 0;}/* Modifies bt->state appropriately, need to get into the bt_event() switch */static void error_recovery(struct si_sm_data *bt, char *reason){ unsigned char status; char buf[40]; /* For getting status */ bt->timeout = BT_NORMAL_TIMEOUT; /* various places want to retry */ status = BT_STATUS; printk(KERN_DEBUG "BT: %s in %s %s\n", reason, STATE2TXT, STATUS2TXT(buf)); (bt->error_retries)++; if (bt->error_retries > BT_RETRY_LIMIT) { printk(KERN_DEBUG "retry limit (%d) exceeded\n", BT_RETRY_LIMIT); bt->state = BT_STATE_HOSED; if (!bt->nonzero_status) printk(KERN_ERR "IPMI: BT stuck, try power cycle\n"); else if (bt->error_retries <= BT_RETRY_LIMIT + 1) { printk(KERN_DEBUG "IPMI: BT reset (takes 5 secs)\n"); bt->state = BT_STATE_RESET1; } return; } /* Sometimes the BMC queues get in an "off-by-one" state...*/ if ((bt->state == BT_STATE_B2H_WAIT) && (status & BT_B2H_ATN)) { printk(KERN_DEBUG "retry B2H_WAIT\n"); return; } printk(KERN_DEBUG "restart command\n"); bt->state = BT_STATE_RESTART;}/* Check the status and (possibly) advance the BT state machine. The default return is SI_SM_CALL_WITH_DELAY. */static enum si_sm_result bt_event(struct si_sm_data *bt, long time){ unsigned char status; char buf[40]; /* For getting status */ int i; status = BT_STATUS; bt->nonzero_status |= status; if ((bt_debug & BT_DEBUG_STATES) && (bt->state != bt->last_state)) printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n", STATE2TXT, STATUS2TXT(buf), bt->timeout, time); bt->last_state = bt->state; if (bt->state == BT_STATE_HOSED) return SI_SM_HOSED; if (bt->state != BT_STATE_IDLE) { /* do timeout test */ bt->timeout -= time; if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) { error_recovery(bt, "timed out"); return SI_SM_CALL_WITHOUT_DELAY; } } switch (bt->state) { case BT_STATE_IDLE: /* check for asynchronous messages */ if (status & BT_SMS_ATN) { BT_CONTROL(BT_SMS_ATN); /* clear it */ return SI_SM_ATTN; } return SI_SM_IDLE; case BT_STATE_XACTION_START: if (status & BT_H_BUSY) { BT_CONTROL(BT_H_BUSY); break; } if (status & BT_B2H_ATN) break; bt->state = BT_STATE_WRITE_BYTES; return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ case BT_STATE_WRITE_BYTES: if (status & (BT_B_BUSY | BT_H2B_ATN)) break; BT_CONTROL(BT_CLR_WR_PTR); write_all_bytes(bt); BT_CONTROL(BT_H2B_ATN); /* clears too fast to catch? */ bt->state = BT_STATE_WRITE_CONSUME; return SI_SM_CALL_WITHOUT_DELAY; /* it MIGHT sail through */ case BT_STATE_WRITE_CONSUME: /* BMCs usually blow right thru here */ if (status & (BT_H2B_ATN | BT_B_BUSY)) break; bt->state = BT_STATE_B2H_WAIT; /* fall through with status */ /* Stay in BT_STATE_B2H_WAIT until a packet matches. However, spinning hard here, constantly reading status, seems to hold off the generation of B2H_ATN so ALWAYS return CALL_WITH_DELAY. */ case BT_STATE_B2H_WAIT: if (!(status & BT_B2H_ATN)) break; /* Assume ordered, uncached writes: no need to wait */ if (!(status & BT_H_BUSY)) BT_CONTROL(BT_H_BUSY); /* set */ BT_CONTROL(BT_B2H_ATN); /* clear it, ACK to the BMC */ BT_CONTROL(BT_CLR_RD_PTR); /* reset the queue */ i = read_all_bytes(bt); BT_CONTROL(BT_H_BUSY); /* clear */ if (!i) /* Try this state again */ break; bt->state = BT_STATE_READ_END; return SI_SM_CALL_WITHOUT_DELAY; /* for logging */ case BT_STATE_READ_END: /* I could wait on BT_H_BUSY to go clear for a truly clean exit. However, this is already done in XACTION_START and the (possible) extra loop/status/possible wait affects performance. So, as long as it works, just ignore H_BUSY */#ifdef MAKE_THIS_TRUE_IF_NECESSARY if (status & BT_H_BUSY) break;#endif bt->seq++; bt->state = BT_STATE_IDLE; return SI_SM_TRANSACTION_COMPLETE; case BT_STATE_RESET1: reset_flags(bt); bt->timeout = BT_RESET_DELAY; bt->state = BT_STATE_RESET2; break; case BT_STATE_RESET2: /* Send a soft reset */ BT_CONTROL(BT_CLR_WR_PTR); HOST2BMC(3); /* number of bytes following */ HOST2BMC(0x18); /* NetFn/LUN == Application, LUN 0 */ HOST2BMC(42); /* Sequence number */ HOST2BMC(3); /* Cmd == Soft reset */ BT_CONTROL(BT_H2B_ATN); bt->state = BT_STATE_RESET3; break; case BT_STATE_RESET3: if (bt->timeout > 0) return SI_SM_CALL_WITH_DELAY; bt->state = BT_STATE_RESTART; /* printk in debug modes */ break; case BT_STATE_RESTART: /* don't reset retries! */ reset_flags(bt); bt->write_data[2] = ++bt->seq; bt->read_count = 0; bt->nonzero_status = 0; bt->timeout = BT_NORMAL_TIMEOUT; bt->state = BT_STATE_XACTION_START; break; default: /* HOSED is supposed to be caught much earlier */ error_recovery(bt, "internal logic error"); break; } return SI_SM_CALL_WITH_DELAY;}static int bt_detect(struct si_sm_data *bt){ /* It's impossible for the BT status and interrupt registers to be all 1's, (assuming a properly functioning, self-initialized BMC) but that's what you get from reading a bogus address, so we test that first. The calling routine uses negative logic. */ if ((BT_STATUS == 0xFF) && (BT_INTMASK_R == 0xFF)) return 1; reset_flags(bt); return 0;}static void bt_cleanup(struct si_sm_data *bt){}static int bt_size(void){ return sizeof(struct si_sm_data);}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,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -