/*******************************************************************************
* 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.toolinfo;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.ui.console.IOConsoleOutputStream;
import org.eclipse.ui.console.MessageConsole;
import de.innot.avreclipse.AVRPlugin;
import de.innot.avreclipse.core.IMCUProvider;
import de.innot.avreclipse.core.avrdude.AVRDudeAction;
import de.innot.avreclipse.core.avrdude.AVRDudeActionFactory;
import de.innot.avreclipse.core.avrdude.AVRDudeException;
import de.innot.avreclipse.core.avrdude.ProgrammerConfig;
import de.innot.avreclipse.core.avrdude.AVRDudeException.Reason;
import de.innot.avreclipse.core.paths.AVRPath;
import de.innot.avreclipse.core.paths.AVRPathProvider;
import de.innot.avreclipse.core.paths.IPathProvider;
import de.innot.avreclipse.core.preferences.AVRDudePreferences;
import de.innot.avreclipse.core.targets.ClockValuesGenerator;
import de.innot.avreclipse.core.targets.HostInterface;
import de.innot.avreclipse.core.targets.IProgrammer;
import de.innot.avreclipse.core.targets.TargetInterface;
import de.innot.avreclipse.core.targets.ClockValuesGenerator.ClockValuesType;
import de.innot.avreclipse.core.toolinfo.fuses.ByteValues;
import de.innot.avreclipse.core.toolinfo.fuses.FuseType;
import de.innot.avreclipse.core.util.AVRMCUidConverter;
/**
* This class handles all interactions with the avrdude program.
* <p>
* It implements the {@link IMCUProvider} Interface to get a list of all MCUs supported by the
* selected version of AVRDude. Additional methods are available to get a list of all supported
* Programmers.
* </p>
* <p>
* This class implements the Singleton pattern. Use the {@link #getDefault()} method to get the
* instance of this class.
* </p>
*
* @author Thomas Holland
* @since 2.2
*/
public class AVRDude implements IMCUProvider {
/** The singleton instance of this class */
private static volatile AVRDude instance = null;
/** The preference store for AVRDude */
private final IPreferenceStore fPrefsStore;
/**
* A list of all currently supported MCUs (with avrdude MCU id values), mapped to the
* ConfigEntry
*/
private Map<String, ConfigEntry> fMCUList;
/**
* A list of all currently supported Programmer devices, mapped to their ID.
*/
private Map<String, IProgrammer> fProgrammerList;
/**
* A List of all Programmer ConfigEntries to their respective id's.
*/
private Map<String, ConfigProgrammerEntry> fProgrammerConfigEntries;
/**
* Mapping of the Plugin MCU Id values (as keys) to the avrdude mcu id values (as values)
*/
private Map<String, String> fMCUIdMap = null;
/** The current path to the directory of the avrdude executable */
private IPath fCurrentPath = null;
/** The name of the avrdude executable */
private final static String fCommandName = "avrdude";
/** The Path provider for the avrdude executable */
private final IPathProvider fPathProvider = new AVRPathProvider(
AVRPath.AVRDUDE);
/** Bug 3023718: Remember the last MCU connected to a given programmer */
private final Map<ProgrammerConfig, String> fLastMCUtypeMap = new HashMap<ProgrammerConfig, String>();
private long fLastAvrdudeFinish = 0L;
/**
* A cache of one or more avrdude config files. The config files are stored as
* List<String> with one entry per line
*/
private final Map<IPath, List<String>> fConfigFileCache = new HashMap<IPath, List<String>>();
/**
* Get the singleton instance of the AVRDude class.
*/
public static AVRDude getDefault() {
if (instance == null)
instance = new AVRDude();
return instance;
}
// Prevent Instantiation of the class
private AVRDude() {
fPrefsStore = AVRDudePreferences.getPreferenceStore();
}
/**
* Returns the name of the AVRDude executable.
* <p>
* On Windows Systems the ".exe" extension is not included and needs to be added for access to
* avrdude other than executing the programm.
* </p>
*
* @return String with "avrdude"
*/
public String getCommandName() {
return fCommandName;
}
/**
* Returns the full path to the AVRDude executable.
* <p>
* Note: On Windows Systems the returned path does not include the ".exe" extension.
* </p>
*
* @return <code>IPath</code> to the avrdude executable
*/
public IPath getToolPath() {
IPath path = fPathProvider.getPath();
return path.append(getCommandName());
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.IMCUProvider#getMCUInfo(java.lang.String)
*/
public String getMCUInfo(String mcuid) {
Map<String, String> internalmap;
try {
internalmap = loadMCUList();
} catch (AVRDudeException e) {
// Something went wrong when avrdude was called. The exception has
// already been logged, so just return null
return null;
}
return internalmap.get(mcuid);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.IMCUProvider#getMCUList()
*/
public Set<String> getMCUList() throws IOException {
Map<String, String> internalmap;
try {
internalmap = loadMCUList();
} catch (AVRDudeException e) {
// Something went wrong when avrdude was called. The exception has
// already been logged, but we wrap the Exception in an IOException
throw new IOException("Could not start avrdude");
}
Set<String> idset = internalmap.keySet();
return new HashSet<String>(idset);
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.IMCUProvider#hasMCU(java.lang.String)
*/
public boolean hasMCU(String mcuid) {
Map<String, String> internalmap;
try {
internalmap = loadMCUList();
} catch (AVRDudeException e) {
// Something went wrong when avrdude was called. The exception has
// already been logged, so just return false.
return false;
}
return internalmap.containsKey(mcuid);
}
/**
* Returns the {@link IProgrammer} for a given programmer id.
*
* @param programmerid
* A valid programmer id as used by avrdude.
* @return the programmer type object or <code>null</code> if the <code>programmerid</code> is
* unknown.
* @throws AVRDudeException
*/
public IProgrammer getProgrammer(String programmerid) throws AVRDudeException {
loadProgrammersList(); // update the internal list (if required)
IProgrammer type = fProgrammerList.get(programmerid);
return type;
}
/**
* Returns a List of all currently supported Programmer devices.
*
* @return <code>Set<String></code> with the avrdude id values.
* @throws AVRDudeException
*/
public List<IProgrammer> getProgrammersList() throws AVRDudeException {
Collection<IProgrammer> list = loadProgrammersList().values();
// Return a copy of the original list
return new ArrayList<IProgrammer>(list);
}
/**
* Returns a Set of all currently supported Programmer devices.
*
* @return <code>Set<String></code> with the avrdude id values.
* @throws AVRDudeException
*/
public Set<String> getProgrammerIDs() throws AVRDudeException {
Set<String> allids = loadProgrammersList().keySet();
// Return a copy of the original list
return new HashSet<String>(allids);
}
/**
* Returns the {@link ConfigEntry} for the given Programmer device.
*
* @param programmerid
* <code>String</code> with the avrdude id of the programmer
* @return <code>ConfigEntry</code> containing all known information extracted from the avrdude
* executable
* @throws AVRDudeException
*/
public ConfigProgrammerEntry getProgrammerInfo(String programmerid) throws AVRDudeException {
loadProgrammersList(); // update the list (if required)
return fProgrammerConfigEntries.get(programmerid);
}
/**
* Returns the section of the avrdude.conf configuration file describing the the given
* ConfigEntry.
* <p>
* The extract is returned as a multiline <code>String</code> that can be used directly in an
* Text Control in the GUI.
* </p>
* <p>
* Note: The first call to this method may take some time, as the complete avrdude.conf file is
* read and and split into lines (currently around 450 Kbyte). This method is Synchronized, so
* it is safe to call it multiple times.
*
* @param entry
* The <code>ConfigEntry</code> for which to get the avrdude.conf entry.
* @return A <code>String</code> with the relevant lines, separated with '\n'.
* @throws IOException
* Any Exception reading the configuration file.
*/
public synchronized String getConfigDetailInfo(ConfigEntry entry) throws IOException {
List<String> configcontent = null;
// Test if we have already loaded the config file
IPath configpath = entry.fConfigfile;
if (fConfigFileCache.containsKey(configpath)) {
configcontent = fConfigFileCache.get(configpath);
} else {
// Load the config file
configcontent = loadConfigFile(configpath);
fConfigFileCache.put(configpath, configcontent);
}
// make a string, starting from the given line until the first line that
// does not start with a whitespace
StringBuffer result = new StringBuffer();
// copy every line from the config file until we hit a single ';' in the first column
int index = entry.fLinenumber;
while (true) {
String line = configcontent.get(index++);
if (line.startsWith(";")) {
break;
}
result.append(line.trim()).append('\n');
}
return result.toString();
}
/**
* Return the MCU id value of the device currently attached to the given Programmer.
*
* @param config
* <code>ProgrammerConfig</code> with the Programmer to query.
* @return <code>String</code> with the id of the attached MCU.
* @throws AVRDudeException
*/
public String getAttachedMCU(ProgrammerConfig config, IProgressMonitor monitor)
throws AVRDudeException {
try {
monitor.beginTask("Getting attached MCU", 100);
if (config == null)
throw new AVRDudeException(Reason.NO_PROGRAMMER, "", null);
// Bug 3023718: List of avrdude mcu id's to test. The first entry will be replaced
// by the last muc used with this programmer.
String[] testmcus = { "", "m16", "x128a3" };
String lastMCU = fLastMCUtypeMap.get(config);
if (lastMCU != null) {
testmcus[0] = fMCUIdMap.get(lastMCU);
}
for (String testmcu : testmcus) {
if ("".equals(testmcu)) {
continue;
}
List<String> configoptions = config.getArguments();
configoptions.add("-p" + testmcu);
try {
List<String> stdout = runCommand(configoptions, new SubProgressMonitor(monitor,
30), false, null, config);
if (stdout == null) {
continue;
}
// Parse the output and look for a line "avrdude: Device signature = 0x123456",
// with optionally " (probably mcuid)" appended to the line. This additional
// output was added after the avrdude 6.1 release
Pattern mcuPat = Pattern.compile(".+ signature .+ (0x[\\da-fA-F]{6})( .+)*", Pattern.COMMENTS);
Matcher m;
for (String line : stdout) {
m = mcuPat.matcher(line);
if (!m.matches()) {
continue;
}
// pattern matched. Get the Signature and convert it to a mcu id
String mcuid = Signatures.getDefault().getMCU(m.group(1));
fLastMCUtypeMap.put(config, mcuid);
return mcuid;
}
} catch (AVRDudeException ade) {
if (ade.getReason().equals(Reason.INIT_FAIL)) {
// if the init failed then (maybe) we tried the wrong MCU.
// continue with the next one:
continue;
} else {
throw ade;
}
}
// did not find a signature. Try the next candidate.
}
// Signature not found. This probably means that our simple parser is
// broken
throw new AVRDudeException(Reason.PARSE_ERROR,
"Could not find a valid Signature in the avrdude output", null);
} finally {
monitor.done();
}
}
/**
* Return the Fuse Bytes of the device currently attached to the given Programmer.
* <p>
* The values are read by calling avrdude with the "-U" option to read all available fusebytes
* and storing them in tempfiles in the system temp directory. These files are read to get the
* values and deleted afterwards.
* </p>
*
* @param config
* <code>ProgrammerConfig</code> with the Programmer to query.
* @param monitor
* <code>IProgressMonitor to cancel the operation.
* @return <code>FuseByteValues</code> with the values and the MCU id of the attached MCU.
* @throws AVRDudeException
*/
public ByteValues getFuseBytes(ProgrammerConfig config, IProgressMonitor monitor)
throws AVRDudeException {
try {
monitor.beginTask("Reading Fuse Bytes", 100);
// First get the attached MCU
String mcuid = getAttachedMCU(config, new SubProgressMonitor(monitor, 20));
// Check if the found MCU is actually supported by avrdude
if (!hasMCU(mcuid)) {
throw new AVRDudeException(
Reason.UNKNOWN_MCU,
"Found "
+ AVRMCUidConverter.id2name(mcuid)
+ " MCU signature. This MCU is not supported by the selected version of avrdude.");
}
ByteValues values = new ByteValues(FuseType.FUSE, mcuid);
int fusebytecount = values.getByteCount();
List<String> args = new ArrayList<String>(config.getArguments());
args.add("-p" + getMCUInfo(mcuid));
IPath tempdir = getTempDir();
List<Integer> fuseindices = new ArrayList<Integer>();
for (int i = 0; i < fusebytecount; i++) {
String tmpfilename = tempdir.append("fuse" + i + ".hex").toOSString();
String bytename = values.getByteName(i);
// Skip the fusebyte if its name is null. This will prevent avrdude from failing to
// read the undefined fusebyte 3 of XMega MCUs.
if (bytename != null) {
AVRDudeAction action = AVRDudeActionFactory.readFuseByte(mcuid, i, tmpfilename);
args.add(action.getArgument());
fuseindices.add(i);
}
}
List<String> stdout = runCommand(args, new SubProgressMonitor(monitor, 80), false,
null, config);
if (stdout == null) {
return null;
}
// get the temporary files, read and parse them and delete them afterwards
for (int i : fuseindices) {
File tmpfile = tempdir.append("fuse" + i + ".hex").toFile();
try {
BufferedReader in = new BufferedReader(new FileReader(tmpfile));
String valueString = in.readLine();
in.close();
if (valueString == null) {
throw new AVRDudeException(Reason.UNKNOWN,
"Temporary file generated by avrdude is empty.");
}
int value = Integer.decode(valueString);
values.setValue(i, value);
// Delete the temporary file.
if (!tmpfile.delete()) {
// tmpfile deletion has failed (unlikely). Well, there is not much we
// can do about it so we just log it. Especially Windows users are used to a
// bazillion stale tempfiles in their temp directory anyway.
IStatus status = new Status(IStatus.WARNING, AVRPlugin.PLUGIN_ID,
"Could not delete temporary file [" + tmpfile.toString() + "]",
null);
AVRPlugin.getDefault().log(status);
}
} catch (FileNotFoundException fnfe) {
throw new AVRDudeException(Reason.UNKNOWN, "Can't read temporary file", fnfe);
} catch (IOException ioe) {
throw new AVRDudeException(Reason.UNKNOWN, "Can't read temporary file", ioe);
}
}
return values;
} finally {
monitor.done();
}
}
/**
* Return the lockbits of the device currently attached to the given Programmer.
* <p>
* The values are read by calling avrdude with the "-U" option to read all available locks
* (currently only one) and storing them in tempfiles in the system temp directory. These files
* are read to get the values and deleted afterwards.
* </p>
*
* @param config
* <code>ProgrammerConfig</code> with the Programmer to query.
* @return <code>LockbitsByteValues</code> with the values and the MCU id of the attached MCU.
* @throws AVRDudeException
*/
public ByteValues getLockbits(ProgrammerConfig config, IProgressMonitor monitor)
throws AVRDudeException {
try {
monitor.beginTask("Reading Lockbits", 100);
// First get the attached MCU
String mcuid = getAttachedMCU(config, new SubProgressMonitor(monitor, 20));
// Check if the found MCU is actually supported by avrdude
if (!hasMCU(mcuid)) {
throw new AVRDudeException(
Reason.UNKNOWN_MCU,
"Found "
+ AVRMCUidConverter.id2name(mcuid)
+ " MCU signature. This MCU is not supported by the selected version of avrdude.");
}
ByteValues values = new ByteValues(FuseType.LOCKBITS, mcuid);
int locksbytecount = values.getByteCount();
List<String> args = new ArrayList<String>(config.getArguments());
args.add("-p" + getMCUInfo(mcuid));
IPath tempdir = getTempDir();
for (int i = 0; i < locksbytecount; i++) {
String tmpfilename = tempdir.append("lock" + i + ".hex").toOSString();
AVRDudeAction action = AVRDudeActionFactory.readLockbitByte(mcuid, i, tmpfilename);
args.add(action.getArgument());
}
List<String> stdout = runCommand(args, monitor, false, null, config);
if (stdout == null) {
return null;
}
// get the temporary files, read and parse them and delete them afterwards
for (int i = 0; i < locksbytecount; i++) {
File tmpfile = tempdir.append("lock" + i + ".hex").toFile();
try {
BufferedReader in = new BufferedReader(new FileReader(tmpfile));
String valueString = in.readLine();
in.close();
if (valueString == null) {
throw new AVRDudeException(Reason.UNKNOWN,
"Temporary file generated by avrdude is empty.");
}
int value = Integer.decode(valueString);
values.setValue(i, value);
// Delete the temporary file.
if (!tmpfile.delete()) {
// tmpfile deletion has failed (unlikely). Well, there is not much we
// can do about it so we just log it. Especially Windows users are used to a
// bazillion stale tempfiles in their temp directory anyway.
IStatus status = new Status(IStatus.WARNING, AVRPlugin.PLUGIN_ID,
"Could not delete temporary file [" + tmpfile.toString() + "]",
null);
AVRPlugin.getDefault().log(status);
}
} catch (FileNotFoundException fnfe) {
throw new AVRDudeException(Reason.UNKNOWN, "Can't read temporary file", fnfe);
} catch (IOException ioe) {
throw new AVRDudeException(Reason.UNKNOWN, "Can't read temporary file", ioe);
}
}
return values;
} finally {
monitor.done();
}
}
/**
* Return the current erase cycle counter of the device currently attached to the given
* Programmer.
* <p>
* The value is read by calling avrdude with the "-y" option.
* </p>
*
* @param config
* <code>ProgrammerConfig</code> with the Programmer to query.
* @return <code>int</code> with the values or <code>-1</code> if the counter value is not set.
* @throws AVRDudeException
*/
public int getEraseCycleCounter(ProgrammerConfig config, IProgressMonitor monitor)
throws AVRDudeException {
try {
monitor.beginTask("Read Erase Cycle Counter", 100);
// First get the attached MCU
String mcuid = getAttachedMCU(config, new SubProgressMonitor(monitor, 20));
List<String> args = new ArrayList<String>(config.getArguments());
args.add("-p" + getMCUInfo(mcuid));
List<String> stdout = runCommand(args, new SubProgressMonitor(monitor, 80), false,
null, config);
if (stdout == null) {
return -1;
}
// Parse the output and look for a line "avrdude: current erase-rewrite cycle count is
// xx"
Pattern mcuPat = Pattern.compile(".* erase-rewrite cycle count .*? ([0-9]+) .*", Pattern.COMMENTS);
Matcher m;
for (String line : stdout) {
m = mcuPat.matcher(line);
if (!m.matches()) {
continue;
}
// pattern matched. Get the cycle count and return it as an int
return Integer.parseInt(m.group(1));
}
// Cycle count not found. This probably means that no cycle count has been set yet
return -1;
} finally {
monitor.done();
}
}
/**
* Set the erase cycle counter of the device currently attached to the given Programmer.
* <p>
* The value is set by calling avrdude with the "-Y xxxx" option. The method returns the new
* value as read from the MCU as a crosscheck that the value has been written.
* </p>
*
* @param config
* <code>ProgrammerConfig</code> with the Programmer to query.
* @return <code>int</code> with the values or <code>-1</code> if the counter value is not set.
* @throws AVRDudeException
*/
public int setEraseCycleCounter(ProgrammerConfig config, int newcounter,
IProgressMonitor monitor) throws AVRDudeException {
try {
monitor.beginTask("Setting Erase Cylce Counter", 100);
// First get the attached MCU
String mcuid = getAttachedMCU(config, new SubProgressMonitor(monitor, 20));
List<String> args = new ArrayList<String>(config.getArguments());
args.add("-p" + getMCUInfo(mcuid));
args.add("-Y" + (newcounter & 0xffff));
runCommand(args, new SubProgressMonitor(monitor, 60), false, null, config);
// return the current value of the device as a crosscheck.
return getEraseCycleCounter(config, new SubProgressMonitor(monitor, 20));
} finally {
monitor.done();
}
}
/**
* Tries to guess the target interface from the AVRDude config id and the driver type from
* avrdude.conf.
* <p>
* This has been tested with avrdude 5.6. Future versions of avrdude might return wrong results.
* </p>
*
* @param avrdudeid
* The id of the programmer as used by avrdude.
* @return The interface used by the programmer.
*/
public static TargetInterface getInterface(String avrdudeid, String type) {
// Check the serial bootloader types
if (type.equals("avr910") || type.equals("butterfly") || type.equals("butterfly_mk")) {
return TargetInterface.BOOTLOADER;
}
// Check if this is one of the HVSP supporting devices
if (avrdudeid.endsWith("hvsp")) {
return TargetInterface.HVSP;
}
// Check if this is one of the PP supporting devices
if (avrdudeid.endsWith("pp")) {
return TargetInterface.PP;
}
// Check if this is one of the DebugWire supporting devices
if (avrdudeid.endsWith("dw")) {
return TargetInterface.DW;
}
// First check for ISP devices (to filter JTAG devices in ISP mode)
if (avrdudeid.endsWith("isp")) {
return TargetInterface.ISP;
}
// Check if this is JTAG device
if (avrdudeid.contains("jtag")) {
return TargetInterface.JTAG;
}
// I think we filtered everything out, so what is left is a normal ISP programmer.
return TargetInterface.ISP;
}
final static HostInterface[] SERIALBBPORT = new HostInterface[] { HostInterface.SERIAL_BB };
final static HostInterface[] SERIALPORT = new HostInterface[] { HostInterface.SERIAL };
final static HostInterface[] PARALLELPORT = new HostInterface[] { HostInterface.PARALLEL };
final static HostInterface[] USBPORT = new HostInterface[] { HostInterface.USB };
final static HostInterface[] SERIAL_USB_PORT = new HostInterface[] { HostInterface.SERIAL,
HostInterface.USB };
/**
* Tries to guess the host interface from the AVRDude programmer id and type.
* <p>
* This has been tested with avrdude 5.6. Future versions of avrdude might return wrong results.
* </p>
*
* @param avrdudeid
* The id of the programmer as used by avrdude.
* @return The interface used by the programmer.
*/
public static HostInterface[] getHostInterfaces(String avrdudeid, String avrdudetype) {
String type = avrdudetype.toLowerCase(); // just in case
if (type.equals("serbb")) {
return Arrays.copyOf(SERIALBBPORT, SERIALBBPORT.length);
}
if (type.equals("serial")) {
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
if (type.equals("parallel") || type.equals("par")) {
return Arrays.copyOf(PARALLELPORT, PARALLELPORT.length);
}
if (type.contains("usb") || type.contains("avrftdi")) {
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (avrdudeid.startsWith("stk500") || type.equals("stk500")) {
// The STK500 has only a serial port
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
if (type.equals("stk500v2")) {
// This can be either USB or SERIAL. We need to check the id
if (avrdudeid.equals("avrispv2") || avrdudeid.equals("avrispmkII")
|| avrdudeid.equals("avrisp2")) {
// The AVR ISP MkII uses the STK500v2 protokoll, but over USB
return Arrays.copyOf(USBPORT, USBPORT.length);
}
// All other STK500v2 versions / clones use the serial port
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
if (type.startsWith("stk600")) {
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (type.equals("avr910") || type.equals("butterfly") || type.equals("butterfly_mk")) {
// Bootloader types
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
if (type.startsWith("dragon")) {
// AVR Dragon
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (avrdudeid.equals("jtag2dw")) {
// JTAG ICE mkII in debugWire mode
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (avrdudeid.equals("jtag1")) {
// Atmel JTAG ICE (mkI)
// AVR ICE MkI normally uses the Serial port, but some clones have an USB port as well
// AVRDUDE 6.x says that this is a "serial" connection_type.
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
if (avrdudeid.equals("jtagii") || avrdudeid.startsWith("jtagii_")) {
// Atmel JTAG ICE mkII
// AVR ICE MkII has both a serial and an usb port.
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (type.startsWith("jtag")) {
return Arrays.copyOf(SERIAL_USB_PORT, SERIAL_USB_PORT.length);
}
if (type.startsWith("arduino")) {
// All newer Arduinos have an USB Port, but some old versions came with a serial port.
return Arrays.copyOf(SERIAL_USB_PORT, SERIAL_USB_PORT.length);
}
if (type.startsWith("buspirate")) {
// some very old Buspirate Boards have only a serial port, but I think we can ignore them.
return Arrays.copyOf(USBPORT, USBPORT.length);
}
if (type.equals("wiring")) {
// AVRDUDE doc says:
// Wiring boards are supported, utilizing STK500 V2.x protocol,
// but a simple DTR/RTS toggle to set the boards into programming mode
return Arrays.copyOf(SERIALPORT, SERIALPORT.length);
}
// TODO remove when testing is finished
throw new IllegalArgumentException("Can't determine host interface from type: '" + type + "'");
}
/**
* Internal method to read the config file with the given path and split it into lines.
*
* @param path
* <code>IPath</code> to a configuration file.
* @return A <code>List<String></code> with all lines of the given configuration file
* @throws IOException
* Any Exception reading the configuration file.
*/
private List<String> loadConfigFile(IPath path) throws IOException {
// The default avrdude.conf file has some 12.000+ lines, however custom
// avrdude.conf files might be much smaller, so we start with 100 lines
// and let the ArrayList grow as required
List<String> content = new ArrayList<String>(100);
BufferedReader br = null;
try {
File configfile = path.toFile();
br = new BufferedReader(new FileReader(configfile));
String line;
while ((line = br.readLine()) != null) {
content.add(line);
}
} finally {
if (br != null)
br.close();
}
return content;
}
/**
* @return Map<mcu id, avrdude id> of all supported MCUs
* @throws AVRDudeException
*/
private Map<String, String> loadMCUList() throws AVRDudeException {
if (!getToolPath().equals(fCurrentPath)) {
// toolpath has changed, reload the list
fMCUList = null;
fMCUIdMap = null;
fCurrentPath = getToolPath();
}
if (fMCUIdMap != null) {
// return stored map
return fMCUIdMap;
}
fMCUList = new HashMap<String, ConfigEntry>();
// Execute avrdude with the "-p?" to get a list of all supported mcus.
readAVRDudeConfigOutput(fMCUList, "-p?", "-v");
// The returned list has avrdude mcu id values, which are not the same
// as the ones used in this Plugin. Instead the returned name is
// converted into an Pluin mcu id value.
fMCUIdMap = new HashMap<String, String>(fMCUList.size());
Collection<ConfigEntry> allentries = fMCUList.values();
for (ConfigEntry entry : allentries) {
String mcuid = AVRMCUidConverter.name2id(entry.fDescription);
fMCUIdMap.put(mcuid, entry.fAvrdudeId);
}
return fMCUIdMap;
}
/**
* @return Map<mcu id, avrdude id> of all supported Programmer devices.
* @throws AVRDudeException
*/
private Map<String, IProgrammer> loadProgrammersList() throws AVRDudeException {
if (!getToolPath().equals(fCurrentPath)) {
// toolpath has changed, reload the list
fProgrammerList = null;
fCurrentPath = getToolPath();
}
if (fProgrammerList == null) {
fProgrammerConfigEntries = new HashMap<String, ConfigProgrammerEntry>();
// Execute avrdude with the "-c?" to get a list of all supported
// programmers.
readAVRDudeProgrammerInfo(fProgrammerConfigEntries);
// Convert the ConfigEntries to IProgrammerTypes
fProgrammerList = new HashMap<String, IProgrammer>();
for (String id : fProgrammerConfigEntries.keySet()) {
IProgrammer type = new ProgrammerType(id);
fProgrammerList.put(id, type);
}
}
return fProgrammerList;
}
/**
* Internal method to execute avrdude and parse the output as ConfigEntries.
*
* @see #loadMCUList()
* @see #loadProgrammersList()
*
* @param resultmap
* @param arguments
* @throws AVRDudeException
*/
private void readAVRDudeConfigOutput(Map<String, ConfigEntry> resultmap, String... arguments)
throws AVRDudeException {
List<String> stdout = runCommand(arguments);
if (stdout == null) {
return;
}
// Avrdude output for configuration items looks like:
// " id = description [pathtoavrdude.conf:line]"
// The following pattern splits this into the four groups:
// id / description / path / line
Pattern mcuPat = Pattern.compile("\\s* (\\S+) \\s* = \\s* (.+?) \\s* \\[ (.+) : (\\d+) \\] \\.*", Pattern.COMMENTS);
Matcher m;
for (String line : stdout) {
m = mcuPat.matcher(line);
if (!m.matches()) {
continue;
}
String descr = m.group(2);
if (descr.startsWith("deprecated")) {
continue;
}
ConfigEntry entry = new ConfigEntry();
entry.fAvrdudeId = m.group(1);
entry.fDescription = descr;
entry.fConfigfile = new Path(m.group(3));
entry.fLinenumber = Integer.valueOf(m.group(4));
resultmap.put(entry.fAvrdudeId, entry);
}
}
/**
* Internal method to execute avrdude and parse the output as ConfigEntries.
*
* @see #loadMCUList()
* @see #loadProgrammersList()
*
* @param resultmap
* @throws AVRDudeException
*/
private void readAVRDudeProgrammerInfo(Map<String, ConfigProgrammerEntry> resultmap)
throws AVRDudeException {
// First run the command to get the name of the conf file
String configFname = null;
Pattern pat = Pattern.compile("\\s*System wide configuration file is \"([^\"]+)\"");
Matcher m;
List<String> lines = runCommand("-v", "-c?");
if (lines == null) {
return;
}
for (String line : lines) {
m = pat.matcher(line);
if (m.matches()) {
configFname = m.group(1);
break;
}
}
if (configFname == null) {
return;
}
// Load the config file
try {
lines = loadConfigFile(new Path(configFname));
} catch (IOException e) {
// TODO Maybe we need to issue a warning
return;
}
ConfigProgrammerEntry entry = null;
Pattern linePat = Pattern.compile("\\s* (\\S+) \\s* = \\s* (.+?) ;", Pattern.COMMENTS);
Pattern parentPat = Pattern.compile("programmer \\s+ parent \\s+ \" ([^\"]+) \"", Pattern.COMMENTS);
String k;
String v;
String vstr;
int lineno = 0;
for (String line : lines) {
lineno++;
if (entry != null) {
entry.fConfigLines.add(line);
}
if (line.startsWith("#")) {
continue;
} else if (line.startsWith("programmer")) {
// Start a new programmer
entry = new ConfigProgrammerEntry();
entry.fConfigfile = new Path(configFname);
entry.fLinenumber = lineno;
// If there is a parent referal ...
m = parentPat.matcher(line);
if (m.matches()) {
String parentId = m.group(1);
if (resultmap.containsKey(parentId)) {
entry.fParent = resultmap.get(parentId);
}
}
continue;
} else if (line.startsWith(";")) {
entry = null;
continue;
}
if (entry != null) {
m = linePat.matcher(line);
if (m.matches()) {
k = m.group(1);
v = m.group(2);
vstr = v;
if (vstr.startsWith("\"") && vstr.endsWith("\"")) {
vstr = vstr.substring(1, vstr.length() - 1);
}
if (k.equals("id")) {
entry.fAvrdudeId = vstr;
resultmap.put(entry.fAvrdudeId, entry);
} else if (k.equals("desc")) {
entry.fDescription = vstr;
} else if (k.equals("type")) {
entry.fType = vstr;
} else if (k.equals("connection_type")) {
entry.fConnectionType = vstr;
}
}
}
}
}
/**
* Get the command name and the current version of avrdude.
* <p>
* The name is defined in {@link #fCommandName}. The version is gathered by executing with the
* "-v" option and parsing the output.
* </p>
*
* @return <code>String</code> with the command name and version
* @throws AVRDudeException
*/
public String getNameAndVersion() throws AVRDudeException {
// Execute avrdude with the "-v" option and parse the
// output.
// Just "-v" will have avrdude complain about a missing programmer => AVRDudeException.
// So we supply a dummy (but existing) programmer. AVRDude will now complain about the
// missing part, but we can ignore this because we got what we wanted: the version number.
List<String> stdout = runCommand("-v", "-cstk500v2");
if (stdout == null) {
// Return default name on failures
return getCommandName() + " n/a";
}
// look for a line matching "*Version TheVersionNumber *"
Pattern mcuPat = Pattern.compile(".* Version \\s+ ([\\d\\.]+) .*", Pattern.COMMENTS);
Matcher m;
for (String line : stdout) {
m = mcuPat.matcher(line);
if (!m.matches()) {
continue;
}
return getCommandName() + " " + m.group(1);
}
// could not read the version from the output, probably the regex has a
// mistake. Return a reasonable default.
return getCommandName() + " ?.?";
}
/**
* Runs avrdude with the given arguments.
* <p>
* The Output of stdout and stderr are merged and returned in a <code>List<String></code>.
* </p>
* <p>
* If the command fails to execute an entry is written to the log and an
* {@link AVRDudeException} with the reason is thrown.
* </p>
*
* @param arguments
* Zero or more arguments for avrdude
* @return A list of all output lines, or <code>null</code> if the command could not be
* launched.
* @throws AVRDudeException
* when avrdude cannot be started or when avrdude returned an
*/
public List<String> runCommand(String... arguments) throws AVRDudeException {
List<String> arglist = new ArrayList<String>(1);
for (String arg : arguments) {
arglist.add(arg);
}
return runCommand(arglist, new NullProgressMonitor(), false, null, null);
}
/**
* Runs avrdude with the given arguments.
* <p>
* The Output of stdout and stderr are merged and returned in a <code>List<String></code>.
* If the "use Console" flag is set in the Preferences, the complete output is shown on a
* Console as well.
* </p>
* <p>
* If the command fails to execute an entry is written to the log and an
* {@link AVRDudeException} with the reason is thrown.
* </p>
*
* @param arguments
* <code>List<String></code> with the arguments
* @param monitor
* <code>IProgressMonitor</code> to cancel the running process.
* @param forceconsole
* If <code>true</code> all output is copied to the console, regardless of the "use
* console" flag.
* @param cwd
* <code>IPath</code> with a current working directory or <code>null</code> to use
* the default working directory (usually the one defined with the system property
* <code>user.dir</code). May not be empty.
* @param programmerconfig
* The AVRDude Programmer configuration currently is use. Required for the AVRDude
* invocation delay value. If <code>null</code> no invocation delay will be done.
* @return A list of all output lines, or <code>null</code> if the command could not be
* launched.
* @throws AVRDudeException
* when avrdude cannot be started or when avrdude returned an error errors.
*/
public List<String> runCommand(List<String> arglist, IProgressMonitor monitor,
boolean forceconsole, IPath cwd, ProgrammerConfig programmerconfig)
throws AVRDudeException {
try {
monitor.beginTask("Running avrdude", 100);
// Check if the CWD is valid
if (cwd != null && cwd.isEmpty()) {
throw new AVRDudeException(Reason.INVALID_CWD,
"CWD does not point to a valid directory.");
}
String command = getToolPath().toOSString();
// Check if the user has a custom configuration file
IPreferenceStore avrdudeprefs = AVRDudePreferences.getPreferenceStore();
boolean usecustomconfig = avrdudeprefs
.getBoolean(AVRDudePreferences.KEY_USECUSTOMCONFIG);
if (usecustomconfig) {
String newconfigfile = avrdudeprefs.getString(AVRDudePreferences.KEY_CONFIGFILE);
arglist.add("-C" + newconfigfile);
}
// Set up the External Command
ExternalCommandLauncher avrdude = new ExternalCommandLauncher(command, arglist, cwd);
avrdude.redirectErrorStream(true);
MessageConsole console = null;
// Set the Console (if requested by the user in the preferences)
if (fPrefsStore.getBoolean(AVRDudePreferences.KEY_USECONSOLE) || forceconsole) {
console = AVRPlugin.getDefault().getConsole("AVRDude");
avrdude.setConsole(console);
}
ICommandOutputListener outputlistener = new OutputListener(monitor);
avrdude.setCommandOutputListener(outputlistener);
// This will delay the actual avrdude call if the previous call finished less than the
// user provided time in milliseconds
avrdudeInvocationDelay(programmerconfig, console, new SubProgressMonitor(monitor, 10));
// Run avrdude
try {
fAbortReason = null;
int result = avrdude.launch(new SubProgressMonitor(monitor, 80));
// Test if avrdude was aborted
if (fAbortReason != null) {
throw new AVRDudeException(fAbortReason, fAbortLine);
}
if (result == -1) {
throw new AVRDudeException(Reason.USER_CANCEL, "");
}
} catch (IOException e) {
// Something didn't work while running the external command
throw new AVRDudeException(Reason.NO_AVRDUDE_FOUND,
"Cannot run AVRDude executable. Please check the AVR path preferences.", e);
}
// Everything was fine: get the ooutput from avrdude and return it
// to the caller
List<String> stdout = avrdude.getStdOut();
monitor.worked(10);
return stdout;
} finally {
monitor.done();
fLastAvrdudeFinish = System.currentTimeMillis();
}
}
/**
* Delay for the user specified invocation delay time.
* <p>
* This method will take the user supplied delay value from the given ProgrammerConfig, check
* how much time has passed since the last avrdude call finished and - if actually required -
* wait for the remaining milliseconds.
* </p>
* <p>
* While sleeping this method will wake up every 10 ms to check if the user has cancelled, in
* which case an {@link AVRDudeException} with {@link Reason#USER_CANCEL} is thrown.
* </p>
*
* @param programmerconfig
* contains the delay value. if <code>null</code> this method returns immediatly.
* @param console
* If not <code>null</code>, then the start and end of the delay is logged on the
* console.
* @param monitor
* polled for user cancel event.
* @throws AVRDudeException
* when the user cancelles the delay.
*/
private void avrdudeInvocationDelay(final ProgrammerConfig programmerconfig,
MessageConsole console, IProgressMonitor monitor) throws AVRDudeException {
// return if no ProgrammerConfig is available
if (programmerconfig == null) {
return;
}
// Get the optional avrdude invocation delay value
String delayvalue = programmerconfig.getPostAvrdudeDelay();
if (delayvalue == null || delayvalue.length() == 0) {
return;
}
final int delay = Integer.decode(delayvalue);
if (delay == 0) {
return;
}
IOConsoleOutputStream ostream = null;
if (console != null) {
ostream = console.newOutputStream();
}
final long targetmillis = fLastAvrdudeFinish + delay;
// Quick exit if the delay has already expired
int targetdelay = (int) (targetmillis - System.currentTimeMillis());
if (targetdelay < 1) {
return;
}
try {
monitor.beginTask("delay", targetdelay);
writeOutput(ostream, "\n>>> avrdude invocation delay: " + targetdelay
+ " milliseconds\n");
// delay for specified amount of milliseconds
// To allow user cancel during long delays we check the monitor every 10
// milliseconds.
// This is the fix for Bug 2071415
while (System.currentTimeMillis() < targetmillis) {
if (monitor.isCanceled()) {
writeOutput(ostream, ">>> avrdude invocation delay: cancelled\n");
throw new AVRDudeException(Reason.USER_CANCEL, "User cancelled");
}
Thread.sleep(10);
}
writeOutput(ostream, ">>> avrdude invocation delay: finished\n");
} catch (InterruptedException e) {
throw new AVRDudeException(Reason.USER_CANCEL, "System interrupt");
} catch (IOException e) {
// ignore exception
} finally {
if (ostream != null) {
try {
ostream.close();
} catch (IOException e) {
// ignore exception
}
}
monitor.done();
}
}
/**
* Convenience method to print a message to the given stream. This method checks that the stream
* exists.
*
* @param ostream
* @param message
* @throws IOException
*/
private void writeOutput(IOConsoleOutputStream ostream, String message) throws IOException {
if (ostream != null) {
ostream.write(message);
}
}
/**
* Get the path to the System temp directory.
*
* @return <code>IPath</code>
*/
private IPath getTempDir() {
String tmpdir = System.getProperty("java.io.tmpdir");
return new Path(tmpdir);
}
/**
* The Reason code why avrdude was aborted (or <code>null</code> if avrdude finished normally)
*/
protected volatile Reason fAbortReason;
/** The line from the avrdude output that caused the abort */
protected String fAbortLine;
/**
* Internal class to listen to the output of avrdude and cancel avrdude if the certain key
* Strings appears in the output.
* <p>
* They are:
* <ul>
* <li><code>timeout</code></li>
* <li><code>Can't open device</code></li>
* <li><code>can't open config file</code></li>
* <li><code>Can't find programmer id</code></li>
* <li><code>AVR Part ???? not found</code></li>
* </ul>
* </p>
* <p>
* Once any of these Strings is found in the output the associated Reason is set and avrdude is
* aborted via the ProgressMonitor.
* </p>
*/
private class OutputListener implements ICommandOutputListener {
private final IProgressMonitor fProgressMonitor;
public OutputListener(IProgressMonitor monitor) {
fProgressMonitor = monitor;
}
// private Reason fAbortReason;
// private String fAbortLine;
/*
* (non-Javadoc)
* @see
* de.innot.avreclipse.core.toolinfo.ICommandOutputListener#init(org.eclipse.core.runtime
* .IProgressMonitor)
*/
public void init(IProgressMonitor monitor) {
// fProgressMonitor = monitor;
// fAbortLine = null;
// fAbortReason = null;
}
public void handleLine(String line, StreamSource source) {
boolean abort = false;
if (line.contains("timeout")) {
abort = true;
fAbortReason = Reason.TIMEOUT;
} else if (line.contains("can't open device")) {
abort = true;
fAbortReason = Reason.PORT_BLOCKED;
} else if (line.contains("can't open config file")) {
abort = true;
fAbortReason = Reason.CONFIG_NOT_FOUND;
} else if (line.contains("Can't find programmer id")) {
abort = true;
fAbortReason = Reason.UNKNOWN_PROGRAMMER;
} else if (line.contains("no programmer has been specified")) {
abort = true;
fAbortReason = Reason.NO_PROGRAMMER;
} else if (line.matches("AVR Part.+not found")) {
abort = true;
fAbortReason = Reason.UNKNOWN_MCU;
} else if (line.endsWith("execution aborted")) {
abort = true;
fAbortReason = Reason.USER_CANCEL;
} else if (line.contains("usbdev_open(): Found ")) {
// This is not an error, but probably a message due to -v option
} else if (line.contains("usbdev_open")) {
abort = true;
fAbortReason = Reason.NO_USB;
} else if (line.contains("failed to sync with")) {
abort = true;
fAbortReason = Reason.SYNC_FAIL;
} else if (line.contains("initialization failed")) {
// don't set the abort flag so that the progress monitor does not get canceled. This
// is a hack to make the #getAttachedMCU() method work.
fAbortLine = line;
fAbortReason = Reason.INIT_FAIL;
} else if (line.contains("NO_TARGET_POWER")) {
abort = true;
fAbortReason = Reason.NO_TARGET_POWER;
} else if (line.contains("can't set buffers for")) {
abort = true;
fAbortReason = Reason.INVALID_PORT;
} else if (line.contains("error in USB receive")) {
abort = true;
fAbortReason = Reason.USB_RECEIVE_ERROR;
} else if (line.contains("Operation not permitted")) {
abort = true;
fAbortReason = Reason.OPERATION_NOT_PERMITTED;
}
if (abort) {
fProgressMonitor.setCanceled(true);
fAbortLine = line;
}
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.ICommandOutputListener#getAbortLine()
*/
public String getAbortLine() {
return fAbortLine;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.toolinfo.ICommandOutputListener#getAbortReason()
*/
public Reason getAbortReason() {
return fAbortReason;
}
}
/**
* Container class for AVRDude configuration entries.
* <p>
* This class is stores the four informations that avrdude supplies about a Programming device
* or a MCU part:
* </p>
* <ul>
* <li>{@link #avrdudeid} = AVRDude internal id</li>
* <li>{@link #description} = Human readable description</li>
* <li>{@link #configfile} = Path to the avrdude configuration file which declares this
* programmer or part</li>
* <li>{@link #linenumber} = Line number within the configuration file where the definition
* starts</li>
* </ul>
*
*/
public static class ConfigEntry {
/** AVRDude internal id for this entry */
String fAvrdudeId;
/** (Human readable) description of this entry */
String fDescription;
/** Path to the configuration file which contains the definition */
public IPath fConfigfile;
/** line number of the start of the definition */
public int fLinenumber;
}
/**
* Class for AVRDude configuration programmer entries.
* <p>
* This class is stores the information that avrdude supplies about a Programming device:
* </p>
* <ul>
* <li>{@link #avrdudeid} = AVRDude internal id</li>
* <li>{@link #description} = Human readable description</li>
* </ul>
*/
public static class ConfigProgrammerEntry extends ConfigEntry {
/** The type */
String fType;
String fConnectionType;
/** Optional parent */
ConfigProgrammerEntry fParent;
List<String> fConfigLines;
ConfigProgrammerEntry() {
fConfigLines = new ArrayList<String>();
}
String getType() {
String type = fType;
if (type == null || type == "") {
if (fParent != null) {
type = fParent.getType();
}
}
return type;
}
String getConnectionType() {
String type = fConnectionType;
if (type == null || type == "") {
if (fParent != null) {
type = fParent.getConnectionType();
}
}
// Connection type is a new avrdude 6.x property
// If it not present, fall back to getType
if (type == null || type == "") {
type = getType();
}
return type;
}
String getDescription() {
String desc = fDescription;
if (desc == null || desc == "") {
if (fParent != null) {
desc = fParent.getDescription();
}
}
return desc;
}
List<String> getConfigLines() {
List<String> lines = new ArrayList<String>();
lines.addAll(fConfigLines);
if (fParent != null) {
lines.add("\nparent " + fParent.fAvrdudeId + "\n");
lines.addAll(fParent.getConfigLines());
}
return lines;
}
}
private class ProgrammerType implements IProgrammer {
private String fAvrdudeId;
private String fDescription;
private HostInterface fHostInterface[];
private TargetInterface fTargetInterface;
private String fType;
private String fConnectionType;
private int[] fClockFrequencies;
protected ProgrammerType(String id) {
fAvrdudeId = id;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammerType#getId()
*/
public String getId() {
return fAvrdudeId;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammerType#getDescription()
*/
public String getDescription() {
if (fDescription == null) {
try {
ConfigProgrammerEntry entry = getProgrammerInfo(fAvrdudeId);
fDescription = entry.getDescription();
} catch (AVRDudeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
fDescription = "";
}
}
return fDescription;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammer#getAdditionalInfo()
*/
public String getAdditionalInfo() {
String addinfo = null;
try {
ConfigProgrammerEntry entry = getProgrammerInfo(fAvrdudeId);
addinfo = entry.getConfigLines().toString();
} catch (AVRDudeException e) {
addinfo = "<not found>";
}
return "avrdude.conf entry for this programmer:\n\n" + addinfo;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammerType#getHostInterface()
*/
public HostInterface[] getHostInterfaces() {
if (fHostInterface == null) {
String type = getConnectionType();
fHostInterface = AVRDude.getHostInterfaces(fAvrdudeId, type);
}
return fHostInterface;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammerType#getTargetInterfaces()
*/
public TargetInterface getTargetInterface() {
if (fTargetInterface == null) {
String type = getType();
fTargetInterface = getInterface(fAvrdudeId, type);
}
return fTargetInterface;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammer#getTargetInterfaceClockFrequencies()
*/
public int[] getTargetInterfaceClockFrequencies() {
if (fClockFrequencies == null) {
String type = getType();
TargetInterface tif = getTargetInterface();
ClockValuesType protocol = null;
if (type.equals("usbtiny")) {
// USBTiny pogrammer
protocol = ClockValuesType.USBTINY;
} else if (type.equals("jtagmki")) {
// AVR ICE MkI programmer
protocol = ClockValuesType.JTAG1;
} else if (type.startsWith("jtagmkii") || type.startsWith("dragon")) {
// AVR ICE MkII or AVR Dragon
switch (tif) {
case JTAG:
case DW:
protocol = ClockValuesType.JTAG2;
break;
default:
protocol = ClockValuesType.AVRISPMK2;
}
} else if (type.startsWith("stk500")) {
// AVR ISP (mkII), STK500 or compatible
protocol = ClockValuesType.STK500;
} else if (type.startsWith("stk600")) {
// STK600 board
protocol = ClockValuesType.STK600;
}
if (protocol != null) {
fClockFrequencies = ClockValuesGenerator.getValues(protocol);
} else {
fClockFrequencies = new int[] {};
}
}
return fClockFrequencies;
}
/*
* (non-Javadoc)
* @see de.innot.avreclipse.core.targets.IProgrammer#isDaisyChainCapable()
*/
public boolean isDaisyChainCapable() {
TargetInterface ti = getTargetInterface();
if (!ti.equals(TargetInterface.JTAG)) {
return false;
}
return true;
}
/**
*
*/
private String getType() {
if (fType == null) {
ConfigProgrammerEntry entry;
try {
entry = getProgrammerInfo(fAvrdudeId);
fType = entry.getType();
} catch (AVRDudeException e) {
fType = "";
}
}
return fType;
}
/**
*
*/
private String getConnectionType() {
if (fConnectionType == null) {
ConfigProgrammerEntry entry;
try {
entry = getProgrammerInfo(fAvrdudeId);
fConnectionType = entry.getConnectionType();
} catch (AVRDudeException e) {
fConnectionType = "";
}
}
return fConnectionType;
}
@Override
public boolean hasParent() {
ConfigProgrammerEntry entry;
try {
entry = getProgrammerInfo(fAvrdudeId);
return entry.fParent != null;
} catch (AVRDudeException e) {
}
return false;
}
}
}