void BaseWedgeI2CBus::moduleRead(unsigned int module, uint8_t address, int offset, int len, uint8_t *buf) { CHECK_LE(offset, 255); selectQsfp(module); CHECK_NE(selectedPort_, NO_PORT); // CP2112 uses addresses in the on-the-wire format, while we generally // pass them around in Linux standard format. address <<= 1; // Note that we don't use the writeRead() command, since this // locks up the CP2112 chip if it times out. We perform a separate write, // followed by a read. This releases the I2C bus between operations, but // that's okay since there aren't any other master devices on the bus. // Also note that we can't read more than 128 bytes at a time. dev_.writeByte(address, offset); if (len > 128) { dev_.read(address, MutableByteRange(buf, 128)); dev_.writeByte(address, offset + 128); dev_.read(address, MutableByteRange(buf + 128, len - 128)); } else { dev_.read(address, MutableByteRange(buf, len)); } unselectQsfp(); }
void WedgeI2CBus::verifyBus(bool autoReset) { // Make sure the I2C bus is functioning, and reset the CP2112 device if not. // // Immediately after wedge is powered on, the CP2112 device appears to be in // a state where all I2C operations time out. (It doesn't actually signal // anything on the I2C bus.) Resetting the chip fixes it. // // We have an AT24C64B EEPROM attached at address 0xa2 which we should always // be able to read from successfully. uint8_t tmpBuf[8]; try { dev_->read(ADDR_EEPROM, MutableByteRange(tmpBuf, sizeof(tmpBuf))); } catch (const UsbError& ex) { // The read failed. // Reset the device, and then confirm that we can read this time. // // Note: This can be slightly dangerous for other reasons. We have seen // some 3M QSFP optics that can lock up and hold SDA low forever. If we // reset the CP2112 chip with SDA held low it can't reset successfully, // and disappears from the USB bus completely. (It never re-enumerates.) // If the bus is in this state resetting the chip will make it completely // inaccessible. if (autoReset) { try { dev_->resetDevice(); } catch (const UsbDeviceResetError& ex2) { } } else { VLOG(1) << "initial read from CP2112 failed; I2C bus appears hung"; throw; } } }
void BaseWedgeI2CBus::moduleWrite(unsigned int module, uint8_t address, int offset, int len, uint8_t *buf) { selectQsfp(module); CHECK_NE(selectedPort_, NO_PORT); // CP2112 uses addresses in the on-the-wire format, while we generally // pass them around in Linux standard format. address <<= 1; // The CP2112 can only write 61 bytes at a time, and we burn one for // the offset CHECK_LE(len, 60); // XXX: surely there's an easier way to do this? uint8_t output[61]; // USB buffer size; output[0] = offset; memcpy(output + 1, buf, len); dev_.write(address, MutableByteRange(output, len + 1)); unselectQsfp(); }