/* 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 #include #include #include "dos_cpt.h" #include #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 logging; 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", logging.logPath); fprintf(rcPtr, "# Set logging=1 to activate logging:\n"); fprintf(rcPtr, "LOGGING=%d\n", logging.logging); fprintf(rcPtr, "# Set both logging and query=1 to always query writes:\n"); fprintf(rcPtr, "QUERY=%d\n", logging.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; logging.logging = 0; logging.query = 0; strlcpy(logging.logPath, "sp12log.txt", sizeof(logging.logPath)); KandaMode = 3; /* if it remains 3 after reading _sp12rc, */ /* check how we were called */ if ((sp12rc = getenv("SP12")) != NULL) { strlcpy(rcPath, sp12rc, sizeof(rcPath)); #if defined(__WIN32__) if (rcPath[strlen(rcPath)-1] != '\\') strcat(rcPath, "\\", ); #else if (rcPath[strlen(rcPath)-1] != '/') strlcat(rcPath, "/", sizeof(rcPath)); #endif printf("Path to _sp12rc and _sp12dev: %s\n", rcPath); strlcat(rcPath, "_sp12rc", sizeof(rcPath)); } else { strlcpy(rcPath, "_sp12rc", sizeof(rcPath)); 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", logging.logPath); sscanf(rcLine, "LOGGING=%d", &logging.logging); sscanf(rcLine, "QUERY=%d", &logging.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 defined(__WIN32__) if (strrchr(howWeAreCalled, '\\') != NULL) howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1); #else if (strrchr(howWeAreCalled, '/') != NULL) howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1); #endif 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, "); 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 "); 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 defined(__WIN32__) if (strrchr(howWeAreCalled, '\\') != NULL) howWeAreCalled = (strrchr(howWeAreCalled, '\\') + 1); #else if (strrchr(howWeAreCalled, '/') != NULL) howWeAreCalled = (strrchr(howWeAreCalled, '/') + 1); #endif 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) { strlcpy(rcPath, sp12dev, sizeof(rcPath)); #if defined(__WIN32__) if (rcPath[strlen(rcPath)-1] != '\\') strcat(rcPath, "\\"); #else if (rcPath[strlen(rcPath)-1] != '/') strlcat(rcPath, "/", sizeof(rcPath)); #endif strlcat(rcPath, "_sp12dev", sizeof(rcPath)); } else { strlcpy(rcPath, "_sp12dev", sizeof(rcPath)); } 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: strlcpy(device.madeBy, "Atmel", sizeof(device.madeBy)); break; default: strlcpy(device.madeBy, "an unknown manufacturer", sizeof(device.madeBy)); } begin = 0; iFlag = 0; found = 0; strlcpy(LOCK_MESSAGE, "", sizeof(LOCK_MESSAGE)); strlcpy(FUSES_MESSAGE, "", sizeof(FUSES_MESSAGE)); strlcpy(device.name, "unknown device, or no device", sizeof(device.name)); 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) { strlcpy(device.name, rawLine, sizeof(device.name)); } 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) { strlcpy(rcPath, sp12dev, sizeof(rcPath)); #if defined(__WIN32__) if (rcPath[strlen(rcPath)-1] != '\\') strcat(rcPath, "\\"); #else if (rcPath[strlen(rcPath)-1] != '/') strlcat(rcPath, "/", sizeof(rcPath)); #endif strlcat(rcPath, "_sp12dev", sizeof(rcPath)); } else { strlcpy(rcPath, "_sp12dev", sizeof(rcPath)); } 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); }