summaryrefslogtreecommitdiff
path: root/src/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/init.c')
-rw-r--r--src/init.c854
1 files changed, 854 insertions, 0 deletions
diff --git a/src/init.c b/src/init.c
new file mode 100644
index 0000000..a9798a1
--- /dev/null
+++ b/src/init.c
@@ -0,0 +1,854 @@
+/* SP12: A serial programmer for working with Atmel AVR uCs */
+/* Copyright (C) 1997-2008 Ken Huntington, Kevin Towers, */
+/* Artur Pundsack, Pitronics. */
+
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, */
+/* MA 02111-1307, USA. */
+
+/* Pitronics can be reached by email: sbolt@xs4all.nl */
+/* Kevin Towers can be reached by email: ktowers@omnexcontrols.com */
+/* Ken Huntington can be reached by email: kenh@compmore.net */
+/* Artur Pundsack can be reached by email: ap@pa-tec.de */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "dos_cpt.h"
+#include <time.h>
+#include "sp12.h"
+
+
+const char RC_USAGE[] = {
+ "# SP12: A serial programmer for working with Atmel AVR uCs \n"
+ "# Copyright (C) 1997-2008 Ken Huntington, Kevin Towers,\n"
+ "# Artur Pundsack, Pitronics.\n"
+ "# \n"
+ "# This program is free software; you can redistribute it and/or \n"
+ "# modify it under the terms of the GNU General Public License \n"
+ "# as published by the Free Software Foundation; either version 2 \n"
+ "# of the License, or (at your option) any later version. \n"
+ "# \n"
+ "# This program is distributed in the hope that it will be useful, \n"
+ "# but WITHOUT ANY WARRANTY; without even the implied warranty of \n"
+ "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the \n"
+ "# GNU General Public License for more details.\n"
+ "# \n"
+ "# You should have received a copy of the GNU General Public License \n"
+ "# along with this program; if not, write to the Free Software \n"
+ "# Foundation, Inc., 59 Temple Place - Suite 330, Boston, \n"
+ "# MA 02111-1307, USA. \n"
+ "# \n"
+ "# Pitronics can be reached by email: sbolt@xs4all.nl \n"
+ "# Kevin Towers can be reached by email: ktowers@omnexcontrols.com\n"
+ "# Ken Huntington can be reached by email: kenh@compmore.net \n"
+ "# \n"
+ "# Runtime configuration file to initialise time constants,\n"
+ "# parallel port, logging and port mode (sp12 native or Kanda \n"
+ "# compatible).\n"
+ "# Sp12 will make this file when first started; it will appear \n"
+ "# in the directory as defined by the environment variable SP12,\n"
+ "# or in the current directory if this variable is not set.\n"
+ "# The next time sp12 starts, time constants, port address, logging\n"
+ "# parameters and port mode will be read from this file.\n"
+ "# The primary parallel port address will be automatically selected.\n"
+ "# if you need to use, for instance, the secondary (0x278), then\n"
+ "# by all means edit this file manually, but don't change the *format*\n"
+ "# of the NNNN=ddd type statements, only the values. Note that\n"
+ "# BYTEWRITE is an upper limit; the value actually used is dynamically\n"
+ "# adjusted when sp12 runs. Check changes.doc for other details.\n"};
+
+/*--------------------- The global constants ------------------------*/
+
+unsigned long PROGRAM_ENABLE = 0xAC53FFFFL;
+unsigned long CHIP_ERASE = 0xAC80FFFFL;
+unsigned long READ_DEVICE = 0x30FF00FFL;
+unsigned long READ_PROG_LO = 0x200000FFL;
+unsigned long READ_PROG_HI = 0x280000FFL;
+unsigned long WRITE_PROG_LO = 0x40000000L;
+unsigned long WRITE_PROG_HI = 0x48000000L;
+unsigned long LOAD_PAGE_LO = 0x40000000L;
+unsigned long LOAD_PAGE_HI = 0x48000000L;
+unsigned long LOAD_EXT_ADDR = 0x4D000000L; /* AP, 15.01.2008: Added*/
+unsigned long WRITE_PAGE = 0x4C0000FFL;
+unsigned long READ_EEPROM = 0xA00000FFL;
+unsigned long WRITE_EEPROM = 0xC0000000L;
+unsigned long POLL_RDY_BSY = 0;
+unsigned long WRITE_LOCK_HM = 0;
+unsigned long WRITE_LOCK_LM = 0;
+unsigned long READ_LOCK = 0;
+unsigned long WRITE_FUSES_HM = 0;
+unsigned long WRITE_FUSES_LM = 0;
+unsigned long READ_FUSES = 0;
+unsigned long WRITE_HIGH_FUSES_HM = 0;
+unsigned long WRITE_HIGH_FUSES_LM = 0;
+unsigned long READ_HIGH_FUSES = 0;
+unsigned long WRITE_EXTD_FUSES_HM = 0;
+unsigned long WRITE_EXTD_FUSES_LM = 0;
+unsigned long READ_EXTD_FUSES = 0;
+unsigned long READ_CALIBRATION = 0;
+char CALIB_MESSAGE[MAXLEN] = "";
+char LOCK_MESSAGE[MAXLEN] = "";
+char FUSES_MESSAGE[MAXLEN] = "";
+char H_FUSES_MESSAGE[MAXLEN] = "";
+char X_FUSES_MESSAGE[MAXLEN] = "";
+int POLL_RBLSB = 0;
+int POLL_RBLEN = 0;
+int W_LOCKLSB = 0;
+int R_LOCKLSB = 0;
+int W_LOCKLEN = 0;
+int R_LOCKLEN = 0;
+int W_FUSESLSB = 0;
+int R_FUSESLSB = 0;
+int W_FUSESLEN = 0;
+int R_FUSESLEN = 0;
+int WH_FUSESLSB = 0;
+int RH_FUSESLSB = 0;
+int WH_FUSESLEN = 0;
+int RH_FUSESLEN = 0;
+int WX_FUSESLSB = 0;
+int RX_FUSESLSB = 0;
+int WX_FUSESLEN = 0;
+int RX_FUSESLEN = 0;
+int CALIBLSB = 0;
+int CALIBLEN = 0;
+
+/*------------------- No more global constants ----------------------*/
+
+/*---------------------- The global variables -----------------------*/
+
+unsigned char SCK;
+unsigned char MOSI;
+unsigned char MISO_BITNR;
+unsigned char MISO_INV;
+unsigned char RESET;
+unsigned char ENABLE;
+unsigned char PORTPOWER;
+unsigned char DEFAULT_EXIT_STATE;
+unsigned char PORTACTIVE;
+
+struct timeConsts timeConst;
+struct device_info device;
+struct logging log;
+
+int portAddress;
+int portStatus;
+int writeRetries;
+float clockSpdDefault;
+unsigned char outbyte;
+int KandaMode;
+int ExtAddr; /* AP, 15.01.2008: Added, Marker for crossing the 64k */
+ /* Flash boundary */
+
+/*-------------------- No more global variables ---------------------*/
+
+/* Look for available parallel ports, like the bios does, but */
+/* setting the data register to 0x80 and avoiding some rare */
+/* difficulties. The port address actually set will be the */
+/* last one available in the order 0x278, 0x378, 0x3BC, but */
+/* the status of all three will be reported in the rc file. */
+
+void checkParPort(FILE *rcPtr) {
+
+ printf("Looking for parallel ports...\n");
+
+ outportb(0x278, 0x80); /* Check secondary address, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x278) == 0x80) {
+ portAddress = 0x278;
+ fprintf(rcPtr, "# Parallel port available at 0x278\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x278\n");
+ }
+
+ outportb(0x378, 0x80); /* Check primary address, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x378) == 0x80) {
+ portAddress = 0x378;
+ fprintf(rcPtr, "# Parallel port available at 0x378\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x378\n");
+ }
+
+ outportb(0x3BC, 0x80); /* Check address on MDPA, report */
+ /* status in rc file, set portAddress */
+ delay(1); /* Give port time to react */
+ if (inportb(0x3BC) == 0x80) {
+ portAddress = 0x3BC;
+ fprintf(rcPtr, "# Parallel port available at 0x3BC\n");
+ } else {
+ fprintf(rcPtr, "# NO parallel port at 0x3BC\n");
+ }
+
+ fprintf(rcPtr, "# This port address will be used:\n");
+ fprintf(rcPtr, "PORT=%#x\n", portAddress);
+}
+
+/* Calibrate several time constants for delays that take care */
+/* of minimum pulse widths. portAddress must be valid. */
+
+void initTimeConst(FILE *rcPtr) {
+
+ clock_t start, end;
+ unsigned long reference;
+ unsigned long Ti; /* Timing loop counter */
+ unsigned long Ni; /* Temp. ref while calibrating */
+ /* timeConst.port */
+
+ printf("Calibrating delay loop. This may take some time...\n");
+
+ /*
+ * Measure time taken by timing loop,
+ * calibrate reference
+ */
+ reference = 1e6; /* 1e6 gives reasonable speed and */
+ /* accuracy in slow PCs like a 286 */
+ do {
+ start = clock();
+ loopDelay(reference);
+ end = clock();
+ /*
+ * Calculate elapsed time in seconds
+ */
+ timeConst.loop = ((double) (end - start)) / CLOCKS_PER_SEC;
+ if (timeConst.loop < 0.005) {
+ reference = reference * 4;
+ continue;
+ }
+ if (timeConst.loop < 1)
+ reference = (unsigned long) (reference * 1.2 / timeConst.loop);
+ } while (timeConst.loop < 1);
+
+ /*
+ * This line alters timeConst.loop as calculated by the loop
+ * above by a factor of two, when compiled by Linux gcc 2.7.
+ * Compiler magic...
+ */
+ timeConst.loop = ((double) (end - start)) / CLOCKS_PER_SEC;
+
+ /*
+ * Measure time taken by outportb() calls
+ */
+ Ni = 1e4;
+ do {
+ start = clock();
+ for (Ti = 0; Ti < Ni; Ti++) {
+ outportb(portAddress, 0x00);
+ }
+ end = clock();
+ /*
+ * Make timeConst.port equal to elapsed time in seconds
+ */
+ timeConst.port = ((double) (end - start)) / CLOCKS_PER_SEC;
+ if (timeConst.port < 0.005) {
+ Ni = Ni * 4;
+ continue;
+ }
+ if (timeConst.port < 1)
+ Ni = (unsigned long) (Ni * 1.2 / timeConst.port);
+ } while (timeConst.port < 1);
+ /*
+ * Set timeConst.port to time taken by 1 outportb() in seconds.
+ */
+ timeConst.port = (timeConst.port - Ni * timeConst.loop / reference) / Ni;
+ /*
+ * Set timeConst.loop to count for 1 second
+ */
+ timeConst.loop = reference / timeConst.loop;
+
+ /* All time constants set to 1 or larger; a check on time constants */
+ /* being zero is later used to estimate rc-file validity */
+
+ /* Calculate pageWrite for 64ms */
+ timeConst.pageWrite = timeConst.loop * 64e-3;
+ if (timeConst.pageWrite == 0)
+ timeConst.pageWrite = 1;
+
+ /* Calculate byteWrite for 12ms */
+ timeConst.byteWrite = timeConst.loop * 12e-3;
+ if (timeConst.byteWrite == 0)
+ timeConst.byteWrite = 1;
+
+ /* Calculate resetPuls for 50ms */
+ timeConst.resetPuls = timeConst.loop * 50e-3;
+ if (timeConst.resetPuls == 0)
+ timeConst.resetPuls = 1;
+
+ /* Set the other time constants in milliseconds */
+ timeConst.programEnable = 40;
+ timeConst.chipErase = 120; /* assume ATMega worst case */
+ timeConst.powerOn = 200;
+
+ /* Set the default uC clock speed in MHz */
+ clockSpdDefault = 0.7;
+
+ fprintf(rcPtr, "# Set KANDA=1 to adapt the parallel port pinout\n");
+ fprintf(rcPtr, "# to the cable/dongles supplied with the\n");
+ fprintf(rcPtr, "# Atmel/Kanda STK200/300 starter kits.\n");
+ fprintf(rcPtr, "# When KANDA=0 (default) the pinout conforms\n");
+ fprintf(rcPtr, "# to the original SP12 cable and Ken Huntington's\n");
+ fprintf(rcPtr, "# dongle.\n");
+ fprintf(rcPtr, "# If KANDA is removed or set to 3, sp12 will behave\n");
+ fprintf(rcPtr, "# according to the name by which it is called,\n");
+ fprintf(rcPtr, "# 'sp12' or 'sp12.kanda'.\n");
+ fprintf(rcPtr, "KANDA=%d\n", KandaMode);
+ fprintf(rcPtr, "# Set the log path/file to log writes and locks.\n");
+ fprintf(rcPtr, "# (less than 200 characters)\n");
+ fprintf(rcPtr, "LOGPATH=%s\n", log.logPath);
+ fprintf(rcPtr, "# Set logging=1 to activate logging:\n");
+ fprintf(rcPtr, "LOGGING=%d\n", log.logging);
+ fprintf(rcPtr, "# Set both logging and query=1 to always query writes:\n");
+ fprintf(rcPtr, "QUERY=%d\n", log.query);
+ fprintf(rcPtr, "# Chip erase delay, 120ms ATMega, 20ms others:\n");
+ fprintf(rcPtr, "CHIPERASE=%d\n", timeConst.chipErase);
+ fprintf(rcPtr, "# Time constant for 200ms will be:\n");
+ fprintf(rcPtr, "POWERON=%d\n", timeConst.powerOn);
+ fprintf(rcPtr, "# Set clockSpdDefault to a suitable value in MHz.\n");
+ fprintf(rcPtr, "CLOCKSPDDEFAULT=%.1f\n", clockSpdDefault);
+ fprintf(rcPtr, "# loopCount is a calibrated number which\n");
+ fprintf(rcPtr, "# represents about 1 second, and used to\n");
+ fprintf(rcPtr, "# calculate time constants as demanded by\n");
+ fprintf(rcPtr, "# clockSpdDefault and the -Mn.n clock speed\n");
+ fprintf(rcPtr, "# setting.\n");
+ fprintf(rcPtr, "LOOPCOUNT=%g\n", timeConst.loop);
+ fprintf(rcPtr, "# Do not change anything below this line.\n");
+ fprintf(rcPtr, "# ---------------------------------------\n");
+ fprintf(rcPtr, "PORTACCESSTIME=%g\n", timeConst.port);
+ fprintf(rcPtr, "BYTEWRITE=%ld\n", timeConst.byteWrite);
+ fprintf(rcPtr, "PAGEWRITE=%ld\n", timeConst.pageWrite);
+ fprintf(rcPtr, "PROGRAMENABLE=%d\n", timeConst.programEnable);
+ fprintf(rcPtr, "RESETPULS=%ld\n", timeConst.resetPuls);
+}
+
+/* Look for the rc file, using environment variable `SP12' (if */
+/* this variable has not been set, the current directory is used). */
+/* If the rc file exists, then read the time constants and the */
+/* parallel port address from this file. If the rc file is not */
+/* found, then calibrate the time constants, check for available */
+/* parallel ports, set (usually the primary) address, and write */
+/* it all into a new rc file. Afterwards check if the parallel */
+/* port is actually there (guarding against a corrupt rc file, and */
+/* setting the port data register to 0x80), and also check if the */
+/* time constants aren't 0. exit(1) if a check fails, else set */
+/* parallel port data register 0x00, and portStatus to */
+/* portAddress + 1 */
+/* Based on either the KANDA setting in _sp12rc or the name by */
+/* which the program is called (sp12 or sp12.kanda), the */
+/* parallel port pinout is adapted to the cable/dongles supplied */
+/* with the Atmel/Kanda STK200/300 starter kits, or to the */
+/* original SP12 cable and Ken Huntington's dongle. */
+
+void initSp12(char *howWeAreCalled) {
+
+ FILE *rcPtr;
+ char *sp12rc;
+ char rcPath[MAXLEN];
+ char rcLine[MAXLEN];
+
+ timeConst.sckLO = 0;
+ timeConst.sckHI = 0;
+ timeConst.resetPuls = 0;
+ timeConst.programEnable = 0;
+ timeConst.chipErase = 0;
+ timeConst.byteWrite = 0;
+ timeConst.powerOn = 0;
+
+ portAddress = 0;
+ portStatus = 0;
+ writeRetries = 0;
+ timeConst.byteWriteAdjusted = 0;
+ timeConst.pageWriteAdjusted = 0;
+ log.logging = 0;
+ log.query = 0;
+ strcpy(log.logPath, "sp12log.txt");
+ KandaMode = 3; /* if it remains 3 after reading _sp12rc, */
+ /* check how we were called */
+
+ if ((sp12rc = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12rc);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ printf("Path to _sp12rc and _sp12dev: %s\n", rcPath);
+ strcat(rcPath, "_sp12rc");
+ } else {
+ strcpy(rcPath, "_sp12rc");
+ printf("Path to _sp12rc and _sp12dev: Local directory\n");
+ }
+ if ((rcPtr = fopen(rcPath, "r")) != NULL) {
+ /*
+ * Read data from existing rc file, or...
+ */
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ sscanf(rcLine, "PORT=%x", &portAddress);
+ sscanf(rcLine, "KANDA=%d", &KandaMode);
+ sscanf(rcLine, "LOGPATH=%s", log.logPath);
+ sscanf(rcLine, "LOGGING=%d", &log.logging);
+ sscanf(rcLine, "QUERY=%d", &log.query);
+ sscanf(rcLine, "RESETPULS=%ld", &timeConst.resetPuls);
+ sscanf(rcLine, "CHIPERASE=%d", &timeConst.chipErase);
+ sscanf(rcLine, "POWERON=%d", &timeConst.powerOn);
+ sscanf(rcLine, "CLOCKSPDDEFAULT=%f", &clockSpdDefault);
+ sscanf(rcLine, "LOOPCOUNT=%lg", &timeConst.loop);
+ sscanf(rcLine, "PORTACCESSTIME=%lg", &timeConst.port);
+ sscanf(rcLine, "BYTEWRITE=%ld", &timeConst.byteWrite);
+ sscanf(rcLine, "PAGEWRITE=%ld", &timeConst.pageWrite);
+ sscanf(rcLine, "PROGRAMENABLE=%d", &timeConst.programEnable);
+ }
+ } else {
+ /*
+ * Make a new rc file
+ */
+ delay(100); /* helps calibration to reach same values every time */
+ if ((rcPtr = fopen(rcPath, "w")) == NULL) {
+ fprintf(stderr, "Unable to open %s for writing.\n", rcPath);
+ exit(1);
+ }
+ if (strrchr(howWeAreCalled, '/') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1);
+ if (strrchr(howWeAreCalled, '\\') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1);
+ if (strchr(howWeAreCalled, 'k') == NULL) {
+ KandaMode = 0; /* default is sp12 cable/dongle */
+ } else {
+ KandaMode = 1;
+ }
+ fprintf(rcPtr, "%s\n", RC_USAGE);
+ checkParPort(rcPtr);
+ if (portAddress) /* port must be valid, else segfault */
+ initTimeConst(rcPtr);
+ }
+ fclose(rcPtr);
+ if (!portAddress) /* no port found -> remove broken _sp12rc */
+ remove(rcPath);
+
+ /*
+ * Do check if the time constants aren't zero...
+ */
+ if (portAddress == 0 || timeConst.loop == 0 || timeConst.port == 0
+ || timeConst.programEnable == 0 || timeConst.chipErase == 0
+ || timeConst.byteWrite == 0 || timeConst.powerOn == 0
+ || timeConst.pageWrite == 0) {
+ fprintf(stderr, "Initialisation has failed (parallel port not found,\n");
+ fprintf(stderr, "or %s corrupt).\n", rcPath);
+ exit(1);
+ }
+ timeConst.byteWriteDefault = timeConst.byteWrite;
+ timeConst.pageWriteDefault = timeConst.pageWrite;
+ /*
+ * And if the port is actually there.
+ */
+ outportb(portAddress, 0x80);
+ if (inportb(portAddress) != 0x80) {
+ fprintf(stderr, "Initialisation has failed (parallel port not\n");
+ fprintf(stderr, "responding, or %s corrupt).\n", rcPath);
+ exit(1);
+ } else {
+ outportb(portAddress, 0x00);
+ portStatus = portAddress + 1;
+ }
+ /*
+ * Now set the parallel port pinout to match
+ * either the original sp12 cable and Ken's `dongle'
+ * or the cable/dongle as supplied with the
+ * Atmel/Kanda STK200/300 starter kits.
+ */
+ if (strrchr(howWeAreCalled, '/') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1);
+ if (strrchr(howWeAreCalled, '\\') != NULL)
+ howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1);
+ if (KandaMode == 0 || (KandaMode == 3 && strchr(howWeAreCalled, 'k') == NULL)) {
+ SCK = S_SCK;
+ MOSI = S_MOSI;
+ MISO_BITNR = S_MISO_BITNR;
+ MISO_INV = S_MISO_INV;
+ RESET = S_RESET;
+ ENABLE = S_ENABLE;
+ PORTPOWER = S_PORTPOWER;
+ DEFAULT_EXIT_STATE = S_DEFAULT_EXIT_STATE;
+ PORTACTIVE = S_PORTACTIVE;
+ printf("Running in SP12 cable/dongle compatible mode.\n");
+ } else if (KandaMode == 1 || \
+ (KandaMode == 3 && strchr(howWeAreCalled, 'k') != NULL)) {
+ SCK = K_SCK;
+ MOSI = K_MOSI;
+ MISO_BITNR = K_MISO_BITNR;
+ MISO_INV = K_MISO_INV;
+ RESET = K_RESET;
+ ENABLE = K_ENABLE;
+ PORTPOWER = K_PORTPOWER;
+ DEFAULT_EXIT_STATE = K_DEFAULT_EXIT_STATE;
+ PORTACTIVE = K_PORTACTIVE;
+ printf("Running in Kanda cable/dongle compatible mode.\n");
+ } else {
+ fprintf(stderr, "Call me 'sp12' or 'sp12.kanda', or set KANDA=0 or KANDA=1\n");
+ exit(1);
+ }
+ outbyte = PORTPOWER; /* Default: start with `port power' on */
+ /* (all zeroes when KANDA=1) */
+}
+
+
+/* Extract a lock or fuse command (read, write) from a rawLine */
+/* scanned from _sp12dev. Determine the lsb position and length */
+/* of the lock or fuses bit block, which is marked as something */
+/* like '6543' or 'iiiiii' or 'ooooooo' */
+
+int extractFuseCmnd(char *rawLine, unsigned long *commandHM, \
+ unsigned long *commandLM, int *fusesLsb, int *fusesLen) {
+
+ int idx = 0;
+ int idy = 0;
+ int chr = 0;
+ int fusesMsb = 0;
+ int firstBit = 0;
+ char procLine[MAXLEN];
+ char hiMask[MAXLEN] = "";
+ char loMask[MAXLEN] = "";
+
+ /*
+ * Command must be eight nibbles long, separated by spaces,
+ * containing only the characters we scanned for;
+ * but removing trailing spaces doesn't hurt.
+ */
+ for (idx = strlen(rawLine) - 1; idx > 0; idx--) {
+ if (rawLine[idx] != ' ')
+ break;
+ else
+ rawLine[idx] = '\0';
+ }
+// printf("len %i, raw %s\n", strlen(rawLine), rawLine);
+ if (strlen(rawLine) != 39)
+ return(1);
+ /*
+ * Remove the spaces
+ */
+ idy = 0;
+ for (idx = 0; idx < strlen(rawLine); idx++) {
+ if (rawLine[idx] != ' ') {
+ procLine[idy++] = rawLine[idx];
+ procLine[idy] = '\0';
+ }
+ }
+ /*
+ * Convert the characters into ones and zeroes,
+ * determine the position of the fuse lsb
+ */
+ firstBit = 0;
+ for (idx = 0; idx < strlen(procLine); idx++) {
+ chr = procLine[idx];
+ if (chr == 'h' || chr == 'x') {
+ hiMask[idx] = '1';
+ loMask[idx] = '1';
+ } else if (chr == 'l') {
+ hiMask[idx] = '0';
+ loMask[idx] = '0';
+ } else {
+ if (!firstBit) {
+ firstBit = 1;
+ fusesMsb = 31 - idx;
+ }
+ *fusesLsb = 31 - idx;
+ hiMask[idx] = '1';
+ loMask[idx] = '0';
+ }
+// printf("hiMask %s\n", hiMask);
+// printf("loMask %s\n", loMask);
+ }
+ *fusesLen = fusesMsb - *fusesLsb + 1;
+ *commandHM = strtoul(hiMask, NULL, 2);
+ *commandLM = strtoul(loMask, NULL, 2);
+ return(0);
+}
+
+
+/* Takes a devicecode (like 0392) or identifier ((-i)433) and sets */
+/* the device commands, messages and other parameters as found in */
+/* _sp12dev. */
+/* Returns 1 if _sp12dev not found; 2 if a line in _sp12dev is */
+/* corrupt; 3 if _sp12dev has no entry for the deviceCode or */
+/* identifier and the deviceCode (as an int) if all is well. */
+
+int setDevicePars(int initFlag, char *identifier) {
+
+ FILE *rcPtr;
+ char rcPath[MAXLEN];
+ char *sp12dev;
+ int deviceCode = 0;
+ char rcLine[MAXLEN] = "";
+ char rawLine[MAXLEN] = "";
+ char match[MAXLEN] = "";
+ int begin = 0; /* high when device code matches */
+ int found = 0; /* high when device was identified */
+ int iNum = 0;
+ int iFlag = 0; /* high inside ident ('begin') block */
+ int idx = 0;
+ int idy = 0;
+ int idz = 0;
+ unsigned long dummyHi = 0;
+
+
+ if ((sp12dev = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12dev);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ strcat(rcPath, "_sp12dev");
+ } else {
+ strcpy(rcPath, "_sp12dev");
+ }
+
+ if ((rcPtr = fopen(rcPath, "r")) == NULL) {
+ return(1);
+ }
+ /*
+ * Fetch commands
+ */
+ readDeviceCode();
+ deviceCode = deviceCode | device.sigByte_2;
+ deviceCode = deviceCode << 8;
+ deviceCode = deviceCode | device.sigByte_1;
+ switch (device.sigByte_0) {
+ case 0x1E:
+ strcpy(device.madeBy, "Atmel");
+ break;
+ default:
+ strcpy(device.madeBy, "an unknown manufacturer");
+ }
+ begin = 0;
+ iFlag = 0;
+ found = 0;
+ strcpy(LOCK_MESSAGE, "");
+ strcpy(FUSES_MESSAGE, "");
+ strcpy(device.name, "unknown device, or no device");
+ device.flashLimit = 0;
+ device.eepromLimit = 0;
+ device.pageMode = 0;
+ device.pageSize = 0;
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ if (sscanf(rcLine, "begin %[0-9A-Fa-f]", rawLine)) {
+ if (!initFlag && strtol(rawLine, NULL, 16) == deviceCode) {
+ begin = 1;
+ found = 1;
+ } else if (!iFlag) {
+ begin = 0;
+ }
+ iFlag = 1;
+ } else if (sscanf(rcLine, "-i%[0-9a-zA-Z]", rawLine)) {
+ /*
+ * All defined characters must be there, in the right order
+ */
+ idy = 0;
+ idz = 0;
+ for (idx = 0; idx < strlen(identifier); idx++) {
+ if (identifier[idx] > 90) /* reduce a-z to A-Z */
+ identifier[idx] = identifier[idx] - 32;
+ if (identifier[idx] == rawLine[idy]) {
+ match[idz++] = identifier[idx];
+ if (++idy >= strlen(rawLine))
+ break;
+ } else if (idz && identifier[idx] < 58 && identifier[idx] > 47) {
+ break;
+ }
+ }
+ if (strcmp(match, rawLine) == 0) {
+ begin = 1;
+ found++;
+ } else if (iFlag && initFlag) {
+ begin = 0;
+ }
+ iFlag = 0;
+ }
+ if(found > 1) {
+ fprintf(stderr, "More than one ident: ");
+ return(2);
+ }
+
+ if (begin && sscanf(rcLine, "DEVICENAME = %[A-Z ()0-9a-z]", rawLine) == 1) {
+ strcpy(device.name, rawLine);
+ }
+ if (begin && sscanf(rcLine, "FLASHSIZE = %i", &iNum) == 1) {
+ device.flashLimit = iNum;
+ }
+ if (begin && sscanf(rcLine, "EEPROMSIZE = %i", &iNum) == 1) {
+ device.eepromLimit = iNum;
+ }
+ if (begin && sscanf(rcLine, "PAGEMODE = %i", &iNum) == 1) {
+ device.pageMode = iNum;
+ }
+ if (begin && sscanf(rcLine, "PAGESIZE = %i", &iNum) == 1) {
+ device.pageSize = iNum;
+ }
+ if (begin && sscanf(rcLine, "POLL_RDY_BSY = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &POLL_RDY_BSY, \
+ &POLL_RBLSB, &POLL_RBLEN)) {
+ fprintf(stderr, "_sp12dev line 'POLL_RDY_BSY' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "WRITE_LOCK = %[hlxi 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_LOCK_HM, &WRITE_LOCK_LM, \
+ &W_LOCKLSB, &W_LOCKLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_LOCK' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_LOCK = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_LOCK, &R_LOCKLSB, \
+ &R_LOCKLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_LOCK' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "LOCK_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(LOCK_MESSAGE, rawLine, MAXLEN - strlen(LOCK_MESSAGE) - 2);
+ LOCK_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_FUSES_HM, &WRITE_FUSES_LM,
+ &W_FUSESLSB, &W_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_FUSES, &R_FUSESLSB, \
+ &R_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(FUSES_MESSAGE, rawLine, MAXLEN - strlen(FUSES_MESSAGE) - 2);
+ FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_HIGH_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_HIGH_FUSES_HM, &WRITE_HIGH_FUSES_LM,
+ &WH_FUSESLSB, &WH_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_HIGH_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_HIGH_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_HIGH_FUSES, &RH_FUSESLSB,
+ &RH_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_HIGH_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "HIGH_FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(H_FUSES_MESSAGE, rawLine, MAXLEN - strlen(H_FUSES_MESSAGE) - 2);
+ H_FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "WRITE_EXTD_FUSES = %[hlxi 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &WRITE_EXTD_FUSES_HM, &WRITE_EXTD_FUSES_LM,
+ &WX_FUSESLSB, &WX_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'WRITE_EXTD_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "READ_EXTD_FUSES = %[hlxo 0-9A-Z]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_EXTD_FUSES, &RX_FUSESLSB,
+ &RX_FUSESLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_EXTD_FUSES' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "EXTD_FUSES_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(X_FUSES_MESSAGE, rawLine, MAXLEN - strlen(X_FUSES_MESSAGE) - 2);
+ X_FUSES_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ if (begin && sscanf(rcLine, "READ_CALIBRATION = %[hlxo 0-9]", rawLine) == 1) {
+ if (extractFuseCmnd(rawLine, &dummyHi, &READ_CALIBRATION, &CALIBLSB,
+ &CALIBLEN)) {
+ fprintf(stderr, "_sp12dev line 'READ_CALIBRATION' corrupt\n");
+ return(2);
+ }
+ }
+ if (begin && sscanf(rcLine, "CALIB_MESSAGE = %[^][]", rawLine) == 1) {
+ strncat(CALIB_MESSAGE, rawLine, MAXLEN - strlen(CALIB_MESSAGE) - 2);
+ CALIB_MESSAGE[MAXLEN - 1] = '\0';
+ }
+ }
+ fclose(rcPtr);
+ if (!found)
+ return(3);
+ device.initPerformed = 1;
+ return(deviceCode);
+}
+
+/* Collects all device codes and names from _sp12dev, and shows */
+/* them on screen. */
+/* Returns 1 if _sp12dev not found, else zero. */
+
+int supported(void) {
+
+ FILE *rcPtr;
+ char rcPath[MAXLEN];
+ char *sp12dev;
+ char rcLine[MAXLEN];
+ char rawLine[MAXLEN];
+ int begin = 0;
+ int found = 0;
+
+ if ((sp12dev = getenv("SP12")) != NULL) {
+ strcpy(rcPath, sp12dev);
+#ifdef LINUX
+ if (rcPath[strlen(rcPath)-1] != '/')
+ strcat (rcPath, "/");
+#else
+ if (rcPath[strlen(rcPath)-1] != '\\')
+ strcat (rcPath, "\\");
+#endif
+ strcat(rcPath, "_sp12dev");
+ } else {
+ strcpy(rcPath, "_sp12dev");
+ }
+
+ if ((rcPtr = fopen(rcPath, "r")) == NULL) {
+ return(1);
+ }
+ /*
+ * Fetch and show codes and names
+ */
+ begin = 0;
+ printf("Supported devices:\n");
+ while (fgets(rcLine, MAXLEN, rcPtr) != NULL) {
+ if (sscanf(rcLine, "begin %[0-9A-Fa-f]", rawLine)) {
+ printf("%s, ", rawLine);
+ begin = 1;
+ found = 1;
+ }
+ if (begin && sscanf(rcLine, "DEVICENAME = %[A-Z ()0-9a-z]", rawLine) == 1) {
+ begin = 0;
+ printf("%s\n", rawLine);
+ }
+ }
+ fclose(rcPtr);
+ if (!found)
+ printf("No devices found.\n");
+ else
+ printf("\n");
+ return(0);
+}