ipmi_bt_sm.c
来自「linux 内核源代码」· C语言 代码 · 共 671 行 · 第 1/2 页
C
671 行
{ unsigned char i; /* length is "framing info", minimum = 4: NetFn, Seq, Cmd, cCode. Keep layout of first four bytes aligned with write_data[] */ bt->read_data[0] = BMC2HOST; bt->read_count = bt->read_data[0]; if (bt->read_count < 4 || bt->read_count >= IPMI_MAX_MSG_LENGTH) { if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "BT: bad raw rsp len=%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 internally for length byte */ if (bt_debug & BT_DEBUG_MSG) { int max = bt->read_count; printk(KERN_WARNING "BT: got %d bytes seq=0x%02X", max, bt->read_data[2]); if (max > 16) max = 16; for (i = 0; i < max; i++) printk (" %02x", bt->read_data[i]); printk ("%s\n", bt->read_count == max ? "" : " ..."); } /* per the spec, the (NetFn[1], Seq[2], Cmd[3]) tuples must match */ if ((bt->read_data[3] == bt->write_data[3]) && (bt->read_data[2] == bt->write_data[2]) && ((bt->read_data[1] & 0xF8) == (bt->write_data[1] & 0xF8))) return 1; if (bt_debug & BT_DEBUG_MSG) printk(KERN_WARNING "IPMI BT: bad packet: " "want 0x(%02X, %02X, %02X) got (%02X, %02X, %02X)\n", bt->write_data[1] | 0x04, bt->write_data[2], bt->write_data[3], bt->read_data[1], bt->read_data[2], bt->read_data[3]); return 0;}/* Restart if retries are left, or return an error completion code */static enum si_sm_result error_recovery(struct si_sm_data *bt, unsigned char status, unsigned char cCode){ char *reason; bt->timeout = bt->BT_CAP_req2rsp; switch (cCode) { case IPMI_TIMEOUT_ERR: reason = "timeout"; break; default: reason = "internal error"; break; } printk(KERN_WARNING "IPMI BT: %s in %s %s ", /* open-ended line */ reason, STATE2TXT, STATUS2TXT); /* Per the IPMI spec, retries are based on the sequence number known only to this module, so manage a restart here. */ (bt->error_retries)++; if (bt->error_retries < bt->BT_CAP_retries) { printk("%d retries left\n", bt->BT_CAP_retries - bt->error_retries); bt->state = BT_STATE_RESTART; return SI_SM_CALL_WITHOUT_DELAY; } printk("failed %d retries, sending error response\n", bt->BT_CAP_retries); if (!bt->nonzero_status) printk(KERN_ERR "IPMI BT: stuck, try power cycle\n"); /* this is most likely during insmod */ else if (bt->seq <= (unsigned char)(bt->BT_CAP_retries & 0xFF)) { printk(KERN_WARNING "IPMI: BT reset (takes 5 secs)\n"); bt->state = BT_STATE_RESET1; return SI_SM_CALL_WITHOUT_DELAY; } /* Concoct a useful error message, set up the next state, and be done with this sequence. */ bt->state = BT_STATE_IDLE; switch (cCode) { case IPMI_TIMEOUT_ERR: if (status & BT_B_BUSY) { cCode = IPMI_NODE_BUSY_ERR; bt->state = BT_STATE_LONG_BUSY; } break; default: break; } force_result(bt, cCode); return SI_SM_TRANSACTION_COMPLETE;}/* Check status and (usually) take action and change this state machine. */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; if ((bt_debug & BT_DEBUG_STATES) && (bt->state != last_printed)) { printk(KERN_WARNING "BT: %s %s TO=%ld - %ld \n", STATE2TXT, STATUS2TXT, bt->timeout, time); last_printed = bt->state; } /* Commands that time out may still (eventually) provide a response. This stale response will get in the way of a new response so remove it if possible (hopefully during IDLE). Even if it comes up later it will be rejected by its (now-forgotten) seq number. */ if ((bt->state < BT_STATE_WRITE_BYTES) && (status & BT_B2H_ATN)) { drain_BMC2HOST(bt); BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); } if ((bt->state != BT_STATE_IDLE) && (bt->state < BT_STATE_PRINTME)) { /* check timeout */ bt->timeout -= time; if ((bt->timeout < 0) && (bt->state < BT_STATE_RESET1)) return error_recovery(bt, status, IPMI_TIMEOUT_ERR); } switch (bt->state) { /* Idle state first checks for asynchronous messages from another channel, then does some opportunistic housekeeping. */ case BT_STATE_IDLE: if (status & BT_SMS_ATN) { BT_CONTROL(BT_SMS_ATN); /* clear it */ return SI_SM_ATTN; } if (status & BT_H_BUSY) /* clear a leftover H_BUSY */ BT_CONTROL(BT_H_BUSY); /* Read BT capabilities if it hasn't been done yet */ if (!bt->BT_CAP_outreqs) BT_STATE_CHANGE(BT_STATE_CAPABILITIES_BEGIN, SI_SM_CALL_WITHOUT_DELAY); bt->timeout = bt->BT_CAP_req2rsp; BT_SI_SM_RETURN(SI_SM_IDLE); 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); case BT_STATE_WRITE_BYTES: if (status & BT_H_BUSY) BT_CONTROL(BT_H_BUSY); /* clear */ BT_CONTROL(BT_CLR_WR_PTR); write_all_bytes(bt); BT_CONTROL(BT_H2B_ATN); /* can clear too fast to catch */ BT_STATE_CHANGE(BT_STATE_WRITE_CONSUME, SI_SM_CALL_WITHOUT_DELAY); case BT_STATE_WRITE_CONSUME: if (status & (BT_B_BUSY | BT_H2B_ATN)) BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); BT_STATE_CHANGE(BT_STATE_READ_WAIT, SI_SM_CALL_WITHOUT_DELAY); /* Spinning hard can suppress B2H_ATN and force a timeout */ case BT_STATE_READ_WAIT: if (!(status & BT_B2H_ATN)) BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); BT_CONTROL(BT_H_BUSY); /* set */ /* Uncached, ordered writes should just proceeed serially but some BMCs don't clear B2H_ATN with one hit. Fast-path a workaround without too much penalty to the general case. */ BT_CONTROL(BT_B2H_ATN); /* clear it to ACK the BMC */ BT_STATE_CHANGE(BT_STATE_CLEAR_B2H, SI_SM_CALL_WITHOUT_DELAY); case BT_STATE_CLEAR_B2H: if (status & BT_B2H_ATN) { /* keep hitting it */ BT_CONTROL(BT_B2H_ATN); BT_SI_SM_RETURN(SI_SM_CALL_WITH_DELAY); } BT_STATE_CHANGE(BT_STATE_READ_BYTES, SI_SM_CALL_WITHOUT_DELAY); case BT_STATE_READ_BYTES: if (!(status & BT_H_BUSY)) /* check in case of retry */ BT_CONTROL(BT_H_BUSY); BT_CONTROL(BT_CLR_RD_PTR); /* start of BMC2HOST buffer */ i = read_all_bytes(bt); /* true == packet seq match */ BT_CONTROL(BT_H_BUSY); /* NOW clear */ if (!i) /* Not my message */ BT_STATE_CHANGE(BT_STATE_READ_WAIT, SI_SM_CALL_WITHOUT_DELAY); bt->state = bt->complete; return bt->state == BT_STATE_IDLE ? /* where to next? */ SI_SM_TRANSACTION_COMPLETE : /* normal */ SI_SM_CALL_WITHOUT_DELAY; /* Startup magic */ case BT_STATE_LONG_BUSY: /* For example: after FW update */ if (!(status & BT_B_BUSY)) { reset_flags(bt); /* next state is now IDLE */ bt_init_data(bt, bt->io); } return SI_SM_CALL_WITH_DELAY; /* No repeat printing */ case BT_STATE_RESET1: reset_flags(bt); drain_BMC2HOST(bt); BT_STATE_CHANGE(BT_STATE_RESET2, SI_SM_CALL_WITH_DELAY); 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->timeout = BT_RESET_DELAY * 1000000; BT_STATE_CHANGE(BT_STATE_RESET3, SI_SM_CALL_WITH_DELAY); case BT_STATE_RESET3: /* Hold off everything for a bit */ if (bt->timeout > 0) return SI_SM_CALL_WITH_DELAY; drain_BMC2HOST(bt); BT_STATE_CHANGE(BT_STATE_RESTART, SI_SM_CALL_WITH_DELAY); case BT_STATE_RESTART: /* don't reset retries or seq! */ bt->read_count = 0; bt->nonzero_status = 0; bt->timeout = bt->BT_CAP_req2rsp; BT_STATE_CHANGE(BT_STATE_XACTION_START, SI_SM_CALL_WITH_DELAY); /* Get BT Capabilities, using timing of upper level state machine. Set outreqs to prevent infinite loop on timeout. */ case BT_STATE_CAPABILITIES_BEGIN: bt->BT_CAP_outreqs = 1; { unsigned char GetBT_CAP[] = { 0x18, 0x36 }; bt->state = BT_STATE_IDLE; bt_start_transaction(bt, GetBT_CAP, sizeof(GetBT_CAP)); } bt->complete = BT_STATE_CAPABILITIES_END; BT_STATE_CHANGE(BT_STATE_XACTION_START, SI_SM_CALL_WITH_DELAY); case BT_STATE_CAPABILITIES_END: i = bt_get_result(bt, BT_CAP, sizeof(BT_CAP)); bt_init_data(bt, bt->io); if ((i == 8) && !BT_CAP[2]) { bt->BT_CAP_outreqs = BT_CAP[3]; bt->BT_CAP_req2rsp = BT_CAP[6] * 1000000; bt->BT_CAP_retries = BT_CAP[7]; } else printk(KERN_WARNING "IPMI BT: using default values\n"); if (!bt->BT_CAP_outreqs) bt->BT_CAP_outreqs = 1; printk(KERN_WARNING "IPMI BT: req2rsp=%ld secs retries=%d\n", bt->BT_CAP_req2rsp / 1000000L, bt->BT_CAP_retries); bt->timeout = bt->BT_CAP_req2rsp; return SI_SM_CALL_WITHOUT_DELAY; default: /* should never occur */ return error_recovery(bt, status, IPMI_ERR_UNSPECIFIED); } 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 + =
减小字号Ctrl + -
显示快捷键?