From 09381c6097773b08622c810428a84840b5b08070 Mon Sep 17 00:00:00 2001 From: Dimitri Sokolyuk Date: Wed, 6 Jan 2010 17:55:16 +0000 Subject: AVR SP12 programmer --- src/init.c | 854 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 854 insertions(+) create mode 100644 src/init.c (limited to 'src/init.c') 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 +#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 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); +} -- cgit v1.2.3