/* SP12: A serial programmer for working with Atmel AVR uCs */ /* Copyright (C) 1997-2003 Ken Huntington, Kevin Towers, 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 */ #include #include #include #include #include "sp12.h" #include "dos_cpt.h" /* Converts address or data string to number, automatically */ /* selecting hex (0xNNN), oct (0NNN), binary (BNNN) or */ /* dec (NNN) input. */ unsigned int str2num(char *addressOrData) { unsigned int number; /* * Determine format (hex, decimal or binary) by looking at prefix * and convert data to unsigned int using base 2, 8, 10 or 16 */ if (addressOrData[1] == 'x') number = (unsigned int) strtol(addressOrData, NULL, 16); else if (addressOrData[0] == '0') number = (unsigned int) strtol(addressOrData, NULL, 8); else if (addressOrData[0] == 'B') number = (unsigned int) strtol(&addressOrData[1], NULL, 2); else number = (unsigned int) strtol(addressOrData, NULL, 10); return(number); } /* convert unsigned int to a string-representation of the */ /* binary number (ascii `0' is 48, ascii `1' is 49) */ void num2bin(unsigned char buffer, char *binary) { int Bi; for (Bi = 7; Bi >= 0; Bi--) { binary[Bi] = (buffer & 0x01) + 48; buffer = buffer >> 1; } binary[8] = '\0'; } /* Reads program file in Atmel generic format (address:data) or */ /* Intel HEX format; stores data into into flashBuf by address. */ /* flashBuf by address. The two numbers in the generic format are */ /* hex without `0x'. For example: 00000c:99e1 */ /* Note: the 16-bit bufLimit is not suitable for Mega address space. */ int readFlashFile(char flashPath[], unsigned int flashBuf[], long bufLimit) { FILE *flashBufPtr; char wordstring[WORDLEN]; long address; unsigned int data; char buffer[MAXLEN]; long offsetAddress = 0L; int dataByteCount; unsigned int lineAddress; int recordType; int idx; int checksum; int tmp; if ((flashBufPtr = fopen(flashPath, "r")) == NULL) { return(1); } else { if (fgets(wordstring, WORDLEN, flashBufPtr) != NULL) { /* * Try Atmel generic format (default of the Atmel assembler). */ if (wordstring[FLASH_DATAPTR] == ':') { fseek(flashBufPtr, 0, SEEK_SET); while (fgets(wordstring, WORDLEN, flashBufPtr) != NULL) { sscanf(wordstring, "%lx", &address); if (wordstring[FLASH_DATAPTR] != ':') { return(1); } sscanf(&wordstring[FLASH_DATAPTR + 1], "%x", &data); if (address < bufLimit) flashBuf[address] = data; else return(1); } fclose(flashBufPtr); return(0); } /* * Try Intel HEX format. */ if (wordstring[0] == ':') { fseek(flashBufPtr, 0, SEEK_SET); while (fgets(buffer, MAXLEN, flashBufPtr) != NULL) { checksum = 0; sscanf(&buffer[1], "%2x", &dataByteCount); checksum += dataByteCount; sscanf(&buffer[3], "%4x", &lineAddress); sscanf(&buffer[3], "%2x", &tmp); checksum += tmp; sscanf(&buffer[5], "%2x", &tmp); checksum += tmp; sscanf(&buffer[7], "%2x", &recordType); checksum += recordType; /* * Record type 02 means update offset address */ if (recordType == 2) { sscanf(&buffer[9], "%4lx", &offsetAddress); sscanf(&buffer[9], "%2x", &tmp); checksum += tmp; sscanf(&buffer[11], "%2x", &tmp); checksum += tmp; checksum = (unsigned char) (0x100 - (unsigned char) checksum); sscanf(&buffer[13], "%2x", &tmp); if (checksum != tmp) return(1); } if (recordType == 0) { for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) { sscanf(&buffer[idx], "%2x", &data); /* * Use word addresses for the AT90 2-byte data format */ address = lineAddress / 2; address = address + (offsetAddress * 0x10) / 2; if (address < bufLimit) { if (lineAddress & 0x0001) /* is it an odd address? */ /* then set MSB of data */ flashBuf[address] &= (data << 8) | 0x00ff; else /* else set LSB of data */ flashBuf[address] &= data | 0xff00; } else { return(1); } lineAddress++; checksum += data; } checksum = (unsigned char) (0x100 - (unsigned char) checksum); sscanf(&buffer[idx], "%2x", &tmp); if (checksum != tmp) return(1); } } fclose(flashBufPtr); return(0); } } } return(1); } /* Reads eeprom file in format address:data, stores data into */ /* eepromBuf by address. Both numbers can be hexadecimal (0xC4), */ /* decimal (196), octal (0304) or binary (B11000100). */ int readEepromFile(char eepromPath[], unsigned int eepromBuf[], long bufLimit) { FILE *eepromBufPtr; char wordstring[WORDLEN]; long address; unsigned int data; char buffer[MAXLEN]; char *dataPtr; long offsetAddress = 0L; int dataByteCount; unsigned int lineAddress; int recordType; int idx; int checksum; int tmp; if ((eepromBufPtr = fopen(eepromPath, "r")) == NULL) { return(1); } else { if (fgets(wordstring, WORDLEN, eepromBufPtr) != NULL) { /* * Try sp12 eeprom format. */ if (strchr(wordstring, ':') != &wordstring[0]) { fseek(eepromBufPtr, 0, SEEK_SET); while (fgets(wordstring, WORDLEN, eepromBufPtr) != NULL) { if ((dataPtr = strchr(wordstring, ':')) == NULL) { return(1); } dataPtr[0] = '\0'; address =str2num(wordstring); data = str2num(++dataPtr); if (address < bufLimit) eepromBuf[address] = data; else return(1); } fclose(eepromBufPtr); return(0); } /* * Try Intel HEX format. */ if (strchr(wordstring, ':') == &wordstring[0]) { fseek(eepromBufPtr, 0, SEEK_SET); while (fgets(buffer, MAXLEN, eepromBufPtr) != NULL) { checksum = 0; sscanf(&buffer[1], "%2x", &dataByteCount); checksum += dataByteCount; sscanf(&buffer[3], "%4x", &lineAddress); sscanf(&buffer[3], "%2x", &tmp); checksum += tmp; sscanf(&buffer[5], "%2x", &tmp); checksum += tmp; sscanf(&buffer[7], "%2x", &recordType); checksum += recordType; /* * Record type 02 means update offset address */ if (recordType == 2) { sscanf(&buffer[9], "%4lx", &offsetAddress); sscanf(&buffer[9], "%2x", &tmp); checksum += tmp; sscanf(&buffer[11], "%2x", &tmp); checksum += tmp; checksum = (unsigned char) (0x100 - (unsigned char) checksum); sscanf(&buffer[13], "%2x", &tmp); if (checksum != tmp) return(1); } if (recordType == 0) { address = lineAddress; address = address + (offsetAddress * 0x10) / 2; /* * Use byte addresses for single byte eeprom data format. */ for (idx = 9; idx < 9 + dataByteCount * 2; idx += 2) { sscanf(&buffer[idx], "%2x", &data); if (address < bufLimit) eepromBuf[address] = data; else return(1); address++; checksum += data; } checksum = (unsigned char) (0x100 - (unsigned char) checksum); sscanf(&buffer[idx], "%2x", &tmp); if (checksum != tmp) return(1); } } fclose(eepromBufPtr); return(0); } } } return(1); } /* Calculate checksum over buffer[]; int bufLimit is number of */ /* elements in buffer. */ unsigned int checksum(unsigned int buffer[], long bufLimit) { long idx; unsigned int chksum = 0; /* * cyclic right shift 1 of chksum + buffer element */ for (idx = 0; idx < bufLimit; idx++) { if (buffer[idx] == 0xfff) buffer[idx] = 0xff; chksum = ((chksum>>1) + ((chksum & 1) ?0x8000:0) + buffer[idx]) & 0xffff; } return(chksum); } /* Routines to write the flash or eeprom buffer into a file, */ /* using Intel HEX format or as a hex dump with ascii translation. */ #define BYTES_PER_LINE 16 static int flash_intel_hex (FILE *filePtr, unsigned int *buffer, long addr, long length) { int line_cnt = 0; /* line byte count */ int line_len = 0; /* expected line length */ unsigned char checksum = 0; unsigned char byt; length *= 2; while (length > 0) { if (addr > 0xFFFF) { /* add record type 02 */ fprintf (filePtr, ":020000021000EC\n"); addr = 0x10000 - addr; } if (line_cnt == 0) { /* write line header */ line_len = (length > BYTES_PER_LINE) ? BYTES_PER_LINE : length; fprintf (filePtr, ":%02X%04lX00", line_len, addr); checksum = line_len; checksum += addr & 0xff; checksum += addr >> 8; } if (addr & 0x0001) { /* is it an odd address? */ byt = *buffer >> 8; /* write MSB */ buffer++; } else { byt = *buffer & 0x00ff; /* write LSB */ } fprintf (filePtr, "%02X", byt); checksum += byt; if (++line_cnt == line_len) { /* write checksum */ fprintf (filePtr, "%02X\n", -checksum & 0xff); line_cnt = 0; } addr++; length--; } fprintf (filePtr, ":00000001FF\n"); /* write the terminator */ return (0); } static int eeprom_intel_hex (FILE *filePtr, unsigned int *buffer, unsigned int addr, long length) { int line_cnt = 0; /* line byte count */ int line_len = 0; /* expected line length */ unsigned char checksum = 0; while (length > 0) { if (line_cnt == 0) { /* write line header */ line_len = (length > BYTES_PER_LINE) ? BYTES_PER_LINE : length; fprintf (filePtr, ":%02X%04X00", line_len, addr); checksum = line_len; checksum += addr & 0xff; checksum += addr >> 8; } fprintf (filePtr, "%02X", *buffer); checksum += *buffer; if (++line_cnt == line_len) { /* write checksum */ fprintf (filePtr, "%02X\n", -checksum & 0xff); line_cnt = 0; } buffer++; addr++; length--; } fprintf (filePtr, ":00000001FF\n"); /* write the terminator */ return (0); } int file_hex_ascii(FILE *filePtr, unsigned int buffer[], int bufLimit, int twoByteFlag) { unsigned long address; char dataStr[MAXLEN]; char asciiStr[MAXLEN]; char lineBuf[MAXLEN]; unsigned char loByte, hiByte; int addressPerLine = 0x10; dataStr[0] = '\0'; asciiStr[0] = '\0'; for (address = 0; address < bufLimit; address++) { if ((address > 0) && (address % addressPerLine) == 0) { if ((fprintf(filePtr, "%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr)) == EOF) return(1); dataStr[0] = '\0'; asciiStr[0] = '\0'; } if (twoByteFlag) { addressPerLine = 0x08; loByte = (unsigned char) buffer[address]; hiByte = (unsigned char) (buffer[address] >> 8); snprintf(lineBuf, sizeof(lineBuf), "%02x %02x ", hiByte, loByte); strlcat(dataStr, lineBuf, sizeof(dataStr)); if ((hiByte > 31) && (hiByte < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", hiByte); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); if ((loByte > 31) && (loByte < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", loByte); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); } else { snprintf(lineBuf, sizeof(lineBuf), "%02x ", buffer[address]); strlcat(dataStr, lineBuf, sizeof(dataStr)); if ((buffer[address] > 31) && (buffer[address] < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", buffer[address]); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); } } if ((fprintf(filePtr, "%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr)) == EOF) return(1); fprintf(filePtr, "Checksum: %04x\n", checksum(buffer, bufLimit)); return(0); } /* Writes flashBuf[] into FILE *flashBufPtr using either Intel HEX */ /* or the `native' format. */ /* If write to file fails, return(1), else return(0) */ int fileFlashBuf(unsigned int flashBuf[], long bufLimit, char flashPath[], int intel_flag, int hex_ascii_flag) { FILE *flashBufPtr; long address; if ((flashBufPtr = fopen(flashPath, "w")) == NULL) return(1); if (hex_ascii_flag) { file_hex_ascii(flashBufPtr, flashBuf, bufLimit, 1); } else if (intel_flag) { flash_intel_hex(flashBufPtr, flashBuf, 0, bufLimit); } else { for (address = 0; address < bufLimit; address++) { if ((fprintf(flashBufPtr, "%06lx:%04x\n", address, flashBuf[address])) == EOF) return(1); } } fclose(flashBufPtr); return(0); } /* Writes eepromBuf[] into FILE *eepromBufPtr; */ /* If write to file fails, return(1), else return(0) */ int fileEepromBuf(unsigned int eepromBuf[], long bufLimit, char eepromPath[], int intel_flag, int hex_ascii_flag) { FILE *eepromBufPtr; long address; char binary[9] = "76543210"; if ((eepromBufPtr = fopen(eepromPath, "w")) == NULL) return(1); if (hex_ascii_flag) { file_hex_ascii(eepromBufPtr, eepromBuf, bufLimit, 0); } else if (intel_flag) { eeprom_intel_hex(eepromBufPtr, eepromBuf, 0, bufLimit); } else { if ((fprintf(eepromBufPtr, "Address: Data in hex, dec, oct, bin\n")) == EOF) return(1); for (address = 0; address < bufLimit; address++) { num2bin((unsigned char) eepromBuf[address], binary); if ((fprintf(eepromBufPtr, " %#06lx: %#04x %3d %#4o %s\n", address, eepromBuf[address], eepromBuf[address], eepromBuf[address], binary)) == EOF) return(1); } } fclose(eepromBufPtr); return(0); } /* Writes buffer[] to stdout as a hex dump with ascii translation */ void printBuffer(unsigned int buffer[], long bufLimit, int twoByteFlag) { long address; char dataStr[MAXLEN]; char asciiStr[MAXLEN]; char lineBuf[MAXLEN]; unsigned char loByte, hiByte; int addressPerLine = 0x10; dataStr[0] = '\0'; asciiStr[0] = '\0'; for (address = 0; address < bufLimit; address++) { if ((address > 0) && (address % addressPerLine) == 0) { printf("%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr); dataStr[0] = '\0'; asciiStr[0] = '\0'; } if (twoByteFlag) { addressPerLine = 0x08; loByte = (unsigned char) buffer[address]; hiByte = (unsigned char) (buffer[address] >> 8); snprintf(lineBuf, sizeof(lineBuf), "%02x %02x ", hiByte, loByte); strlcat(dataStr, lineBuf, sizeof(dataStr)); if ((hiByte > 31) && (hiByte < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", hiByte); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); if ((loByte > 31) && (loByte < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", loByte); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); } else { snprintf(lineBuf, sizeof(lineBuf), "%02x ", buffer[address]); strlcat(dataStr, lineBuf, sizeof(dataStr)); if ((buffer[address] > 31) && (buffer[address] < 127)) snprintf(lineBuf, sizeof(lineBuf), "%c", buffer[address]); else snprintf(lineBuf, sizeof(lineBuf), "."); strlcat(asciiStr, lineBuf, sizeof(asciiStr)); } } printf("%06lx %s %s\n", address - addressPerLine, dataStr, asciiStr); printf("Checksum: %04x\n", checksum(buffer, bufLimit)); } /* Add spaces until lenght or max is reached */ /* Try to break line if necessary */ void formatStr(char *logStr, int length, int max) { int idx; int limit; int idxL = 0; limit = length; for (idx = 0; idx < limit; idx++) { if (idx == max) { logStr[idx] = '\0'; break; } if ((++idxL > length / 2) && logStr[idx] == ' ') { logStr[idx] = '\n'; limit = limit + idxL; idxL = 0; } if (logStr[idx] == '\0') { logStr[idx] = ' '; logStr[idx + 1] = '\0'; } } } /* Add a line to the log about a write command */ int logWrites(char *commandStr, unsigned int address, int data, char *path, unsigned int buffer[], int overlayFlag, int *error) { FILE *logPtr; time_t curTime; struct tm *locTime; unsigned int areaChksum = 0; unsigned int *queryBuf; int idx; long adrsCnt; char deviceName[MAXLEN]; char fileName[MAXLEN]; char *fileNamePtr; if (logging.logging != 1) return(0); formatStr(commandStr, 6, MAXLEN); strlcpy(deviceName, device.name, sizeof(deviceName)); if (strcmp(deviceName, "AT90(L)S2343 or Tiny22(L)") == 0) strlcpy(deviceName, "2343/Tiny22", sizeof(deviceName)); formatStr(deviceName, 12, MAXLEN); queryBuf = malloc(FLASHBUF_UPPERLIMIT * sizeof (unsigned int)); if (queryBuf == NULL) { printf ("ERROR: No memory available for buffer!\n"); return(0); } curTime = time(NULL); locTime = localtime(&curTime); if ((logPtr = fopen(logging.logPath, "a")) == NULL) { free(queryBuf); return(1); } fprintf(logPtr, "%s %s ", deviceName, commandStr); if (strchr(commandStr, 'f') != NULL) { strlcpy(fileName, path, sizeof(fileName)); fileNamePtr = fileName; idx = strlen(fileName); #if defined(__WIN32__) while (idx-- > 0 && fileName[idx] != '\\') #else while (idx-- > 0 && fileName[idx] != '/') #endif fileNamePtr = &fileName[idx]; formatStr(fileNamePtr, 14, MAXLEN - idx); fprintf(logPtr, "%s ", fileNamePtr); if (logging.query) { if (strchr(commandStr, 'p') != NULL) { readFlashArea(queryBuf, device.flashLimit); areaChksum = checksum(queryBuf, device.flashLimit); *error = 0; for (adrsCnt = 0; adrsCnt < device.flashLimit; adrsCnt++) { if (overlayFlag && buffer[adrsCnt] == 0xffff) continue; if (queryBuf[adrsCnt] != buffer[adrsCnt]) { *error = 1; break; } } } else { readEepromArea(queryBuf, device.eepromLimit); areaChksum = checksum(queryBuf, device.eepromLimit); *error = 0; for (adrsCnt = 0; adrsCnt < device.eepromLimit; adrsCnt++) { if (overlayFlag && buffer[adrsCnt] > 0xFF) continue; if (queryBuf[adrsCnt] != (unsigned char) buffer[adrsCnt]) { *error = 1; break; } } } if (*error) fprintf(logPtr, "readback ERROR %s", asctime(locTime)); else fprintf(logPtr, "checksum %04x %s", areaChksum, asctime(locTime)); } else { fprintf(logPtr, "not queried %s", asctime(locTime)); } } else { if (strchr(commandStr, 'p') != NULL) fprintf(logPtr, "%08x:%#06x ", address, data); else fprintf(logPtr, "%08x:%#04x ", address, data); if (*error) fprintf(logPtr, "readback ERROR %s", asctime(locTime)); else fprintf(logPtr, "verified %s", asctime(locTime)); } fclose(logPtr); free(queryBuf); return(0); } /* Add a line to the log about a lock command */ int logLocks(unsigned int buffer[], char *lockBits) { FILE *logPtr; time_t curTime; struct tm *locTime; unsigned int areaChksum = 0; char deviceName[MAXLEN]; char lBits[MAXLEN] = ""; if (logging.logging != 1) return(0); snprintf(lBits, sizeof(lBits), "%02lx", strtol(lockBits, NULL, 2)); strlcpy(deviceName, device.name, sizeof(deviceName)); formatStr(deviceName, 12, MAXLEN); curTime = time(NULL); locTime = localtime(&curTime); if ((logPtr = fopen(logging.logPath, "a")) == NULL) return(1); fprintf(logPtr, "%s -L%s ", deviceName, lBits); readFlashArea(buffer, device.flashLimit); areaChksum = checksum(buffer, device.flashLimit); fprintf(logPtr, "flash %04x ", areaChksum); readEepromArea(buffer, device.eepromLimit); areaChksum = checksum(buffer, device.eepromLimit); fprintf(logPtr, "eeprom %04x %s", areaChksum, asctime(locTime)); fclose(logPtr); return(0); }