/*******************************************************************************
* Copyright (c) 2008, 2011 Thomas Holland (thomas@innot.de) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Thomas Holland - initial API and implementation
*******************************************************************************/
package de.innot.avreclipse.core.avrdude;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.managedbuilder.core.IConfiguration;
import org.eclipse.cdt.managedbuilder.core.IOption;
import org.eclipse.cdt.managedbuilder.core.ITool;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import de.innot.avreclipse.AVRPlugin;
import de.innot.avreclipse.PluginIDs;
import de.innot.avreclipse.core.avrdude.AVRDudeAction.Action;
import de.innot.avreclipse.core.avrdude.AVRDudeAction.FileType;
import de.innot.avreclipse.core.avrdude.AVRDudeAction.MemType;
import de.innot.avreclipse.core.toolinfo.fuses.Fuses;
/**
* This class provides some static methods to get {@link AVRDudeAction} objects for common
* scenarios.
*
* @author Thomas Holland
* @since 2.2
*
*/
public class AVRDudeActionFactory {
/** The name of the fuses for 1, 2 or 3 fusebytes */
private final static MemType[][] FUSENAMEMAP = {
{},
{ MemType.fuse },
{ MemType.lfuse, MemType.hfuse },
{ MemType.lfuse, MemType.hfuse, MemType.efuse },
{ MemType.fuse0, MemType.fuse1, MemType.fuse2, MemType.fuse3 },
{ MemType.fuse0, MemType.fuse1, MemType.fuse2, MemType.fuse3, MemType.fuse4 },
{ MemType.fuse0, MemType.fuse1, MemType.fuse2, MemType.fuse3, MemType.fuse4,
MemType.fuse5 } };
/** The name of the lockbit bytes (currently only one) */
private final static MemType[][] LOCKSNAMEMAP = { {}, { MemType.lock } };
/**
* Get a list of actions to read all readable elements of an MCU and write them to the given
* folder.
* <p>
* This method needs the mcu id to determine what memories are readable from the device.
* </p>
* <p>
* It will call avrdude to get a list of all memory types and safe them with the following
* filenames and formats <table>
* <tr>
* <th>Memory</th>
* <th>Filename</th>
* <th>Type</th>
* </tr>
* <tr>
* <td>flash</td>
* <td>flash.hex</td>
* <td>ihex</td>
* </tr>
* <tr>
* <td>eeprom</td>
* <td>eeprom.hex</td>
* <td>ihex</td>
* </tr>
* <tr>
* <td>fuses</td>
* <td>mcuid.fuses</td>
* <td>fuses format</td>
* </tr>
* <tr>
* <td>lock</td>
* <td>mcuid.lock</td>
* <td>lock format</td>
* </tr>
* <tr>
* <td>calibration</td>
* <td>calibration</td>
* <td>hex values</td>
* </tr>
* <tr>
* <td>signature</td>
* <td>signature</td>
* <td>hex values</td>
* </tr>
* </table>
*
*
* @param mcuid
* The mcu id value.
* @param backupfolderpath
* Path to an existing folder
* @return <code>List<String></code> with all actions required to backup the mcu
*/
public static List<AVRDudeAction> backupActions(String mcuid, String backupfolderpath) {
List<AVRDudeAction> actions = new ArrayList<AVRDudeAction>();
// TODO load the list of memories from avrdude
// Get the number of fuses for the given mcu
// int fusecount = Fuses.getMCUInfo(mcuid);
int fusecount = 3;
MemType[] fusenames = FUSENAMEMAP[fusecount];
IPath destpath = new Path(backupfolderpath);
String flashfile = destpath.append("flash.hex").toOSString();
String eepromfile = destpath.append("eeprom.eep").toOSString();
String signaturefile = destpath.append("signature").toOSString();
actions.add(new AVRDudeAction(MemType.signature, Action.read, signaturefile, FileType.hex));
actions.add(new AVRDudeAction(MemType.flash, Action.read, flashfile, FileType.iHex));
actions.add(new AVRDudeAction(MemType.eeprom, Action.read, eepromfile, FileType.iHex));
for (MemType type : fusenames) {
// TODO change this to fuses format once fuse files are implemented.
actions.add(new AVRDudeAction(type, Action.read, destpath.append(type.toString())
.toOSString(), FileType.hex));
}
// TODO lock and calibration are still missing
return actions;
}
/**
* Create an {@link AVRDudeAction} to write the flash image file defined in the given build
* configuration to the MCU.
* <p>
* If plugin.xml has not been modified, the filename will be
* <code>${BuildArtifactBaseFileName}.hex</code>. The variable is not resolved. It is up to
* the caller to resolve any variables in the generated arguments of the returned avrdude
* action.
* </p>
* <p>
* The generated action uses {@link AVRDudeAction.FileType#auto} to let avrdude determine the
* file type.
* </p>
*
* @param buildcfg
* <code>IConfiguration</code> from which to extract the flash image file name.
* @return <code>AVRDudeAction</code> to write the flash.
*/
public static AVRDudeAction writeFlashAction(IConfiguration buildcfg) {
AVRDudeAction action = null;
ITool[] tools = buildcfg.getToolsBySuperClassId(PluginIDs.PLUGIN_TOOLCHAIN_TOOL_FLASH);
// Test if there is a Generate Flash Image tool in the toolchain of the
// configuration.
if (tools.length != 0) {
// Tool does exist, extract the filename from the output option
// We cannot get the name directly from the output element, because
// the reference from the output element to the name-declaring
// output option is not resolved when we call
// outputelement.getValue().
ITool objcopy = tools[0];
IOption outputoption = objcopy
.getOptionBySuperClassId("de.innot.avreclipse.objcopy.flash.option.output");
String filename = (String) outputoption.getValue();
action = writeFlashAction(filename);
}
return action;
}
/**
* Create an {@link AVRDudeAction} to write the given flash image file to the MCU.
* <p>
* The generated action uses {@link AVRDudeAction.FileType#auto} to let avrdude determine the
* file type.
* </p>
* <p>
* Any macros in the filename are not resolved. It is up to the caller to resolve any macros as
* required.
* </p>
*
* @param filename
* <code>String</code> with the flash image file name.
* @return <code>AVRDudeAction</code>
*/
public static AVRDudeAction writeFlashAction(String filename) {
return new AVRDudeAction(MemType.flash, Action.write, filename, FileType.auto);
}
/**
* Create an {@link AVRDudeAction} to write the eeprom image file defined in the given build
* configuration to the MCU.
* <p>
* If plugin.xml has not been modified, the filename will be
* <code>${BuildArtifactBaseFileName}.eep</code>. The variable is not resolved. It is up to
* the caller to resolve any variables in the generated arguments of the returned avrdude
* action.
* </p>
* <p>
* The generated action uses {@link AVRDudeAction.FileType#auto} to let avrdude determine the
* file type.
* </p>
*
* @param buildcfg
* <code>IConfiguration</code> from which to extract the eeprom image file name.
* @return <code>AVRDudeAction</code> to write the eeprom.
*/
public static AVRDudeAction writeEEPROMAction(IConfiguration buildcfg) {
AVRDudeAction action = null;
ITool[] tools = buildcfg.getToolsBySuperClassId(PluginIDs.PLUGIN_TOOLCHAIN_TOOL_EEPROM);
// Test if there is a Generate EEPROM Image tool in the toolchain of the
// configuration.
if (tools.length != 0) {
// Tool does exist, extract the filename from the output option
// We cannot get the name directly from the output element, because
// the reference from the output element to the name-declaring
// output option is not resolved when we call
// outputelement.getValue().
ITool objcopy = tools[0];
IOption outputoption = objcopy
.getOptionBySuperClassId("de.innot.avreclipse.objcopy.eeprom.option.output");
String filename = (String) outputoption.getValue();
action = writeEEPROMAction(filename);
}
return action;
}
/**
* Create an {@link AVRDudeAction} to write the given eeprom image file to the MCU.
* <p>
* The generated action uses {@link AVRDudeAction.FileType#auto} to let avrdude determine the
* file type.
* </p>
* <p>
* Any macros in the filename are not resolved. It is up to the caller to resolve any macros as
* required.
* </p>
*
* @param filename
* <code>String</code> with the flash image file name.
* @return <code>AVRDudeAction</code>
*/
public static AVRDudeAction writeEEPROMAction(String filename) {
return new AVRDudeAction(MemType.eeprom, Action.write, filename, FileType.auto);
}
/**
* Create a List of {@link AVRDudeAction} objects to write the all given fuse byte values.
* <p>
* If a fuse byte value is <code>-1</code>, no action is created for it.
* </p>
*
* @param mcuid
* <code>String</code> with a valid MCU id value.
* @param fusevalues
* Array of <code>int</code> with the byte values (0-255). All other values are
* ignored (no action created).
* @return <code>List<AVRDudeAction></code> with all actions. List may be empty if the
* MCU has no fuses or no valid fuse byte value was given.
*/
public static List<AVRDudeAction> writeFuseBytes(String mcuid, int fusevalues[]) {
List<AVRDudeAction> fuseactions = new ArrayList<AVRDudeAction>();
if (mcuid == null) {
// missing mcuid -> return empty list
return fuseactions;
}
// Test if this is a 1 Fuse or 2-3 Fuse MCU
int fusecount;
try {
fusecount = Fuses.getDefault().getFuseByteCount(mcuid);
} catch (IOException e) {
// Can't access the FuseDescription Objects?
// Log the Exception and return an empty list.
IStatus status = new Status(IStatus.ERROR, AVRPlugin.PLUGIN_ID,
"Can't access the FuseDescription file for " + mcuid, e);
AVRPlugin.getDefault().log(status);
return fuseactions;
}
// Do some checks.
if (fusecount <= 0) {
// given MCU has no fuses.
// return an empty List
return fuseactions;
}
MemType[] fusenames = FUSENAMEMAP[fusecount];
// iterate over all fusenames until we run out of names or out of values
for (int i = 0; i < fusenames.length && i < fusevalues.length; i++) {
int value = fusevalues[i];
if (0 <= value && value <= 255) {
fuseactions.add(new AVRDudeAction(fusenames[i], Action.write, fusevalues[i]));
}
}
return fuseactions;
}
/**
* Create an {@link AVRDudeAction} to read single fuse byte from the MCU.
* <p>
* The generated action uses {@link AVRDudeAction.FileType#hex}, so the created file will have
* exactly one single hex value in C Format ("0xFF").
* </p>
* <p>
* Any macros in the filename are not resolved. It is up to the caller to resolve any macros as
* required.
* </p>
*
* @param mcuid
* ID of the MCU to determine the correct fuse name
* @param fuseindex
* The fuse byte to read (0-2)
* @param filename
* <code>String</code> for the target file.
* @return <code>AVRDudeAction</code>
*/
public static AVRDudeAction readFuseByte(String mcuid, int fuseindex, String filename) {
int fusecount;
try {
fusecount = Fuses.getDefault().getFuseByteCount(mcuid);
} catch (IOException e) {
return null;
}
MemType[] fusenames = FUSENAMEMAP[fusecount];
if (!(0 <= fuseindex && fuseindex < fusenames.length)) {
return null;
}
return new AVRDudeAction(fusenames[fuseindex], Action.read, filename, FileType.hex);
}
/**
* Create a List of {@link AVRDudeAction} objects to write the all given lockbit byte values.
* <p>
* If a byte value is <code>-1</code>, no action is created for it.
* </p>
*
* @param mcuid
* <code>String</code> with a valid MCU id value.
* @param values
* Array of <code>int</code> with the byte values (0-255). All other values are
* ignored (no action created).
* @return <code>List<AVRDudeAction></code> with all actions. List may be empty if the
* MCU has no locks or no valid lockbit byte value was given.
*/
public static List<AVRDudeAction> writeLockbitBytes(String mcuid, int values[]) {
List<AVRDudeAction> actions = new ArrayList<AVRDudeAction>();
if (mcuid == null) {
// no mcu -> no actions
return actions;
}
// Get the number of lockbit bytes the mcu supports. Currently this will always be 1, as all
// current AVR MCUs have one and only one lockbit byte. However I left this code in just in
// case future AVR MCUs will have more than one lockbit byte.
int count;
try {
count = Fuses.getDefault().getLockbitsByteCount(mcuid);
} catch (IOException e) {
// Can't access the LockbitsDescription Objects?
// Log the Exception and return an empty list.
IStatus status = new Status(IStatus.ERROR, AVRPlugin.PLUGIN_ID,
"Can't access the LockbitsDescription file for " + mcuid, e);
AVRPlugin.getDefault().log(status);
return actions;
}
// Do some checks.
if (count <= 0) {
// given MCU has no fuses.
// return an empty List
return actions;
}
// Get the name mapping
MemType[] allnames = LOCKSNAMEMAP[count];
// iterate over all names until we run out of names or out of values
int maxcount = Math.min(allnames.length, values.length);
for (int i = 0; i < maxcount; i++) {
int value = values[i];
if (0 <= value && value <= 255) {
actions.add(new AVRDudeAction(allnames[i], Action.write, values[i]));
}
}
return actions;
}
/**
* Create an {@link AVRDudeAction} to read single lockbit byte from the MCU.
* <p>
* The generated action uses {@link AVRDudeAction.FileType#hex}, so the created file will have
* exactly one single hex value in C Format ("0xFF").
* </p>
* <p>
* Any macros in the filename are not resolved. It is up to the caller to resolve any macros as
* required.
* </p>
*
* @param mcuid
* ID of the MCU to determine the correct lock name
* @param index
* The lockbit byte to read. Currently only <code>0</code> is supported.
* @param filename
* <code>String</code> for the target file.
* @return <code>AVRDudeAction</code>
*/
public static AVRDudeAction readLockbitByte(String mcuid, int index, String filename) {
int count;
try {
count = Fuses.getDefault().getLockbitsByteCount(mcuid);
} catch (IOException e) {
return null;
}
MemType[] allnames = LOCKSNAMEMAP[count];
if (!(0 <= index && index < allnames.length)) {
return null;
}
return new AVRDudeAction(allnames[index], Action.read, filename, FileType.hex);
}
}