static int smic_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) { u_char *cp, data; int i, state; /* First, start the message with the address. */ if (!smic_start_write(sc, req->ir_addr)) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: WRITE_START address: %02x\n", req->ir_addr); #endif if (req->ir_requestlen == 0) { /* Send the command as the last byte. */ if (!smic_write_last(sc, req->ir_command)) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", req->ir_command); #endif } else { /* Send the command. */ if (!smic_write_next(sc, req->ir_command)) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Wrote command: %02x\n", req->ir_command); #endif /* Send the payload. */ cp = req->ir_request; for (i = 0; i < req->ir_requestlen - 1; i++) { if (!smic_write_next(sc, *cp++)) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Wrote data: %02x\n", cp[-1]); #endif } if (!smic_write_last(sc, *cp)) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Write last data: %02x\n", *cp); #endif } /* Start the read phase by reading the NetFn/LUN. */ if (smic_start_read(sc, &data) != 1) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Read address: %02x\n", data); #endif if (data != IPMI_REPLY_ADDR(req->ir_addr)) { device_printf(sc->ipmi_dev, "SMIC: Reply address mismatch\n"); return (0); } /* Read the command. */ if (smic_read_byte(sc, &data) != 1) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Read command: %02x\n", data); #endif if (data != req->ir_command) { device_printf(sc->ipmi_dev, "SMIC: Command mismatch\n"); return (0); } /* Read the completion code. */ state = smic_read_byte(sc, &req->ir_compcode); if (state == 0) return (0); #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Read completion code: %02x\n", req->ir_compcode); #endif /* Finally, read the reply from the BMC. */ i = 0; while (state == 1) { state = smic_read_byte(sc, &data); if (state == 0) return (0); if (i < req->ir_replybuflen) { req->ir_reply[i] = data; #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: Read data: %02x\n", data); } else { device_printf(sc->ipmi_dev, "SMIC: Read short %02x byte %d\n", data, i + 1); #endif } i++; } /* Terminate the transfer. */ if (!smic_read_end(sc)) return (0); req->ir_replylen = i; #ifdef SMIC_DEBUG device_printf(sc->ipmi_dev, "SMIC: READ finished (%d bytes)\n", i); if (req->ir_replybuflen < i) #else if (req->ir_replybuflen < i && req->ir_replybuflen != 0) #endif device_printf(sc->ipmi_dev, "SMIC: Read short: %zd buffer, %d actual\n", req->ir_replybuflen, i); return (1); }
static int ssif_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) { u_char ssif_buf[SMBUS_DATA_SIZE]; device_t dev = sc->ipmi_dev; device_t smbus = sc->ipmi_ssif_smbus; u_char *cp, block, count, offset; size_t len; int error; /* Acquire the bus while we send the request. */ if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) return (0); /* * First, send out the request. Begin by filling out the first * packet which includes the NetFn/LUN and command. */ ssif_buf[0] = req->ir_addr; ssif_buf[1] = req->ir_command; if (req->ir_requestlen > 0) bcopy(req->ir_request, &ssif_buf[2], min(req->ir_requestlen, SMBUS_DATA_SIZE - 2)); /* Small requests are sent with a single command. */ if (req->ir_requestlen <= 30) { #ifdef SSIF_DEBUG dump_buffer(dev, "WRITE_SINGLE", ssif_buf, req->ir_requestlen + 2); #endif error = smbus_error(smbus_bwrite(smbus, sc->ipmi_ssif_smbus_address, SMBUS_WRITE_SINGLE, req->ir_requestlen + 2, ssif_buf)); if (error) { #ifdef SSIF_ERROR_DEBUG device_printf(dev, "SSIF: WRITE_SINGLE error %d\n", error); #endif goto fail; } } else { /* Longer requests are sent out in 32-byte messages. */ #ifdef SSIF_DEBUG dump_buffer(dev, "WRITE_START", ssif_buf, SMBUS_DATA_SIZE); #endif error = smbus_error(smbus_bwrite(smbus, sc->ipmi_ssif_smbus_address, SMBUS_WRITE_START, SMBUS_DATA_SIZE, ssif_buf)); if (error) { #ifdef SSIF_ERROR_DEBUG device_printf(dev, "SSIF: WRITE_START error %d\n", error); #endif goto fail; } len = req->ir_requestlen - (SMBUS_DATA_SIZE - 2); cp = req->ir_request + (SMBUS_DATA_SIZE - 2); while (len > 0) { #ifdef SSIF_DEBUG dump_buffer(dev, "WRITE_CONT", cp, min(len, SMBUS_DATA_SIZE)); #endif error = smbus_error(smbus_bwrite(smbus, sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, min(len, SMBUS_DATA_SIZE), cp)); if (error) { #ifdef SSIF_ERROR_DEBUG device_printf(dev, "SSIF: WRITE_CONT error %d\n", error); #endif goto fail; } cp += SMBUS_DATA_SIZE; len -= SMBUS_DATA_SIZE; } /* * The final WRITE_CONT transaction has to have a non-zero * length that is also not SMBUS_DATA_SIZE. If our last * WRITE_CONT transaction in the loop sent SMBUS_DATA_SIZE * bytes, then len will be 0, and we send an extra 0x00 byte * to terminate the transaction. */ if (len == 0) { char c = 0; #ifdef SSIF_DEBUG dump_buffer(dev, "WRITE_CONT", &c, 1); #endif error = smbus_error(smbus_bwrite(smbus, sc->ipmi_ssif_smbus_address, SMBUS_WRITE_CONT, 1, &c)); if (error) { #ifdef SSIF_ERROR_DEBUG device_printf(dev, "SSIF: WRITE_CONT error %d\n", error); #endif goto fail; } } } /* Release the bus. */ smbus_release_bus(smbus, dev); /* Give the BMC 100ms to chew on the request. */ pause("ssifwt", hz / 10); /* Try to read the first packet. */ read_start: if (smbus_request_bus(smbus, dev, SMB_WAIT) != 0) return (0); count = SMBUS_DATA_SIZE; error = smbus_error(smbus_bread(smbus, sc->ipmi_ssif_smbus_address, SMBUS_READ_START, &count, ssif_buf)); if (error == ENXIO || error == EBUSY) { smbus_release_bus(smbus, dev); #ifdef SSIF_DEBUG device_printf(dev, "SSIF: READ_START retry\n"); #endif /* Give the BMC another 10ms. */ pause("ssifwt", hz / 100); goto read_start; } if (error) { #ifdef SSIF_ERROR_DEBUG device_printf(dev, "SSIF: READ_START failed: %d\n", error); #endif goto fail; } #ifdef SSIF_DEBUG device_printf("SSIF: READ_START: ok\n"); #endif /* * If this is the first part of a multi-part read, then we need to * skip the first two bytes. */ if (count == SMBUS_DATA_SIZE && ssif_buf[0] == 0 && ssif_buf[1] == 1) offset = 2; else offset = 0; /* We had better get the reply header. */ if (count < 3) { device_printf(dev, "SSIF: Short reply packet\n"); goto fail; } /* Verify the NetFn/LUN. */ if (ssif_buf[offset] != IPMI_REPLY_ADDR(req->ir_addr)) { device_printf(dev, "SSIF: Reply address mismatch\n"); goto fail; } /* Verify the command. */ if (ssif_buf[offset + 1] != req->ir_command) { device_printf(dev, "SMIC: Command mismatch\n"); goto fail; } /* Read the completion code. */ req->ir_compcode = ssif_buf[offset + 2]; /* If this is a single read, just copy the data and return. */ if (offset == 0) { #ifdef SSIF_DEBUG dump_buffer(dev, "READ_SINGLE", ssif_buf, count); #endif len = count - 3; bcopy(&ssif_buf[3], req->ir_reply, min(req->ir_replybuflen, len)); goto done; } /* * This is the first part of a multi-read transaction, so copy * out the payload and start looping. */ #ifdef SSIF_DEBUG dump_buffer(dev, "READ_START", ssif_buf + 2, count - 2); #endif bcopy(&ssif_buf[5], req->ir_reply, min(req->ir_replybuflen, count - 5)); len = count - 5; block = 1; for (;;) { /* Read another packet via READ_CONT. */ count = SMBUS_DATA_SIZE; error = smbus_error(smbus_bread(smbus, sc->ipmi_ssif_smbus_address, SMBUS_READ_CONT, &count, ssif_buf)); if (error) { #ifdef SSIF_ERROR_DEBUG printf("SSIF: READ_CONT failed: %d\n", error); #endif goto fail; } #ifdef SSIF_DEBUG device_printf(dev, "SSIF: READ_CONT... ok\n"); #endif /* Verify the block number. 0xff marks the last block. */ if (ssif_buf[0] != 0xff && ssif_buf[0] != block) { device_printf(dev, "SSIF: Read wrong block %d %d\n", ssif_buf[0], block); goto fail; } if (ssif_buf[0] != 0xff && count < SMBUS_DATA_SIZE) { device_printf(dev, "SSIF: Read short middle block, length %d\n", count); goto fail; } #ifdef SSIF_DEBUG if (ssif_buf[0] == 0xff) dump_buffer(dev, "READ_END", ssif_buf + 1, count - 1); else dump_buffer(dev, "READ_CONT", ssif_buf + 1, count - 1); #endif if (len < req->ir_replybuflen) bcopy(&ssif_buf[1], &req->ir_reply[len], min(req->ir_replybuflen - len, count - 1)); len += count - 1; /* If this was the last block we are done. */ if (ssif_buf[0] != 0xff) break; block++; } done: /* Save the total length and return success. */ req->ir_replylen = len; smbus_release_bus(smbus, dev); return (1); fail: smbus_release_bus(smbus, dev); return (0); }
/* * Send a request message and collect the reply. Returns true if we * succeed. */ static int kcs_polled_request(struct ipmi_softc *sc, struct ipmi_request *req) { u_char *cp, data; int i, state; IPMI_IO_LOCK(sc); /* Send the request. */ if (!kcs_start_write(sc)) { device_printf(sc->ipmi_dev, "KCS: Failed to start write\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: WRITE_START... ok\n"); #endif if (!kcs_write_byte(sc, req->ir_addr)) { device_printf(sc->ipmi_dev, "KCS: Failed to write address\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Wrote address: %02x\n", req->ir_addr); #endif if (req->ir_requestlen == 0) { if (!kcs_write_last_byte(sc, req->ir_command)) { device_printf(sc->ipmi_dev, "KCS: Failed to write command\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n", req->ir_command); #endif } else { if (!kcs_write_byte(sc, req->ir_command)) { device_printf(sc->ipmi_dev, "KCS: Failed to write command\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Wrote command: %02x\n", req->ir_command); #endif cp = req->ir_request; for (i = 0; i < req->ir_requestlen - 1; i++) { if (!kcs_write_byte(sc, *cp++)) { device_printf(sc->ipmi_dev, "KCS: Failed to write data byte %d\n", i + 1); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Wrote data: %02x\n", cp[-1]); #endif } if (!kcs_write_last_byte(sc, *cp)) { device_printf(sc->ipmi_dev, "KCS: Failed to write last dta byte\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Wrote last data: %02x\n", *cp); #endif } /* Read the reply. First, read the NetFn/LUN. */ if (kcs_read_byte(sc, &data) != 1) { device_printf(sc->ipmi_dev, "KCS: Failed to read address\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Read address: %02x\n", data); #endif if (data != IPMI_REPLY_ADDR(req->ir_addr)) { device_printf(sc->ipmi_dev, "KCS: Reply address mismatch\n"); goto fail; } /* Next we read the command. */ if (kcs_read_byte(sc, &data) != 1) { device_printf(sc->ipmi_dev, "KCS: Failed to read command\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Read command: %02x\n", data); #endif if (data != req->ir_command) { device_printf(sc->ipmi_dev, "KCS: Command mismatch\n"); goto fail; } /* Next we read the completion code. */ if (kcs_read_byte(sc, &req->ir_compcode) != 1) { device_printf(sc->ipmi_dev, "KCS: Failed to read completion code\n"); goto fail; } #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Read completion code: %02x\n", req->ir_compcode); #endif /* Finally, read the reply from the BMC. */ i = 0; for (;;) { state = kcs_read_byte(sc, &data); if (state == 0) { device_printf(sc->ipmi_dev, "KCS: Read failed on byte %d\n", i + 1); goto fail; } if (state == 2) break; if (i < req->ir_replybuflen) { req->ir_reply[i] = data; #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: Read data %02x\n", data); } else { device_printf(sc->ipmi_dev, "KCS: Read short %02x byte %d\n", data, i + 1); #endif } i++; } IPMI_IO_UNLOCK(sc); req->ir_replylen = i; #ifdef KCS_DEBUG device_printf(sc->ipmi_dev, "KCS: READ finished (%d bytes)\n", i); if (req->ir_replybuflen < i) #else if (req->ir_replybuflen < i && req->ir_replybuflen != 0) #endif device_printf(sc->ipmi_dev, "KCS: Read short: %zd buffer, %d actual\n", req->ir_replybuflen, i); return (1); fail: kcs_error(sc); IPMI_IO_UNLOCK(sc); return (0); }