package de.uniluebeck.itm.wsn.drivers.jennic; import com.google.inject.Inject; import de.uniluebeck.itm.wsn.drivers.core.ChipType; import de.uniluebeck.itm.wsn.drivers.core.Connection; import de.uniluebeck.itm.wsn.drivers.core.exception.*; import de.uniluebeck.itm.wsn.drivers.isense.exception.FlashTypeReadFailedException; import de.uniluebeck.itm.wsn.drivers.jennic.exception.SectorEraseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import static de.uniluebeck.itm.util.StringUtils.toHexString; public class JennicHelper { private static final Logger log = LoggerFactory.getLogger(JennicHelper.class); private static final int TIMEOUT_WAIT_DATA_AVAILABLE_MILLIS = 2500; private final Connection connection; @Inject public JennicHelper(Connection connection) { this.connection = connection; } public FlashType getFlashType() throws Exception { // Send flash type read request sendBootloaderMessage(Messages.flashTypeReadRequestMessage()); // Read flash type read response byte[] response = receiveBootloaderReply(Messages.FLASH_TYPE_READ_RESPONSE); // Throw error if reading failed if (response[1] != 0x00) { log.error(String.format("Failed to read flash type: Response should be 0x00, yet it is: 0x%02x", response[1] ) ); throw new FlashTypeReadFailedException(); } // Determine flash type if (response[2] == (byte) 0xBF && response[3] == (byte) 0x49) { return FlashType.SST25VF010A; } else if (response[2] == (byte) 0x10 && response[3] == (byte) 0x10) { return FlashType.STM25P10A; } else if (response[2] == (byte) 0x1F && response[3] == (byte) 0x60) { return FlashType.Atmel25F512; } else if (response[2] == (byte) 0x12 && response[3] == (byte) 0x12) { return FlashType.STM25P40; } else { return FlashType.Unknown; } } void enableFlashErase() throws Exception { // log.debug("Setting FLASH status register to zero"); sendBootloaderMessage(Messages.statusRegisterWriteMessage((byte) 0x00)); // see // AN // - // 1007 byte[] response = receiveBootloaderReply(Messages.WRITE_SR_RESPONSE); if (response[1] != 0x0) { log.error(String.format("Failed to write status register.")); throw new FlashEraseFailedException(); } } public void eraseFlash(Sector sector) throws Exception { enableFlashErase(); log.trace("Erasing sector " + sector); sendBootloaderMessage(Messages.sectorEraseRequestMessage(sector)); byte[] response = receiveBootloaderReply(Messages.SECTOR_ERASE_RESPONSE); if (response[1] != 0x0) { log.error(String.format("Failed to erase flash sector.")); throw new SectorEraseException(sector); } } public void configureFlash(ChipType chipType) throws Exception { log.trace("Configuring flash"); // only new chips need to be configured if (chipType != ChipType.JN5121) { // determine flash type FlashType flashType = getFlashType(); // send flash configure request sendBootloaderMessage(Messages.flashConfigureRequestMessage(flashType)); // read flash configure response byte[] response = receiveBootloaderReply(Messages.FLASH_CONFIGURE_RESPONSE); // throw error if configuration failed if (response[1] != 0x00) { if (log.isErrorEnabled()) { log.error("Failed to configure flash ROM: response should be 0x00, is: ", toHexString(response[1])); } throw new FlashConfigurationFailedException(); } } log.trace("Done. Flash is configured."); } public void sendBootloaderMessage(byte[] message) throws IOException { if (log.isTraceEnabled()) { log.trace("Sending bootloader request: {}", toHexString(message)); } // allocate buffer for length + message + checksum byte[] data = new byte[message.length + 2]; // prepend length (of message + checksum) data[0] = (byte) (message.length + 1); // copy message into the buffer System.arraycopy(message, 0, data, 1, message.length); // calculate and append checksum data[data.length - 1] = Messages.calculateChecksum(data, 0, data.length - 1); // send message final OutputStream outStream = connection.getOutputStream(); outStream.write(data); outStream.flush(); } public byte[] receiveBootloaderReply(int expectedType) throws TimeoutException, UnexpectedResponseException, InvalidChecksumException, IOException, NullPointerException { final InputStream inputStream = connection.getInputStream(); connection.waitDataAvailable(TIMEOUT_WAIT_DATA_AVAILABLE_MILLIS); int bootLoaderReplyLength = inputStream.read(); byte[] bootLoaderReply = new byte[bootLoaderReplyLength - 1]; // read rest of the reply (except of the checksum) for (int i = 0; i < (bootLoaderReplyLength - 1); ++i) { connection.waitDataAvailable(TIMEOUT_WAIT_DATA_AVAILABLE_MILLIS); bootLoaderReply[i] = (byte) inputStream.read(); } if (log.isTraceEnabled()) { log.trace("Received bootloader reply: {}", toHexString(bootLoaderReply)); } // read checksum connection.waitDataAvailable(TIMEOUT_WAIT_DATA_AVAILABLE_MILLIS); byte checksumReceived = (byte) inputStream.read(); if (log.isTraceEnabled()) { log.trace("Received bootloader reply checksum: {}", toHexString(checksumReceived)); } // concatenate length field and actual reply for checksum calculation byte[] fullBootLoaderReply = new byte[bootLoaderReply.length + 1]; fullBootLoaderReply[0] = (byte) bootLoaderReplyLength; System.arraycopy(bootLoaderReply, 0, fullBootLoaderReply, 1, bootLoaderReply.length); // throw exception if checksums differ byte checksumCalculated = Messages.calculateChecksum(fullBootLoaderReply); if (checksumCalculated != checksumReceived) { String msg = "Bootloader reply checksum mismatch (received " + toHexString(checksumReceived) + ", calculated" + toHexString(checksumCalculated) + ")"; throw new InvalidChecksumException(msg); } // check if the response type is unexpected if (bootLoaderReply[0] != expectedType) { throw new UnexpectedResponseException(expectedType, (int) bootLoaderReply[0]); } return bootLoaderReply; } public boolean waitForConnection() { try { // send flash read request (in fact, this could be any message to which the device is supposed to respond) sendBootloaderMessage(Messages.flashReadRequestMessage(0x24, 0x20)); receiveBootloaderReply(Messages.FLASH_READ_RESPONSE); log.trace("Device connection established"); return true; } catch (TimeoutException e) { try { connection.clear(); } catch (IOException e1) { log.error("Exception while cleaning the stream.", e1); } log.trace("waitForConnection timed out!"); return false; } catch (Exception e) { throw new RuntimeException(e); } } public byte[] readFlash(int address, int len) throws Exception { // Send flash program request sendBootloaderMessage(Messages.flashReadRequestMessage(address, len)); // Read flash program response byte[] response = receiveBootloaderReply(Messages.FLASH_READ_RESPONSE); // Remove type and success octet byte[] data = new byte[response.length - 2]; System.arraycopy(response, 2, data, 0, response.length - 2); // Return data return data; } public void writeFlash(int address, byte[] data) throws IOException, NullPointerException, TimeoutException, UnexpectedResponseException, InvalidChecksumException, FlashProgramFailedException { // Send flash program request // log.debug("Sending program request for address " + address + " with " + data.length + " bytes"); sendBootloaderMessage(Messages.flashProgramRequestMessage(address, data)); // Read flash program response byte[] response = receiveBootloaderReply(Messages.FLASH_PROGRAM_RESPONSE); // Throw error if writing failed if (response[1] != 0x0) { log.error( String.format("Failed to write to flash: Response should be 0x00, yet it is: 0x%02x", response[1]) ); throw new FlashProgramFailedException(); } } }