├── .gitattributes ├── CHANGELOG ├── software ├── .gitignore ├── LICENSE ├── include │ ├── utils.h │ ├── ulog.h │ ├── gpio.h │ └── piepro.h ├── src │ ├── utils.c │ ├── ulog.c │ ├── main.c │ ├── gpio.c │ └── piepro.c ├── test │ ├── README.md │ ├── test_main.c │ ├── uTest.h │ ├── test_generateData.c │ ├── uTest.c │ └── test_gpio.c ├── README.md ├── man │ └── piepro.1 └── Makefile ├── hardware ├── .gitignore ├── manufacturing │ ├── pi-eeprom-programmer-1.0.0.zip │ ├── BOM.csv │ └── gerbers │ │ ├── pi-eeprom-programmer-NPTH.drl │ │ ├── pi-eeprom-programmer-B_Paste.gbr │ │ ├── pi-eeprom-programmer-F_Paste.gbr │ │ ├── pi-eeprom-programmer-B_Silkscreen.gbr │ │ ├── pi-eeprom-programmer-Edge_Cuts.gbr │ │ ├── pi-eeprom-programmer-PTH.drl │ │ ├── pi-eeprom-programmer-B_Mask.gbr │ │ ├── pi-eeprom-programmer-F_Mask.gbr │ │ ├── pi-eeprom-programmer-F_Cu.gbr │ │ └── pi-eeprom-programmer-B_Cu.gbr ├── LICENSE ├── README.md └── design │ └── pi-eeprom-programmer.kicad_pro ├── TODO ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.zip binary 2 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | v1.0.0 2 | ======== 3 | Initial Release -------------------------------------------------------------------------------- /software/.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | lib 3 | build 4 | test/data 5 | 6 | *.o -------------------------------------------------------------------------------- /hardware/.gitignore: -------------------------------------------------------------------------------- 1 | pi-eeprom-programmer-backups 2 | fp-info-cache 3 | 4 | *.kicad_prl -------------------------------------------------------------------------------- /hardware/manufacturing/pi-eeprom-programmer-1.0.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewteall/pi-eeprom-programmer/HEAD/hardware/manufacturing/pi-eeprom-programmer-1.0.0.zip -------------------------------------------------------------------------------- /hardware/manufacturing/BOM.csv: -------------------------------------------------------------------------------- 1 | "Id";"Designator";"Package";"Quantity";"Designation";"Supplier and ref"; 2 | 1;"J1";"PinSocket_2x20_P2.54mm_Horizontal";1;"Raspberry Pi GPIO Connector";;; 3 | 2;"U1";"DIP-28_W15.24mm_Socket";1;"ZIF Socket";;; 4 | 3;"U2";"DIP-8_W7.62mm_Socket";1;"24C01-24C1024";;; 5 | 4;"J2";"PinHeader_1x03_P2.54mm_Vertical";1;"Conn_01x03";;; 6 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-NPTH.drl: -------------------------------------------------------------------------------- 1 | M48 2 | ; DRILL file {KiCad 6.0.11+dfsg-1} date Mon 04 Dec 2023 01:58:20 PM EST 3 | ; FORMAT={-:-/ absolute / inch / decimal} 4 | ; #@! TF.CreationDate,2023-12-04T13:58:20-05:00 5 | ; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.11+dfsg-1 6 | ; #@! TF.FileFunction,NonPlated,1,2,NPTH 7 | FMAT,2 8 | INCH 9 | % 10 | G90 11 | G05 12 | T0 13 | M30 14 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODO 2 | =========== 3 | Add 12v circuit support for some EEPROMs. 4 | 5 | Move Pin/EEPROM configs to external file. 6 | 7 | Support Programming of Microcontrollers like the ATTiny series 8 | 9 | Allow custom file paths and custom file names for tests 10 | 11 | Make the Man Page Better - https://itsfoss.com/linux-man-page-guide/ 12 | 13 | Provide Build Builds and Zip of hardware for downloads in releases on github -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-B_Paste.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:25-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Paste,Bot* 6 | G04 #@! TF.FilePolarity,Positive* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:25* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 APERTURE END LIST* 15 | M02* 16 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-F_Paste.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:25-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Paste,Top* 6 | G04 #@! TF.FilePolarity,Positive* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:25* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 APERTURE END LIST* 15 | M02* 16 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-B_Silkscreen.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:26-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Legend,Bot* 6 | G04 #@! TF.FilePolarity,Positive* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:26* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 APERTURE END LIST* 15 | M02* 16 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-Edge_Cuts.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:26-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Profile,NP* 6 | %FSLAX46Y46*% 7 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 8 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:26* 9 | %MOMM*% 10 | %LPD*% 11 | G01* 12 | G04 APERTURE LIST* 13 | G04 #@! TA.AperFunction,Profile* 14 | %ADD10C,0.050000*% 15 | G04 #@! TD* 16 | G04 APERTURE END LIST* 17 | D10* 18 | X132435600Y-41071500D02* 19 | X202435600Y-41071500D01* 20 | X202435600Y-41071500D02* 21 | X202435600Y-97071500D01* 22 | X202435600Y-97071500D02* 23 | X132435600Y-97071500D01* 24 | X132435600Y-97071500D02* 25 | X132435600Y-41071500D01* 26 | M02* 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution License 2 | 3 | Copyright (c) 2023 Andrew Teall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the “Software”), to deal 7 | in the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. -------------------------------------------------------------------------------- /software/LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution License 2 | 3 | Copyright (c) 2023 Andrew Teall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the “Software”), to deal 7 | in the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so. 11 | 12 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. -------------------------------------------------------------------------------- /software/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 1 3 | 4 | /** 5 | * @brief Converts a number to it's corresponding binary string. 6 | * @param num The number to convert. 7 | * @param *binStrBuf The buffer to store the converted string. 8 | * @param strLenBuf Size of the binStrBuf buffer. 9 | * @return char* The pointer to the buffer of the converted number. 10 | */ 11 | char* num2binStr(int num, char* binStrBuf, int strBufLen); 12 | 13 | /** 14 | * @brief Converts a binary string to it's corresponding value. 15 | * @param *binStr The string to convert 16 | * @return int The resulting number of the conversion. -1 if error. 17 | */ 18 | int binStr2num(char *binStr); 19 | 20 | /** 21 | * @brief Converts a string to it's corresponding value. 22 | * @param numStr The string to convert 23 | * @return int The resulting number of the conversion. -1 if error. 24 | */ 25 | int str2num(char *numStr); 26 | 27 | #endif -------------------------------------------------------------------------------- /software/include/ulog.h: -------------------------------------------------------------------------------- 1 | #ifndef ULOG_H 2 | #define ULOG_H 1 3 | 4 | /** 5 | * @brief Defines the Logging Levels used by uLog. 6 | */ 7 | enum LOGLEVEL {OFF=0, FATAL, ERROR, WARNING, INFO, DEBUG, TRACE,}; 8 | 9 | /** 10 | * @brief Prints a message to stdout with the loggingLevel prepended. 11 | * @param verbosity The logging level of the generated log. 12 | * @param logMessage The formatted string to output to stdout. 13 | * @param ... The formatted string parameters. 14 | */ 15 | void ulog(int , const char* logMessage, ...); 16 | 17 | /** 18 | * @brief Sets the logging level used by the program 19 | * @param newLogLevel The logging level to set the program to. 20 | * @returns uint8_t Returns 0 if loggingLevel is successfully set. 1 if not. 21 | */ 22 | int setLoggingLevel(int logLevel); 23 | 24 | /** 25 | * @brief Gets the logging level used by the program 26 | * @returns int Returns the loggingLevel. 27 | */ 28 | int getLoggingLevel(void); 29 | #endif -------------------------------------------------------------------------------- /software/src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* Converts a number to a binary String */ 4 | char* num2binStr(int num, char* binStrBuf, int strBufLen){ 5 | for (int i=strBufLen-2; i >= 0; i--){ 6 | binStrBuf[i] = (char)((num%2)+0x30); 7 | num >>= 1; 8 | } 9 | if(num != 0){ 10 | return (char*) -1; 11 | } 12 | binStrBuf[strBufLen-1] = 0; 13 | return binStrBuf; 14 | } 15 | 16 | /* Converts a binary string to a number. */ 17 | int binStr2num(char *binStr){ 18 | char* indexPtr; 19 | long num = strtol(binStr, &indexPtr, 2); 20 | if (*indexPtr != '\0' || num < 0){ 21 | num = -1; 22 | } 23 | return (int)num; 24 | } 25 | 26 | /* Converts a number string to a number */ 27 | int str2num(char *numStr){ 28 | char* indexPtr; 29 | int index = 0; 30 | int base = 10; 31 | 32 | if ((numStr[0] == '0' && (numStr[1] == 'x' || numStr[1] == 'X')) || numStr[0] == '$'){ 33 | // convert hexidecimal number 34 | index = 2; 35 | if(numStr[0] == '$'){ 36 | index = 1; 37 | } 38 | base = 16; 39 | } else if ((numStr[0] == '0' && (numStr[1] == 'b' || numStr[1] == 'B')) || numStr[0] == '%'){ 40 | // convert binary number 41 | index = 2; 42 | if(numStr[0] == '%'){ 43 | index = 1; 44 | } 45 | base = 2; 46 | } 47 | 48 | long num = strtol(numStr+index, &indexPtr, base); 49 | if (*indexPtr != '\0' || num < 0){ 50 | num = -1; 51 | } 52 | return (int)num; 53 | } 54 | -------------------------------------------------------------------------------- /software/src/ulog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "ulog.h" 5 | 6 | /* Strings that correlate to LOGLEVEL enum so that LOGLEVEL can be printed */ 7 | const char *LOGLEVELSTRINGS[] = {"OFF","FATAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE",}; 8 | 9 | /* Static global Logging Level to track verbosity across the program */ 10 | static int loggingLevel = WARNING; 11 | 12 | /* Logging method to support filtering out logs by verbosity */ 13 | void ulog(int verbosity, const char* logMessage,...) { 14 | if (verbosity <= loggingLevel){ 15 | char logBuf[120]; 16 | va_list args; 17 | va_start(args, logMessage); 18 | vsnprintf(logBuf,sizeof(logBuf),logMessage, args); 19 | va_end(args); 20 | fprintf(stderr,"%8s: %s\n", LOGLEVELSTRINGS[verbosity],logBuf); 21 | } 22 | } 23 | 24 | /* Sets the LoggingLevel to the specified newLogLevel. Fails and returns -1 if 25 | an invalid newLogLevel is passed. Otherwise, returns 0. */ 26 | int setLoggingLevel(int newLogLevel){ 27 | if( newLogLevel <= TRACE && newLogLevel >= OFF){ 28 | loggingLevel = newLogLevel; 29 | ulog(INFO,"Setting Logging Level to [%i] %s",newLogLevel,LOGLEVELSTRINGS[newLogLevel]); 30 | return 0; 31 | } else { 32 | ulog(ERROR,"Invalid Logging Level. Log Level could not be set."); 33 | return -1; 34 | } 35 | } 36 | 37 | /* Returns the currently set LoggingLevel */ 38 | int getLoggingLevel(){ 39 | return loggingLevel; 40 | } 41 | -------------------------------------------------------------------------------- /software/test/README.md: -------------------------------------------------------------------------------- 1 | ## __Testing__ 2 | 3 | ### __Quick Start__ 4 | Plug the device to be used for testing into the GPIO Pins. 5 | 6 | To run parallel EEPROM tests: 7 | ```sh 8 | make EEPROM_MODEL={EEPROM_MODEL} test 9 | ``` 10 | 11 | To run I2C EEPROM tests: 12 | ```sh 13 | make EEPROM_MODEL={EEPROM_MODEL} test_I2C 14 | ``` 15 | 16 | Where {EEPROM_MODEL} is the model of EEPROM you want to test(without brackets). 17 | 18 | ### __Running Tests__ 19 | 20 | To just build the test runner: 21 | ```sh 22 | make test_runner_piepro 23 | ``` 24 | 25 | From there the test suit can be run with: 26 | 27 | ```sh 28 | ./bin/test_runner_piepro -m EEPROM_MODEL 29 | ``` 30 | Where EEPROM_MODEL is the model of EEPROM you want to test. 31 | 32 | By default all Unit and Functional tests are run. You can limit which set is run with the `-u` or `--unit` flag to only runs Unit Tests or the `-f` or `--functional` flag to only runs Functional Tests. 33 | 34 | Test data is required to run the tests and can automatically be generated with the `-g` or `--generate` flag. To only generate the test data and then exit the `-go` or `--generate-only` flag may be set. 35 | 36 | By default Parallel EEPROM tests are run. To run the I2C tests use the `-I2C` flag. 37 | 38 | All suites and tests are run when the test runner is invoked with out the `-s N` or `--suite N` flag which will specify to only run `N` suite. The same goes for `-t N` or `--test N` flag which will only run the specified `N` test. 39 | 40 | The final flag, `-l N` or `--limit N`, will artificially limit the size of the EEPROM to size `N`. This is useful for quickly testing a certain test on large EEPROMS. 41 | 42 | ### __Writing Tests__ 43 | 44 | The included UTest framework should be used to write tests. Suites and tests may be added in the appropriate functions with the supplied functions in `uTest.h`. 45 | -------------------------------------------------------------------------------- /software/README.md: -------------------------------------------------------------------------------- 1 | # __A Raspberry Pi GPIO EEPROM Writer and Reader__ 2 | 3 | A way to write Parallel and Serial EEPROMs using the Raspberry Pi GPIO pins. 4 | 5 | This repository contains the software component for the Raspberry Pi EEPROM Programmer. The software is designed to work in conjunction with the hardware components to enable programming and updating of EEPROMs (Electrically Erasable Programmable Read-Only Memory) using a Raspberry Pi or the software can be used with a breadboard with the appropriate circuit wired up. 6 | 7 | ## __Introduction__ 8 | 9 | The Raspberry Pi EEPROM Programmer software provides a user-friendly interface to interact with the hardware and perform programming tasks on EEPROM chips. 10 | 11 | This README provides an overview of the features, installation instructions, and usage guidelines for the Raspberry Pi EEPROM Programmer software. 12 | 13 | ## __Building__ 14 | See [Quick Start](../README.md#quick-start) 15 | 16 | ## __Installation__ 17 | See [Quick Start](../README.md#quick-start) 18 | 19 | ## __Flags and Options__ 20 | See [Flags](../README.md#command-line-options) 21 | 22 | ## __Quirks__ 23 | --no-validate-write has no affect on I2C devices. 24 | 25 | ## __Updating__ 26 | Updates can be performed by just running 27 | ```sh 28 | make install 29 | ``` 30 | and the new version will be installed over the existing verison. 31 | 32 | ## __Uninstall__ 33 | To uninstall just run 34 | ```sh 35 | make uninstall 36 | ``` 37 | from your source code directory. 38 | 39 | ## __Features, Bugs, and Contributing__ 40 | If there's anything you'd like to add or find a bug please open an [issue](https://github.com/andrewteall/pi-eeprom-programmer/issues). If you'd like to implement those chages yourself please feel free to open a PR or fork the repository and make it your own. 41 | 42 | 43 | ## __License__ 44 | This software project is licensed under the [MIT-0 License](LICENSE). You are free to use, modify, and distribute the project according to the terms of the license. -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-PTH.drl: -------------------------------------------------------------------------------- 1 | M48 2 | ; DRILL file {KiCad 6.0.11+dfsg-1} date Mon 04 Dec 2023 01:58:20 PM EST 3 | ; FORMAT={-:-/ absolute / inch / decimal} 4 | ; #@! TF.CreationDate,2023-12-04T13:58:20-05:00 5 | ; #@! TF.GenerationSoftware,Kicad,Pcbnew,6.0.11+dfsg-1 6 | ; #@! TF.FileFunction,Plated,1,2,PTH 7 | FMAT,2 8 | INCH 9 | ; #@! TA.AperFunction,Plated,PTH,ComponentDrill 10 | T1C0.0315 11 | ; #@! TA.AperFunction,Plated,PTH,ComponentDrill 12 | T2C0.0394 13 | % 14 | G90 15 | G05 16 | T1 17 | X7.079Y-2.0697 18 | X7.079Y-2.1697 19 | X7.079Y-2.2697 20 | X7.079Y-2.3697 21 | X7.079Y-2.4697 22 | X7.079Y-2.5697 23 | X7.079Y-2.6697 24 | X7.079Y-2.7697 25 | X7.079Y-2.8697 26 | X7.079Y-2.9697 27 | X7.079Y-3.0697 28 | X7.079Y-3.1697 29 | X7.079Y-3.2697 30 | X7.079Y-3.3697 31 | X7.2276Y-2.9222 32 | X7.2276Y-3.0222 33 | X7.2276Y-3.1222 34 | X7.2276Y-3.2222 35 | X7.5276Y-2.9222 36 | X7.5276Y-3.0222 37 | X7.5276Y-3.1222 38 | X7.5276Y-3.2222 39 | X7.679Y-2.0697 40 | X7.679Y-2.1697 41 | X7.679Y-2.2697 42 | X7.679Y-2.3697 43 | X7.679Y-2.4697 44 | X7.679Y-2.5697 45 | X7.679Y-2.6697 46 | X7.679Y-2.7697 47 | X7.679Y-2.8697 48 | X7.679Y-2.9697 49 | X7.679Y-3.0697 50 | X7.679Y-3.1697 51 | X7.679Y-3.2697 52 | X7.679Y-3.3697 53 | T2 54 | X5.2778Y-1.7686 55 | X5.2778Y-1.8686 56 | X5.2778Y-1.9686 57 | X5.2778Y-2.0686 58 | X5.2778Y-2.1686 59 | X5.2778Y-2.2686 60 | X5.2778Y-2.3686 61 | X5.2778Y-2.4686 62 | X5.2778Y-2.5686 63 | X5.2778Y-2.6686 64 | X5.2778Y-2.7686 65 | X5.2778Y-2.8686 66 | X5.2778Y-2.9686 67 | X5.2778Y-3.0686 68 | X5.2778Y-3.1686 69 | X5.2778Y-3.2686 70 | X5.2778Y-3.3686 71 | X5.2778Y-3.4686 72 | X5.2778Y-3.5686 73 | X5.2778Y-3.6686 74 | X5.3778Y-1.7686 75 | X5.3778Y-1.8686 76 | X5.3778Y-1.9686 77 | X5.3778Y-2.0686 78 | X5.3778Y-2.1686 79 | X5.3778Y-2.2686 80 | X5.3778Y-2.3686 81 | X5.3778Y-2.4686 82 | X5.3778Y-2.5686 83 | X5.3778Y-2.6686 84 | X5.3778Y-2.7686 85 | X5.3778Y-2.8686 86 | X5.3778Y-2.9686 87 | X5.3778Y-3.0686 88 | X5.3778Y-3.1686 89 | X5.3778Y-3.2686 90 | X5.3778Y-3.3686 91 | X5.3778Y-3.4686 92 | X5.3778Y-3.5686 93 | X5.3778Y-3.6686 94 | X6.188Y-1.768 95 | X6.288Y-1.768 96 | X6.388Y-1.768 97 | T0 98 | M30 99 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-B_Mask.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:26-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Soldermask,Bot* 6 | G04 #@! TF.FilePolarity,Negative* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:26* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | %ADD10R,1.700000X1.700000*% 15 | %ADD11O,1.700000X1.700000*% 16 | %ADD12R,1.600000X1.600000*% 17 | %ADD13O,1.600000X1.600000*% 18 | G04 APERTURE END LIST* 19 | D10* 20 | X136595201Y-44921500D03* 21 | D11* 22 | X134055201Y-44921500D03* 23 | X136595201Y-47461500D03* 24 | X134055201Y-47461500D03* 25 | X136595201Y-50001500D03* 26 | X134055201Y-50001500D03* 27 | X136595201Y-52541500D03* 28 | X134055201Y-52541500D03* 29 | X136595201Y-55081500D03* 30 | X134055201Y-55081500D03* 31 | X136595201Y-57621500D03* 32 | X134055201Y-57621500D03* 33 | X136595201Y-60161500D03* 34 | X134055201Y-60161500D03* 35 | X136595201Y-62701500D03* 36 | X134055201Y-62701500D03* 37 | X136595201Y-65241500D03* 38 | X134055201Y-65241500D03* 39 | X136595201Y-67781500D03* 40 | X134055201Y-67781500D03* 41 | X136595201Y-70321500D03* 42 | X134055201Y-70321500D03* 43 | X136595201Y-72861500D03* 44 | X134055201Y-72861500D03* 45 | X136595201Y-75401500D03* 46 | X134055201Y-75401500D03* 47 | X136595201Y-77941500D03* 48 | X134055201Y-77941500D03* 49 | X136595201Y-80481500D03* 50 | X134055201Y-80481500D03* 51 | X136595201Y-83021500D03* 52 | X134055201Y-83021500D03* 53 | X136595201Y-85561500D03* 54 | X134055201Y-85561500D03* 55 | X136595201Y-88101500D03* 56 | X134055201Y-88101500D03* 57 | X136595201Y-90641500D03* 58 | X134055201Y-90641500D03* 59 | X136595201Y-93181500D03* 60 | X134055201Y-93181500D03* 61 | D12* 62 | X179806600Y-52571500D03* 63 | D13* 64 | X179806600Y-55111500D03* 65 | X179806600Y-57651500D03* 66 | X179806600Y-60191500D03* 67 | X179806600Y-62731500D03* 68 | X179806600Y-65271500D03* 69 | X179806600Y-67811500D03* 70 | X179806600Y-70351500D03* 71 | X179806600Y-72891500D03* 72 | X179806600Y-75431500D03* 73 | X179806600Y-77971500D03* 74 | X179806600Y-80511500D03* 75 | X179806600Y-83051500D03* 76 | X179806600Y-85591500D03* 77 | X195046600Y-85591500D03* 78 | X195046600Y-83051500D03* 79 | X195046600Y-80511500D03* 80 | X195046600Y-77971500D03* 81 | X195046600Y-75431500D03* 82 | X195046600Y-72891500D03* 83 | X195046600Y-70351500D03* 84 | X195046600Y-67811500D03* 85 | X195046600Y-65271500D03* 86 | X195046600Y-62731500D03* 87 | X195046600Y-60191500D03* 88 | X195046600Y-57651500D03* 89 | X195046600Y-55111500D03* 90 | X195046600Y-52571500D03* 91 | D12* 92 | X183581600Y-74225000D03* 93 | D13* 94 | X183581600Y-76765000D03* 95 | X183581600Y-79305000D03* 96 | X183581600Y-81845000D03* 97 | X191201600Y-81845000D03* 98 | X191201600Y-79305000D03* 99 | X191201600Y-76765000D03* 100 | X191201600Y-74225000D03* 101 | D10* 102 | X162255200Y-44907200D03* 103 | D11* 104 | X159715200Y-44907200D03* 105 | X157175200Y-44907200D03* 106 | M02* 107 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-F_Mask.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:26-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Soldermask,Top* 6 | G04 #@! TF.FilePolarity,Negative* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:26* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | %ADD10R,1.700000X1.700000*% 15 | %ADD11O,1.700000X1.700000*% 16 | %ADD12R,1.600000X1.600000*% 17 | %ADD13O,1.600000X1.600000*% 18 | G04 APERTURE END LIST* 19 | D10* 20 | X136595201Y-44921500D03* 21 | D11* 22 | X134055201Y-44921500D03* 23 | X136595201Y-47461500D03* 24 | X134055201Y-47461500D03* 25 | X136595201Y-50001500D03* 26 | X134055201Y-50001500D03* 27 | X136595201Y-52541500D03* 28 | X134055201Y-52541500D03* 29 | X136595201Y-55081500D03* 30 | X134055201Y-55081500D03* 31 | X136595201Y-57621500D03* 32 | X134055201Y-57621500D03* 33 | X136595201Y-60161500D03* 34 | X134055201Y-60161500D03* 35 | X136595201Y-62701500D03* 36 | X134055201Y-62701500D03* 37 | X136595201Y-65241500D03* 38 | X134055201Y-65241500D03* 39 | X136595201Y-67781500D03* 40 | X134055201Y-67781500D03* 41 | X136595201Y-70321500D03* 42 | X134055201Y-70321500D03* 43 | X136595201Y-72861500D03* 44 | X134055201Y-72861500D03* 45 | X136595201Y-75401500D03* 46 | X134055201Y-75401500D03* 47 | X136595201Y-77941500D03* 48 | X134055201Y-77941500D03* 49 | X136595201Y-80481500D03* 50 | X134055201Y-80481500D03* 51 | X136595201Y-83021500D03* 52 | X134055201Y-83021500D03* 53 | X136595201Y-85561500D03* 54 | X134055201Y-85561500D03* 55 | X136595201Y-88101500D03* 56 | X134055201Y-88101500D03* 57 | X136595201Y-90641500D03* 58 | X134055201Y-90641500D03* 59 | X136595201Y-93181500D03* 60 | X134055201Y-93181500D03* 61 | D12* 62 | X179806600Y-52571500D03* 63 | D13* 64 | X179806600Y-55111500D03* 65 | X179806600Y-57651500D03* 66 | X179806600Y-60191500D03* 67 | X179806600Y-62731500D03* 68 | X179806600Y-65271500D03* 69 | X179806600Y-67811500D03* 70 | X179806600Y-70351500D03* 71 | X179806600Y-72891500D03* 72 | X179806600Y-75431500D03* 73 | X179806600Y-77971500D03* 74 | X179806600Y-80511500D03* 75 | X179806600Y-83051500D03* 76 | X179806600Y-85591500D03* 77 | X195046600Y-85591500D03* 78 | X195046600Y-83051500D03* 79 | X195046600Y-80511500D03* 80 | X195046600Y-77971500D03* 81 | X195046600Y-75431500D03* 82 | X195046600Y-72891500D03* 83 | X195046600Y-70351500D03* 84 | X195046600Y-67811500D03* 85 | X195046600Y-65271500D03* 86 | X195046600Y-62731500D03* 87 | X195046600Y-60191500D03* 88 | X195046600Y-57651500D03* 89 | X195046600Y-55111500D03* 90 | X195046600Y-52571500D03* 91 | D12* 92 | X183581600Y-74225000D03* 93 | D13* 94 | X183581600Y-76765000D03* 95 | X183581600Y-79305000D03* 96 | X183581600Y-81845000D03* 97 | X191201600Y-81845000D03* 98 | X191201600Y-79305000D03* 99 | X191201600Y-76765000D03* 100 | X191201600Y-74225000D03* 101 | D10* 102 | X162255200Y-44907200D03* 103 | D11* 104 | X159715200Y-44907200D03* 105 | X157175200Y-44907200D03* 106 | M02* 107 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-F_Cu.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:25-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Copper,L1,Top* 6 | G04 #@! TF.FilePolarity,Positive* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:25* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 #@! TA.AperFunction,ComponentPad* 15 | %ADD10R,1.700000X1.700000*% 16 | G04 #@! TD* 17 | G04 #@! TA.AperFunction,ComponentPad* 18 | %ADD11O,1.700000X1.700000*% 19 | G04 #@! TD* 20 | G04 #@! TA.AperFunction,ComponentPad* 21 | %ADD12R,1.600000X1.600000*% 22 | G04 #@! TD* 23 | G04 #@! TA.AperFunction,ComponentPad* 24 | %ADD13O,1.600000X1.600000*% 25 | G04 #@! TD* 26 | G04 APERTURE END LIST* 27 | D10* 28 | X136595201Y-44921500D03* 29 | D11* 30 | X134055201Y-44921500D03* 31 | X136595201Y-47461500D03* 32 | X134055201Y-47461500D03* 33 | X136595201Y-50001500D03* 34 | X134055201Y-50001500D03* 35 | X136595201Y-52541500D03* 36 | X134055201Y-52541500D03* 37 | X136595201Y-55081500D03* 38 | X134055201Y-55081500D03* 39 | X136595201Y-57621500D03* 40 | X134055201Y-57621500D03* 41 | X136595201Y-60161500D03* 42 | X134055201Y-60161500D03* 43 | X136595201Y-62701500D03* 44 | X134055201Y-62701500D03* 45 | X136595201Y-65241500D03* 46 | X134055201Y-65241500D03* 47 | X136595201Y-67781500D03* 48 | X134055201Y-67781500D03* 49 | X136595201Y-70321500D03* 50 | X134055201Y-70321500D03* 51 | X136595201Y-72861500D03* 52 | X134055201Y-72861500D03* 53 | X136595201Y-75401500D03* 54 | X134055201Y-75401500D03* 55 | X136595201Y-77941500D03* 56 | X134055201Y-77941500D03* 57 | X136595201Y-80481500D03* 58 | X134055201Y-80481500D03* 59 | X136595201Y-83021500D03* 60 | X134055201Y-83021500D03* 61 | X136595201Y-85561500D03* 62 | X134055201Y-85561500D03* 63 | X136595201Y-88101500D03* 64 | X134055201Y-88101500D03* 65 | X136595201Y-90641500D03* 66 | X134055201Y-90641500D03* 67 | X136595201Y-93181500D03* 68 | X134055201Y-93181500D03* 69 | D12* 70 | X179806600Y-52571500D03* 71 | D13* 72 | X179806600Y-55111500D03* 73 | X179806600Y-57651500D03* 74 | X179806600Y-60191500D03* 75 | X179806600Y-62731500D03* 76 | X179806600Y-65271500D03* 77 | X179806600Y-67811500D03* 78 | X179806600Y-70351500D03* 79 | X179806600Y-72891500D03* 80 | X179806600Y-75431500D03* 81 | X179806600Y-77971500D03* 82 | X179806600Y-80511500D03* 83 | X179806600Y-83051500D03* 84 | X179806600Y-85591500D03* 85 | X195046600Y-85591500D03* 86 | X195046600Y-83051500D03* 87 | X195046600Y-80511500D03* 88 | X195046600Y-77971500D03* 89 | X195046600Y-75431500D03* 90 | X195046600Y-72891500D03* 91 | X195046600Y-70351500D03* 92 | X195046600Y-67811500D03* 93 | X195046600Y-65271500D03* 94 | X195046600Y-62731500D03* 95 | X195046600Y-60191500D03* 96 | X195046600Y-57651500D03* 97 | X195046600Y-55111500D03* 98 | X195046600Y-52571500D03* 99 | D12* 100 | X183581600Y-74225000D03* 101 | D13* 102 | X183581600Y-76765000D03* 103 | X183581600Y-79305000D03* 104 | X183581600Y-81845000D03* 105 | X191201600Y-81845000D03* 106 | X191201600Y-79305000D03* 107 | X191201600Y-76765000D03* 108 | X191201600Y-74225000D03* 109 | D10* 110 | X162255200Y-44907200D03* 111 | D11* 112 | X159715200Y-44907200D03* 113 | X157175200Y-44907200D03* 114 | M02* 115 | -------------------------------------------------------------------------------- /software/man/piepro.1: -------------------------------------------------------------------------------- 1 | .\" Manpage for piepro. 2 | .\" Contact https://github.com/andrewteall/pi-eeprom-programmer/issues to correct errors or typos. 3 | 4 | .TH piepro 1 "15 Sep 2023" "1.0.0" "User Commands" 5 | 6 | .SH NAME 7 | piepro \- Read and Write EEPROMS via Raspberry pi 4/400 GPIO 8 | 9 | .SH SYNOPSIS 10 | piepro [OPTION]... 11 | 12 | .SH DESCRIPTION 13 | piepro is a program to read and write EEPROMS via Raspberry pi 4/400 GPIO 14 | 15 | .SH OPTIONS 16 | Mandatory arguments to long options are mandatory for short options too. 17 | 18 | .I 19 | .B -c, --compare 20 | .RS 21 | Compare FILE and EEPROM and print number of differences. 22 | .RE 23 | 24 | .I 25 | .B --chipname 26 | .RS 27 | Specify the chipname to use. Default: gpiochip0 28 | .RE 29 | 30 | .I 31 | .B -d, --dump [N] 32 | .RS 33 | Dump the contents of the EEPROM, 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII 34 | .RE 35 | 36 | .I 37 | .B -e [N], --erase [N] 38 | .RS 39 | Erase eeprom with specified byte. Default: 0xFF 40 | .RE 41 | 42 | .I 43 | .B -f, --force 44 | .RS 45 | Force writing of every byte instead of checking for existing value first. 46 | .RE 47 | 48 | .I 49 | .B -id, --i2c-device-id 50 | .RS 51 | The address id of the I2C device. 52 | .RE 53 | 54 | .I 55 | .B -h, --help 56 | .RS 57 | Print this message and exit. 58 | .RE 59 | 60 | .I 61 | .B -l, --limit N 62 | .RS 63 | Specify the maximum address to operate. 64 | .RE 65 | 66 | .I 67 | .B -m, --model MODEL 68 | .RS 69 | Specify EERPOM device model. Default: AT28C16. 70 | .RE 71 | 72 | .I 73 | .B --no-validate-write 74 | .RS 75 | Do not perform a read directly after writing to verify the data was written. 76 | .RE 77 | 78 | .I 79 | .B -r, --read [N] 80 | .RS 81 | Read the contents of the EEPROM, 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII 82 | .RE 83 | 84 | .I 85 | .B -rb, --read-byte ADDRESS 86 | .RS 87 | Read From specified ADDRESS. 88 | .RE 89 | 90 | .I 91 | .B -q [N], --quick [N] 92 | .RS 93 | Operates on N bytes at once for reads. Page Size if unspecified or writes. Implied --force and --no-validate-write. 94 | .RE 95 | 96 | .I 97 | .B -s, --start N 98 | .RS 99 | Specify the minimum address to operate. 100 | .RE 101 | 102 | .I 103 | .B -t, --text 104 | .RS 105 | Interpret file as a text. Default: binary 106 | .RS 107 | Text File format: 108 | [00000000]00000000 00000000 109 | .RE 110 | .RE 111 | 112 | .I 113 | .B -v, --v[vvvv] 114 | .RS 115 | Set the log verbosity to N, 0=OFF, 1=FATAL, 2=ERROR, 3=WARNING, 4=INFO, 5=DEBUG 6=TRACE. Default: WARNING 116 | .RE 117 | 118 | .I 119 | .B --version 120 | .RS 121 | Print the piepro version and exit. 122 | .RE 123 | 124 | .I 125 | .B -w, --write 126 | .RS 127 | Write EEPROM with specified file. 128 | .RE 129 | 130 | .I 131 | .B -wb, --write-byte ADDRESS DATA 132 | .RS 133 | Write specified DATA to ADDRESS. 134 | .RE 135 | 136 | .I 137 | .B -wd, --write-delay 138 | .RS 139 | Enable write delay. N Number of microseconds to delay between writes. 140 | .RE 141 | 142 | .I 143 | .B -y, --yes 144 | .RS 145 | Automatically answer Yes to write or erase EEPROM. 146 | .RE 147 | 148 | .SH NUMBER FORMATTING 149 | Numbers can be formatted in several ways: 150 | Decimal: 0-9 151 | Hexadecimal: 0x0-[fF] or $0-[fF] 152 | Binary: 00b0-1 or %0-1 153 | 154 | .SH QUIRKS 155 | --no-validate-write has no affect on I2C devices. 156 | 157 | .SH BUGS 158 | No known bugs. 159 | .SH AUTHOR 160 | Andrew Teall https://github.com/andrewteall/pi-eeprom-programmer/issues -------------------------------------------------------------------------------- /software/include/gpio.h: -------------------------------------------------------------------------------- 1 | #ifndef GPIO_H 2 | #define GPIO_H 1 3 | 4 | /** 5 | * @brief Levels used by GPIO. 6 | */ 7 | enum LEVEL {LOW=0, HIGH}; 8 | 9 | /** 10 | * @brief Pin Modes used by GPIO. 11 | */ 12 | enum PIN_MODE {INPUT=0, OUTPUT}; 13 | 14 | struct GPIO_CHIP{ 15 | // GPIO 16 | int isSetup; 17 | int numLinesInUse; 18 | struct gpiod_chip* chip; 19 | struct gpiod_line* gpioLines[40]; 20 | 21 | int numGPIOLines; 22 | char *chipname; 23 | char *consumer; 24 | 25 | // I2C 26 | int isI2CSetup; 27 | int fd; 28 | }; 29 | 30 | /** 31 | * @brief Setups Raspberry Pi GPIO Pins. 32 | * Must be called before any GPIO is used. 33 | * @param *gpioChip Pointer to the GPIO_CHIP struct to be used 34 | * @return int 0 if successful -1 if any error occurs. 35 | */ 36 | int setupGPIO(struct GPIO_CHIP* gpioChip); 37 | 38 | /** 39 | * @brief Set GPIO Pins to input or output. 40 | * @param *gpioChip Pointer to the GPIO_CHIP struct to be used 41 | * @param gpioLineNumber the GPIO number to be control direction. 42 | * @param direction The mode of the GPIO Pin. INPUT or OUTPUT 43 | * @return int 0 if successful -1 if any error occurs. 44 | */ 45 | int setPinModeGPIO(struct GPIO_CHIP* gpioChip, int gpioLineNumber, int direction); 46 | 47 | /** 48 | * @brief Read the value of the specified GPIO Pin. 49 | * @param *gpioChip Pointer to the GPIO_CHIP struct to be used 50 | * @param gpioLineNumber the GPIO number to be read from. 51 | * @return int 0 if successful -1 if any error occurs. 52 | */ 53 | int readGPIO(struct GPIO_CHIP* gpioChip, int gpioLineNumber); 54 | 55 | /** 56 | * @brief Write the value to the specified GPIO Pin. 57 | * @param *gpioChip Pointer to the GPIO_CHIP struct to be used 58 | * @param gpioLineNumber the GPIO number to be written to. 59 | * @param level The value to write. HIGH or LOW. 60 | * @return int 0 if successful -1 or non-zero if any error occurs. 61 | */ 62 | int writeGPIO(struct GPIO_CHIP* gpioChip,int gpioLineNumber,int level); 63 | 64 | /** 65 | * @brief Releases a gpiod_chip and an array of gpiod_lines. 66 | * @param *gpioChip Pointer to the GPIO_CHIP struct to be used 67 | */ 68 | void cleanupGPIO(struct GPIO_CHIP* gpioChip); 69 | 70 | /** 71 | * @brief Sets up GPIO pins to use the dedicated I2C function. 72 | * @param I2CId The address of the I2C device. Default 0x50. 73 | * @return int The file descriptor of the I2C device. -1 if error. 74 | */ 75 | int setupI2C(char I2CId); 76 | 77 | /** 78 | * @brief Reads a byte via the I2C bus. 79 | * @param fd The file descriptor of the I2C device to be used. 80 | * @param buf* Buffer for the bytes that are read. 81 | * @param numBytesToRead The number of bytes to read including the address. 82 | * @param addressSize The number of bytes of the address. 83 | * @return int Number of bytes read from the I2C device or -1 if error. 84 | */ 85 | int readI2C(int fd, char* buf, int numBytesToRead, int addressSize); 86 | 87 | /** 88 | * @brief Writes a byte via the I2C bus. 89 | * @param fd The file descriptor of the I2C device to be used. 90 | * @param data* The data to be written. 91 | * @param numBytesToWrite The number of bytes to write including the address. 92 | * @return int number of bytes writen if successful -1 if any error occurs. 93 | */ 94 | int writeI2C(int fd, char* data, int numBytesToWrite); 95 | 96 | /** 97 | * @brief Closes the specified File Descriptor(fd). 98 | * @param int The file descriptor of the I2C device to be closed. 99 | */ 100 | void cleanupI2C(int fd); 101 | 102 | #endif -------------------------------------------------------------------------------- /software/test/test_main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../include/piepro.h" // For END and EEPROM_MODEL_STRINGS 6 | 7 | int main(int argc,char *argv[]){ 8 | int testType = 0; 9 | int eepromModel = END; 10 | int generateData = 0; 11 | int suiteToRun = -1; 12 | int testToRun = -1; 13 | int runUnitTests = 1; 14 | int runFuncTests = 1; 15 | int numTestsFailed = 0; 16 | int limit = -1; 17 | 18 | // Parse Command Line Parameters 19 | for(int i = 1; i < argc; i++){ 20 | // -f --functional 21 | if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--functional")){ 22 | runUnitTests = 0; 23 | } 24 | 25 | // -g --generate 26 | if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--generate")){ 27 | generateData = 1; 28 | } 29 | 30 | // -go --generate-only 31 | if (!strcmp(argv[i], "-go") || !strcmp(argv[i], "--generate-only")){ 32 | generateData = 2; 33 | } 34 | 35 | // -i2c 36 | if (!strcmp(argv[i], "-i2c")){ 37 | testType = 1; 38 | } 39 | 40 | // -l --limit 41 | if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--limit")){ 42 | if (i != argc-1) { 43 | limit = (int)strtol(argv[i+1], NULL, 0); 44 | } else { 45 | printf("%s Flag must have a value specified\n", argv[i]); 46 | return 1; 47 | } 48 | } 49 | 50 | // -m --model 51 | if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--model")){ 52 | if (i != argc-1) { 53 | eepromModel = 0; 54 | while(eepromModel < END && strcmp(argv[i+1], EEPROM_MODEL_STRINGS[eepromModel])){ 55 | eepromModel++; 56 | } 57 | if(eepromModel == END){ 58 | printf("Unsupported EEPROM Model\n"); 59 | return 1; 60 | } 61 | } else { 62 | printf("%s Flag must have a model specified\n", argv[i]); 63 | return 1; 64 | } 65 | } 66 | 67 | // -s --suite 68 | if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--suite")){ 69 | if (i != argc-1) { 70 | suiteToRun = (int)strtol(argv[i+1], NULL, 0); 71 | } else { 72 | printf("%s Flag must have a value specified\n", argv[i]); 73 | return 1; 74 | } 75 | } 76 | 77 | // -t --test 78 | if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--test")){ 79 | if (i != argc-1) { 80 | testToRun = (int)strtol(argv[i+1], NULL, 0); 81 | } else { 82 | printf("%s Flag must have a value specified\n", argv[i]); 83 | return 1; 84 | } 85 | } 86 | 87 | // -u --unit 88 | if (!strcmp(argv[i], "-u") || !strcmp(argv[i], "--unit")){ 89 | runFuncTests = 0; 90 | } 91 | } 92 | 93 | // Make sure EEPROM Model is set 94 | if(eepromModel == END){ 95 | fprintf(stderr,"No EEPROM model specified. Please Specify an EEPROM model with the -m or --model flag.\n"); 96 | return 1; 97 | } 98 | 99 | // Determine which tests to run 100 | if(testType && runUnitTests){ 101 | numTestsFailed = run_I2C_tests(suiteToRun, testToRun); 102 | } else if(runUnitTests){ 103 | numTestsFailed = run_gpio_tests(suiteToRun, testToRun); 104 | } 105 | if(runFuncTests){ 106 | numTestsFailed = run_piepro_functional_tests(testType, eepromModel, generateData, suiteToRun, testToRun, limit); 107 | } 108 | 109 | // Print the results of the tests 110 | printResults(); 111 | 112 | // Return the number of failed tests 113 | return numTestsFailed; 114 | } -------------------------------------------------------------------------------- /software/test/uTest.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_EXPECT_H 2 | #define TEST_EXPECT_H 1 3 | 4 | /** 5 | * @brief Defines a Test. 6 | */ 7 | struct TEST_CONTAINER{ 8 | int testNumber; 9 | char* description; 10 | int suiteNumber; 11 | void (*test)(void); 12 | 13 | int expected; 14 | int actual; 15 | int status; 16 | int inverted; 17 | unsigned int timeToRunSec; 18 | unsigned int timeToRunMSec; 19 | }; 20 | 21 | /** 22 | * @brief Defines a Suite. 23 | */ 24 | struct SUITE_CONTAINER{ 25 | int suiteNumber; 26 | char* description; 27 | }; 28 | 29 | /** 30 | * @brief Compare expectation to actual and set the test status based on the comparison. 31 | * @param expectation The expected value. 32 | * @param actual The actual value. 33 | */ 34 | void expect(int expectation, int actual); 35 | 36 | /** 37 | * @brief Compare expectationNot to actual and set the test status based on the comparison. 38 | * @param expectation The value not expected. 39 | * @param actual The actual value. 40 | */ 41 | void expectNot(int expectationNot, int actual); 42 | 43 | /** 44 | * @brief Returns the current Functional Suite. 45 | * @return int The current Functional Suite. 46 | */ 47 | int getCurrentFuncSuite(); 48 | 49 | /** 50 | * @brief Returns the current Unit Suite. 51 | * @return int The current Unit Suite. 52 | */ 53 | int getCurrentUnitSuite(); 54 | 55 | /** 56 | * @brief Adds a new Functional Suite. 57 | * @param suiteDescription A point to the Suite Description. 58 | * @return int Just returns 0. 59 | */ 60 | int addFuncSuite(char* suiteDescription); 61 | 62 | /** 63 | * @brief Adds a new Unit Suite. 64 | * @param suiteDescription A point to the Suite Description. 65 | * @return int Just returns 0. 66 | */ 67 | int addUnitSuite(char* suiteDescription); 68 | 69 | /** 70 | * @brief Adds a new Functional Test. 71 | * @param description A pointer to the Test Description. 72 | * @param suiteNumber The Suite to add the test to. 73 | * @param testPtr Pointer to the test to add. 74 | * @return int Just returns 0. 75 | */ 76 | int addFuncTest(char* description, int suiteNumber, void (*testPtr)()); 77 | 78 | /** 79 | * @brief Adds a new Unit Test. 80 | * @param description A pointer to the Test Description. 81 | * @param suiteNumber The Suite to add the test to. 82 | * @param testPtr Pointer to the test to add. 83 | * @return int Just returns 0. 84 | */ 85 | int addUnitTest(char* description, int suiteNumber, void (*testPtr)()); 86 | 87 | /** 88 | * @brief Adds a initializer function to be run before every Functional Suite. 89 | * @param funcSuiteInitToAdd Pointer to the function to run before every Functional Suite. 90 | * @return int Just returns 0. 91 | */ 92 | int addFuncSuiteInit(void (*funcSuiteInitToAdd)(void)); 93 | 94 | /** 95 | * @brief Adds a initializer function to be run before every Functional Test. 96 | * @param funcTestInitToAdd Pointer to the function to run before every Functional Test. 97 | * @return int Just returns 0. 98 | */ 99 | int addFuncTestInit(void (*funcTestInitToAdd)(void)); 100 | 101 | /** 102 | * @brief Adds a initializer function to be run before every Unit Suite. 103 | * @param uintSuiteInitToAdd Pointer to the function to run before every Unit Suite. 104 | * @return int Just returns 0. 105 | */ 106 | int addunitSuiteInit(void (*unitSuiteInitToAdd)(void)); 107 | 108 | /** 109 | * @brief Adds a initializer function to be run before every Unit Test. 110 | * @param funcTestInitToAdd Pointer to the function to run before every Unit Test. 111 | * @return int Just returns 0. 112 | */ 113 | int addUnitTestInit(void (*unitTestInitToAdd)(void)); 114 | 115 | /** 116 | * @brief Runs Functional tests. 117 | * @param suiteNum The Functional Suite to run. -1 for all. 118 | * @param testNum The Functional Test to run. -1 for all. 119 | */ 120 | void runFuncTests(int suiteNum, int testNum); 121 | 122 | /** 123 | * @brief Runs Unit tests. 124 | * @param suiteNum The Unit Suite to run. -1 for all. 125 | * @param testNum The Unit Test to run. -1 for all. 126 | */ 127 | void runUnitTests(int suiteNum, int testNum); 128 | 129 | /** 130 | * @brief Prints The number of tests failed. 131 | */ 132 | void printResults(); 133 | 134 | /** 135 | * @brief Returns the number of tests failed globally. 136 | * @return int The number of tests failed globally. 137 | */ 138 | int getTestFailCounter(); 139 | #endif -------------------------------------------------------------------------------- /software/src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "piepro.h" 4 | 5 | int main(int argc, char *argv[]){ 6 | int error = 0; 7 | 8 | if (argc == 1){ 9 | printHelp(); 10 | error = -1; 11 | } else { 12 | /*********************************************************************/ 13 | /************************* Init Program ******************************/ 14 | struct EEPROM eeprom; 15 | struct GPIO_CONFIG gpioConfig; 16 | struct OPTIONS options; 17 | 18 | if(parseCommandLineOptions(&options, argc, argv)){ 19 | fprintf(stderr,"Error parsing command line options\n"); 20 | return -1; 21 | } 22 | 23 | if(initHardware(&options, &eeprom, &gpioConfig)){ 24 | fprintf(stderr,"Error initializing hardware\n"); 25 | return -1; 26 | } 27 | 28 | /*********************************************************************/ 29 | /************************* Program Start ****************************/ 30 | switch(options.action){ 31 | case WRITE_FILE_TO_ROM: 32 | case COMPARE_FILE_TO_ROM: 33 | { 34 | // open file to read 35 | FILE* romFile = fopen(options.filename, "r"); 36 | if(romFile == NULL){ 37 | fprintf(stderr,"Error Opening File\n"); 38 | error = -1; 39 | break; 40 | } 41 | if(options.action == WRITE_FILE_TO_ROM){ 42 | char confirmation = 'n'; 43 | if(options.promptUser){ 44 | printf("Are you sure you want to write to the EEPROM? y/N\n"); 45 | confirmation = getchar(); 46 | } 47 | if(confirmation == 'y' || confirmation == 'Y' || !options.promptUser){ 48 | error = writeFileToEEPROM(&gpioConfig, &eeprom, romFile); 49 | fprintf(stdout,"Wrote %i bytes\n", eeprom.byteWriteCounter); 50 | } else { 51 | printf("Aborting write operation.\n"); 52 | break; 53 | } 54 | 55 | } else { 56 | int bytesNotMatched = compareFileToEEPROM(&gpioConfig, &eeprom, romFile); 57 | if(bytesNotMatched == 0) { 58 | fprintf(stdout,"All bytes match\n"); 59 | } else if(bytesNotMatched == -1){ 60 | fprintf(stdout,"Error comparing file.\n"); 61 | error = -1; 62 | } else { 63 | error = -1; 64 | fprintf(stderr,"%i bytes do not match\n", bytesNotMatched); 65 | } 66 | } 67 | fclose(romFile); 68 | break; 69 | } 70 | case DUMP_ROM: 71 | error = printEEPROMContents(&gpioConfig, &eeprom, options.dumpFormat); 72 | break; 73 | case ERASE_ROM: 74 | { 75 | char confirmation = 'n'; 76 | if(options.promptUser){ 77 | printf("Are you sure you want to erase the EEPROM? y/N\n"); 78 | confirmation = getchar(); 79 | } 80 | if(confirmation == 'y' || confirmation == 'Y' || !options.promptUser){ 81 | error = eraseEEPROM(&gpioConfig, &eeprom, options.eraseByte); 82 | } else { 83 | printf("Aborting erase operation.\n"); 84 | break; 85 | } 86 | 87 | if(error == 0) { 88 | fprintf(stdout,"Sucessfully Erased %i bytes in EEPROM with 0x%02x\n", \ 89 | eeprom.byteWriteCounter, options.eraseByte); 90 | } else { 91 | error = -1; 92 | fprintf(stdout,"Unable to completely erase EEPROM with 0x%02x\n", options.eraseByte); 93 | } 94 | break; 95 | } 96 | case WRITE_SINGLE_BYTE_TO_ROM: 97 | error = writeByteToAddress(&gpioConfig, &eeprom, options.addressParam, options.dataParam); 98 | if(!error){ 99 | fprintf(stdout,"Wrote Byte: 0x%02x to Address: 0x%02x\n", options.dataParam, options.addressParam); 100 | } else { 101 | fprintf(stderr,"Error writing Byte: 0x%02x to Address: 0x%02x\n",options.dataParam,options.addressParam); 102 | } 103 | break; 104 | case READ_SINGLE_BYTE_FROM_ROM: 105 | { 106 | int readVal = readByteFromAddress(&gpioConfig, &eeprom, options.addressParam); 107 | if(readVal != -1){ 108 | fprintf(stdout,"0x%02x\n", readVal); 109 | } else { 110 | error = readVal; 111 | } 112 | break; 113 | } 114 | case NOTHING: 115 | fprintf(stdout,"No action specified. Run piepro -h for a list of options\n"); 116 | break; 117 | } 118 | 119 | /*********************************************************************/ 120 | /************************ Program Cleanup ****************************/ 121 | cleanupHardware(&gpioConfig, &eeprom); 122 | 123 | } 124 | 125 | return error; 126 | } -------------------------------------------------------------------------------- /hardware/LICENSE: -------------------------------------------------------------------------------- 1 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. 2 | Statement of Purpose 3 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). 4 | 5 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. 6 | 7 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 8 | 9 | 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: 10 | 11 | the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; 12 | moral rights retained by the original author(s) and/or performer(s); 13 | publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; 14 | rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; 15 | rights protecting the extraction, dissemination, use and reuse of data in a Work; 16 | database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and 17 | other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 18 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 19 | 20 | 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 21 | 22 | 4. Limitations and Disclaimers. 23 | 24 | No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. 25 | Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. 26 | Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. 27 | Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. -------------------------------------------------------------------------------- /software/Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Global Variables 3 | ################################################################################ 4 | # Compiler 5 | CC := gcc 6 | # CC := clang 7 | 8 | # Output Targets 9 | TARGET_EXEC := piepro 10 | TARGET_TEST_EXEC := test_runner_piepro 11 | TARGET_LIB := libpiepro.so 12 | 13 | # Libraries needed for Linker 14 | LIBRARIES := gpiod 15 | 16 | # Folder Paths 17 | BIN_DIR := bin/ 18 | LIB_DIR := lib/ 19 | MAN_DIR := man/ 20 | SRC_DIR := src/ 21 | TEST_SRC_DIR := test/ 22 | TEST_DATA_DIR := $(TEST_SRC_DIR)data 23 | INC_DIR := include/ 24 | BUILD_DIR := build/ 25 | 26 | # Lib Build Config 27 | LIB_EXCLUDE_FILES := main.o 28 | LIB_EXCLUDE_FILES := $(addprefix $(BUILD_DIR)$(SRC_DIR),$(LIB_EXCLUDE_FILES)) 29 | 30 | # These libs are needed to run(the former) and to build (the latter) 31 | # Not used in current Makefile 32 | DEPS := libgpiod2 libgpiod-dev 33 | 34 | ################################################################################ 35 | # Environment Variables 36 | ################################################################################ 37 | # PREFIX is environment variable, but if it is not set, then set default value 38 | ifeq ($(PREFIX),) 39 | PREFIX := /usr/local 40 | endif 41 | 42 | # MAN_PATH is environment variable, but if it is not set, then set default value 43 | ifeq ($(MAN_PATH),) 44 | MAN_PATH := /usr/local/man/man1/ 45 | endif 46 | 47 | ################################################################################ 48 | # Flags 49 | ################################################################################ 50 | # Define Include Flags 51 | INC_FLAGS := $(addprefix -I,$(INC_DIR)) 52 | 53 | # Define Linker Flags 54 | LDFLAGS := $(addprefix -l,$(LIBRARIES)) 55 | 56 | # Define Compiler Flags 57 | CFLAGS := $(INC_FLAGS) -Wall 58 | CFLAGS_LIB := $(INC_FLAGS) -Wall -shared -fPIC 59 | CFLAGS_TEST := $(INC_FLAGS) -Wall -Wno-implicit-function-declaration -g -DWITH_COLOR 60 | 61 | ################################################################################ 62 | # Source and Object Variables 63 | ################################################################################ 64 | # Define all the C files we want to compile 65 | SRCS := $(shell find $(SRC_DIR) -maxdepth 1 -name '*.c' -printf '%f\n') 66 | TEST_SRCS := $(shell find $(TEST_SRC_DIR) -maxdepth 1 -name '*.c' -printf '%f\n') 67 | 68 | # Define Output objects from Src Groups. 69 | # Convert *.c names to *.o and prepend the build path 70 | OBJS := $(subst .c.o,.o,$(SRCS:%=$(BUILD_DIR)$(SRC_DIR)%.o)) 71 | LIB_OBJS := $(subst .c.o,.o,$(SRCS:%=$(BUILD_DIR)$(LIB_DIR)%.o)) 72 | TEST_OBJS := $(subst .c.o,.o,$(TEST_SRCS:%=$(BUILD_DIR)$(TEST_SRC_DIR)%.o)) 73 | 74 | ################################################################################ 75 | # Object Build 76 | ################################################################################ 77 | # Object step for C source bin 78 | $(BUILD_DIR)$(SRC_DIR)%.o: $(SRC_DIR)%.c 79 | mkdir -p $(dir $@) 80 | $(CC) $(CFLAGS) -c $^ -o $@ 81 | 82 | # Object step for C source lib 83 | $(BUILD_DIR)$(LIB_DIR)%.o: $(SRC_DIR)%.c 84 | mkdir -p $(dir $@) 85 | $(CC) $(CFLAGS_LIB) -c $^ -o $@ 86 | 87 | # Object step for C source test 88 | $(BUILD_DIR)$(TEST_SRC_DIR)%.o: $(TEST_SRC_DIR)/%.c 89 | mkdir -p $(dir $@) 90 | $(CC) $(CFLAGS_TEST) -c $^ -o $@ 91 | 92 | ################################################################################ 93 | # Target Build 94 | ################################################################################ 95 | .PHONY: all 96 | all: piepro test_runner_piepro libpiepro 97 | 98 | .PHONY: piepro 99 | piepro: $(BIN_DIR)$(TARGET_EXEC) 100 | 101 | .PHONY: libpiepro 102 | libpiepro: $(LIB_DIR)$(TARGET_LIB) 103 | 104 | .PHONY: test_runner_piepro 105 | test_runner_piepro: $(BIN_DIR)$(TARGET_TEST_EXEC) 106 | 107 | # Bin Build 108 | $(BIN_DIR)$(TARGET_EXEC): $(OBJS) 109 | mkdir -p $(BIN_DIR) 110 | $(CC) -o $(BIN_DIR)$(TARGET_EXEC) $(OBJS) $(LDFLAGS) 111 | 112 | # Lib Build 113 | $(LIB_DIR)$(TARGET_LIB): $(LIB_OBJS) 114 | mkdir -p $(LIB_DIR) 115 | $(CC) -o $(LIB_DIR)$(TARGET_LIB) $(filter-out $(LIB_EXCLUDE_FILES),$(LIB_OBJS)) $(LDFLAGS) 116 | 117 | # Test Build 118 | $(BIN_DIR)$(TARGET_TEST_EXEC): $(TEST_OBJS) $(OBJS) 119 | mkdir -p $(BIN_DIR) 120 | $(CC) -o $(BIN_DIR)$(TARGET_TEST_EXEC) $(TEST_OBJS) $(filter-out $(LIB_EXCLUDE_FILES),$(OBJS)) $(LDFLAGS) 121 | 122 | ################################################################################ 123 | # Test 124 | ################################################################################ 125 | .PHONY: test 126 | test: test_runner_piepro test_run 127 | 128 | .PHONY: test_I2C 129 | test_I2C: test_runner_piepro test_run_I2C 130 | 131 | .PHONY: test_run 132 | test_run: 133 | @echo "Do you have EEPROM_MODEL set?" 134 | $(BIN_DIR)$(TARGET_TEST_EXEC) -m $(EEPROM_MODEL) -g 135 | 136 | .PHONY: test_run_I2C 137 | test_run_I2C: 138 | @echo "Do you have EEPROM_MODEL set?" 139 | $(BIN_DIR)$(TARGET_TEST_EXEC) -i2c -m $(EEPROM_MODEL) -g 140 | 141 | ################################################################################ 142 | # Install and Clean 143 | ################################################################################ 144 | .PHONY: install-all 145 | install-all: install install-lib 146 | 147 | .PHONY: uninstall-all 148 | uninstall-all: uninstall uninstall-lib 149 | 150 | .PHONY: install 151 | install: 152 | mkdir -p $(DESTDIR)$(PREFIX)/$(BIN_DIR) 153 | install $(BIN_DIR)$(TARGET_EXEC) $(DESTDIR)$(PREFIX)/$(BIN_DIR)$(TARGET_EXEC) 154 | mkdir -p $(MAN_PATH) 155 | install -g 0 -o 0 -m 0644 $(MAN_DIR)$(TARGET_EXEC).1 $(MAN_PATH) 156 | gzip $(MAN_PATH)$(TARGET_EXEC).1 157 | 158 | .PHONY: install-lib 159 | install-lib: 160 | mkdir -p $(DESTDIR)$(PREFIX)/$(LIB_DIR) 161 | install $(LIB_DIR)$(TARGET_LIB) $(DESTDIR)$(PREFIX)/$(LIB_DIR)$(TARGET_LIB) 162 | mkdir -p $(DESTDIR)$(PREFIX)/$(INC_DIR) 163 | install $(INC_DIR)$(TARGET_EXEC).h $(DESTDIR)$(PREFIX)/$(INC_DIR)$(TARGET_EXEC).h 164 | ldconfig 165 | 166 | .PHONY: uninstall 167 | uninstall: 168 | if [ -f $(DESTDIR)$(PREFIX)/$(BIN_DIR)$(TARGET_EXEC) ]; then rm $(DESTDIR)$(PREFIX)/$(BIN_DIR)$(TARGET_EXEC) ;fi 169 | if [ -f $(MAN_PATH)$(TARGET_EXEC).1.gz ]; then rm $(MAN_PATH)$(TARGET_EXEC).1.gz ;fi 170 | 171 | .PHONY: uninstall-lib 172 | uninstall-lib: 173 | if [ -f $(DESTDIR)$(PREFIX)/$(LIB_DIR)$(TARGET_LIB) ]; then rm $(DESTDIR)$(PREFIX)/$(LIB_DIR)$(TARGET_LIB) ;fi 174 | if [ -f $(DESTDIR)$(PREFIX)/$(INC_DIR)$(TARGET_EXEC).h ]; then rm $(DESTDIR)$(PREFIX)/$(INC_DIR)$(TARGET_EXEC).h ;fi 175 | ldconfig 176 | 177 | .PHONY: clean 178 | clean: 179 | if [ -f $(BIN_DIR)$(TARGET_EXEC) ]; then rm $(BIN_DIR)$(TARGET_EXEC);fi 180 | if [ -f $(LIB_DIR)$(TARGET_LIB) ]; then rm $(LIB_DIR)$(TARGET_LIB);fi 181 | if [ -f $(BIN_DIR)$(TARGET_TEST_EXEC) ]; then rm $(BIN_DIR)$(TARGET_TEST_EXEC);fi 182 | if [ -d $(BUILD_DIR) ]; then rm -r $(BUILD_DIR);fi 183 | if [ -d $(TEST_DATA_DIR) ]; then rm -r $(TEST_DATA_DIR);fi 184 | 185 | .PHONY: test_clean 186 | test_clean: 187 | if [ -f $(BIN_DIR)$(TARGET_TEST_EXEC) ]; then rm $(BIN_DIR)$(TARGET_TEST_EXEC);fi 188 | if [ -d $(TEST_DATA_DIR) ]; then rm -r $(TEST_DATA_DIR);fi 189 | -------------------------------------------------------------------------------- /hardware/README.md: -------------------------------------------------------------------------------- 1 | # __A Raspberry Pi GPIO EEPROM Writer and Reader__ 2 | 3 | A way to write Parallel and Serial EEPROMs using the Raspberry Pi GPIO pins. 4 | 5 | ## __Introduction__ 6 | The Raspberry Pi EEPROM Programmer is a hardware project that facilitates the programming and updating of EEPROMs using a Raspberry Pi. Instead of having to wire a circuit up on a bread board, the idea is just to be able to plug the EEPROM in to the pcb via a socket of some sort and then plug the pcb into the Raspberry Pi's GPIO pins. 7 | 8 | This README provides an overview of the features, hardware design, and usage instructions for the Raspberry Pi EEPROM Programmer. 9 | 10 | ## __Folder Structure__ 11 | 12 | ### Hardware Design 13 | The hardware design files for the Raspberry Pi EEPROM Programmer can be found in the [hardware](../hardware/design/) directory of this repository. The design includes: 14 | 15 | - **Kicad Project File:** The Kicad 6 project file to open the designs. 16 | - **Schematic:** The schematic diagram detailing the connections between components and the Raspberry Pi. 17 | - **PCB Layout:** The PCB layout design files used for creating the physical printed circuit board. 18 | 19 | The hardware design is created using industry-standard design software, and you are encouraged to review, modify, or contribute to the design according to your needs. 20 | 21 | ### Manufacturing 22 | The manufacturing files for the Raspberry Pi EEPROM Programmer can be found in the [manufacturing](../hardware/manufacturing/) directory of this repository. 23 | 24 | - **Bill of Materials:** A list of all components required for building the programmer. 25 | - **Gerbers:** Ready to build design files for the programmer. 26 | 27 | ### Repository Related Files 28 | 29 | - **.README.md:** Resource on how to build, use, and design this hardware. 30 | - **.gitignore:** Hardware specific gitignore for this repository. 31 | 32 | ## __Design Updates to the Hardware/Contributing__ 33 | Starting with version 1.0.0 I have tried not to have any breaking changes which should be very easy given such simple hardware. 34 | If you want to contribute and make any changes to this repo please ensure that you don't break any backwards compatibility to the hardware unless you plan on forking it and making it your own. When making non-breaking minor changes to the design please use a branch with the next minor version(1.+.0) or next patch version(1.x.+) and update the schematic and pcb accordingly. 35 | 36 | ## __Building the Hardware in Kicad__ 37 | When generating the appropriate gerber and drill files they should go in the [gerbers](./manufacturing/gerbers/) folder. Any new version should provide a zipped file in [manufacturing](./manufacturing/) folder containing all the build files and replacing the old version. 38 | ``` 39 | zip -j manufacturing/pi-eeprom-programmer-$(git rev-parse --abbrev-ref HEAD).zip manufacturing/gerbers/* 40 | ``` 41 | This should make it easy to upload the design to the pcb manufacturers. The defaults in Kicad should befine to generate gerbers and drill files only the output directory should have to be changed. 42 | 43 | ## __BOM__ 44 | | ID | Designator | Package | Quantity | Designation | Part Number/Link | 45 | |:--:|:----------:|------------------------------------|:--------:|-----------------------------|:---------------------------:| 46 | | 1 | J1 | PinSocket_2x20_P2.54mm_Horizontal | 1 | Raspberry Pi GPIO Connector | [SFH11-PBPC-D20-RA-BK](https://www.digikey.com/en/products/detail/sullins-connector-solutions/SFH11-PBPC-D20-RA-BK/1990101) | 47 | | 2 | U1 | DIP-28_W15.24mm_Socket | 1 | ZIF Socket | [28-6554-11](https://www.digikey.com/en/products/detail/aries-electronics/28-6554-11/27594) | 48 | | 3 | U2 | DIP-8_W7.62mm_Socket | 1 | | | 49 | | 4 | J2 | PinHeader_1x03_P2.54mm_Vertical | 1 | Conn_01x03 | [TS-103-G-A](https://www.digikey.com/en/products/detail/samtec-inc/TS-103-G-A/1105459) | 50 | | 5 | * | Conn_Jumper_Shorting | 1 | * | [QPC02SXGN-RC](https://www.digikey.com/en/products/detail/sullins-connector-solutions/QPC02SXGN-RC/2618262) | 51 | 52 | \* The Jumper Shunt is not listed anywhere in the schematic 53 | 54 | __Notes:__ 55 | * U2 is not required unless you are not using the ZIF Socket or 28-Pin IC Socket in which case an 8-Pin IC Socket may be used. 56 | * Similarly U1 may be substituted for a less expensive 28-Pin IC Socket. 57 | * In certain cases if the ZIF socket is not used both the 28-Pin Socket and 8-Pin Socket may be used at the same time. 58 | * All the parts can be found on via other sources so feel free to find them where they are the cheapest. 59 | 60 | 61 | ## __Assembling the PCB__ 62 | There are a very low number of components on the board and assembly is straight forward. One of the biggest options you have is the type of socket used. The BOM includes a Universal ZIF Socket(U1) that allows for plugging in both narrow and wide ICs, however a standard 28-Pin Wide DIP Socket(U1) can be used if you only need to program wide chips or an 8-Pin DIP Socket(U2) can be used for small serial EEPROMs. You can use either or, with a little encouragement, have both sockets in place at the same time. This is much cheaper than using the ZIF sockets but not as elegant(or as good for the IC Pins). 63 | 64 | J2 is a 1x3 Pin Header meant to be used with a shunt jumper to select between the different types of EEPROMs, 24-Pin or 28-Pin Parallel EEPROMS or 8 Pin Serial EEPROMs. You can also install a small SPDT sliding switch in place of the Pin Headers and the serial programming will work in either position. 65 | 66 | J1 should be right angle 40-Pin connector. If you use a standard vertical 40-Pin Connector it will orient the EEPROM Programmer vertical/perpendicular to a Pi 400 and parallel and updside down to a standard Pi 4. 67 | 68 | While I've included sources for all the parts feel free to use your own or source them from wherever you like or can find them cheapest. I've used a number of combinations of different components in testing and they've all worked fine. 69 | 70 | Start by install the Sockets(U1/U2)(smallest first if you are trying to use both). Then install the Pin Header(JP2) and add your jumper shunt. Finally, install the right angle 40-Pin Connector. 71 | 72 | ## __Installation/Usage__ 73 | The Pi EEPROM Programmer should plug right into the GPIO Pins of the Raspberry Pi 4 or 400. Take note to make sure the pins are aligned correctly if you opt not to use a keyed 40-Pin connector. 74 | 75 | For the Pi 400 the Programmer should stick out of the back of the case and for a Pi 4 the programmer should be vertical straight up assuming you are using the standard components and not others mentioned above. 76 | 77 | You can then use the software provided in this repositor to read and write EEPROMs. 78 | 79 | ## __EEPROM Voltages__ 80 | See [A Quick Note About EEPROM Voltages](../README.md#a-quick-note-about-eeprom-voltages) 81 | 82 | ## __Features, Bugs, and Contributing__ 83 | If there's anything you'd like to add or find a bug please open an [issue](https://github.com/andrewteall/pi-eeprom-programmer/issues). If you'd like to implement those chages yourself please feel free to open a PR or fork the repository and make it your own. 84 | 85 | 86 | ## __License__ 87 | This hardware project is licensed under the [CC0](LICENSE). You are free to use, modify, and distribute the project according to the terms of the license. 88 | -------------------------------------------------------------------------------- /software/include/piepro.h: -------------------------------------------------------------------------------- 1 | #ifndef PIEPRO_H 2 | #define PIEPRO_H 1 3 | #include "gpio.h" 4 | 5 | #define MAJOR "1" 6 | #define MINOR "0" 7 | #define PATCH "0" 8 | #define VERSION MAJOR "." MINOR "." PATCH 9 | 10 | #define MAX_ADDRESS_PINS 16 11 | #define MAX_DATA_PINS 8 12 | 13 | /** 14 | * @brief Enumeration of the different supported EEProm models. 15 | */ 16 | enum EEPROM_MODEL {XL2816,XL28C16, 17 | AT28C16,AT28C64,AT28C256, 18 | AT24C01,AT24C02,AT24C04,AT24C08,AT24C16,AT24C32,AT24C64,AT24C128,AT24C256,AT24C512,END}; 19 | 20 | /** 21 | * @brief Enumeration of the different types of supported EEProm protocols. 22 | */ 23 | enum EEPROM_TYPE {PARALLEL,I2C}; 24 | 25 | /** 26 | * @brief Defines file types supported. 27 | */ 28 | enum FILE_TYPE {TEXT_FILE,BINARY_FILE}; 29 | 30 | /** 31 | * @brief Defines print formats when dumping the EEPROM. 32 | */ 33 | enum PRINT_FORMAT {PRETTY_WITH_ASCII=0,BINARY,TEXT,LABELED,PRETTY}; 34 | 35 | /** 36 | * @brief Defines the supported functions of the programmer. 37 | */ 38 | enum APP_FUNCTIONS { 39 | NOTHING, 40 | HELP, 41 | VER, 42 | WRITE_FILE_TO_ROM, 43 | COMPARE_FILE_TO_ROM, 44 | WRITE_SINGLE_BYTE_TO_ROM, 45 | READ_SINGLE_BYTE_FROM_ROM, 46 | ERASE_ROM, 47 | DUMP_ROM 48 | }; 49 | 50 | /** 51 | * @brief Defines the different SoC board types supported. 52 | */ 53 | enum BOARD_TYPE{RPI4}; 54 | 55 | /** 56 | * @brief Array of EEProm model strings correlating to the EEProm models. 57 | */ 58 | extern const char* EEPROM_MODEL_STRINGS[]; 59 | 60 | /** 61 | * @brief Array of EEProm model sizes correlating to the EEProm models. 62 | */ 63 | extern const int EEPROM_MODEL_SIZE[]; 64 | 65 | /** 66 | * @brief Array of EEProm model address pin counts correlating to the EEProm models. 67 | */ 68 | extern const int EEPROM_ADDRESS_LENGTH[]; 69 | 70 | /** 71 | * @brief Array of EEProm model data pin counts correlating to the EEProm models. 72 | */ 73 | extern const int EEPROM_DATA_LENGTH[]; 74 | 75 | /** 76 | * @brief Array of EEProm model write cycle timings correlating to the EEProm models. 77 | */ 78 | extern const int EEPROM_WRITE_CYCLE_USEC[]; 79 | 80 | /** 81 | * @struct OPTIONS 82 | * @brief This structure contains all the configuration parameters for a given 83 | * system. 84 | */ 85 | struct OPTIONS { 86 | // Options 87 | char* filename; 88 | long limit; 89 | long startValue; 90 | int dumpFormat; 91 | int validateWrite; 92 | int force; 93 | int action; 94 | int fileType; 95 | int eepromModel; 96 | int writeCycleUSec; 97 | int useWriteCyclePolling; 98 | int boardType; 99 | int quick; 100 | int readChunk; 101 | int promptUser; 102 | // Single Read/Write Parameters 103 | int addressParam; 104 | int dataParam; 105 | // I2C Specific 106 | char i2cId; 107 | char* consumer; 108 | char* chipname; 109 | int numGPIOLines; 110 | char eraseByte; 111 | }; 112 | 113 | /** 114 | * @brief EEPROM struct to hold eeprom configuration parameters. 115 | */ 116 | struct EEPROM{ 117 | // All 118 | int type; 119 | int forceWrite; 120 | int validateWrite; 121 | int addressPins[MAX_ADDRESS_PINS]; 122 | int dataPins[MAX_DATA_PINS]; 123 | int writeEnablePin; 124 | int outputEnablePin; 125 | int chipEnablePin; 126 | int vccPin; 127 | int writeCycleTime; 128 | int useWriteCyclePolling; 129 | int limit; 130 | int startValue; 131 | int fileType; 132 | int quick; 133 | int readChunk; 134 | 135 | int size; 136 | int maxAddressLength; 137 | int maxDataLength; 138 | 139 | // I2C 140 | int fd; 141 | int i2cId; 142 | char writeProtectPin; 143 | int pageSize; 144 | int addressSize; 145 | 146 | // Info 147 | int model; 148 | int byteWriteCounter; 149 | int byteReadCounter; 150 | }; 151 | 152 | /** 153 | * @brief GPIO_CONFIG struct to hold gpio chip configuration parameters. 154 | */ 155 | struct GPIO_CONFIG{ 156 | int numGPIOLines; 157 | char *chipname; 158 | char *consumer; 159 | struct GPIO_CHIP gpioChip; 160 | }; 161 | 162 | /** 163 | * @brief Initializes and EEProm struct with the porvided options. 164 | * @param *eeprom A eeprom struct that contains the eeprom info. 165 | * @param *options A pointer to the OPTIONS struct to reference all the configured options. 166 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 167 | * @return int 0 if successful. Non-zero if error. 168 | */ 169 | int initHardware(struct OPTIONS *options, struct EEPROM* eeprom, struct GPIO_CONFIG* gpioChip); 170 | 171 | /** 172 | * @brief Compares a file to the EEPROM given the specified options. 173 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 174 | * @param *eeprom A eeprom struct that contains the eeprom info. 175 | * @param *romFile A pointer to File to compare. 176 | * @return int 0 if successful. Non-zero if error. 177 | */ 178 | int compareFileToEEPROM(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom,FILE *romFile); 179 | 180 | /** 181 | * @brief Writes a file to the EEPROM given the specified options. 182 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 183 | * @param *eeprom A eeprom struct that contains the eeprom info. 184 | * @param *romFile A pointer to File to read from. 185 | * @return int 0 if successful. Non-zero if error. 186 | */ 187 | int writeFileToEEPROM(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom, FILE *romFile); 188 | 189 | /** 190 | * @brief Read a byte from a specified address. 191 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 192 | * @param *eeprom A eeprom struct that contains the eeprom info. 193 | * @param addressToRead The address to read from on the EEPROM. 194 | * @return int Returns the value of the byte read. 195 | */ 196 | int readByteFromAddress(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom, int addressToRead); 197 | 198 | /** 199 | * @brief Writes a byte to a specified address. 200 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 201 | * @param *eeprom A eeprom struct that contains the eeprom info. 202 | * @param addressToWrite The address to write to on the EEPROM. 203 | * @param dataToWrite The data to write to on the EEPROM. 204 | * @return int Returns 0 if successful -1 if error. 205 | */ 206 | int writeByteToAddress(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom, int addressToWrite, char dataToWrite); 207 | 208 | /** 209 | * @brief Prints the contents of the EEPROM accoring to options. 210 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 211 | * @param *eeprom A eeprom struct that contains the eeprom info. 212 | * @param format The format to print the EEPROM contents. 213 | * @return int Returns 0 if successful -1 if error. 214 | */ 215 | int printEEPROMContents(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom, int format); 216 | 217 | /** 218 | * @brief Writes a byte to a specified address. 219 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 220 | * @param *eeprom A eeprom struct that contains the eeprom info. 221 | * @param eraseByte Byte used to erase EEPROM. 222 | * @return int Returns 0 if successful -1 if error. 223 | */ 224 | int eraseEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char eraseByte); 225 | 226 | /** 227 | * @brief Releases and frees GPIO hardware. 228 | * @param *gpioChip A pointer to the GPIO_CONFIG struct to reference gpio chip to be used. 229 | * @param *eeprom A eeprom struct that contains the eeprom info. 230 | */ 231 | void cleanupHardware(struct GPIO_CONFIG* gpioChip, struct EEPROM* eeprom); 232 | 233 | /** 234 | * @brief Sets default options to configure program 235 | * @param *options The struct of options to use 236 | */ 237 | void setDefaultOptions(struct OPTIONS* options); 238 | 239 | /** 240 | * @brief Parses all the command line Arguments and sets the appropriate options 241 | * in the OPTIONS struct. 242 | * @param options A pointer to the OPTIONS struct to store all the configured 243 | * options. 244 | * @param argc Count of command line arguments. 245 | * @param argv A pointer to the Array of command line arguments. 246 | * @return int 0 is no errors occured. 247 | */ 248 | int parseCommandLineOptions(struct OPTIONS* options,int argc, char* argv[]); 249 | 250 | /** 251 | * @brief Prints the command usage. 252 | */ 253 | void printHelp(void); 254 | 255 | /** 256 | * @brief Prints the current version of the application. 257 | */ 258 | void printVersion(void); 259 | #endif -------------------------------------------------------------------------------- /software/src/gpio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gpio.h" 9 | #include "utils.h" 10 | #include "ulog.h" 11 | 12 | #define MAX_USABLE_GPIO_LINES 34 13 | #define SDA1_PIN 2 14 | #define SCL1_PIN 3 15 | #define BLOCK_SIZE 4096 16 | 17 | enum ALT_MODE {ALT0=0,ALT1,ALT2,ALT3,ALT4,ALT5}; 18 | 19 | // GPIO setup macros. Always use INP_GPIO(x) before using OUT_GPIO(x) or SET_GPIO_ALT(x,y) 20 | #define INP_GPIO(gpio,g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) 21 | #define SET_GPIO_ALT(gpio,g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) 22 | 23 | /* Set the ALT MODE of the specified pin */ 24 | void setPinAltModeGPIO(volatile unsigned int* gpio, int pin, enum ALT_MODE altMode){ 25 | INP_GPIO(gpio,pin); 26 | SET_GPIO_ALT(gpio,pin,altMode); 27 | } 28 | 29 | /* Ensure chip and line number are valid */ 30 | int checkConfigGPIO(struct GPIO_CHIP* gpioChip, int gpioLineNumber){ 31 | int rtnVal = 0; 32 | 33 | if(gpioChip == NULL){ 34 | ulog(ERROR,"gpioChip cannot be NULL."); 35 | return -1; 36 | } 37 | if (gpioChip != NULL && gpioChip->isSetup != 1){ 38 | ulog(ERROR,"GPIO is not setup."); 39 | return -1; 40 | } 41 | if( gpioLineNumber < 0 || gpioLineNumber >= gpioChip->numLinesInUse){ 42 | ulog(ERROR,"Pin Number: %i is out of range of configured Pins",gpioLineNumber); 43 | return -1; 44 | } 45 | return rtnVal; 46 | } 47 | 48 | /* Sets up GPIO to be used */ 49 | int setupGPIO(struct GPIO_CHIP* gpioChip){ 50 | int err = 0; 51 | 52 | if(gpioChip == NULL){ 53 | ulog(ERROR,"gpioChip cannot be NULL"); 54 | return -1; 55 | } 56 | if(gpioChip->chipname == NULL){ 57 | ulog(ERROR,"Chipname cannot be NULL"); 58 | return -1; 59 | } 60 | if(gpioChip->consumer == NULL){ 61 | ulog(ERROR,"Consumer cannot be NULL"); 62 | return -1; 63 | } 64 | if(gpioChip->isSetup){ 65 | ulog(ERROR,"GPIO is already setup"); 66 | return -1; 67 | } 68 | 69 | gpioChip->chip = gpiod_chip_open_by_name(gpioChip->chipname); 70 | if (gpioChip->chip == NULL){ 71 | ulog(ERROR,"Unable to get chip by name: %s",gpioChip->chipname); 72 | return -1; 73 | } 74 | if(gpioChip->numGPIOLines > MAX_USABLE_GPIO_LINES && gpioChip->numGPIOLines < gpiod_chip_num_lines(gpioChip->chip)){ 75 | ulog(ERROR,"Invalid Line Count Requested for Chip. Max for Chip: %i",MAX_USABLE_GPIO_LINES); 76 | return -1; 77 | } 78 | if(gpioChip->numGPIOLines >= gpiod_chip_num_lines(gpioChip->chip) || gpioChip->numGPIOLines < 0){ 79 | ulog(ERROR,"Invalid Number of Lines Requested for Chip. Max: %i",gpiod_chip_num_lines(gpioChip->chip)); 80 | return -1; 81 | } 82 | 83 | for(int i=0; i < gpioChip->numGPIOLines && err == 0; i++){ 84 | gpioChip->gpioLines[i] = gpiod_chip_get_line(gpioChip->chip, i); 85 | if(gpioChip->gpioLines[i] == NULL){ 86 | ulog(ERROR,"Unable to get line: %i",i); 87 | err = -1; 88 | } else { 89 | err = gpiod_line_request_output(gpioChip->gpioLines[i], gpioChip->consumer, 0); 90 | if(err){ 91 | ulog(ERROR,"Error initially requesting line for OUTPUT"); 92 | } 93 | } 94 | } 95 | if(err != -1){ 96 | gpioChip->isSetup = 1; 97 | gpioChip->numLinesInUse = gpioChip->numGPIOLines; 98 | } 99 | 100 | return err; 101 | } 102 | 103 | /* Set the GPIO Pin mode to be INPUT or OUTPUT */ 104 | int setPinModeGPIO(struct GPIO_CHIP* gpioChip, int gpioLineNumber, int pinMode){ 105 | int err = 0; 106 | 107 | if(checkConfigGPIO(gpioChip,gpioLineNumber)){ 108 | return -1; 109 | } 110 | 111 | if(pinMode == OUTPUT){ 112 | // output 113 | err = gpiod_line_set_config(gpioChip->gpioLines[gpioLineNumber],GPIOD_LINE_REQUEST_DIRECTION_OUTPUT,0,0); 114 | if(err){ 115 | ulog(ERROR,"Error requesting OUTPUT"); 116 | } 117 | } else if(pinMode == INPUT){ 118 | // input 119 | err = gpiod_line_set_config(gpioChip->gpioLines[gpioLineNumber],GPIOD_LINE_REQUEST_DIRECTION_INPUT,0,0); 120 | if(err){ 121 | ulog(ERROR,"Error requesting INPUT"); 122 | } 123 | } else { 124 | ulog(ERROR,"Invalid Pin Mode: %i",pinMode); 125 | err = -1; 126 | } 127 | return err; 128 | } 129 | 130 | /* Read from a specified GPIO Pin */ 131 | int readGPIO(struct GPIO_CHIP* gpioChip, int gpioLineNumber){ 132 | int val = 0; 133 | 134 | if(checkConfigGPIO(gpioChip,gpioLineNumber)){ 135 | return -1; 136 | } 137 | 138 | if((gpiod_line_direction(gpioChip->gpioLines[gpioLineNumber]) == GPIOD_LINE_DIRECTION_INPUT)){ 139 | val = gpiod_line_get_value(gpioChip->gpioLines[gpioLineNumber]); 140 | if( val == -1){ 141 | ulog(ERROR,"Failed to read input on Pin %i",gpioLineNumber); 142 | } 143 | } else { 144 | val = -1; 145 | ulog(ERROR,"Pin: %i not configured as INPUT. Cannot be read.",gpioLineNumber); 146 | } 147 | return val; 148 | } 149 | 150 | /* Write a level to a specified GPIO Pin */ 151 | int writeGPIO(struct GPIO_CHIP* gpioChip,int gpioLineNumber,int level){ 152 | int err = 0; 153 | 154 | if(checkConfigGPIO(gpioChip,gpioLineNumber)){ 155 | return -1; 156 | } 157 | if(level != 0 && level != 1){ 158 | ulog(ERROR,"Invalid level %i for line: %i", level,gpioLineNumber); 159 | return -1; 160 | } 161 | 162 | if((gpiod_line_direction(gpioChip->gpioLines[gpioLineNumber]) == GPIOD_LINE_DIRECTION_OUTPUT)){ 163 | err = gpiod_line_set_value(gpioChip->gpioLines[gpioLineNumber],level); 164 | if(err){ 165 | ulog(ERROR,"Cound not set level: %i on line number: %i",level,gpioLineNumber); 166 | } 167 | } else { 168 | err = -1; 169 | ulog(ERROR,"Pin: %i not configured as OUTPUT. Cannot be written.",gpioLineNumber); 170 | } 171 | return err; 172 | } 173 | 174 | /* Release the chip and GPIO lines */ 175 | void cleanupGPIO(struct GPIO_CHIP* gpioChip){ 176 | if(gpioChip->isSetup){ 177 | gpioChip->isSetup = 0; 178 | // Releasing the gpioLines may not be needed since I think it's handled by 179 | // gpiod_chip_close 180 | for(int i=0; i < gpioChip->numLinesInUse; i++){ 181 | gpiod_line_release(gpioChip->gpioLines[i]); 182 | } 183 | gpiod_chip_close(gpioChip->chip); 184 | } 185 | 186 | } 187 | 188 | /*****************************************************************************/ 189 | /*****************************************************************************/ 190 | /*****************************************************************************/ 191 | 192 | /* Map memory used by GPIO */ 193 | volatile unsigned int* mapGPIOMemory(){ 194 | int mem_fd; 195 | void *gpio_map; 196 | 197 | /* open /dev/gpiomem */ 198 | if ((mem_fd = open("/dev/gpiomem", O_RDWR|O_SYNC) ) < 0) { 199 | ulog(ERROR,"Error opening /dev/gpiomem"); 200 | return (volatile unsigned int*)-1; 201 | } 202 | 203 | /* mmap GPIO */ 204 | gpio_map = mmap( 205 | NULL, //Any adddress in our space will do 206 | BLOCK_SIZE, //Map length 207 | PROT_READ|PROT_WRITE, // Enable reading & writting to mapped memory 208 | MAP_SHARED, //Shared with other processes 209 | mem_fd, //File to map 210 | 0 //Offset to GPIO peripheral 211 | ); 212 | 213 | close(mem_fd); //No need to keep mem_fd open after mmap 214 | 215 | if (gpio_map == MAP_FAILED) { 216 | ulog(ERROR,"mmap error");//errno also set! 217 | return (volatile unsigned int*)-1; 218 | } 219 | return (volatile unsigned int*)gpio_map; 220 | } 221 | 222 | /* Sets up an I2C device to be used via the built in I2C pins */ 223 | int setupI2C(char I2CId){ 224 | // Always use volatile pointer! 225 | volatile unsigned int* gpio = mapGPIOMemory(); 226 | if(gpio == (volatile unsigned int*)-1){ 227 | return -1; 228 | } 229 | 230 | setPinAltModeGPIO(gpio, SDA1_PIN, ALT0); 231 | setPinAltModeGPIO(gpio, SCL1_PIN, ALT0); 232 | 233 | ulog(INFO,"Setting up I2C Device with ID: 0x%02x",I2CId); 234 | int fd = open("/dev/i2c-1", O_RDWR ); 235 | if(fd == -1){ 236 | ulog(ERROR,"Error opening device."); 237 | return -1; 238 | } 239 | if(ioctl(fd, I2C_SLAVE, I2CId) == -1){ 240 | ulog(ERROR,"Error configuring device."); 241 | return -1; 242 | } 243 | // Dummy write to make sure device is setup 244 | if(write(fd, NULL, 0) != 0){ 245 | ulog(ERROR,"Device not available"); 246 | return -1; 247 | } 248 | return fd; 249 | } 250 | 251 | /* Read from a specified address via I2C */ 252 | int readI2C(int fd, char* buf, int numBytesToRead, int addressSize){ 253 | int bytesWritten = write(fd, buf, addressSize); 254 | if(bytesWritten == -1){ 255 | ulog(ERROR,"Error reading byte(s) via I2C"); 256 | return -1; 257 | } 258 | int bytesRead = read(fd,buf,numBytesToRead); 259 | if(bytesRead == -1){ 260 | buf[0] = bytesRead; 261 | ulog(ERROR,"Error reading byte(s) via I2C"); 262 | return -1; 263 | } 264 | return bytesRead; 265 | } 266 | 267 | /* Write page to a specified address via I2C */ 268 | int writeI2C(int fd, char* data, int numBytesToWrite){ 269 | // Write the byte[s] 270 | int bytesWritten = write(fd, data, numBytesToWrite); 271 | if(bytesWritten == -1 && data != NULL && numBytesToWrite != 0){ 272 | ulog(ERROR,"Error writing byte(s) via I2C"); 273 | } 274 | return bytesWritten; 275 | } 276 | 277 | /* Closes a specified I2C device */ 278 | void cleanupI2C(int fd){ 279 | close(fd); 280 | } 281 | -------------------------------------------------------------------------------- /software/test/test_generateData.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "../include/utils.h" 7 | 8 | /* Generate a Random Number */ 9 | int getRandomData(int limit){ 10 | return (rand() % (limit + 1)); 11 | } 12 | 13 | /* Get the next set of Data from a text file formatted rom */ 14 | int getAddressOrDataFromTextFile(FILE *inputFile){ 15 | int c; 16 | int strLen = 0; 17 | char textFileString[(sizeof(int)*8)+1]; 18 | 19 | while((c = fgetc(inputFile)) != EOF && strLen != sizeof(int)*8 && c != '\n' && c != ' ' && c != ':'){ 20 | // Will skip characters that are not '1' or '0' 21 | if(c == '1' || c == '0'){ 22 | textFileString[strLen++] = c; 23 | } 24 | } 25 | 26 | textFileString[strLen++] = 0; 27 | if(c == EOF){ 28 | return -1; 29 | } else { 30 | return binStr2num(textFileString); 31 | } 32 | } 33 | 34 | /******************************************************************************/ 35 | /******************************************************************************/ 36 | /******************************************************************************/ 37 | 38 | int generateBinaryFile(char* filename, int size){ 39 | FILE* outputFile = fopen(filename,"w"); 40 | if(outputFile == NULL){ 41 | fprintf(stderr,"Error Opening File\n"); 42 | return 1; 43 | } 44 | 45 | printf("Using output file name: %s\n", filename); 46 | for(int i=0; i < size; i++){ 47 | fputc(getRandomData(256),outputFile); 48 | } 49 | 50 | fclose(outputFile); 51 | 52 | return 0; 53 | } 54 | 55 | int generateTextFile(char* filename, int size){ 56 | FILE* outputFile = fopen(filename,"w"); 57 | if(outputFile == NULL){ 58 | fprintf(stderr,"Error Opening File\n"); 59 | return 1; 60 | } 61 | 62 | printf("Using output file name: %s\n", filename); 63 | char addressString[17]; 64 | char dataString[9]; 65 | for(int i=0; i < size; i++){ 66 | num2binStr(i,addressString,17); 67 | fputs(addressString,outputFile); 68 | fputc(' ',outputFile); 69 | num2binStr((char)getRandomData(256),dataString,9); 70 | fputs(dataString,outputFile); 71 | fputc('\n',outputFile); 72 | } 73 | 74 | fclose(outputFile); 75 | 76 | return 0; 77 | } 78 | 79 | int convertBinaryFileToTextFile(char* inputFilename){ 80 | int inputFilenameLen = strlen(inputFilename); 81 | char outputFilename[inputFilenameLen+15]; 82 | 83 | int found = 0; 84 | int extension = inputFilenameLen; 85 | while(!found && extension > 0){ 86 | if(inputFilename[extension] == '.'){ 87 | found = 1; 88 | } else { 89 | --extension; 90 | } 91 | } 92 | 93 | if(extension != 0){ 94 | strncpy(outputFilename,inputFilename,extension+1); 95 | outputFilename[extension] = '\0'; 96 | 97 | // Decide filename 98 | strcat(outputFilename,".txt\0"); 99 | 100 | printf("Using output file name: %s\n", outputFilename); 101 | 102 | 103 | FILE* inputFile = fopen(inputFilename,"r"); 104 | if(inputFile == NULL){ 105 | fprintf(stderr,"Error Opening File\n"); 106 | return 1; 107 | } 108 | FILE* outputFile = fopen(outputFilename,"w"); 109 | if(outputFile == NULL){ 110 | fprintf(stderr,"Error Opening File\n"); 111 | return 1; 112 | } 113 | 114 | int dataByte; 115 | int address = 0; 116 | char addressString[17]; 117 | char dataString[9]; 118 | while((dataByte = fgetc(inputFile)) != EOF){ 119 | num2binStr(address,addressString,17); 120 | fputs(addressString,outputFile); 121 | fputc(' ',outputFile); 122 | num2binStr((char)dataByte,dataString,9); 123 | fputs(dataString,outputFile); 124 | fputc('\n',outputFile); 125 | 126 | address++; 127 | } 128 | 129 | fclose(inputFile); 130 | fclose(outputFile); 131 | } else { 132 | return 1; 133 | } 134 | return 0; 135 | } 136 | 137 | int convertBinaryFileToUnmatchedBinaryFile(char* inputFilename){ 138 | int inputFilenameLen = strlen(inputFilename); 139 | char outputFilename[inputFilenameLen+15]; 140 | 141 | int found = 0; 142 | int extension = inputFilenameLen; 143 | while(!found && extension > 0){ 144 | if(inputFilename[extension] == '.'){ 145 | found = 1; 146 | } else { 147 | --extension; 148 | } 149 | } 150 | 151 | if(extension != 0){ 152 | strncpy(outputFilename,inputFilename,extension+1); 153 | outputFilename[extension] = '\0'; 154 | 155 | // Decide filename 156 | strcat(outputFilename,"-unmatched.bin\0"); 157 | 158 | printf("Using output file name: %s\n", outputFilename); 159 | 160 | 161 | FILE* inputFile = fopen(inputFilename,"r"); 162 | if(inputFile == NULL){ 163 | fprintf(stderr,"Error Opening File\n"); 164 | return 1; 165 | } 166 | FILE* outputFile = fopen(outputFilename,"w"); 167 | if(outputFile == NULL){ 168 | fprintf(stderr,"Error Opening File\n"); 169 | return 1; 170 | } 171 | 172 | int dataByte; 173 | while((dataByte = fgetc(inputFile)) != EOF){ 174 | fputc(((char)dataByte)+7,outputFile); 175 | } 176 | 177 | fclose(inputFile); 178 | fclose(outputFile); 179 | } else { 180 | return 1; 181 | } 182 | return 0; 183 | } 184 | 185 | int convertTextFileToUnmatchedTextFile(char* inputFilename){ 186 | int inputFilenameLen = strlen(inputFilename); 187 | char outputFilename[inputFilenameLen+15]; 188 | 189 | int found = 0; 190 | int extension = inputFilenameLen; 191 | while(!found && extension > 0){ 192 | if(inputFilename[extension] == '.'){ 193 | found = 1; 194 | } else { 195 | --extension; 196 | } 197 | } 198 | 199 | if(extension != 0){ 200 | strncpy(outputFilename,inputFilename,extension+1); 201 | outputFilename[extension] = '\0'; 202 | 203 | // Decide filename 204 | strcat(outputFilename,"-unmatched.txt\0"); 205 | 206 | printf("Using output file name: %s\n", outputFilename); 207 | 208 | FILE* inputFile = fopen(inputFilename,"r"); 209 | if(inputFile == NULL){ 210 | fprintf(stderr,"Error Opening File\n"); 211 | return 1; 212 | } 213 | FILE* outputFile = fopen(outputFilename,"w"); 214 | if(outputFile == NULL){ 215 | fprintf(stderr,"Error Opening File\n"); 216 | return 1; 217 | } 218 | 219 | int address = 0; 220 | int dataByte; 221 | char addressString[17]; 222 | char dataString[9]; 223 | while(address != -1 && dataByte != -1){ 224 | address = getAddressOrDataFromTextFile(inputFile); 225 | dataByte = getAddressOrDataFromTextFile(inputFile); 226 | if( address != -1 && dataByte != -1){ 227 | num2binStr(address,addressString,17); 228 | fputs(addressString,outputFile); 229 | fputc(' ',outputFile); 230 | 231 | 232 | num2binStr((char)dataByte+7,dataString,9); 233 | fputs(dataString,outputFile); 234 | fputc('\n',outputFile); 235 | } 236 | } 237 | 238 | fclose(inputFile); 239 | fclose(outputFile); 240 | } else { 241 | return 1; 242 | } 243 | return 0; 244 | } 245 | 246 | int generateTestData(char* inputFilename, int size){ 247 | printf("Generating Test Data...\n"); 248 | int error = 0; 249 | error |= generateBinaryFile(inputFilename, size); 250 | error |= convertBinaryFileToTextFile(inputFilename); 251 | error |= convertBinaryFileToUnmatchedBinaryFile(inputFilename); 252 | 253 | int inputFilenameLen = strlen(inputFilename); 254 | char outputFilename[inputFilenameLen+15]; 255 | 256 | int found = 0; 257 | int extension = inputFilenameLen; 258 | while(!found && extension > 0){ 259 | if(inputFilename[extension] == '.'){ 260 | found = 1; 261 | } else { 262 | --extension; 263 | } 264 | } 265 | 266 | if(extension != 0){ 267 | strncpy(outputFilename,inputFilename,extension+1); 268 | outputFilename[extension] = '\0'; 269 | 270 | strcat(outputFilename,".txt\0"); 271 | error |= convertTextFileToUnmatchedTextFile(outputFilename); 272 | } else { 273 | return 1; 274 | } 275 | 276 | { 277 | int inputFilenameLen = strlen(inputFilename); 278 | char outputFilename[inputFilenameLen+15]; 279 | 280 | int found = 0; 281 | int extension = inputFilenameLen; 282 | while(!found && extension > 0){ 283 | if(inputFilename[extension] == '.'){ 284 | found = 1; 285 | } else { 286 | --extension; 287 | } 288 | } 289 | 290 | if(extension != 0){ 291 | strncpy(outputFilename,inputFilename,extension+1); 292 | outputFilename[extension] = '\0'; 293 | 294 | strcat(outputFilename,"-oversized.bin\0"); 295 | error |= generateBinaryFile(outputFilename,size*2); 296 | } else { 297 | return 1; 298 | } 299 | } 300 | 301 | { 302 | int inputFilenameLen = strlen(inputFilename); 303 | char outputFilename[inputFilenameLen+15]; 304 | 305 | int found = 0; 306 | int extension = inputFilenameLen; 307 | while(!found && extension > 0){ 308 | if(inputFilename[extension] == '.'){ 309 | found = 1; 310 | } else { 311 | --extension; 312 | } 313 | } 314 | 315 | if(extension != 0){ 316 | strncpy(outputFilename,inputFilename,extension+1); 317 | outputFilename[extension] = '\0'; 318 | 319 | strcat(outputFilename,"-oversized.txt\0"); 320 | error |= generateTextFile(outputFilename,size*2); 321 | } else { 322 | return 1; 323 | } 324 | } 325 | 326 | return error; 327 | } 328 | -------------------------------------------------------------------------------- /software/test/uTest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "uTest.h" 5 | 6 | #ifdef WITH_COLOR 7 | #define RED "\x1b[31m" 8 | #define GREEN "\x1b[32m" 9 | #define RESET "\x1b[0m" 10 | #else 11 | #define RED "" 12 | #define GREEN "" 13 | #define RESET "" 14 | #endif 15 | 16 | enum TEST_STATUS {FAIL=0, PASS}; 17 | 18 | struct timespec start; 19 | struct timespec stop; 20 | 21 | int currentTestNumber = 0; 22 | int testFailCounter = 0; 23 | struct TEST_CONTAINER* testListPtr; 24 | struct SUITE_CONTAINER* suiteListPtr; 25 | 26 | int functionalSuiteCounter = 0; 27 | int functionalTestCounter = 0; 28 | struct TEST_CONTAINER functionalTestList[1000]; 29 | struct SUITE_CONTAINER functionalSuiteList[100]; 30 | 31 | int unitSuiteCounter = 0; 32 | int unitTestCounter = 0; 33 | struct TEST_CONTAINER unitTestList[1000]; 34 | struct SUITE_CONTAINER unitSuiteList[100]; 35 | 36 | void (*funcTestInit)(void) = NULL; 37 | void (*funcSuiteInit)(void) = NULL; 38 | void (*unitTestInit)(void) = NULL; 39 | void (*unitSuiteInit)(void) = NULL; 40 | 41 | void expect(int expectation, int actual){ 42 | testListPtr[currentTestNumber].expected = expectation; 43 | testListPtr[currentTestNumber].actual = actual; 44 | testListPtr[currentTestNumber].status = (expectation == actual); 45 | testListPtr[currentTestNumber].inverted = 0; 46 | } 47 | 48 | void expectNot(int expectationNot, int actual){ 49 | testListPtr[currentTestNumber].expected = expectationNot; 50 | testListPtr[currentTestNumber].actual = actual; 51 | testListPtr[currentTestNumber].status = (expectationNot != actual); 52 | testListPtr[currentTestNumber].inverted = 1; 53 | } 54 | 55 | int getCurrentFuncSuite(){ 56 | return functionalSuiteCounter-1; 57 | } 58 | 59 | int getCurrentUnitSuite(){ 60 | return unitSuiteCounter-1; 61 | } 62 | 63 | int addFuncSuite(char* suiteDescription){ 64 | functionalSuiteList[functionalSuiteCounter].suiteNumber = functionalSuiteCounter; 65 | functionalSuiteList[functionalSuiteCounter].description = suiteDescription; 66 | functionalSuiteCounter++; 67 | return 0; 68 | } 69 | 70 | int addUnitSuite(char* suiteDescription){ 71 | unitSuiteList[unitSuiteCounter].suiteNumber = unitSuiteCounter; 72 | unitSuiteList[unitSuiteCounter].description = suiteDescription; 73 | unitSuiteCounter++; 74 | return 0; 75 | } 76 | 77 | int addFuncTest(char* description, int suiteNumber, void (*testPtr)()){ 78 | functionalTestList[functionalTestCounter].description = description; 79 | functionalTestList[functionalTestCounter].test = testPtr; 80 | functionalTestList[functionalTestCounter].testNumber = functionalTestCounter; 81 | functionalTestList[functionalTestCounter].suiteNumber = suiteNumber; 82 | functionalTestCounter++; 83 | return 0; 84 | } 85 | 86 | int addUnitTest(char* description, int suiteNumber, void (*testPtr)()){ 87 | unitTestList[unitTestCounter].description = description; 88 | unitTestList[unitTestCounter].test = testPtr; 89 | unitTestList[unitTestCounter].testNumber = unitTestCounter; 90 | unitTestList[unitTestCounter].suiteNumber = suiteNumber; 91 | unitTestCounter++; 92 | return 0; 93 | } 94 | 95 | int addFuncSuiteInit(void (*funcSuiteInitToAdd)(void)){ 96 | funcSuiteInit = funcSuiteInitToAdd; 97 | return 0; 98 | } 99 | 100 | int addFuncTestInit(void (*funcTestInitToAdd)(void)){ 101 | funcTestInit = funcTestInitToAdd; 102 | return 0; 103 | } 104 | 105 | int addunitSuiteInit(void (*unitSuiteInitToAdd)(void)){ 106 | unitSuiteInit = unitSuiteInitToAdd; 107 | return 0; 108 | } 109 | 110 | int addUnitTestInit(void (*unitTestInitToAdd)(void)){ 111 | unitTestInit = unitTestInitToAdd; 112 | return 0; 113 | } 114 | 115 | void printFuncTestStatus(int testNumber){ 116 | if(functionalTestList[testNumber].status == PASS){ 117 | fprintf(stdout, GREEN "passed in %u.%03us" RESET "\n",functionalTestList[testNumber].timeToRunSec,functionalTestList[testNumber].timeToRunMSec); 118 | } else if(functionalTestList[currentTestNumber].inverted){ 119 | fprintf(stdout, RED "failed in %u.%03us Not Expected: %i" RESET "\n", \ 120 | functionalTestList[testNumber].timeToRunSec,functionalTestList[testNumber].timeToRunMSec, \ 121 | functionalTestList[testNumber].expected); 122 | testFailCounter++; 123 | } else { 124 | fprintf(stdout, RED "failed in %u.%03us Expected: %i Actual: %i " RESET "\n", \ 125 | functionalTestList[testNumber].timeToRunSec,functionalTestList[testNumber].timeToRunMSec, \ 126 | functionalTestList[testNumber].expected, functionalTestList[testNumber].actual); 127 | testFailCounter++; 128 | } 129 | } 130 | 131 | void printUnitTestStatus(int testNumber){ 132 | if(unitTestList[testNumber].status == PASS){ 133 | fprintf(stdout, GREEN "passed in %u.%03us" RESET "\n",unitTestList[testNumber].timeToRunSec,unitTestList[testNumber].timeToRunMSec); 134 | } else if(unitTestList[currentTestNumber].inverted){ 135 | fprintf(stdout, RED "failed in %u.%03us Not Expected: %i" RESET "\n", \ 136 | unitTestList[testNumber].timeToRunSec,unitTestList[testNumber].timeToRunMSec, \ 137 | unitTestList[testNumber].expected); 138 | testFailCounter++; 139 | } else { 140 | fprintf(stdout, RED "failed in %u.%03us Expected: %i Actual: %i " RESET "\n", \ 141 | unitTestList[testNumber].timeToRunSec,unitTestList[testNumber].timeToRunMSec, \ 142 | unitTestList[testNumber].expected, unitTestList[testNumber].actual); 143 | testFailCounter++; 144 | } 145 | } 146 | 147 | void runFuncTests(int suiteNum, int testNum){ 148 | setbuf(stdout, NULL); // disable buffering 149 | testListPtr = functionalTestList; 150 | fprintf(stdout, "\nStarting Functional Tests:\n"); 151 | fprintf(stdout, "--------------------------\n\n"); 152 | 153 | for(int j = 0; j < functionalSuiteCounter; j++){ 154 | if(j == suiteNum || suiteNum == -1 || testNum != -1){ 155 | if(testNum == -1){ 156 | if(funcSuiteInit != NULL){ 157 | funcSuiteInit(); 158 | } 159 | fprintf(stdout, "Suite Number: %04i - %s\n",functionalSuiteList[j].suiteNumber,functionalSuiteList[j].description); 160 | fprintf(stdout, "------------------------------------------------------\n"); 161 | } 162 | for(int i = 0; i < functionalTestCounter; i++){ 163 | if(functionalTestList[i].suiteNumber == j && (i == testNum || testNum == -1)){ 164 | if(funcTestInit != NULL){ 165 | funcTestInit(); 166 | } 167 | currentTestNumber = i; 168 | fprintf(stdout, "Test %04i - %-100s: ",functionalTestList[i].testNumber,functionalTestList[i].description); 169 | 170 | clock_gettime(CLOCK_REALTIME, &start); 171 | (*functionalTestList[i].test)(); 172 | clock_gettime(CLOCK_REALTIME, &stop); 173 | functionalTestList[i].timeToRunSec = stop.tv_sec - start.tv_sec; 174 | if(stop.tv_nsec > start.tv_nsec){ 175 | functionalTestList[i].timeToRunMSec = (int)(stop.tv_nsec - start.tv_nsec)/1000000; 176 | } else { 177 | functionalTestList[i].timeToRunMSec = (int)((stop.tv_nsec+1000000000) - start.tv_nsec)/1000000; 178 | --functionalTestList[i].timeToRunSec; 179 | } 180 | 181 | printFuncTestStatus(i); 182 | } 183 | } 184 | if(testNum == -1){ 185 | fprintf(stdout, "\n"); 186 | } 187 | } 188 | } 189 | } 190 | 191 | void runUnitTests(int suiteNum, int testNum){ 192 | setbuf(stdout, NULL); // disable buffering 193 | testListPtr = unitTestList; 194 | fprintf(stdout, "\nStarting Unit Tests:\n"); 195 | fprintf(stdout, "--------------------------\n\n"); 196 | 197 | for(int j = 0; j < unitSuiteCounter; j++){ 198 | if(j == suiteNum || suiteNum == -1 || testNum != -1){ 199 | if(testNum == -1){ 200 | if(unitSuiteInit != NULL){ 201 | unitSuiteInit(); 202 | } 203 | fprintf(stdout, "Suite Number: %04i - %s\n",unitSuiteList[j].suiteNumber,unitSuiteList[j].description); 204 | fprintf(stdout, "------------------------------------------------------\n"); 205 | } 206 | for(int i = 0; i < unitTestCounter; i++){ 207 | if(unitTestList[i].suiteNumber == j && (i == testNum || testNum == -1)){ 208 | if(unitTestInit != NULL){ 209 | unitTestInit(); 210 | } 211 | currentTestNumber = i; 212 | fprintf(stdout, "Test %04i - %-100s: ",unitTestList[i].testNumber,unitTestList[i].description); 213 | 214 | clock_gettime(CLOCK_REALTIME, &start); 215 | (*unitTestList[i].test)(); 216 | clock_gettime(CLOCK_REALTIME, &stop); 217 | unitTestList[i].timeToRunSec = stop.tv_sec - start.tv_sec; 218 | if(stop.tv_nsec > start.tv_nsec){ 219 | unitTestList[i].timeToRunMSec = (int)(stop.tv_nsec - start.tv_nsec)/1000000; 220 | } else { 221 | unitTestList[i].timeToRunMSec = (int)((stop.tv_nsec+1000000000) - start.tv_nsec)/1000000; 222 | --unitTestList[i].timeToRunSec; 223 | } 224 | 225 | printUnitTestStatus(i); 226 | } 227 | } 228 | if(testNum == -1){ 229 | fprintf(stdout, "\n"); 230 | } 231 | } 232 | } 233 | } 234 | 235 | void printResults(){ 236 | if(testFailCounter == 0){ 237 | printf("\nAll Tests Passed\n\n"); 238 | } else { 239 | printf("\n%i Tests Failed\n\n", testFailCounter); 240 | } 241 | } 242 | 243 | int getTestFailCounter(){ 244 | return testFailCounter; 245 | } 246 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # __A Raspberry Pi GPIO EEPROM Writer and Reader__ 2 | 3 | A way to write Parallel and Serial EEPROMs using the Raspberry Pi GPIO pins. 4 | 5 | ## __Quick Start__ 6 | 7 | 8 | ### __Building the Circuit__ 9 | Before reading or writing any EEPROM you have to build the apprpriate circuit to do so. 10 | 11 | You'll need a breadboard, jumper wires, and a 40-pin connector and cable to attach to the GPIO pins of the Raspberry Pi and a way to connect the opposite end to the breadboard. I like using one of the many Raspberry Pi GPIO Breakout boards available. 12 | 13 | __Serial I2C EEPROM Circuit__ 14 | ``` 15 | Raspberry Pi GPIO I2C DIP8 Serial EEPROM 16 | 3V3 (1) (2) 5V GND <- A0 | (1) (8) | VCC -> 3V3 17 | SDA 5 -> GPIO2 (3) (4) 5V GND <- A1 | (2) (7) | WP -> GPIO16 (36) 18 | SCL 6 -> GPIO3 (5) (6) GND GND <- A2 | (3) (6) | SCL -> GPIO2 (3) 19 | GPIO4 (7) (8) GPIO14 GND <- GND | (4) (5) | SDA -> GPIO3 (5) 20 | GND (9) (10) GPIO15 21 | GPIO17 (11) (12) GPIO18 22 | GPIO27 (13) (14) GND 23 | GPIO22 (15) (16) GPIO23 24 | 3V3 (17) (18) GPIO24 25 | GPIO10 (19) (20) GND 26 | GPIO9 (21) (22) GPIO25 27 | GPIO11 (23) (24) GPIO8 28 | GND (25) (26) GPIO7 29 | GPIO0 (27) (28) GPIO1 30 | GPIO5 (29) (30) GND 31 | GPIO6 (31) (32) GPIO12 32 | GPIO13 (33) (34) GND 33 | GPIO19 (35) (36) GPIO16 -> 7 WP 34 | GPIO26 (37) (38) GPIO20 35 | GND (39) (40) GPIO21 36 | ``` 37 | 38 | __Parallel EEPROM Circuit__ 39 | ``` 40 | IC | Raspberry Pi GPIO | IC | Pi | DIP28 Parallel EEPROM | Pi | DIP24 Parallel EEPROM 41 | 3V3 (1) (2) 5V | (7) GPIO4 <- A14 (1) (28) VCC -> 5V | 42 | I/O3 -> GPIO2 (3) (4) 5V | (11) GPIO17 <- A12 (2) (27) /WE -> GPIO14 (8) | 43 | I/O4 -> GPIO3 (5) (6) GND | (13) GPIO27 <- A7 (3) (26) A13 -> GPIO15 (10) | VCC -> 5V 44 | A14 -> GPIO4 (7) (8) GPIO14 -> /WE | (15) GPIO22 <- A6 (4) (25) A8 -> GPIO18 (12) | 45 | GND (9) (10) GPIO15 -> A13 | (19) GPIO10 <- A5 (5) (24) A9 -> GPIO23 (16) | 46 | A12 -> GPIO17 (11) (12) GPIO18 -> A8 | (21) GPIO9 <- A4 (6) (23) A11 -> GPIO24 (18) | /WE -> GPIO14 (8) 47 | A7 -> GPIO27 (13) (14) GND | (23) GPIO11 <- A3 (7) (22) /OE -> GPIO25 (22) | 48 | A6 -> GPIO22 (15) (16) GPIO23 -> A9 | (27) GPIO0 <- A2 (8) (21) A10 -> GPIO8 (24) | 49 | 3V3 (17) (18) GPIO24 -> A11 | (29) GPIO5 <- A1 (9) (20) /CE -> GPIO7 (26) | 50 | A5 -> GPIO10 (19) (20) GND | (31) GPIO6 <- A0 (10) (19) I/O7 -> GPIO1 (28) | 51 | A4 -> GPIO9 (21) (22) GPIO25 -> /OE | (33) GPIO13 <- I/O0 (11) (18) I/O6 -> GPIO12 (32) | 52 | A3 -> GPIO11 (23) (24) GPIO8 -> A10 | (35) GPIO19 <- I/O1 (12) (17) I/O5 -> GPIO16 (36) | 53 | GND (25) (26) GPIO7 -> /CE | (37) GPIO26 <- I/O2 (13) (16) I/O4 -> GPIO3 (38) | 54 | A2 -> GPIO0 (27) (28) GPIO1 -> I/O7| GND <- GND (14) (15) I/O3 -> GPIO2 (40) | 55 | A1 -> GPIO5 (29) (30) GND | 56 | A0 -> GPIO6 (31) (32) GPIO12 -> I/O6| 57 | I/O0 ->GPIO13 (33) (34) GND | 58 | I/O1 ->GPIO19 (35) (36) GPIO16 -> I/O5| 59 | I/O2 ->GPIO26 (37) (38) GPIO20 | 60 | GND (39) (40) GPIO21 -> | 61 | ``` 62 | 63 | Alternatively a pcb can be ordered from any of the various manufacturers from the zipped gerbers contained in `hardware/manufacturing/pi-eeprom-programmer-v.v.v.zip`. 64 | 65 | ### __Installation and Raspberry Pi Setup__ 66 | 67 | If you plan to use serial EEPROMs you'll need to setup I2C on the Raspberry Pi. [Adafruit](https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c) has a great tutorial. Typically, if you're just using parallel EEPROM's then you can proceed to the next step. However, if the `libgpiod2` and `libgpiod-dev` packages are not installed on your Raspberry Pi you will need to to install them. 68 | 69 | Download and compile the programmer software 70 | ``` 71 | git clone git@github.com:andrewteall/pi-eeprom-programmer.git 72 | cd pi-eeprom-programmer/software 73 | make 74 | ``` 75 | 76 | Install piepro 77 | 78 | ``` 79 | make install 80 | ``` 81 | 82 | That's it. You should be able to start reading and writing EEPROMs. 83 | 84 | ### __Installing the IC__ 85 | If you've recreated the circuit exactly from above or are using the pcb the IC should be installed so that the bottom left pin of the IC is aligned to the bottom left of the circuit. 86 | 87 | ### __Reading from an EEPROM__ 88 | 89 | To read an EEPROM, after building the circuit and inserting the IC, run the command 90 | ```sh 91 | piepro -d -m EEPROM_MODEL 92 | ``` 93 | Where EEPROM_MODEL is typically the IC's part number such as at28c64, xl2816, at24c02, etc... This will dump the EEPROM's contents to standard out in a pretty format. If you wish to start or limit the addresses dumped you may specify the `-s ADDRESS` or `-l ADDRESS` flags respectively. 94 | 95 | ### __Writing to an EEPROM__ 96 | 97 | To write to an EEPROM, after building the circuit and inserting the IC, run the command 98 | ```sh 99 | piepro -w file.bin -m EEPROM_MODEL 100 | ``` 101 | To compare you EEPROM to the file after writing, run the command 102 | 103 | ```sh 104 | piepro -c file.bin -m EEPROM_MODEL 105 | ``` 106 | Where EEPROM_MODEL is typically the IC's part number such as at28c64, xl2816, at24c02, etc... 107 | 108 | #### __A Quick Note About EEPROM Voltages__ 109 | Most modern EERPOMs will work but if you're using an obscure EEPROM you need to makes sure it can operate on 5v Vcc and 3.3v levels if it's a parallel EEPROM and 3.3v Vcc and 3.3v levels if it's a serial EEPROM. 110 | 111 | ## __Command Line Options__ 112 | ``` 113 | Usage: piepro [options] 114 | Options: 115 | -c FILE, --compare FILE Compare FILE and EEPROM and print number of differences. 116 | --chipname Specify the chipname to use. Default: gpiochip0 117 | -d [N], --dump [N] Dump the contents of the EEPROM, 118 | 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII 119 | -e [N], --erase [N] Erase eeprom with specified byte. Default: 0xFF 120 | -f, --force Force writing of every byte instead of checking for existing value first. 121 | -id, --i2c-device-id The address id of the I2C device. 122 | -h, --help Print this message and exit. 123 | -l N, --limit N Specify the maximum address to operate. 124 | -m MODEL, --model MODEL Specify EERPOM device model. Default: AT28C16. 125 | --no-validate-write 126 | Do not perform a read directly after writing to verify the data was written. 127 | -r [N], --read [N] Read the contents of the EEPROM, 128 | 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII 129 | -rb N, --read-byte ADDRESS 130 | Read From specified ADDRESS. 131 | -q [N], --quick [N] Operates on N bytes at once for reads. Page Size if unspecified or writes. 132 | Implied --force and --no-validate-write. 133 | -s N, --start N Specify the minimum address to operate. 134 | -t, --text Interpret file as a text. Default: binary 135 | Text File format: 136 | [00000000]00000000 00000000 137 | -v N, --v[vvvv] Set the log verbosity to N, 0=OFF, 1=FATAL, 2=ERROR, 3=WARNING, 4=INFO, 5=DEBUG 6=TRACE. Default: WARNING 138 | --version Print the piepro version and exit. 139 | -w FILE, --write FILE Write EEPROM with specified file. 140 | -wb ADDRESS DATA, --write-byte ADDRESS DATA 141 | Write specified DATA to ADDRESS. 142 | -wd [N], --write-delay N Enable write delay. N Number of microseconds to delay between writes. 143 | -y, --yes Automatically answer Yes to write or erase EEPROM. 144 | 145 | 146 | Supported and tested Models: 147 | Note that the program may work if your EEPROM uses a similar pinout and 148 | voltages to the ones listed. 149 | Model Size(B) Addr Len Data Len Write Cycle(uS) Page Size(B) Address Size(B) 150 | ---------------------------------------------------------------------------------------- 151 | xl2816 2048 11 8 10000 16 0 152 | xl28c16 2048 11 8 10000 16 0 153 | at28c16 2048 11 8 5000 16 0 154 | at28c64 8192 13 8 10000 16 0 155 | at28c256 32768 15 8 1000 64 0 156 | at24c01 128 7 8 5000 8 1 157 | at24c02 256 8 8 5000 8 1 158 | at24c04 512 9 8 5000 16 2 159 | at24c08 1024 10 8 5000 16 2 160 | at24c16 2048 11 8 5000 16 2 161 | at24c32 4096 12 8 5000 32 2 162 | at24c64 8192 13 8 5000 32 2 163 | at24c128 16384 14 8 5000 64 2 164 | at24c256 32768 15 8 5000 64 2 165 | at24c512 65536 16 8 5000 128 2 166 | ``` 167 | 168 | ## __Features, Bugs, and Contributing__ 169 | If there's anything you'd like to add or find a bug please open an [issue](https://github.com/andrewteall/pi-eeprom-programmer/issues). If you'd like to implement those chages yourself please feel free to open a PR or fork the repository and make it your own. 170 | 171 | 172 | ## __License__ 173 | This repository is licensed under the [MIT-0 License](LICENSE) for everything not contained in the hardware folder and it's sub folders. For all files and folders in the hardware folder and it's sub folders, they are licensed under [CC0](https://creativecommons.org/publicdomain/zero/1.0/). You are free to use, modify, and distribute the project according to the terms of these licenses. -------------------------------------------------------------------------------- /hardware/design/pi-eeprom-programmer.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.049999999999999996, 6 | "copper_line_width": 0.19999999999999998, 7 | "copper_text_italic": false, 8 | "copper_text_size_h": 1.5, 9 | "copper_text_size_v": 1.5, 10 | "copper_text_thickness": 0.3, 11 | "copper_text_upright": false, 12 | "courtyard_line_width": 0.049999999999999996, 13 | "dimension_precision": 4, 14 | "dimension_units": 3, 15 | "dimensions": { 16 | "arrow_length": 1270000, 17 | "extension_offset": 500000, 18 | "keep_text_aligned": true, 19 | "suppress_zeroes": false, 20 | "text_position": 0, 21 | "units_format": 1 22 | }, 23 | "fab_line_width": 0.09999999999999999, 24 | "fab_text_italic": false, 25 | "fab_text_size_h": 1.0, 26 | "fab_text_size_v": 1.0, 27 | "fab_text_thickness": 0.15, 28 | "fab_text_upright": false, 29 | "other_line_width": 0.09999999999999999, 30 | "other_text_italic": false, 31 | "other_text_size_h": 1.0, 32 | "other_text_size_v": 1.0, 33 | "other_text_thickness": 0.15, 34 | "other_text_upright": false, 35 | "pads": { 36 | "drill": 1.0, 37 | "height": 1.7, 38 | "width": 1.7 39 | }, 40 | "silk_line_width": 0.12, 41 | "silk_text_italic": false, 42 | "silk_text_size_h": 1.0, 43 | "silk_text_size_v": 1.0, 44 | "silk_text_thickness": 0.15, 45 | "silk_text_upright": false, 46 | "zones": { 47 | "45_degree_only": false, 48 | "min_clearance": 0.508 49 | } 50 | }, 51 | "diff_pair_dimensions": [ 52 | { 53 | "gap": 0.0, 54 | "via_gap": 0.0, 55 | "width": 0.0 56 | } 57 | ], 58 | "drc_exclusions": [], 59 | "meta": { 60 | "filename": "board_design_settings.json", 61 | "version": 2 62 | }, 63 | "rule_severities": { 64 | "annular_width": "error", 65 | "clearance": "error", 66 | "copper_edge_clearance": "error", 67 | "courtyards_overlap": "error", 68 | "diff_pair_gap_out_of_range": "error", 69 | "diff_pair_uncoupled_length_too_long": "error", 70 | "drill_out_of_range": "error", 71 | "duplicate_footprints": "warning", 72 | "extra_footprint": "warning", 73 | "footprint_type_mismatch": "error", 74 | "hole_clearance": "error", 75 | "hole_near_hole": "error", 76 | "invalid_outline": "error", 77 | "item_on_disabled_layer": "error", 78 | "items_not_allowed": "error", 79 | "length_out_of_range": "error", 80 | "malformed_courtyard": "error", 81 | "microvia_drill_out_of_range": "error", 82 | "missing_courtyard": "ignore", 83 | "missing_footprint": "warning", 84 | "net_conflict": "warning", 85 | "npth_inside_courtyard": "ignore", 86 | "padstack": "error", 87 | "pth_inside_courtyard": "ignore", 88 | "shorting_items": "error", 89 | "silk_over_copper": "warning", 90 | "silk_overlap": "warning", 91 | "skew_out_of_range": "error", 92 | "through_hole_pad_without_hole": "error", 93 | "too_many_vias": "error", 94 | "track_dangling": "warning", 95 | "track_width": "error", 96 | "tracks_crossing": "error", 97 | "unconnected_items": "error", 98 | "unresolved_variable": "error", 99 | "via_dangling": "warning", 100 | "zone_has_empty_net": "error", 101 | "zones_intersect": "error" 102 | }, 103 | "rule_severitieslegacy_courtyards_overlap": true, 104 | "rule_severitieslegacy_no_courtyard_defined": false, 105 | "rules": { 106 | "allow_blind_buried_vias": false, 107 | "allow_microvias": false, 108 | "max_error": 0.005, 109 | "min_clearance": 0.0, 110 | "min_copper_edge_clearance": 0.0, 111 | "min_hole_clearance": 0.25, 112 | "min_hole_to_hole": 0.25, 113 | "min_microvia_diameter": 0.19999999999999998, 114 | "min_microvia_drill": 0.09999999999999999, 115 | "min_silk_clearance": 0.0, 116 | "min_through_hole_diameter": 0.3, 117 | "min_track_width": 0.19999999999999998, 118 | "min_via_annular_width": 0.049999999999999996, 119 | "min_via_diameter": 0.39999999999999997, 120 | "use_height_for_length_calcs": true 121 | }, 122 | "track_widths": [ 123 | 0.0 124 | ], 125 | "via_dimensions": [ 126 | { 127 | "diameter": 0.0, 128 | "drill": 0.0 129 | } 130 | ], 131 | "zones_allow_external_fillets": false, 132 | "zones_use_no_outline": true 133 | }, 134 | "layer_presets": [] 135 | }, 136 | "boards": [], 137 | "cvpcb": { 138 | "equivalence_files": [] 139 | }, 140 | "erc": { 141 | "erc_exclusions": [], 142 | "meta": { 143 | "version": 0 144 | }, 145 | "pin_map": [ 146 | [ 147 | 0, 148 | 0, 149 | 0, 150 | 0, 151 | 0, 152 | 0, 153 | 1, 154 | 0, 155 | 0, 156 | 0, 157 | 0, 158 | 2 159 | ], 160 | [ 161 | 0, 162 | 2, 163 | 0, 164 | 1, 165 | 0, 166 | 0, 167 | 1, 168 | 0, 169 | 2, 170 | 2, 171 | 2, 172 | 2 173 | ], 174 | [ 175 | 0, 176 | 0, 177 | 0, 178 | 0, 179 | 0, 180 | 0, 181 | 1, 182 | 0, 183 | 1, 184 | 0, 185 | 1, 186 | 2 187 | ], 188 | [ 189 | 0, 190 | 1, 191 | 0, 192 | 0, 193 | 0, 194 | 0, 195 | 1, 196 | 1, 197 | 2, 198 | 1, 199 | 1, 200 | 2 201 | ], 202 | [ 203 | 0, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 0, 209 | 1, 210 | 0, 211 | 0, 212 | 0, 213 | 0, 214 | 2 215 | ], 216 | [ 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | 0, 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 0, 228 | 2 229 | ], 230 | [ 231 | 1, 232 | 1, 233 | 1, 234 | 1, 235 | 1, 236 | 0, 237 | 1, 238 | 1, 239 | 1, 240 | 1, 241 | 1, 242 | 2 243 | ], 244 | [ 245 | 0, 246 | 0, 247 | 0, 248 | 1, 249 | 0, 250 | 0, 251 | 1, 252 | 0, 253 | 0, 254 | 0, 255 | 0, 256 | 2 257 | ], 258 | [ 259 | 0, 260 | 2, 261 | 1, 262 | 2, 263 | 0, 264 | 0, 265 | 1, 266 | 0, 267 | 2, 268 | 2, 269 | 2, 270 | 2 271 | ], 272 | [ 273 | 0, 274 | 2, 275 | 0, 276 | 1, 277 | 0, 278 | 0, 279 | 1, 280 | 0, 281 | 2, 282 | 0, 283 | 0, 284 | 2 285 | ], 286 | [ 287 | 0, 288 | 2, 289 | 1, 290 | 1, 291 | 0, 292 | 0, 293 | 1, 294 | 0, 295 | 2, 296 | 0, 297 | 0, 298 | 2 299 | ], 300 | [ 301 | 2, 302 | 2, 303 | 2, 304 | 2, 305 | 2, 306 | 2, 307 | 2, 308 | 2, 309 | 2, 310 | 2, 311 | 2, 312 | 2 313 | ] 314 | ], 315 | "rule_severities": { 316 | "bus_definition_conflict": "error", 317 | "bus_entry_needed": "error", 318 | "bus_label_syntax": "error", 319 | "bus_to_bus_conflict": "error", 320 | "bus_to_net_conflict": "error", 321 | "different_unit_footprint": "error", 322 | "different_unit_net": "error", 323 | "duplicate_reference": "error", 324 | "duplicate_sheet_names": "error", 325 | "extra_units": "error", 326 | "global_label_dangling": "warning", 327 | "hier_label_mismatch": "error", 328 | "label_dangling": "error", 329 | "lib_symbol_issues": "warning", 330 | "multiple_net_names": "warning", 331 | "net_not_bus_member": "warning", 332 | "no_connect_connected": "warning", 333 | "no_connect_dangling": "warning", 334 | "pin_not_connected": "error", 335 | "pin_not_driven": "error", 336 | "pin_to_pin": "warning", 337 | "power_pin_not_driven": "error", 338 | "similar_labels": "warning", 339 | "unannotated": "error", 340 | "unit_value_mismatch": "error", 341 | "unresolved_variable": "error", 342 | "wire_dangling": "error" 343 | } 344 | }, 345 | "libraries": { 346 | "pinned_footprint_libs": [], 347 | "pinned_symbol_libs": [] 348 | }, 349 | "meta": { 350 | "filename": "pi-eeprom-programmer.kicad_pro", 351 | "version": 1 352 | }, 353 | "net_settings": { 354 | "classes": [ 355 | { 356 | "bus_width": 12.0, 357 | "clearance": 0.2, 358 | "diff_pair_gap": 0.25, 359 | "diff_pair_via_gap": 0.25, 360 | "diff_pair_width": 0.2, 361 | "line_style": 0, 362 | "microvia_diameter": 0.3, 363 | "microvia_drill": 0.1, 364 | "name": "Default", 365 | "pcb_color": "rgba(0, 0, 0, 0.000)", 366 | "schematic_color": "rgba(0, 0, 0, 0.000)", 367 | "track_width": 0.25, 368 | "via_diameter": 0.8, 369 | "via_drill": 0.4, 370 | "wire_width": 6.0 371 | } 372 | ], 373 | "meta": { 374 | "version": 2 375 | }, 376 | "net_colors": null 377 | }, 378 | "pcbnew": { 379 | "last_paths": { 380 | "gencad": "", 381 | "idf": "", 382 | "netlist": "pi-eeprom-programmer.net", 383 | "specctra_dsn": "", 384 | "step": "", 385 | "vrml": "" 386 | }, 387 | "page_layout_descr_file": "" 388 | }, 389 | "schematic": { 390 | "annotate_start_num": 0, 391 | "drawing": { 392 | "default_line_thickness": 6.0, 393 | "default_text_size": 50.0, 394 | "field_names": [], 395 | "intersheets_ref_own_page": false, 396 | "intersheets_ref_prefix": "", 397 | "intersheets_ref_short": false, 398 | "intersheets_ref_show": false, 399 | "intersheets_ref_suffix": "", 400 | "junction_size_choice": 3, 401 | "label_size_ratio": 0.25, 402 | "pin_symbol_size": 0.0, 403 | "text_offset_ratio": 0.08 404 | }, 405 | "legacy_lib_dir": "", 406 | "legacy_lib_list": [], 407 | "meta": { 408 | "version": 1 409 | }, 410 | "net_format_name": "Pcbnew", 411 | "ngspice": { 412 | "fix_include_paths": true, 413 | "fix_passive_vals": false, 414 | "meta": { 415 | "version": 0 416 | }, 417 | "model_mode": 0, 418 | "workbook_filename": "" 419 | }, 420 | "page_layout_descr_file": "", 421 | "plot_directory": "", 422 | "spice_adjust_passive_values": false, 423 | "spice_external_command": "spice \"%I\"", 424 | "subpart_first_id": 65, 425 | "subpart_id_separator": 0 426 | }, 427 | "sheets": [ 428 | [ 429 | "18bcb91e-b448-4109-a545-6fb892d6125c", 430 | "" 431 | ] 432 | ], 433 | "text_variables": {} 434 | } 435 | -------------------------------------------------------------------------------- /hardware/manufacturing/gerbers/pi-eeprom-programmer-B_Cu.gbr: -------------------------------------------------------------------------------- 1 | G04 #@! TF.GenerationSoftware,KiCad,Pcbnew,6.0.11+dfsg-1* 2 | G04 #@! TF.CreationDate,2023-12-04T13:58:25-05:00* 3 | G04 #@! TF.ProjectId,pi-eeprom-programmer,70692d65-6570-4726-9f6d-2d70726f6772,1.0.0* 4 | G04 #@! TF.SameCoordinates,Original* 5 | G04 #@! TF.FileFunction,Copper,L2,Bot* 6 | G04 #@! TF.FilePolarity,Positive* 7 | %FSLAX46Y46*% 8 | G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)* 9 | G04 Created by KiCad (PCBNEW 6.0.11+dfsg-1) date 2023-12-04 13:58:25* 10 | %MOMM*% 11 | %LPD*% 12 | G01* 13 | G04 APERTURE LIST* 14 | G04 #@! TA.AperFunction,ComponentPad* 15 | %ADD10R,1.700000X1.700000*% 16 | G04 #@! TD* 17 | G04 #@! TA.AperFunction,ComponentPad* 18 | %ADD11O,1.700000X1.700000*% 19 | G04 #@! TD* 20 | G04 #@! TA.AperFunction,ComponentPad* 21 | %ADD12R,1.600000X1.600000*% 22 | G04 #@! TD* 23 | G04 #@! TA.AperFunction,ComponentPad* 24 | %ADD13O,1.600000X1.600000*% 25 | G04 #@! TD* 26 | G04 #@! TA.AperFunction,Conductor* 27 | %ADD14C,0.250000*% 28 | G04 #@! TD* 29 | G04 APERTURE END LIST* 30 | D10* 31 | X136595201Y-44921500D03* 32 | D11* 33 | X134055201Y-44921500D03* 34 | X136595201Y-47461500D03* 35 | X134055201Y-47461500D03* 36 | X136595201Y-50001500D03* 37 | X134055201Y-50001500D03* 38 | X136595201Y-52541500D03* 39 | X134055201Y-52541500D03* 40 | X136595201Y-55081500D03* 41 | X134055201Y-55081500D03* 42 | X136595201Y-57621500D03* 43 | X134055201Y-57621500D03* 44 | X136595201Y-60161500D03* 45 | X134055201Y-60161500D03* 46 | X136595201Y-62701500D03* 47 | X134055201Y-62701500D03* 48 | X136595201Y-65241500D03* 49 | X134055201Y-65241500D03* 50 | X136595201Y-67781500D03* 51 | X134055201Y-67781500D03* 52 | X136595201Y-70321500D03* 53 | X134055201Y-70321500D03* 54 | X136595201Y-72861500D03* 55 | X134055201Y-72861500D03* 56 | X136595201Y-75401500D03* 57 | X134055201Y-75401500D03* 58 | X136595201Y-77941500D03* 59 | X134055201Y-77941500D03* 60 | X136595201Y-80481500D03* 61 | X134055201Y-80481500D03* 62 | X136595201Y-83021500D03* 63 | X134055201Y-83021500D03* 64 | X136595201Y-85561500D03* 65 | X134055201Y-85561500D03* 66 | X136595201Y-88101500D03* 67 | X134055201Y-88101500D03* 68 | X136595201Y-90641500D03* 69 | X134055201Y-90641500D03* 70 | X136595201Y-93181500D03* 71 | X134055201Y-93181500D03* 72 | D12* 73 | X179806600Y-52571500D03* 74 | D13* 75 | X179806600Y-55111500D03* 76 | X179806600Y-57651500D03* 77 | X179806600Y-60191500D03* 78 | X179806600Y-62731500D03* 79 | X179806600Y-65271500D03* 80 | X179806600Y-67811500D03* 81 | X179806600Y-70351500D03* 82 | X179806600Y-72891500D03* 83 | X179806600Y-75431500D03* 84 | X179806600Y-77971500D03* 85 | X179806600Y-80511500D03* 86 | X179806600Y-83051500D03* 87 | X179806600Y-85591500D03* 88 | X195046600Y-85591500D03* 89 | X195046600Y-83051500D03* 90 | X195046600Y-80511500D03* 91 | X195046600Y-77971500D03* 92 | X195046600Y-75431500D03* 93 | X195046600Y-72891500D03* 94 | X195046600Y-70351500D03* 95 | X195046600Y-67811500D03* 96 | X195046600Y-65271500D03* 97 | X195046600Y-62731500D03* 98 | X195046600Y-60191500D03* 99 | X195046600Y-57651500D03* 100 | X195046600Y-55111500D03* 101 | X195046600Y-52571500D03* 102 | D12* 103 | X183581600Y-74225000D03* 104 | D13* 105 | X183581600Y-76765000D03* 106 | X183581600Y-79305000D03* 107 | X183581600Y-81845000D03* 108 | X191201600Y-81845000D03* 109 | X191201600Y-79305000D03* 110 | X191201600Y-76765000D03* 111 | X191201600Y-74225000D03* 112 | D10* 113 | X162255200Y-44907200D03* 114 | D11* 115 | X159715200Y-44907200D03* 116 | X157175200Y-44907200D03* 117 | D14* 118 | X195046600Y-52571500D02* 119 | X195046600Y-51028600D01* 120 | X157182000Y-48768000D02* 121 | X135636000Y-48768000D01* 122 | X135382000Y-46248299D02* 123 | X134055201Y-44921500D01* 124 | X135382000Y-48514000D02* 125 | X135382000Y-46248299D01* 126 | X157175200Y-48761200D02* 127 | X157182000Y-48768000D01* 128 | X192786000Y-48768000D02* 129 | X157182000Y-48768000D01* 130 | X135636000Y-48768000D02* 131 | X135382000Y-48514000D01* 132 | X195046600Y-51028600D02* 133 | X192786000Y-48768000D01* 134 | X157175200Y-44907200D02* 135 | X157175200Y-48761200D01* 136 | X172593000Y-92735400D02* 137 | X172662700Y-92735400D01* 138 | X183553100Y-81845000D02* 139 | X183581600Y-81845000D01* 140 | X179806600Y-85591500D02* 141 | X183553100Y-81845000D01* 142 | X136595201Y-93181500D02* 143 | X172146900Y-93181500D01* 144 | X172662700Y-92735400D02* 145 | X179806600Y-85591500D01* 146 | X172146900Y-93181500D02* 147 | X172593000Y-92735400D01* 148 | X136595201Y-52541500D02* 149 | X179776600Y-52541500D01* 150 | X179776600Y-52541500D02* 151 | X179806600Y-52571500D01* 152 | X189274300Y-55111500D02* 153 | X195046600Y-55111500D01* 154 | X134055201Y-52541500D02* 155 | X135420201Y-51176500D01* 156 | X135420201Y-51176500D02* 157 | X185339300Y-51176500D01* 158 | X185339300Y-51176500D02* 159 | X189274300Y-55111500D01* 160 | X134055201Y-55081500D02* 161 | X135230201Y-56256500D01* 162 | X196736000Y-46736000D02* 163 | X163352480Y-46736000D01* 164 | X163352480Y-46736000D02* 165 | X162255200Y-44907200D01* 166 | X186664600Y-53898800D02* 167 | X191668400Y-58902600D01* 168 | X198628000Y-58420000D02* 169 | X198628000Y-48628000D01* 170 | X198628000Y-48628000D02* 171 | X196736000Y-46736000D01* 172 | X135230201Y-56256500D02* 173 | X150753500Y-56256500D01* 174 | X191668400Y-58902600D02* 175 | X198145400Y-58902600D01* 176 | X150753500Y-56256500D02* 177 | X153111200Y-53898800D01* 178 | X198145400Y-58902600D02* 179 | X198628000Y-58420000D01* 180 | X153111200Y-53898800D02* 181 | X186664600Y-53898800D01* 182 | X136595201Y-57621500D02* 183 | X151674500Y-57621500D01* 184 | X151674500Y-57621500D02* 185 | X154184500Y-55111500D01* 186 | X154184500Y-55111500D02* 187 | X179806600Y-55111500D01* 188 | X135412501Y-58978800D02* 189 | X152273000Y-58978800D01* 190 | X186131200Y-56489600D02* 191 | X189833100Y-60191500D01* 192 | X134055201Y-57621500D02* 193 | X135412501Y-58978800D01* 194 | X154762200Y-56489600D02* 195 | X186131200Y-56489600D01* 196 | X189833100Y-60191500D02* 197 | X195046600Y-60191500D01* 198 | X152273000Y-58978800D02* 199 | X154762200Y-56489600D01* 200 | X136595201Y-60161500D02* 201 | X152538100Y-60161500D01* 202 | X155048100Y-57651500D02* 203 | X179806600Y-57651500D01* 204 | X152538100Y-60161500D02* 205 | X155048100Y-57651500D01* 206 | X153655700Y-62701500D02* 207 | X156165700Y-60191500D01* 208 | X136595201Y-62701500D02* 209 | X153655700Y-62701500D01* 210 | X156165700Y-60191500D02* 211 | X179806600Y-60191500D01* 212 | X187967400Y-61450000D02* 213 | X193765100Y-61450000D01* 214 | X185420000Y-58902600D02* 215 | X187967400Y-61450000D01* 216 | X134055201Y-62701500D02* 217 | X135420201Y-61336500D01* 218 | X193765100Y-61450000D02* 219 | X195046600Y-62731500D01* 220 | X155549600Y-58902600D02* 221 | X185420000Y-58902600D01* 222 | X153115700Y-61336500D02* 223 | X155549600Y-58902600D01* 224 | X135420201Y-61336500D02* 225 | X153115700Y-61336500D01* 226 | X134055201Y-65241500D02* 227 | X135336301Y-66522600D01* 228 | X185089800Y-61518800D02* 229 | X186071000Y-62500000D01* 230 | X192275100Y-62500000D02* 231 | X195046600Y-65271500D01* 232 | X153035000Y-66522600D02* 233 | X158038800Y-61518800D01* 234 | X158038800Y-61518800D02* 235 | X185089800Y-61518800D01* 236 | X186071000Y-62500000D02* 237 | X192275100Y-62500000D01* 238 | X135336301Y-66522600D02* 239 | X153035000Y-66522600D01* 240 | X158985100Y-62731500D02* 241 | X179806600Y-62731500D01* 242 | X153935100Y-67781500D02* 243 | X158985100Y-62731500D01* 244 | X136595201Y-67781500D02* 245 | X153935100Y-67781500D01* 246 | X161575900Y-65271500D02* 247 | X179806600Y-65271500D01* 248 | X156525900Y-70321500D02* 249 | X161575900Y-65271500D01* 250 | X136595201Y-70321500D02* 251 | X156525900Y-70321500D01* 252 | X134055201Y-70321500D02* 253 | X135420201Y-68956500D01* 254 | X155325500Y-68956500D02* 255 | X160274000Y-64008000D01* 256 | X135420201Y-68956500D02* 257 | X155325500Y-68956500D01* 258 | X160274000Y-64008000D02* 259 | X191243100Y-64008000D01* 260 | X191243100Y-64008000D02* 261 | X195046600Y-67811500D01* 262 | X159040500Y-72861500D02* 263 | X164090500Y-67811500D01* 264 | X164090500Y-67811500D02* 265 | X179806600Y-67811500D01* 266 | X136595201Y-72861500D02* 267 | X159040500Y-72861500D01* 268 | X163068000Y-66548000D02* 269 | X157988000Y-71628000D01* 270 | X195046600Y-70351500D02* 271 | X191243100Y-66548000D01* 272 | X191243100Y-66548000D02* 273 | X163068000Y-66548000D01* 274 | X157988000Y-71628000D02* 275 | X135288701Y-71628000D01* 276 | X135288701Y-71628000D02* 277 | X134055201Y-72861500D01* 278 | X136595201Y-77941500D02* 279 | X157592700Y-77941500D01* 280 | X165182700Y-70351500D02* 281 | X179806600Y-70351500D01* 282 | X157592700Y-77941500D02* 283 | X165182700Y-70351500D01* 284 | X190400000Y-70050000D02* 285 | X195046600Y-74696600D01* 286 | X134055201Y-77941500D02* 287 | X135361701Y-79248000D01* 288 | X135361701Y-79248000D02* 289 | X158165800Y-79248000D01* 290 | X158165800Y-79248000D02* 291 | X165788800Y-71625000D01* 292 | X182350000Y-70050000D02* 293 | X190400000Y-70050000D01* 294 | X180775000Y-71625000D02* 295 | X182350000Y-70050000D01* 296 | X165788800Y-71625000D02* 297 | X180775000Y-71625000D01* 298 | X195046600Y-74696600D02* 299 | X195046600Y-75431500D01* 300 | X159243700Y-80481500D02* 301 | X166833700Y-72891500D01* 302 | X136595201Y-80481500D02* 303 | X159243700Y-80481500D01* 304 | X166833700Y-72891500D02* 305 | X179806600Y-72891500D01* 306 | X160513700Y-83021500D02* 307 | X168103700Y-75431500D01* 308 | X168103700Y-75431500D02* 309 | X179806600Y-75431500D01* 310 | X136595201Y-83021500D02* 311 | X160513700Y-83021500D01* 312 | X134055201Y-83021500D02* 313 | X135230201Y-84196500D01* 314 | X181700000Y-72075000D02* 315 | X182375000Y-71400000D01* 316 | X182375000Y-71400000D02* 317 | X190550000Y-71400000D01* 318 | X169299000Y-76700000D02* 319 | X180550000Y-76700000D01* 320 | X135230201Y-84196500D02* 321 | X161802500Y-84196500D01* 322 | X180550000Y-76700000D02* 323 | X181700000Y-75550000D01* 324 | X191201600Y-72051600D02* 325 | X191201600Y-74225000D01* 326 | X181700000Y-75550000D02* 327 | X181700000Y-72075000D01* 328 | X194948100Y-77971500D02* 329 | X195046600Y-77971500D01* 330 | X190550000Y-71400000D02* 331 | X191201600Y-72051600D01* 332 | X191201600Y-74225000D02* 333 | X194948100Y-77971500D01* 334 | X161802500Y-84196500D02* 335 | X169299000Y-76700000D01* 336 | X162444100Y-85561500D02* 337 | X170034100Y-77971500D01* 338 | X183581600Y-74668400D02* 339 | X183581600Y-74225000D01* 340 | X180278500Y-77971500D02* 341 | X183581600Y-74668400D01* 342 | X179806600Y-77971500D02* 343 | X180278500Y-77971500D01* 344 | X170034100Y-77971500D02* 345 | X179806600Y-77971500D01* 346 | X136595201Y-85561500D02* 347 | X162444100Y-85561500D01* 348 | X182910000Y-76765000D02* 349 | X183581600Y-76765000D01* 350 | X164704700Y-88101500D02* 351 | X172294700Y-80511500D01* 352 | X179806600Y-80511500D02* 353 | X179806600Y-79868400D01* 354 | X136595201Y-88101500D02* 355 | X164704700Y-88101500D01* 356 | X179806600Y-79868400D02* 357 | X182910000Y-76765000D01* 358 | X172294700Y-80511500D02* 359 | X179806600Y-80511500D01* 360 | X191201600Y-76765000D02* 361 | X194948100Y-80511500D01* 362 | X135336301Y-89382600D02* 363 | X166116000Y-89382600D01* 364 | X173698600Y-81800000D02* 365 | X180725000Y-81800000D01* 366 | X182425000Y-78025000D02* 367 | X189941600Y-78025000D01* 368 | X180725000Y-81800000D02* 369 | X181625000Y-80900000D01* 370 | X181625000Y-80900000D02* 371 | X181625000Y-78825000D01* 372 | X166116000Y-89382600D02* 373 | X173698600Y-81800000D01* 374 | X181625000Y-78825000D02* 375 | X182425000Y-78025000D01* 376 | X189941600Y-78025000D02* 377 | X191201600Y-76765000D01* 378 | X134055201Y-88101500D02* 379 | X135336301Y-89382600D01* 380 | X194948100Y-80511500D02* 381 | X195046600Y-80511500D01* 382 | X167879700Y-90641500D02* 383 | X175469700Y-83051500D01* 384 | X175469700Y-83051500D02* 385 | X179806600Y-83051500D01* 386 | X179806600Y-83051500D02* 387 | X180448500Y-83051500D01* 388 | X183581600Y-79918400D02* 389 | X183581600Y-79305000D01* 390 | X136595201Y-90641500D02* 391 | X167879700Y-90641500D01* 392 | X180448500Y-83051500D02* 393 | X183581600Y-79918400D01* 394 | X136595201Y-75401500D02* 395 | X158278500Y-75401500D01* 396 | X191243100Y-69088000D02* 397 | X195046600Y-72891500D01* 398 | X164592000Y-69088000D02* 399 | X191243100Y-69088000D01* 400 | X158278500Y-75401500D02* 401 | X164592000Y-69088000D01* 402 | X200152000Y-80498000D02* 403 | X195058500Y-85591500D01* 404 | X154940000Y-42926000D02* 405 | X155702000Y-42164000D01* 406 | X168910000Y-44958000D02* 407 | X196658000Y-44958000D01* 408 | X191201600Y-81845000D02* 409 | X194948100Y-85591500D01* 410 | X200152000Y-48452000D02* 411 | X200152000Y-80498000D01* 412 | X166116000Y-42164000D02* 413 | X168910000Y-44958000D01* 414 | X195058500Y-85591500D02* 415 | X195046600Y-85591500D01* 416 | X154214500Y-47461500D02* 417 | X154940000Y-46736000D01* 418 | X154940000Y-46736000D02* 419 | X154940000Y-42926000D01* 420 | X136595201Y-47461500D02* 421 | X154214500Y-47461500D01* 422 | X194948100Y-85591500D02* 423 | X195046600Y-85591500D01* 424 | X196658000Y-44958000D02* 425 | X200152000Y-48452000D01* 426 | X155702000Y-42164000D02* 427 | X166116000Y-42164000D01* 428 | X136885701Y-50292000D02* 429 | X187452000Y-50292000D01* 430 | X191008000Y-53848000D02* 431 | X196596000Y-53848000D01* 432 | X194962000Y-48150000D02* 433 | X158894000Y-48150000D01* 434 | X158425000Y-43759000D02* 435 | X159004000Y-43180000D01* 436 | X158894000Y-48150000D02* 437 | X158425000Y-47681000D01* 438 | X158425000Y-47681000D02* 439 | X158425000Y-43759000D01* 440 | X196596000Y-53848000D02* 441 | X197104000Y-53340000D01* 442 | X165354000Y-43180000D02* 443 | X168148000Y-45974000D01* 444 | X187452000Y-50292000D02* 445 | X191008000Y-53848000D01* 446 | X199325000Y-48449000D02* 447 | X199325000Y-78773100D01* 448 | X168148000Y-45974000D02* 449 | X196850000Y-45974000D01* 450 | X191201600Y-79305000D02* 451 | X194948100Y-83051500D01* 452 | X197104000Y-50292000D02* 453 | X194962000Y-48150000D01* 454 | X159004000Y-43180000D02* 455 | X165354000Y-43180000D01* 456 | X194948100Y-83051500D02* 457 | X195046600Y-83051500D01* 458 | X196850000Y-45974000D02* 459 | X199325000Y-48449000D01* 460 | X197104000Y-53340000D02* 461 | X197104000Y-50292000D01* 462 | X136595201Y-50001500D02* 463 | X136885701Y-50292000D01* 464 | X199325000Y-78773100D02* 465 | X195046600Y-83051500D01* 466 | X197866000Y-48768000D02* 467 | X196596000Y-47498000D01* 468 | X159715200Y-46685200D02* 469 | X159715200Y-44907200D01* 470 | X195046600Y-57651500D02* 471 | X197348500Y-57651500D01* 472 | X197866000Y-57134000D02* 473 | X197866000Y-48768000D01* 474 | X160528000Y-47498000D02* 475 | X159715200Y-46685200D01* 476 | X197348500Y-57651500D02* 477 | X197866000Y-57134000D01* 478 | X196596000Y-47498000D02* 479 | X160528000Y-47498000D01* 480 | M02* 481 | -------------------------------------------------------------------------------- /software/test/test_gpio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "uTest.h" 5 | #include "../include/ulog.h" 6 | #include "../include/gpio.h" 7 | 8 | 9 | #define NUM_LINES_TO_USE 28 10 | #define I2C_ID 0x50 11 | 12 | int unit_test_actual_result; 13 | int unit_test_expected; 14 | 15 | struct GPIO_CHIP gpioChip; 16 | 17 | char* unitSuiteGPIO0 = "suite_setupGPIO"; 18 | char* unitSuiteGPIO1 = "test_setPinModeGPIO"; 19 | char* unitSuiteGPIO2 = "suite_readGPIO"; 20 | char* unitSuiteGPIO3 = "suite_writeGPIO"; 21 | 22 | char* unitSuiteI2C0 = "suite_setupI2C"; 23 | 24 | void init_gpio_test(){ 25 | gpioChip.consumer = "test"; 26 | gpioChip.chipname = "gpiochip0"; 27 | gpioChip.numGPIOLines = NUM_LINES_TO_USE; 28 | gpioChip.isSetup = 0; 29 | } 30 | 31 | void init_I2C_test(){ 32 | gpioChip.consumer = "test"; 33 | gpioChip.chipname = "gpiochip0"; 34 | gpioChip.numGPIOLines = NUM_LINES_TO_USE; 35 | gpioChip.isSetup = 0; 36 | 37 | setupGPIO(&gpioChip); 38 | setPinModeGPIO(&gpioChip,13, OUTPUT); 39 | setPinModeGPIO(&gpioChip,19, OUTPUT); 40 | setPinModeGPIO(&gpioChip,27, OUTPUT); 41 | 42 | writeGPIO(&gpioChip,13, LOW); 43 | writeGPIO(&gpioChip,19, LOW); 44 | writeGPIO(&gpioChip,26, LOW); 45 | 46 | setPinModeGPIO(&gpioChip,16, OUTPUT); 47 | setPinModeGPIO(&gpioChip,12, OUTPUT); 48 | writeGPIO(&gpioChip,16, HIGH); 49 | writeGPIO(&gpioChip,12, HIGH); 50 | } 51 | 52 | void cleanup_gpio_test(){ 53 | cleanupGPIO(&gpioChip); 54 | } 55 | 56 | void cleanup_I2C_test(int fd){ 57 | cleanupGPIO(&gpioChip); 58 | cleanupI2C(fd); 59 | } 60 | 61 | /******************************************************************************/ 62 | /******************************** GPIO Tests **********************************/ 63 | /******************************************************************************/ 64 | 65 | // SUITE - setupGPIO 66 | // TEST - Test to Setup GPIO 67 | void test_setupGPIO(){ 68 | init_gpio_test(); 69 | 70 | unit_test_actual_result = setupGPIO(&gpioChip); 71 | unit_test_expected = 0; 72 | expect(unit_test_expected,unit_test_actual_result); 73 | 74 | cleanupGPIO(&gpioChip); 75 | } 76 | 77 | // TEST - Test to Setup GPIO Twice 78 | void test_setupGPIOTwice(){ 79 | init_gpio_test(); 80 | 81 | setupGPIO(&gpioChip); 82 | unit_test_actual_result = setupGPIO(&gpioChip); 83 | unit_test_expected = -1; 84 | expect(unit_test_expected,unit_test_actual_result); 85 | 86 | cleanupGPIO(&gpioChip); 87 | } 88 | 89 | // TEST - Test to Setup GPIO with Chip Null 90 | void test_setupGPIOWithChipNull(){ 91 | init_gpio_test(); 92 | 93 | unit_test_actual_result = setupGPIO(NULL); 94 | unit_test_expected = -1; 95 | expect(unit_test_expected,unit_test_actual_result); 96 | 97 | cleanupGPIO(&gpioChip); 98 | } 99 | 100 | // TEST - Test to Setup GPIO with Chipname Null 101 | void test_setupGPIOWithChipnameNull(){ 102 | init_gpio_test(); 103 | gpioChip.chipname = NULL; 104 | 105 | unit_test_actual_result = setupGPIO(&gpioChip); 106 | unit_test_expected = -1; 107 | expect(unit_test_expected,unit_test_actual_result); 108 | 109 | cleanupGPIO(&gpioChip); 110 | } 111 | 112 | // TEST - Test to Setup GPIO with Consumer Null 113 | void test_setupGPIOWithConsumerNull(){ 114 | init_gpio_test(); 115 | gpioChip.consumer = NULL; 116 | 117 | unit_test_actual_result = setupGPIO(&gpioChip); 118 | unit_test_expected = -1; 119 | expect(unit_test_expected,unit_test_actual_result); 120 | 121 | cleanupGPIO(&gpioChip); 122 | } 123 | 124 | // TEST - Test to Setup GPIO with Invalid Chipname 125 | void test_setupGPIOWithInvalidChipname(){ 126 | init_gpio_test(); 127 | gpioChip.chipname = "invalid"; 128 | 129 | unit_test_actual_result = setupGPIO(&gpioChip); 130 | unit_test_expected = -1; 131 | expect(unit_test_expected,unit_test_actual_result); 132 | 133 | cleanupGPIO(&gpioChip); 134 | } 135 | 136 | // TEST - Test to Setup GPIO with number of lines too great for chip 137 | void test_setupGPIOWithNumLinesTooHigh(){ 138 | init_gpio_test(); 139 | gpioChip.numGPIOLines = 82; 140 | 141 | unit_test_actual_result = setupGPIO(&gpioChip); 142 | unit_test_expected = -1; 143 | expect(unit_test_expected,unit_test_actual_result); 144 | 145 | cleanupGPIO(&gpioChip); 146 | } 147 | 148 | // TEST - Test to Setup GPIO with number of lines negative 149 | void test_setupGPIOWithNumLinesNegative(){ 150 | init_gpio_test(); 151 | gpioChip.numGPIOLines = -1; 152 | 153 | unit_test_actual_result = setupGPIO(&gpioChip); 154 | unit_test_expected = -1; 155 | expect(unit_test_expected,unit_test_actual_result); 156 | 157 | cleanupGPIO(&gpioChip); 158 | } 159 | 160 | // TEST - Test to Setup GPIO with number out of range 161 | void test_setupGPIOWithNumLinesOutOfRange(){ 162 | init_gpio_test(); 163 | gpioChip.numGPIOLines = 35; 164 | 165 | unit_test_actual_result = setupGPIO(&gpioChip); 166 | unit_test_expected = -1; 167 | expect(unit_test_expected,unit_test_actual_result); 168 | 169 | cleanupGPIO(&gpioChip); 170 | } 171 | 172 | 173 | // SUITE - Set Pin Mode GPIO 174 | // TEST - Set Pin Mode when gpio has not been setup 175 | void test_setPinModeGPIOWithoutSetup(){ 176 | init_gpio_test(); 177 | 178 | int gpioLineNumber = 0; 179 | int pinMode = OUTPUT; 180 | 181 | unit_test_actual_result = setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 182 | unit_test_expected = -1; 183 | expect(unit_test_expected,unit_test_actual_result); 184 | 185 | cleanupGPIO(&gpioChip); 186 | } 187 | 188 | // TEST - Set Pin Mode to Output 189 | void test_setPinModeGPIOToOutput(){ 190 | init_gpio_test(); 191 | 192 | int gpioLineNumber = 0; 193 | int pinMode = OUTPUT; 194 | 195 | setupGPIO(&gpioChip); 196 | unit_test_actual_result = setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 197 | unit_test_expected = 0; 198 | expect(unit_test_expected,unit_test_actual_result); 199 | 200 | cleanupGPIO(&gpioChip); 201 | } 202 | 203 | // TEST - Set Pin Mode to Input 204 | void test_setPinModeGPIOToInput(){ 205 | init_gpio_test(); 206 | 207 | int gpioLineNumber = 0; 208 | int pinMode = INPUT; 209 | 210 | setupGPIO(&gpioChip); 211 | unit_test_actual_result = setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 212 | unit_test_expected = 0; 213 | expect(unit_test_expected,unit_test_actual_result); 214 | 215 | cleanupGPIO(&gpioChip); 216 | } 217 | 218 | // TEST - Set Pin Mode When Chip is NULL 219 | void test_setPinModeGPIOWhenChipNull(){ 220 | init_gpio_test(); 221 | 222 | int gpioLineNumber = 0; 223 | int pinMode = INPUT; 224 | 225 | setupGPIO(&gpioChip); 226 | unit_test_actual_result = setPinModeGPIO(NULL,gpioLineNumber,pinMode); 227 | unit_test_expected = -1; 228 | expect(unit_test_expected,unit_test_actual_result); 229 | 230 | cleanupGPIO(&gpioChip); 231 | } 232 | 233 | // TEST - Set Pin Mode When Line Number is negative 234 | void test_setPinModeGPIOWhenLineNegative(){ 235 | init_gpio_test(); 236 | 237 | int gpioLineNumber = -1; 238 | int pinMode = INPUT; 239 | 240 | setupGPIO(&gpioChip); 241 | unit_test_actual_result = setPinModeGPIO(&gpioChip, gpioLineNumber, pinMode); 242 | unit_test_expected = -1; 243 | expect(unit_test_expected,unit_test_actual_result); 244 | 245 | cleanupGPIO(&gpioChip); 246 | } 247 | 248 | // TEST - Set Pin Mode When pinMode is negative 249 | void test_setPinModeGPIOWhenModeNegative(){ 250 | init_gpio_test(); 251 | 252 | int gpioLineNumber = 0; 253 | 254 | setupGPIO(&gpioChip); 255 | unit_test_actual_result = setPinModeGPIO(&gpioChip, gpioLineNumber, -1); 256 | unit_test_expected = -1; 257 | expect(unit_test_expected,unit_test_actual_result); 258 | 259 | cleanupGPIO(&gpioChip); 260 | } 261 | 262 | // TEST - Set Pin Mode When Line Number is Out Of Range 263 | void test_setPinModeGPIOWhenLineOutOfRange(){ 264 | init_gpio_test(); 265 | 266 | int gpioLineNumber = NUM_LINES_TO_USE;; 267 | int pinMode = INPUT; 268 | 269 | setupGPIO(&gpioChip); 270 | unit_test_actual_result = setPinModeGPIO(&gpioChip, gpioLineNumber, pinMode); 271 | unit_test_expected = -1; 272 | expect(unit_test_expected,unit_test_actual_result); 273 | 274 | cleanupGPIO(&gpioChip); 275 | } 276 | 277 | 278 | // SUITE - Read GPIO 279 | // TEST - Read GPIO when gpio has not been setup 280 | void test_readGPIOWithoutSetup(){ 281 | init_gpio_test(); 282 | 283 | int gpioLineNumber = 0; 284 | 285 | unit_test_actual_result = readGPIO(&gpioChip, gpioLineNumber); 286 | unit_test_expected = -1; 287 | expect(unit_test_expected, unit_test_actual_result); 288 | 289 | cleanupGPIO(&gpioChip); 290 | } 291 | 292 | // TEST - Read GPIO set to Input 293 | void test_readGPIOSetToInput(){ 294 | init_gpio_test(); 295 | 296 | int gpioLineNumber = 0; 297 | int pinMode = INPUT; 298 | 299 | setupGPIO(&gpioChip); 300 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 301 | unit_test_actual_result = readGPIO(&gpioChip,gpioLineNumber); 302 | unit_test_expected = -1; 303 | expectNot(unit_test_expected,unit_test_actual_result); 304 | 305 | cleanupGPIO(&gpioChip); 306 | } 307 | 308 | // TEST - Read GPIO set to Output 309 | void test_readGPIOSetToOutput(){ 310 | init_gpio_test(); 311 | 312 | int gpioLineNumber = 0; 313 | int pinMode = OUTPUT; 314 | 315 | setupGPIO(&gpioChip); 316 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 317 | unit_test_actual_result = readGPIO(&gpioChip,gpioLineNumber); 318 | unit_test_expected = -1; 319 | expect(unit_test_expected,unit_test_actual_result); 320 | 321 | cleanupGPIO(&gpioChip); 322 | } 323 | 324 | // TEST - Read GPIO Chip Null 325 | void test_readGPIOChipNull(){ 326 | init_gpio_test(); 327 | 328 | int gpioLineNumber = 0; 329 | int pinMode = INPUT; 330 | 331 | setupGPIO(&gpioChip); 332 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 333 | unit_test_actual_result = readGPIO(NULL,gpioLineNumber); 334 | unit_test_expected = -1; 335 | expect(unit_test_expected,unit_test_actual_result); 336 | 337 | cleanupGPIO(&gpioChip); 338 | } 339 | 340 | // TEST - Read GPIO with Negative Line 341 | void test_readGPIOLineNegative(){ 342 | init_gpio_test(); 343 | 344 | int gpioLineNumber = -1; 345 | int pinMode = INPUT; 346 | 347 | setupGPIO(&gpioChip); 348 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 349 | unit_test_actual_result = readGPIO(&gpioChip,gpioLineNumber); 350 | unit_test_expected = -1; 351 | expect(unit_test_expected,unit_test_actual_result); 352 | 353 | cleanupGPIO(&gpioChip); 354 | } 355 | 356 | // TEST - Read GPIO with Line Out of Range 357 | void test_readGPIOLineOutOfRange(){ 358 | init_gpio_test(); 359 | 360 | int gpioLineNumber = NUM_LINES_TO_USE; 361 | int pinMode = INPUT; 362 | 363 | setupGPIO(&gpioChip); 364 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 365 | unit_test_actual_result = readGPIO(&gpioChip,gpioLineNumber); 366 | unit_test_expected = -1; 367 | expect(unit_test_expected,unit_test_actual_result); 368 | 369 | cleanupGPIO(&gpioChip); 370 | } 371 | 372 | 373 | // SUITE - Write GPIO 374 | // TEST - Read GPIO when gpio has not been setup 375 | void test_writeGPIOWithoutSetup(){ 376 | init_gpio_test(); 377 | 378 | int gpioLineNumber = 0; 379 | int valueToWrite = 1; 380 | 381 | unit_test_actual_result = writeGPIO(&gpioChip, gpioLineNumber, valueToWrite); 382 | unit_test_expected = -1; 383 | expect(unit_test_expected, unit_test_actual_result); 384 | 385 | cleanupGPIO(&gpioChip); 386 | } 387 | 388 | // TEST - Write GPIO set to Input 389 | void test_writeGPIOSetToInput(){ 390 | init_gpio_test(); 391 | 392 | int gpioLineNumber = 0; 393 | int valueToWrite = 1; 394 | int pinMode = INPUT; 395 | 396 | setupGPIO(&gpioChip); 397 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 398 | unit_test_actual_result = writeGPIO(&gpioChip,gpioLineNumber, valueToWrite); 399 | unit_test_expected = -1; 400 | expect(unit_test_expected,unit_test_actual_result); 401 | 402 | cleanupGPIO(&gpioChip); 403 | } 404 | 405 | // TEST - Write GPIO set to Output 406 | void test_writeGPIOSetToOutput(){ 407 | init_gpio_test(); 408 | 409 | int gpioLineNumber = 0; 410 | int valueToWrite = 1; 411 | int pinMode = OUTPUT; 412 | 413 | setupGPIO(&gpioChip); 414 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 415 | unit_test_actual_result = writeGPIO(&gpioChip,gpioLineNumber, valueToWrite); 416 | unit_test_expected = -1; 417 | expectNot(unit_test_expected,unit_test_actual_result); 418 | 419 | cleanupGPIO(&gpioChip); 420 | } 421 | 422 | // TEST - Write GPIO Chip Null 423 | void test_writeGPIOChipNull(){ 424 | init_gpio_test(); 425 | 426 | int gpioLineNumber = 0; 427 | int valueToWrite = 1; 428 | int pinMode = INPUT; 429 | 430 | setupGPIO(&gpioChip); 431 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 432 | unit_test_actual_result = writeGPIO(NULL,gpioLineNumber, valueToWrite); 433 | unit_test_expected = -1; 434 | expect(unit_test_expected,unit_test_actual_result); 435 | 436 | cleanupGPIO(&gpioChip); 437 | } 438 | 439 | // TEST - Write GPIO with Negative Line 440 | void test_writeGPIOLineNegative(){ 441 | init_gpio_test(); 442 | 443 | int gpioLineNumber = -1; 444 | int valueToWrite = 1; 445 | int pinMode = INPUT; 446 | 447 | setupGPIO(&gpioChip); 448 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 449 | unit_test_actual_result = writeGPIO(&gpioChip,gpioLineNumber, valueToWrite); 450 | unit_test_expected = -1; 451 | expect(unit_test_expected,unit_test_actual_result); 452 | 453 | cleanupGPIO(&gpioChip); 454 | } 455 | 456 | // TEST - Write GPIO with Line Out of Range 457 | void test_writeGPIOLineOutOfRange(){ 458 | init_gpio_test(); 459 | 460 | int gpioLineNumber = NUM_LINES_TO_USE; 461 | int valueToWrite = 1; 462 | int pinMode = INPUT; 463 | 464 | setupGPIO(&gpioChip); 465 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 466 | unit_test_actual_result = writeGPIO(&gpioChip,gpioLineNumber, valueToWrite); 467 | unit_test_expected = -1; 468 | expect(unit_test_expected,unit_test_actual_result); 469 | 470 | cleanupGPIO(&gpioChip); 471 | } 472 | 473 | // TEST - Write GPIO with invalid value 474 | void test_writeGPIOInvalidValue(){ 475 | init_gpio_test(); 476 | 477 | int gpioLineNumber = 0; 478 | int valueToWrite = 3; 479 | int pinMode = INPUT; 480 | 481 | setupGPIO(&gpioChip); 482 | setPinModeGPIO(&gpioChip,gpioLineNumber,pinMode); 483 | unit_test_actual_result = writeGPIO(&gpioChip,gpioLineNumber, valueToWrite); 484 | unit_test_expected = -1; 485 | expect(unit_test_expected,unit_test_actual_result); 486 | 487 | cleanupGPIO(&gpioChip); 488 | } 489 | 490 | /******************************************************************************/ 491 | /******************************** I2C Tests ***********************************/ 492 | /******************************************************************************/ 493 | 494 | // SUITE - Setup I2C 495 | // TEST - Test to Setup I2C with Wrong Device 496 | void test_setupI2CWithWrongID(){ 497 | init_I2C_test(); 498 | int wrongI2cId = 0x55; 499 | 500 | unit_test_actual_result = setupI2C(wrongI2cId); 501 | unit_test_expected = -1; 502 | expect(unit_test_expected,unit_test_actual_result); 503 | 504 | cleanup_I2C_test(unit_test_actual_result); 505 | } 506 | 507 | // TEST - Test to Setup I2C 508 | void test_setupI2C(){ 509 | init_I2C_test(); 510 | 511 | unit_test_actual_result = setupI2C(I2C_ID); 512 | unit_test_expected = -1; 513 | expectNot(unit_test_expected,unit_test_actual_result); 514 | 515 | cleanup_I2C_test(unit_test_actual_result); 516 | } 517 | 518 | // TEST - Test to Setup I2C Twice 519 | void test_setupI2CTwice(){ 520 | init_I2C_test(); 521 | 522 | setupI2C(I2C_ID); 523 | unit_test_actual_result = setupI2C(I2C_ID); 524 | unit_test_expected = -1; 525 | expectNot(unit_test_expected,unit_test_actual_result); 526 | 527 | cleanup_I2C_test(unit_test_actual_result); 528 | } 529 | 530 | 531 | /******************************************************************************/ 532 | /******************************* Test Runners *********************************/ 533 | /******************************************************************************/ 534 | 535 | int run_gpio_tests(int suiteToRun, int testToRun){ 536 | setLoggingLevel(OFF); 537 | 538 | addUnitSuite(unitSuiteGPIO0); 539 | addUnitTest("Setup GPIO", getCurrentUnitSuite(), test_setupGPIO); 540 | addUnitTest("Setup GPIO Twice", getCurrentUnitSuite(), test_setupGPIOTwice); 541 | addUnitTest("Setup GPIO with Chip Null", getCurrentUnitSuite(), test_setupGPIOWithChipNull); 542 | addUnitTest("Setup GPIO with Chipname Null", getCurrentUnitSuite(), test_setupGPIOWithChipnameNull); 543 | addUnitTest("Setup GPIO with Consumer Null", getCurrentUnitSuite(), test_setupGPIOWithConsumerNull); 544 | addUnitTest("Setup GPIO with Invalid", getCurrentUnitSuite(), test_setupGPIOWithInvalidChipname); 545 | addUnitTest("Setup GPIO with number of lines too great for chip", getCurrentUnitSuite(), test_setupGPIOWithNumLinesTooHigh); 546 | addUnitTest("Setup GPIO with number of lines negative", getCurrentUnitSuite(), test_setupGPIOWithNumLinesNegative); 547 | addUnitTest("Setup GPIO with number out of range", getCurrentUnitSuite(), test_setupGPIOWithNumLinesOutOfRange); 548 | 549 | addUnitSuite(unitSuiteGPIO1); 550 | addUnitTest("Set Pin Mode when gpio has not been setup", getCurrentUnitSuite(), test_setPinModeGPIOWithoutSetup); 551 | addUnitTest("Set Pin Mode to Output", getCurrentUnitSuite(), test_setPinModeGPIOToOutput); 552 | addUnitTest("Set Pin Mode to Input", getCurrentUnitSuite(), test_setPinModeGPIOToInput); 553 | addUnitTest("Set Pin Mode When Chip is NULL", getCurrentUnitSuite(), test_setPinModeGPIOWhenChipNull); 554 | addUnitTest("Set Pin Mode When Line Number is negative", getCurrentUnitSuite(), test_setPinModeGPIOWhenLineNegative); 555 | addUnitTest("Set Pin Mode When pinMode is negative", getCurrentUnitSuite(), test_setPinModeGPIOWhenModeNegative); 556 | addUnitTest("Set Pin Mode When Line Number is Out Of Range", getCurrentUnitSuite(), test_setPinModeGPIOWhenLineOutOfRange); 557 | 558 | addUnitSuite(unitSuiteGPIO2); 559 | addUnitTest("Read GPIO when gpio has not been setup", getCurrentUnitSuite(), test_readGPIOWithoutSetup); 560 | addUnitTest("Read GPIO set to Input", getCurrentUnitSuite(), test_readGPIOSetToInput); 561 | addUnitTest("Read GPIO set to Output", getCurrentUnitSuite(), test_readGPIOSetToOutput); 562 | addUnitTest("Read GPIO Chip Null", getCurrentUnitSuite(), test_readGPIOChipNull); 563 | addUnitTest("Read GPIO with Negative Line", getCurrentUnitSuite(), test_readGPIOLineNegative); 564 | addUnitTest("Read GPIO with Line Out of Range", getCurrentUnitSuite(), test_readGPIOLineOutOfRange); 565 | 566 | addUnitSuite(unitSuiteGPIO3); 567 | addUnitTest("Write GPIO when gpio has not been setup", getCurrentUnitSuite(), test_writeGPIOWithoutSetup); 568 | addUnitTest("Write GPIO set to Input", getCurrentUnitSuite(), test_writeGPIOSetToInput); 569 | addUnitTest("Write GPIO set to Output", getCurrentUnitSuite(), test_writeGPIOSetToOutput); 570 | addUnitTest("Write GPIO Chip Null", getCurrentUnitSuite(), test_writeGPIOChipNull); 571 | addUnitTest("Write GPIO with Negative Line", getCurrentUnitSuite(), test_writeGPIOLineNegative); 572 | addUnitTest("Write GPIO with Line Out of Range", getCurrentUnitSuite(), test_writeGPIOLineOutOfRange); 573 | addUnitTest("Write GPIO with invalid value", getCurrentUnitSuite(), test_writeGPIOInvalidValue); 574 | 575 | 576 | runUnitTests(suiteToRun,testToRun); 577 | 578 | 579 | return getTestFailCounter(); 580 | } 581 | 582 | int run_I2C_tests(int suiteToRun, int testToRun){ 583 | setLoggingLevel(OFF); 584 | 585 | addUnitSuite(unitSuiteI2C0); 586 | addUnitTest("Setup I2C with Wrong Device", getCurrentUnitSuite(), test_setupI2CWithWrongID); 587 | addUnitTest("Setup I2C", getCurrentUnitSuite(), test_setupI2C); 588 | addUnitTest("Setup I2C Twice", getCurrentUnitSuite(), test_setupI2CTwice); 589 | 590 | runUnitTests(suiteToRun,testToRun); 591 | 592 | return getTestFailCounter(); 593 | } -------------------------------------------------------------------------------- /software/src/piepro.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "piepro.h" 7 | #include "utils.h" 8 | #include "gpio.h" 9 | #include "ulog.h" 10 | 11 | static char* const chipname = "gpiochip0"; 12 | static char* const consumer = "Pi EEPROM Programmer"; 13 | 14 | const char* EEPROM_MODEL_STRINGS[] = { 15 | "xl2816","xl28c16", 16 | "at28c16","at28c64","at28c256", 17 | "at24c01","at24c02","at24c04","at24c08","at24c16", 18 | "at24c32","at24c64","at24c128","at24c256","at24c512" 19 | }; 20 | 21 | const int EEPROM_MODEL_SIZE[] = { 22 | 2048,2048, 23 | 2048,8192,32768, 24 | 128,256,512,1024,2048, 25 | 4096,8192,16384,32768,65536 26 | }; 27 | 28 | const int EEPROM_ADDRESS_LENGTH[] = { 29 | 11,11, 30 | 11,13,15, 31 | 7,8,9,10,11, 32 | 12,13,14,15,16 33 | }; 34 | 35 | const int EEPROM_DATA_LENGTH[] = { 36 | 8,8, 37 | 8,8,8, 38 | 8,8,8,8,8, 39 | 8,8,8,8,8 40 | }; 41 | 42 | const int EEPROM_WRITE_CYCLE_USEC[] = { 43 | 10000,10000, 44 | 5000,10000,1000, 45 | 5000,5000,5000,5000,5000, 46 | 5000,5000,5000,5000,5000 47 | }; 48 | 49 | const int EEPROM_PAGE_SIZE[] = { 50 | 16,16, 51 | 16,16,64, 52 | 8,8,16,16,16, 53 | 32,32,64,64,128 54 | }; 55 | 56 | const int EEPROM_ADDRESS_SIZE[] = { 57 | 0,0, 58 | 0,0,0, 59 | 1,1,2,2,2, 60 | 2,2,2,2,2 61 | }; 62 | 63 | /****************************************************************************** 64 | ******************************************************************************* 65 | ******************************************************************************/ 66 | 67 | /* Local function wrapper to writeGPIO */ 68 | void setPinLevel(struct GPIO_CONFIG* gpioConfig, int pin, int level){ 69 | writeGPIO(&gpioConfig->gpioChip, pin, level); 70 | } 71 | 72 | /* Local function wrapper to readGPIO */ 73 | int getPinLevel(struct GPIO_CONFIG* gpioConfig, int pin){ 74 | return readGPIO(&gpioConfig->gpioChip, pin); 75 | } 76 | 77 | /* Local function wrapper to setPinModeGPIO */ 78 | void setPinMode(struct GPIO_CONFIG* gpioConfig, int pin, int mode){ 79 | setPinModeGPIO(&gpioConfig->gpioChip, pin, mode); 80 | } 81 | 82 | /* Set Address eeprom to value to read from or write to */ 83 | void setAddressPins(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int addressToSet){ 84 | for (char pin = 0; pin < eeprom->maxAddressLength; pin++){ 85 | if (!((eeprom->model == AT28C64) && ((pin == 13) || (pin == 14)))){ 86 | setPinLevel(gpioConfig, eeprom->addressPins[(int)pin], (addressToSet & 1)); 87 | addressToSet >>= 1; 88 | } 89 | } 90 | } 91 | 92 | /* Set Data eeprom to value to write */ 93 | void setDataPins(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char dataToSet){ 94 | for (char pin = 0; pin < eeprom->maxDataLength; pin++){ 95 | setPinLevel(gpioConfig, eeprom->dataPins[(int)pin], (dataToSet & 1)); 96 | dataToSet >>= 1; 97 | } 98 | } 99 | 100 | /* Poll device or wait until write cycle finishes */ 101 | int finishWriteCycle(struct EEPROM* eeprom, struct GPIO_CONFIG* gpioConfig, int dataToCheck){ 102 | // Finish Write Cycle 103 | if(eeprom->useWriteCyclePolling){ 104 | struct timespec start; 105 | struct timespec stop; 106 | // Dummy write to wait for Ack from write 107 | clock_gettime(CLOCK_REALTIME, &start); 108 | if (eeprom->type == I2C){ 109 | do { 110 | clock_gettime(CLOCK_REALTIME, &stop); 111 | } while(writeI2C(eeprom->fd, NULL, 0) == -1 && ((stop.tv_sec - start.tv_sec) < 5)); 112 | } else { 113 | setPinMode(gpioConfig, eeprom->dataPins[7], INPUT); 114 | setPinLevel(gpioConfig, eeprom->outputEnablePin, LOW); 115 | clock_gettime(CLOCK_REALTIME, &start); 116 | do { 117 | clock_gettime(CLOCK_REALTIME, &stop); 118 | } while((getPinLevel(gpioConfig, eeprom->dataPins[7]) ^ (dataToCheck >> 7)) && ((stop.tv_sec - start.tv_sec) < 5)); 119 | } 120 | return 0; 121 | } else { 122 | // Wait Write Cycle time as per datasheet 123 | usleep(eeprom->writeCycleTime); 124 | } 125 | return 0; 126 | } 127 | 128 | /* Writes bytes to an EEPROM via Parallel GPIO */ 129 | int setBytesParallel(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* data, \ 130 | int addressToWrite, int numBytesToWrite){ 131 | int numBytesWritten = 0; 132 | for(int j = 0; j < numBytesToWrite; j++){ 133 | ulog(TRACE,"Writing byte: %i to address: %i", data[j], addressToWrite); 134 | // set the address 135 | setAddressPins(gpioConfig, eeprom, addressToWrite); 136 | 137 | // disable output from the chip 138 | setPinLevel(gpioConfig, eeprom->outputEnablePin, HIGH); 139 | 140 | // set the rpi to output on it's gpio data lines 141 | for(int i = 0; i < eeprom->maxDataLength; i++){ 142 | setPinMode(gpioConfig, eeprom->dataPins[i], OUTPUT); 143 | } 144 | 145 | // Set the data eeprom to the data to be written 146 | setDataPins(gpioConfig, eeprom, data[j]); 147 | 148 | // perform the write 149 | setPinLevel(gpioConfig, eeprom->writeEnablePin, LOW); 150 | usleep(1); 151 | setPinLevel(gpioConfig, eeprom->writeEnablePin, HIGH); 152 | 153 | finishWriteCycle(eeprom, gpioConfig, data[j]); 154 | ++addressToWrite; 155 | ++numBytesWritten; 156 | } 157 | return numBytesWritten; 158 | } 159 | 160 | /* Write a single byte to an EEPROM via Parallel GPIO */ 161 | int setByteParallel(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int addressToWrite, char data){ 162 | return setBytesParallel(gpioConfig, eeprom, &data, addressToWrite, 1); 163 | } 164 | 165 | /* Reads bytes from an EEPROM via Parallel GPIO */ 166 | int getBytesParallel(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* buf, \ 167 | int addressToRead, int numBytesToRead){ 168 | int numBytesRead = 0; 169 | for(int j = 0; j < numBytesToRead; j++){ 170 | int byteVal = 0; 171 | ulog(TRACE,"Comparing byte: %i to address: %i", buf[j], addressToRead); 172 | // set the address 173 | setAddressPins(gpioConfig, eeprom, addressToRead); 174 | // enable output from the chip 175 | setPinLevel(gpioConfig, eeprom->outputEnablePin, LOW); 176 | // set the rpi to input on it's gpio data lines 177 | for(int i=0;imaxDataLength;i++){ 178 | setPinMode(gpioConfig, eeprom->dataPins[i], INPUT); 179 | } 180 | // read the eeprom and store to string 181 | for(int i = eeprom->maxDataLength-1; i >= 0; i--){ 182 | byteVal <<= 1; 183 | byteVal |= (getPinLevel(gpioConfig, eeprom->dataPins[i]) & 1); 184 | } 185 | buf[j] = byteVal; 186 | ++addressToRead; 187 | ++numBytesRead; 188 | } 189 | return numBytesRead; 190 | } 191 | 192 | /* Read a single byte from and EEPROM via Parallel GPIO */ 193 | int getByteParallel(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int address){ 194 | char buf[eeprom->addressSize+1]; 195 | getBytesParallel(gpioConfig, eeprom, buf, address, 1); 196 | return *buf; 197 | } 198 | 199 | /* Populates the array for the I2C format */ 200 | void setAddressinBuffer(struct EEPROM* eeprom, int address, char* buf){ 201 | int i = eeprom->addressSize-1; 202 | 203 | // Set the Address 204 | while(i >= 0){ 205 | buf[i--] = address & 0xFF; 206 | address >>= 8; // i is 1 at first so we shift 8 bits each pass for each byte of the address 207 | } 208 | 209 | if(address != 0){ 210 | ulog(WARNING,"Address out of Range for EEPROM: %i", address); 211 | } 212 | } 213 | 214 | /* Reads bytes from an EEPROM via I2C */ 215 | int getBytesI2C(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* buf, int addressToRead, int numBytesToRead){ 216 | setAddressinBuffer(eeprom, addressToRead, buf); 217 | setPinLevel(gpioConfig, eeprom->writeProtectPin, HIGH); 218 | return readI2C(eeprom->fd, buf, numBytesToRead, eeprom->addressSize); 219 | } 220 | 221 | /* Read a single byte from and EEPROM via I2C */ 222 | int getByteI2C(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int address){ 223 | char buf[eeprom->addressSize + 1]; 224 | getBytesI2C(gpioConfig, eeprom, buf, address, 1); 225 | return *buf; 226 | } 227 | 228 | /* Writes bytes to an EEPROM via I2C */ 229 | int setBytesI2C(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* data, int addressToWrite, int numBytesToWrite){ 230 | int bufSize = eeprom->addressSize + numBytesToWrite; 231 | char buf[bufSize]; 232 | 233 | // Disable Write Protection 234 | setPinLevel(gpioConfig, eeprom->writeProtectPin, LOW); 235 | 236 | // Set the Address 237 | setAddressinBuffer(eeprom, addressToWrite, buf); 238 | 239 | // Set the Data 240 | for(int i = 0; i < numBytesToWrite && i+eeprom->addressSize < bufSize;i++){ 241 | buf[eeprom->addressSize+i] = data[i]; 242 | } 243 | 244 | // Do the write 245 | int numBytesWritten = writeI2C(eeprom->fd, buf, bufSize); 246 | finishWriteCycle(eeprom, NULL, 0); 247 | return numBytesWritten; 248 | } 249 | 250 | /* Write a single byte to an EEPROM via I2C */ 251 | int setByteI2C(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int addressToWrite, char data){ 252 | return setBytesI2C(gpioConfig, eeprom, &data, addressToWrite, 1); 253 | } 254 | 255 | /* Fast forward to start value when reading file and return the size*/ 256 | int setupFileToRead(FILE *romFile, int startValue){ 257 | fseek(romFile, 0L, SEEK_END); 258 | unsigned long size = ftell(romFile); 259 | rewind(romFile); 260 | if(size > startValue){ 261 | fseek(romFile, startValue, SEEK_SET); 262 | } 263 | return size; 264 | } 265 | 266 | /* Get the next set of Data from a text file formatted rom */ 267 | int getNextFromTextFile(struct EEPROM *eeprom, FILE *romFile){ 268 | int c; 269 | int strLen = 0; 270 | char textFileString[(sizeof(int)*8)+1]; 271 | 272 | while((c = fgetc(romFile)) != EOF && strLen != sizeof(int)*8 && c != '\n' && c != ' ' && c != ':'){ 273 | // Will skip characters that are not '1' or '0' 274 | if(c == '1' || c == '0'){ 275 | textFileString[strLen++] = c; 276 | } 277 | } 278 | 279 | textFileString[strLen++] = 0; 280 | if(c == EOF){ 281 | return -1; 282 | } else { 283 | return binStr2num(textFileString); 284 | } 285 | } 286 | 287 | /* Sets all parameters for the EEPROM to be used */ 288 | void setEEPROMParameters(struct OPTIONS* options, struct EEPROM* eeprom){ 289 | eeprom->model = options->eepromModel; 290 | eeprom->i2cId = options->i2cId; 291 | eeprom->forceWrite = options->force; 292 | eeprom->validateWrite = options->validateWrite; 293 | eeprom->startValue = options->startValue; 294 | eeprom->fileType = options->fileType; 295 | 296 | eeprom->byteWriteCounter = 0; 297 | eeprom->byteReadCounter = 0; 298 | 299 | eeprom->useWriteCyclePolling = options->useWriteCyclePolling; 300 | 301 | eeprom->size = EEPROM_MODEL_SIZE[eeprom->model]; 302 | eeprom->maxAddressLength = EEPROM_ADDRESS_LENGTH[eeprom->model]; 303 | eeprom->maxDataLength = (EEPROM_DATA_LENGTH[eeprom->model]); 304 | eeprom->pageSize = EEPROM_PAGE_SIZE[eeprom->model]; 305 | eeprom->addressSize = EEPROM_ADDRESS_SIZE[eeprom->model]; 306 | eeprom->quick = options->quick; 307 | if( options->readChunk == -1 || options->readChunk == 0){ 308 | eeprom->readChunk = eeprom->pageSize; 309 | } else { 310 | eeprom->readChunk = options->readChunk; 311 | } 312 | 313 | if( options->writeCycleUSec == -1 || options->writeCycleUSec == 0){ 314 | eeprom->writeCycleTime = EEPROM_WRITE_CYCLE_USEC[eeprom->model]; 315 | }else{ 316 | eeprom->writeCycleTime = options->writeCycleUSec; 317 | } 318 | 319 | if( options->limit == -1){ 320 | eeprom->limit = EEPROM_MODEL_SIZE[eeprom->model]; 321 | }else{ 322 | eeprom->limit = options->limit; 323 | } 324 | 325 | if (eeprom->model >= AT24C01 && eeprom->model <= AT24C512){ 326 | eeprom->type = I2C; 327 | } else { 328 | eeprom->type = PARALLEL; 329 | } 330 | 331 | } 332 | 333 | /* Sets all parameters to use GPIO */ 334 | void setGPIOConfigParameters(struct OPTIONS* options, struct GPIO_CONFIG* gpioConfig){ 335 | gpioConfig->gpioChip.chipname = options->chipname; 336 | gpioConfig->gpioChip.numGPIOLines = options->numGPIOLines; 337 | gpioConfig->gpioChip.consumer = options->consumer; 338 | 339 | gpioConfig->chipname = options->chipname; 340 | gpioConfig->numGPIOLines = options->numGPIOLines; 341 | gpioConfig->consumer = options->consumer; 342 | 343 | gpioConfig->gpioChip.isSetup = 0; 344 | } 345 | 346 | /****************************************************************************** 347 | ******************************************************************************* 348 | ******************************************************************************/ 349 | 350 | /* Initialize Raspberry Pi to perform action on EEPROM */ 351 | int initHardware(struct OPTIONS *options, struct EEPROM *eeprom, struct GPIO_CONFIG* gpioConfig){ 352 | if(options->eepromModel == END){ 353 | ulog(ERROR,"EEPROM model must be specified."); 354 | return -1; 355 | } 356 | setEEPROMParameters(options, eeprom); 357 | ulog(DEBUG,"Starting GPIO Initialization"); 358 | setGPIOConfigParameters(options, gpioConfig); 359 | if(setupGPIO(&gpioConfig->gpioChip)){ 360 | ulog(ERROR, "Failed to setup GPIO"); 361 | return -1; 362 | } 363 | if (eeprom->model >= AT24C01 && eeprom->model <= AT24C512){ 364 | // 2; // 8 // 3 // I2C Pins 365 | // 3; // 9 // 5 // I2C Pins 366 | 367 | eeprom->addressPins[0] = 13; // 23 // 33 368 | eeprom->addressPins[1] = 19; // 24 // 35 369 | eeprom->addressPins[2] = 26; // 25 // 37 370 | 371 | eeprom->vccPin = 12; // 26 // 32 372 | eeprom->writeProtectPin = 16; // 27 // 36 373 | 374 | 375 | for(int i=0;i<3;i++){ 376 | setPinMode(gpioConfig,eeprom->addressPins[i], OUTPUT); 377 | setPinLevel(gpioConfig,eeprom->addressPins[i], LOW); 378 | } 379 | 380 | if (eeprom->i2cId != 0x50){ 381 | setAddressPins(gpioConfig, eeprom, eeprom->i2cId - 0x50); 382 | } 383 | 384 | setPinMode(gpioConfig,eeprom->writeProtectPin, OUTPUT); 385 | setPinMode(gpioConfig,eeprom->vccPin, OUTPUT); 386 | setPinLevel(gpioConfig,eeprom->writeProtectPin, HIGH); 387 | setPinLevel(gpioConfig,eeprom->vccPin, HIGH); 388 | 389 | eeprom->fd = setupI2C(eeprom->i2cId); 390 | if(eeprom->fd == -1){ 391 | ulog(ERROR,"Cannot setup I2C device"); 392 | cleanupGPIO(&gpioConfig->gpioChip); 393 | return -1; 394 | } 395 | 396 | } else { 397 | /* GPIO // WiPi // Pin */ 398 | eeprom->addressPins[14] = 4; // 7 // 7 !Not Used on AT28C16 399 | eeprom->addressPins[12] = 17; // 0 // 11 !Not Used on AT28C16 400 | 401 | eeprom->addressPins[7] = 27; // 2 // 13 402 | eeprom->addressPins[6] = 22; // 3 // 15 403 | eeprom->addressPins[5] = 10; // 12 // 19 404 | eeprom->addressPins[4] = 9; // 13 // 21 405 | eeprom->addressPins[3] = 11; // 14 // 23 406 | eeprom->addressPins[2] = 0; // 30 // 27 407 | eeprom->addressPins[1] = 5; // 21 // 29 408 | eeprom->addressPins[0] = 6; // 22 // 31 409 | 410 | eeprom->dataPins[0] = 13; // 23 // 33 411 | eeprom->dataPins[1] = 19; // 24 // 35 412 | eeprom->dataPins[2] = 26; // 25 // 37 413 | 414 | if (eeprom->model == AT28C64 || eeprom->model == AT28C256){ 415 | eeprom->writeEnablePin = 14; // 15 // 8 416 | 417 | eeprom->addressPins[13] = 15; // 16 // 10 418 | eeprom->addressPins[11] = 24; // 5 // 18 419 | } else if (eeprom->model <= AT28C16){ 420 | eeprom->writeEnablePin = 24; // 5 // 18 421 | } 422 | 423 | eeprom->addressPins[8] = 18; // 1 // 12 424 | eeprom->addressPins[9] = 23; // 4 // 16 425 | 426 | eeprom->outputEnablePin = 25; // 6 // 22 427 | eeprom->addressPins[10] = 8; // 10 // 23 428 | eeprom->chipEnablePin = 7; // 11 // 26 429 | eeprom->dataPins[7] = 1; // 31 // 28 430 | eeprom->dataPins[6] = 12; // 26 // 32 431 | eeprom->dataPins[5] = 16; // 27 // 36 432 | eeprom->dataPins[4] = 3;//20; // 28 // 38 433 | eeprom->dataPins[3] = 2;//21; // 29 // 40 434 | 435 | for(int i=0;imaxAddressLength;i++){ 436 | if ((eeprom->model == AT28C64) && ((i == 13) || (i == 14))){ 437 | // handle NC pins 438 | setPinMode(gpioConfig,eeprom->addressPins[i], INPUT); 439 | } else { 440 | // ulog(DEBUG,"Setting Mode for Pin: %i",i); 441 | setPinMode(gpioConfig,eeprom->addressPins[i], OUTPUT); 442 | } 443 | } 444 | 445 | for(int i=0;imaxDataLength;i++){ 446 | setPinMode(gpioConfig,eeprom->dataPins[i], INPUT); 447 | } 448 | 449 | setPinMode(gpioConfig,eeprom->chipEnablePin, OUTPUT); 450 | setPinMode(gpioConfig,eeprom->outputEnablePin, OUTPUT); 451 | setPinMode(gpioConfig,eeprom->writeEnablePin, OUTPUT); 452 | setPinLevel(gpioConfig,eeprom->chipEnablePin, LOW); 453 | setPinLevel(gpioConfig,eeprom->outputEnablePin, HIGH); 454 | setPinLevel(gpioConfig,eeprom->writeEnablePin, HIGH); 455 | } 456 | 457 | usleep(5000); //startup delay 458 | ulog(DEBUG,"Finished GPIO Initialization"); 459 | return 0; 460 | } 461 | 462 | /* Read specified number of bytes starting from specified Address */ 463 | int readNumBytesFromAddress(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* byteBuffer, \ 464 | int addressToRead, int numBytesToRead){ 465 | int numBytesRead = 0; 466 | 467 | if(addressToRead > eeprom->size-1){ 468 | ulog(ERROR,"Address out of range of EEPROM: 0x%02x plus 0x%02x bytes", addressToRead, numBytesToRead); 469 | return -1; 470 | } 471 | 472 | if(addressToRead + numBytesToRead - 1 > eeprom->size-1){ 473 | numBytesToRead = ((eeprom->size-1) - addressToRead) + 1; 474 | ulog(WARNING,"Addresses requested exceeds eeprom size. Only reading %i bytes", numBytesToRead); 475 | } 476 | 477 | if(addressToRead + numBytesToRead - 1 > eeprom->limit){ 478 | numBytesToRead = eeprom->limit - addressToRead ; 479 | } 480 | 481 | int maxBytesToRead = eeprom->readChunk - (addressToRead % eeprom->readChunk); 482 | if(maxBytesToRead < numBytesToRead){ 483 | numBytesToRead = maxBytesToRead; 484 | ulog(DEBUG,"Addresses requested crosses page boundary. Only reading %i bytes", numBytesToRead); 485 | } 486 | 487 | if (eeprom->type == I2C){ 488 | numBytesRead = getBytesI2C(gpioConfig, eeprom, byteBuffer, addressToRead, numBytesToRead); 489 | if(numBytesRead != -1){ 490 | eeprom->byteReadCounter += numBytesRead; 491 | } 492 | } else { 493 | numBytesRead = getBytesParallel(gpioConfig, eeprom, byteBuffer, addressToRead, numBytesToRead); 494 | if(numBytesRead != -1){ 495 | eeprom->byteReadCounter += numBytesRead; 496 | } 497 | } 498 | 499 | // return the number of bytes read 500 | return numBytesRead; 501 | } 502 | 503 | /* Read byte from specified Address */ 504 | int readByteFromAddress(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int addressToRead){ 505 | int byteVal = 0; 506 | 507 | if(addressToRead > eeprom->size-1){ 508 | ulog(ERROR,"Address out of range of EEPROM: 0x%02x", addressToRead); 509 | return -1; 510 | } 511 | if (eeprom->type == I2C){ 512 | byteVal = getByteI2C(gpioConfig, eeprom, addressToRead); 513 | } else { 514 | byteVal = getByteParallel(gpioConfig, eeprom, addressToRead); 515 | } 516 | eeprom->byteReadCounter++; 517 | // return the number 518 | return byteVal; 519 | } 520 | 521 | /* Write specified number of bytes starting from specified Address */ 522 | int writeNumBytesToAddress(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char* byteBuffer, \ 523 | int addressToWrite, int numBytesToWrite){ 524 | int numBytesWritten = 0; 525 | 526 | if(addressToWrite > eeprom->size-1){ 527 | ulog(ERROR,"Address out of range of EEPROM: 0x%02x plus 0x%02x bytes", addressToWrite, numBytesToWrite); 528 | return -1; 529 | } 530 | 531 | if(addressToWrite + numBytesToWrite - 1 > eeprom->size-1){ 532 | numBytesToWrite = ((eeprom->size-1) - addressToWrite) + 1; 533 | ulog(WARNING,"Addresses requested exceeds eeprom size. Only writing %i bytes", numBytesToWrite); 534 | } 535 | 536 | if(addressToWrite + numBytesToWrite - 1 > eeprom->limit){ 537 | numBytesToWrite = eeprom->limit - addressToWrite ; 538 | } 539 | 540 | int maxBytesToWrite = eeprom->pageSize - (addressToWrite % eeprom->pageSize); 541 | if(maxBytesToWrite < numBytesToWrite){ 542 | numBytesToWrite = maxBytesToWrite; 543 | ulog(DEBUG,"Addresses requested crosses page boundary. Only writing %i bytes", numBytesToWrite); 544 | } 545 | 546 | if (eeprom->type == I2C){ 547 | numBytesWritten = setBytesI2C(gpioConfig, eeprom, byteBuffer, addressToWrite, numBytesToWrite); 548 | } else { 549 | numBytesWritten = setBytesParallel(gpioConfig, eeprom, byteBuffer, addressToWrite, numBytesToWrite); 550 | numBytesWritten += eeprom->addressSize; 551 | } 552 | 553 | if(numBytesWritten != -1){ 554 | eeprom->byteWriteCounter += numBytesWritten - eeprom->addressSize; 555 | } 556 | if(numBytesWritten-eeprom->addressSize != numBytesToWrite){ 557 | ulog(ERROR,"Cannot write %i bytes starting at address: %i", numBytesToWrite, addressToWrite); 558 | numBytesWritten = -1; 559 | } 560 | 561 | return numBytesWritten - eeprom->addressSize; 562 | } 563 | 564 | /* Write specified byte to specified address */ 565 | int writeByteToAddress(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int addressToWrite, char dataToWrite){ 566 | int err = 0; 567 | 568 | if(addressToWrite > eeprom->size-1){ 569 | ulog(ERROR,"Address out of range of EEPROM: 0x%02x", addressToWrite); 570 | return -1; 571 | } 572 | 573 | if (eeprom->type == I2C){ 574 | if (eeprom->forceWrite || dataToWrite != readByteFromAddress(gpioConfig, eeprom, addressToWrite)){ 575 | if (setByteI2C(gpioConfig, eeprom, addressToWrite, dataToWrite) != -1){ 576 | ulog(DEBUG,"Wrote Byte %i at Address %i", dataToWrite, addressToWrite); 577 | eeprom->byteWriteCounter++; 578 | } else { 579 | ulog(WARNING,"Failed to Write Byte %i at Address %i", dataToWrite, addressToWrite); 580 | err = -1; 581 | } 582 | } 583 | } else { 584 | if (eeprom->forceWrite || dataToWrite != readByteFromAddress(gpioConfig, eeprom, addressToWrite)){ 585 | setByteParallel(gpioConfig ,eeprom, addressToWrite, dataToWrite); 586 | if (eeprom->validateWrite == 1){ 587 | if (dataToWrite != readByteFromAddress(gpioConfig, eeprom, addressToWrite)){ 588 | ulog(WARNING,"Failed to Write Byte %i at Address %i", dataToWrite, addressToWrite); 589 | err = -1; 590 | } else { 591 | ulog(DEBUG,"Wrote Byte %i at Address %i", dataToWrite, addressToWrite); 592 | eeprom->byteWriteCounter++; 593 | } 594 | } else { 595 | eeprom->byteWriteCounter++; 596 | } 597 | } 598 | } 599 | 600 | return err; 601 | } 602 | 603 | /* Open and write a text file to EEPROM */ 604 | int writeTextFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM *eeprom, FILE *romFile){ 605 | int err = 0; 606 | int addressToWrite = 0; 607 | int dataToWrite = 0; 608 | 609 | if(eeprom->quick){ 610 | ulog(ERROR,"Quick action, -q or --quick, not valid for text files"); 611 | err = -1; 612 | } else { 613 | while(addressToWrite < eeprom->limit && addressToWrite != -1 && dataToWrite != -1){ 614 | addressToWrite = getNextFromTextFile(eeprom, romFile); 615 | dataToWrite = getNextFromTextFile(eeprom, romFile); 616 | 617 | if((unsigned int)addressToWrite < eeprom->limit && (unsigned int)addressToWrite >= eeprom->startValue){ 618 | if(addressToWrite != -1 && dataToWrite != -1){ 619 | err |= writeByteToAddress(gpioConfig, eeprom, addressToWrite, dataToWrite); 620 | } else { 621 | ulog(ERROR,"Cannot process text file"); 622 | return -1; 623 | } 624 | } 625 | } 626 | } 627 | 628 | return err; 629 | } 630 | 631 | /* Compare a text file to EEPROM */ 632 | int compareTextFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM *eeprom, FILE *romFile){ 633 | int address = 0; 634 | int data = 0; 635 | int bytesNotMatched = 0; 636 | 637 | if(eeprom->quick){ 638 | ulog(ERROR,"Quick action, -q or --quick, not valid for text files"); 639 | bytesNotMatched = -1; 640 | } else { 641 | while((unsigned int)address < eeprom->limit && address != -1 && data != -1){ 642 | address = getNextFromTextFile(eeprom, romFile); 643 | data = getNextFromTextFile(eeprom, romFile); 644 | 645 | if((unsigned int)address < eeprom->limit && (unsigned int)address >= eeprom->startValue ){ 646 | if(address != -1 && data != -1){ 647 | int byte = readByteFromAddress(gpioConfig, eeprom, address); 648 | if (byte != data){ 649 | ulog(INFO,"Byte at Address 0x%02x does not match. EEPROM: %i File: %i", address, byte, data); 650 | bytesNotMatched++; 651 | } 652 | } else { 653 | ulog(ERROR,"Cannot process text file"); 654 | return -1; 655 | } 656 | } 657 | } 658 | } 659 | 660 | return bytesNotMatched; 661 | } 662 | 663 | /* Open and write a binary file to the EEPROM */ 664 | int writeBinaryFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, FILE *romFile){ 665 | int dataToWrite; 666 | int addressToWrite = eeprom->startValue; 667 | int err = 0; 668 | unsigned int fileSize = setupFileToRead(romFile, eeprom->startValue); 669 | 670 | if(eeprom->quick){ 671 | int numBytesToWrite = eeprom->pageSize; 672 | if(eeprom->limit-eeprom->startValue < eeprom->pageSize){ 673 | numBytesToWrite = eeprom->limit-eeprom->startValue; 674 | } 675 | int bytesWritten = numBytesToWrite; 676 | char bytesToWriteBuf[numBytesToWrite]; 677 | 678 | while(addressToWrite < eeprom->limit && err != -1 && addressToWrite < fileSize){ 679 | int i = 0; 680 | 681 | while(bytesWritten < numBytesToWrite){ 682 | bytesToWriteBuf[i++] = bytesToWriteBuf[bytesWritten++]; 683 | } 684 | 685 | while((i < numBytesToWrite && addressToWrite+i < eeprom->limit && (dataToWrite = fgetc(romFile)) != EOF)) { 686 | bytesToWriteBuf[i++] = (char)dataToWrite; 687 | } 688 | 689 | bytesWritten = writeNumBytesToAddress(gpioConfig, eeprom, bytesToWriteBuf, addressToWrite, i); 690 | if( bytesWritten != -1){ 691 | ulog(DEBUG,"Wrote %i bytes",bytesWritten); 692 | addressToWrite += bytesWritten; 693 | } else{ 694 | err = -1; 695 | } 696 | } 697 | } else { 698 | while((addressToWrite < eeprom->limit && addressToWrite < fileSize && (dataToWrite = fgetc(romFile)) != EOF)) { 699 | err |= writeByteToAddress(gpioConfig, eeprom, addressToWrite++, (char)dataToWrite); 700 | } 701 | } 702 | 703 | return err; 704 | } 705 | 706 | /* Compare a binary file to EEPROM */ 707 | int compareBinaryFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, FILE *romFile){ 708 | int dataToCompare; 709 | int addressToCompare = eeprom->startValue; 710 | int bytesNotMatched = 0; 711 | unsigned int fileSize = setupFileToRead(romFile, eeprom->startValue); 712 | 713 | if(eeprom->quick){ 714 | int numBytesToCompare = eeprom->readChunk; 715 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 716 | numBytesToCompare = eeprom->limit - eeprom->startValue; 717 | } 718 | int bytesRead = numBytesToCompare; 719 | char bytesToCompareBuf[numBytesToCompare+eeprom->addressSize]; 720 | char bytesFromFileBuf[numBytesToCompare+eeprom->addressSize]; 721 | 722 | while(addressToCompare < eeprom->limit && bytesNotMatched != -1 && addressToCompare < fileSize){ 723 | int i = 0; 724 | 725 | while(bytesRead < numBytesToCompare){ 726 | bytesFromFileBuf[i++] = bytesFromFileBuf[bytesRead++]; 727 | } 728 | 729 | while((i < numBytesToCompare && addressToCompare+i < fileSize && \ 730 | addressToCompare+i < eeprom->limit && (dataToCompare = fgetc(romFile)) != EOF)) { 731 | bytesFromFileBuf[i++] = (char)dataToCompare; 732 | } 733 | 734 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, bytesToCompareBuf, addressToCompare, i); 735 | if( bytesRead != -1){ 736 | for(int i = 0;i < bytesRead; i++){ 737 | if(bytesFromFileBuf[i] != bytesToCompareBuf[i]){ 738 | ulog(INFO,"Byte at Address 0x%02x does not match. EEPROM: %i File: %i", \ 739 | addressToCompare+i, bytesToCompareBuf[i], bytesFromFileBuf[i]); 740 | bytesNotMatched++; 741 | } 742 | } 743 | addressToCompare += bytesRead; 744 | } else{ 745 | bytesNotMatched = -1; 746 | } 747 | } 748 | } else { 749 | while(((dataToCompare = fgetc(romFile)) != EOF) && addressToCompare < fileSize && addressToCompare < eeprom->limit) { 750 | char byte = readByteFromAddress(gpioConfig, eeprom, addressToCompare); 751 | if (byte != (char)dataToCompare){ 752 | ulog(INFO,"Byte at Address 0x%02x does not match. EEPROM: %i File: %i", \ 753 | addressToCompare, byte, (char)dataToCompare); 754 | bytesNotMatched++; 755 | } 756 | addressToCompare++; 757 | } 758 | } 759 | 760 | return bytesNotMatched; 761 | } 762 | 763 | /* Compare a file to EEPROM */ 764 | int compareFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, FILE *romFile){ 765 | if (eeprom->fileType == TEXT_FILE){ 766 | return compareTextFileToEEPROM(gpioConfig, eeprom, romFile); 767 | } else { 768 | return compareBinaryFileToEEPROM(gpioConfig, eeprom, romFile); 769 | } 770 | } 771 | 772 | /* Open and write a file to EEPROM */ 773 | int writeFileToEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, FILE *romFile){ 774 | if (eeprom->fileType == TEXT_FILE){ 775 | return writeTextFileToEEPROM(gpioConfig, eeprom, romFile); 776 | } else { 777 | return writeBinaryFileToEEPROM(gpioConfig, eeprom, romFile); 778 | } 779 | } 780 | 781 | /* Prints the EEPROM's Contents to the specified limit */ 782 | /* This whole section is a mess. */ 783 | int printEEPROMContents(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, int format){ 784 | int err = 0; 785 | 786 | if (eeprom->limit == -1 || eeprom->limit > EEPROM_MODEL_SIZE[eeprom->model]){ 787 | eeprom->limit = EEPROM_MODEL_SIZE[eeprom->model]; 788 | } 789 | 790 | int beginPrint = eeprom->startValue; 791 | 792 | switch (format) { 793 | case LABELED: 794 | if(eeprom->quick){ 795 | int addressToRead = eeprom->startValue; 796 | int numBytesToRead = eeprom->readChunk; 797 | 798 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 799 | numBytesToRead = eeprom->limit - eeprom->startValue; 800 | } 801 | int bytesRead = numBytesToRead; 802 | char buffer[numBytesToRead+eeprom->addressSize]; 803 | 804 | while(addressToRead < eeprom->limit && err != -1){ 805 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, buffer, addressToRead, numBytesToRead); 806 | if(bytesRead != -1){ 807 | int bufferIdx = 0; 808 | for(int j = 0; bufferIdx < bytesRead && addressToRead+j < eeprom->limit; j++){ 809 | fprintf(stdout,"Address: %i Data: %i \n", addressToRead+j, buffer[bufferIdx++]); 810 | } 811 | addressToRead += bytesRead; 812 | } else{ 813 | ulog(ERROR,"Could not read bytes"); 814 | err = -1; 815 | } 816 | } 817 | } else { 818 | for (int i = eeprom->startValue; i < eeprom->limit; i++){ 819 | fprintf(stdout,"Address: %i Data: %i \n", i, readByteFromAddress(gpioConfig, eeprom, i)); 820 | } 821 | } 822 | break; 823 | case BINARY: 824 | setLoggingLevel(OFF); 825 | for (int i=0; i < eeprom->startValue; i++) { 826 | putc(0xFF,stdout); 827 | } 828 | 829 | if(eeprom->quick){ 830 | int addressToRead = eeprom->startValue; 831 | int numBytesToRead = eeprom->readChunk; 832 | 833 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 834 | numBytesToRead = eeprom->limit - eeprom->startValue; 835 | } 836 | int bytesRead = numBytesToRead; 837 | char buffer[numBytesToRead + eeprom->addressSize]; 838 | 839 | while(addressToRead < eeprom->limit && err != -1){ 840 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, buffer, addressToRead, numBytesToRead); 841 | if(bytesRead != -1){ 842 | int bufferIdx = 0; 843 | for(int j = 0; bufferIdx < bytesRead && addressToRead+j < eeprom->limit; j++){ 844 | putc(buffer[bufferIdx++], stdout); 845 | } 846 | addressToRead += bytesRead; 847 | } else{ 848 | ulog(ERROR,"Could not read bytes"); 849 | err = -1; 850 | } 851 | } 852 | } else { 853 | for (int i = eeprom->startValue; i < eeprom->limit; i++) { 854 | putc(readByteFromAddress(gpioConfig, eeprom, i), stdout); 855 | } 856 | } 857 | break; 858 | case TEXT: 859 | if(eeprom->quick){ 860 | int addressToRead = eeprom->startValue; 861 | int numBytesToRead = eeprom->readChunk; 862 | 863 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 864 | numBytesToRead = eeprom->limit - eeprom->startValue; 865 | } 866 | int bytesRead = numBytesToRead; 867 | char buffer[numBytesToRead+eeprom->addressSize]; 868 | 869 | while(addressToRead < eeprom->limit && err != -1){ 870 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, buffer, addressToRead, numBytesToRead); 871 | if(bytesRead != -1){ 872 | int bufferIdx = 0; 873 | for(int j = 0; bufferIdx < bytesRead && addressToRead+j < eeprom->limit; j++){ 874 | char addressBinStr[eeprom->maxAddressLength+1]; 875 | char dataBinStr[eeprom->maxDataLength+1]; 876 | 877 | if(num2binStr(addressToRead+j, addressBinStr, eeprom->maxAddressLength+1) == (char*)-1){ 878 | ulog(WARNING,"String Buffer not large enough to hold number: %i",addressToRead+j); 879 | } 880 | if(num2binStr(buffer[bufferIdx++], dataBinStr,eeprom->maxDataLength+1) == (char*)-1){ 881 | ulog(WARNING,"String Buffer not large enough to hold number: %i", buffer[bufferIdx - 1]); 882 | } 883 | if ( eeprom->startValue < 0x100 && eeprom->limit < 0x100){ 884 | char shortAddressBinStr[9]; 885 | strncpy(shortAddressBinStr,&addressBinStr[8] ,9); 886 | fprintf(stdout,"%s %s\n", shortAddressBinStr, dataBinStr); 887 | } else { 888 | fprintf(stdout,"%s %s\n", addressBinStr, dataBinStr); 889 | } 890 | } 891 | addressToRead += bytesRead; 892 | } else{ 893 | ulog(ERROR,"Could not read bytes"); 894 | err = -1; 895 | } 896 | } 897 | } else { 898 | for (int i = eeprom->startValue; i < eeprom->limit; i++) { 899 | char addressBinStr[eeprom->maxAddressLength+1]; 900 | char dataBinStr[eeprom->maxDataLength+1]; 901 | 902 | if(num2binStr(i, addressBinStr, eeprom->maxAddressLength+1) == (char*)-1){ 903 | ulog(WARNING,"String Buffer not large enough to hold number: %i", i); 904 | } 905 | 906 | if(num2binStr(readByteFromAddress(gpioConfig, eeprom, i), dataBinStr, eeprom->maxDataLength+1) == (char*)-1){ 907 | ulog(WARNING,"String Buffer not large enough to hold number: %i", i); 908 | } 909 | 910 | if (eeprom->startValue < 0x100 && eeprom->limit < 0x100){ 911 | char shortAddressBinStr[9]; 912 | strncpy(shortAddressBinStr, &addressBinStr[8], 9); 913 | fprintf(stdout,"%s %s\n", shortAddressBinStr, dataBinStr); 914 | } else { 915 | fprintf(stdout,"%s %s\n", addressBinStr, dataBinStr); 916 | } 917 | } 918 | } 919 | break; 920 | case PRETTY: 921 | if ((eeprom->startValue % 16) != 0){ 922 | eeprom->startValue = eeprom->startValue - (eeprom->startValue % 16); 923 | } 924 | 925 | fprintf(stdout," 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); 926 | fprintf(stdout,"Device ===============================================================\n"); 927 | 928 | if(eeprom->quick){ 929 | int addressToRead = eeprom->startValue; 930 | int numBytesToRead = eeprom->readChunk; 931 | 932 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 933 | numBytesToRead = eeprom->limit - eeprom->startValue; 934 | } 935 | int bytesRead = numBytesToRead; 936 | char buffer[numBytesToRead+eeprom->addressSize]; 937 | 938 | while(addressToRead < eeprom->limit && err != -1){ 939 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, buffer, addressToRead, numBytesToRead); 940 | if(bytesRead != -1){ 941 | int bufferIdx = 0; 942 | for(int j = 0; bufferIdx < bytesRead && addressToRead+j < eeprom->limit; j++){ 943 | if((addressToRead+j) % 16 == 0){ 944 | fprintf(stdout,"%04x | ", addressToRead+j); 945 | } 946 | if(addressToRead+j >= beginPrint){ 947 | fprintf(stdout,"%02x ", buffer[bufferIdx++]); 948 | } else { 949 | fprintf(stdout," "); 950 | bufferIdx++; 951 | } 952 | if((addressToRead + j + 1) % 16 == 0 && (addressToRead + j + 1) != eeprom->limit){ 953 | fprintf(stdout,"\n"); 954 | } 955 | } 956 | addressToRead += bytesRead; 957 | } else{ 958 | ulog(ERROR,"Could not read bytes"); 959 | err = -1; 960 | } 961 | } 962 | fprintf(stdout,"\n"); 963 | } else { 964 | for (int i = eeprom->startValue; i < eeprom->limit; i++){ 965 | fprintf(stdout,"%04x | ", i); 966 | for(int j = 0; j < 16 && i < eeprom->limit; j++){ 967 | if(i >= beginPrint){ 968 | fprintf(stdout,"%02x ", readByteFromAddress(gpioConfig, eeprom, i++)); 969 | } else { 970 | fprintf(stdout," "); 971 | i++; 972 | } 973 | } 974 | i--; 975 | fprintf(stdout,"\n"); 976 | } 977 | } 978 | break; 979 | case PRETTY_WITH_ASCII: 980 | default: 981 | if ((eeprom->startValue % 16) != 0){ 982 | eeprom->startValue = eeprom->startValue - (eeprom->startValue % 16); 983 | } 984 | 985 | fprintf(stdout," 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F\n"); 986 | fprintf(stdout,"Device ===============================================================\n"); 987 | 988 | if(eeprom->quick){ 989 | int addressToRead = eeprom->startValue; 990 | int numBytesToRead = eeprom->readChunk; 991 | 992 | if(eeprom->limit - eeprom->startValue < eeprom->readChunk){ 993 | numBytesToRead = eeprom->limit - eeprom->startValue; 994 | } 995 | 996 | int bytesRead = numBytesToRead; 997 | char buffer[numBytesToRead+eeprom->addressSize]; 998 | int printBuffer[16]; 999 | 1000 | int printBufferOffset = 0; 1001 | while(printBufferOffset < beginPrint % 16 ){ 1002 | printBuffer[printBufferOffset++] = -1; 1003 | } 1004 | 1005 | while(addressToRead < eeprom->limit && err != -1){ 1006 | bytesRead = readNumBytesFromAddress(gpioConfig, eeprom, buffer, addressToRead, numBytesToRead); 1007 | if(bytesRead != -1){ 1008 | int bufferIdx = 0; 1009 | for(int j = 0; bufferIdx < bytesRead && addressToRead+j < eeprom->limit; j++){ 1010 | if((addressToRead+j) % 16 == 0){ 1011 | fprintf(stdout,"%04x | ", addressToRead+j); 1012 | } 1013 | if(addressToRead+j >= beginPrint){ 1014 | fprintf(stdout,"%02x ", buffer[bufferIdx]); 1015 | printBuffer[printBufferOffset++] = buffer[bufferIdx]; 1016 | ++bufferIdx; 1017 | } else { 1018 | fprintf(stdout," "); 1019 | ++bufferIdx; 1020 | } 1021 | 1022 | if((addressToRead + j + 1) % 16 == 0 && (addressToRead + j + 1) != eeprom->limit){ 1023 | fprintf(stdout," "); 1024 | for(int i=0; i<16 && (addressToRead + i + 1) != eeprom->limit; i++){ 1025 | if(addressToRead+i >= beginPrint){ 1026 | if(printBuffer[i] != -1 && printBuffer[i] > 31 && printBuffer[i] < 127){ 1027 | fprintf(stdout,"%c", printBuffer[i]); 1028 | } else { 1029 | fprintf(stdout,"."); 1030 | } 1031 | } else { 1032 | fprintf(stdout," "); 1033 | } 1034 | } 1035 | printBufferOffset = 0; 1036 | fprintf(stdout,"\n"); 1037 | } 1038 | 1039 | if(addressToRead + j + 1 == eeprom->limit){ 1040 | fprintf(stdout," "); 1041 | for(int i=j+1; i<16; i++){ 1042 | fprintf(stdout," "); 1043 | } 1044 | for(int i=0; i<16 && (addressToRead + i) != eeprom->limit; i++){ 1045 | if(addressToRead+i >= beginPrint){ 1046 | if(printBuffer[i] != -1 && printBuffer[i] > 31 && printBuffer[i] < 127){ 1047 | fprintf(stdout,"%c", printBuffer[i]); 1048 | } else { 1049 | fprintf(stdout,"."); 1050 | } 1051 | } else { 1052 | fprintf(stdout," "); 1053 | } 1054 | } 1055 | printBufferOffset = 0; 1056 | fprintf(stdout,"\n"); 1057 | } 1058 | } 1059 | addressToRead += bytesRead; 1060 | } else{ 1061 | ulog(ERROR,"Could not read bytes"); 1062 | err = -1; 1063 | } 1064 | } 1065 | } else { 1066 | for (int i = eeprom->startValue; i < eeprom->limit; i++){ 1067 | fprintf(stdout,"%04x | ", i); 1068 | for(int j = 0; j < 16 && i < eeprom->limit; j++){ 1069 | if(i >= beginPrint){ 1070 | fprintf(stdout,"%02x ", readByteFromAddress(gpioConfig, eeprom, i++)); 1071 | } else { 1072 | fprintf(stdout," "); 1073 | i++; 1074 | } 1075 | } 1076 | while(i % 16 != 0){ 1077 | fprintf(stdout," "); 1078 | i++; 1079 | } 1080 | i -= 16; 1081 | fprintf(stdout," "); 1082 | for(int j = 0; j < 16 && i < eeprom->limit; j++){ 1083 | if(i >= beginPrint){ 1084 | char c = readByteFromAddress(gpioConfig, eeprom, i++); 1085 | if(c > 31 && c < 127){ 1086 | fprintf(stdout,"%c", c); 1087 | } else { 1088 | fprintf(stdout,"."); 1089 | } 1090 | } else { 1091 | fprintf(stdout," "); 1092 | i++; 1093 | } 1094 | } 1095 | 1096 | 1097 | i--; 1098 | 1099 | fprintf(stdout,"\n"); 1100 | } 1101 | } 1102 | break; 1103 | } 1104 | 1105 | return err; 1106 | } 1107 | 1108 | /* Erase EEPROM */ 1109 | int eraseEEPROM(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom, char eraseByte){ 1110 | int err = 0; 1111 | 1112 | // Set the start value to erase 1113 | if(eeprom->startValue >= eeprom->size){ 1114 | ulog(ERROR,"Address out of range of EEPROM: 0x%02x",eeprom->startValue); 1115 | return -1; 1116 | } 1117 | 1118 | if(eeprom->quick){ 1119 | // Check to see if paging is supported 1120 | if(eeprom->pageSize < 0){ 1121 | ulog(ERROR,"EEPROM does not support paging. Remove -q or --quick flag."); 1122 | return -1; 1123 | } 1124 | 1125 | // Setup variables 1126 | int addressToWrite = eeprom->startValue; 1127 | int numBytesToWrite = eeprom->pageSize; 1128 | 1129 | // Initialize Array to 0s the size of the number of bytes to write 1130 | if(eeprom->limit - eeprom->startValue < eeprom->pageSize){ 1131 | numBytesToWrite = eeprom->limit - eeprom->startValue; 1132 | } 1133 | char bytesToWriteBuf[numBytesToWrite + eeprom->addressSize]; 1134 | memset(bytesToWriteBuf, eraseByte, numBytesToWrite + eeprom->addressSize); 1135 | 1136 | // Perform the erase 1137 | while(addressToWrite < eeprom->limit && err != -1){ 1138 | int bytesWritten = writeNumBytesToAddress(gpioConfig, eeprom, bytesToWriteBuf, addressToWrite, numBytesToWrite); 1139 | if( bytesWritten != -1){ 1140 | addressToWrite += bytesWritten; 1141 | } else { 1142 | err = -1; 1143 | } 1144 | } 1145 | } else { 1146 | // Perform the erase 1147 | for(int i = eeprom->startValue; i < eeprom->limit; i++) { 1148 | err |= writeByteToAddress(gpioConfig, eeprom, i, eraseByte); 1149 | } 1150 | } 1151 | return err; 1152 | } 1153 | 1154 | /* Free and release hardware */ 1155 | void cleanupHardware(struct GPIO_CONFIG* gpioConfig, struct EEPROM* eeprom){ 1156 | cleanupGPIO(&gpioConfig->gpioChip); 1157 | cleanupI2C(eeprom->fd); 1158 | } 1159 | 1160 | /****************************************************************************** 1161 | ******************************************************************************* 1162 | ******************************************************************************/ 1163 | 1164 | void printSupportedEEPROMs(){ 1165 | fprintf(stdout,"Tested Models:\n"); 1166 | fprintf(stdout,"\tNote that the program may work if your EEPROM uses a similar pinout and\n"); 1167 | fprintf(stdout,"\tvoltages to the ones listed.\n"); 1168 | fprintf(stdout," Model Size(B) Addr Len Data Len Write Cycle(uS) Page Size(B) Address Size(B)\n"); 1169 | fprintf(stdout,"----------------------------------------------------------------------------------------\n"); 1170 | for(int i=0; i != END; i++){ 1171 | fprintf(stdout,"%-10s %7i %8i %9i %14i %13i %13i\n", \ 1172 | EEPROM_MODEL_STRINGS[i], \ 1173 | EEPROM_MODEL_SIZE[i], \ 1174 | EEPROM_ADDRESS_LENGTH[i], \ 1175 | EEPROM_DATA_LENGTH[i], \ 1176 | EEPROM_WRITE_CYCLE_USEC[i], \ 1177 | EEPROM_PAGE_SIZE[i], \ 1178 | EEPROM_ADDRESS_SIZE[i] \ 1179 | ); 1180 | } 1181 | } 1182 | 1183 | /* Prints version number */ 1184 | void printVersion(){ 1185 | fprintf(stdout,"piepro v%s\n",VERSION); 1186 | } 1187 | 1188 | /* Prints help message */ 1189 | void printHelp(){ 1190 | printVersion(); 1191 | fprintf(stdout,"Usage: piepro [options]\n"); 1192 | fprintf(stdout,"Options:\n"); 1193 | fprintf(stdout," -c FILE, --compare FILE Compare FILE and EEPROM and print number of differences.\n"); 1194 | fprintf(stdout," --chipname Specify the chipname to use. Default: gpiochip0\n"); 1195 | fprintf(stdout," -d [N], --dump [N] Dump the contents of the EEPROM\n"); 1196 | fprintf(stdout," 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII\n"); 1197 | fprintf(stdout," -e [N], --erase [N] Erase eeprom with specified byte. Default: 0xFF\n"); 1198 | fprintf(stdout," -f, --force Force writing of every byte instead of checking for existing value first.\n"); 1199 | fprintf(stdout," -id, --i2c-device-id The address id of the I2C device.\n"); 1200 | fprintf(stdout," -h, --help Print this message and exit.\n"); 1201 | fprintf(stdout," -l N, --limit N Specify the maximum address to operate.\n"); 1202 | fprintf(stdout," -m MODEL, --model MODEL Specify EERPOM device model. Default: AT28C16.\n"); 1203 | fprintf(stdout," --no-validate-write \n"); 1204 | fprintf(stdout," Do not perform a read directly after writing to verify the data was written.\n"); 1205 | fprintf(stdout," -r [N], --read [N] Read the contents of the EEPROM\n"); 1206 | fprintf(stdout," 0=PRETTY_WITH_ASCII, 1=BINARY, 2=TEXT, 3=LABELED, 4=PRETTY. Default: PRETTY_WITH_ASCII\n"); 1207 | fprintf(stdout," -rb N, --read-byte ADDRESS\n"); 1208 | fprintf(stdout," Read From specified ADDRESS.\n"); 1209 | fprintf(stdout," -q [N], --quick [N] Operates on N bytes at once for reads. Page Size if unspecified or writes.\n"); 1210 | fprintf(stdout," Implied --force and --no-validate-write.\n"); 1211 | fprintf(stdout," -rb N, --read-byte ADDRESS \n"); 1212 | fprintf(stdout," Read From specified ADDRESS.\n"); 1213 | fprintf(stdout," -s N, --start N Specify the minimum address to operate.\n"); 1214 | fprintf(stdout," -t, --text Interpret file as a text. Default: binary\n"); 1215 | fprintf(stdout," Text File format:\n"); 1216 | fprintf(stdout," [00000000]00000000 00000000\n"); 1217 | fprintf(stdout," -v N, --v[vvvv] Set the log verbosity to N, 0=OFF, 1=FATAL, 2=ERROR, 3=WARNING, 4=INFO, 5=DEBUG 6=TRACE. Default: WARNING\n"); 1218 | fprintf(stdout," --version Print the piepro version and exit.\n"); 1219 | fprintf(stdout," -w FILE, --write FILE Write EEPROM with specified file.\n"); 1220 | fprintf(stdout," -wb ADDRESS DATA, --write-byte ADDRESS DATA \n"); 1221 | fprintf(stdout," Write specified DATA to ADDRESS.\n"); 1222 | fprintf(stdout," -wd [N], --write-delay N Enable write delay. N Number of microseconds to delay between writes.\n"); 1223 | fprintf(stdout," -y, --yes Automatically answer Yes to write or erase EEPROM.\n"); 1224 | fprintf(stdout,"\n"); 1225 | fprintf(stdout,"\n"); 1226 | printSupportedEEPROMs(); 1227 | } 1228 | 1229 | /* Sets default options */ 1230 | void setDefaultOptions(struct OPTIONS* options){ 1231 | // Options 1232 | options->filename = NULL; 1233 | options->limit = -1; 1234 | options->startValue = 0; 1235 | options->dumpFormat = PRETTY_WITH_ASCII; 1236 | options->validateWrite = 1; 1237 | options->force = 0; 1238 | options->action = NOTHING; 1239 | options->fileType = BINARY_FILE; 1240 | options->eepromModel = END; 1241 | options->writeCycleUSec = -1; 1242 | options->useWriteCyclePolling = 1; 1243 | options->boardType = RPI4; 1244 | options->eraseByte = 0xFF; 1245 | options->quick = 0; 1246 | options->readChunk = -1; 1247 | options->promptUser = 1; 1248 | // Single Read/Write Parameters 1249 | options->addressParam = 0; 1250 | options->dataParam = 0; 1251 | // I2C Specific 1252 | options->i2cId = 0x50; 1253 | options->consumer = consumer; 1254 | options->chipname = chipname; 1255 | options->numGPIOLines = 28; 1256 | } 1257 | 1258 | /* Parses and processes all command line arguments */ 1259 | int parseCommandLineOptions(struct OPTIONS* options, int argc, char* argv[]){ 1260 | setDefaultOptions(options); 1261 | 1262 | options->filename = argv[argc-1]; 1263 | for(int i = 1; i < argc; i++){ 1264 | // -h --help 1265 | if (!(strcmp(argv[i], "-h")) || (!strcmp(argv[i], "--help"))){ 1266 | printHelp(); 1267 | options->action = HELP; 1268 | return 0; 1269 | } 1270 | 1271 | // --version 1272 | if (!strcmp(argv[i], "--version")){ 1273 | printVersion(); 1274 | options->action = VER; 1275 | return 0; 1276 | } 1277 | 1278 | //-v -vvvv 1279 | if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--v") || !strcmp(argv[i], "--vv") || \ 1280 | !strcmp(argv[i], "--vvv") || !strcmp(argv[i], "--vvvv") || !strcmp(argv[i], "--vvvvv") || \ 1281 | !strcmp(argv[i], "--vvvvvv")){ 1282 | int verbosity = 0; 1283 | if (!strcmp(argv[i], "-v")){ 1284 | if (i != argc-1) { 1285 | verbosity = str2num(argv[i+1]); 1286 | } else { 1287 | ulog(ERROR,"%s Flag must have a verbosity level specified", argv[i]); 1288 | return -1; 1289 | } 1290 | } else { 1291 | verbosity = strlen(argv[i]) - 2; 1292 | } 1293 | if (setLoggingLevel(verbosity)){ 1294 | return -1; 1295 | } 1296 | } 1297 | } 1298 | 1299 | for(int i = 1; i < argc; i++){ 1300 | // -c --compare 1301 | if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--compare")){ 1302 | if (options->action != COMPARE_FILE_TO_ROM && options->action != NOTHING){ 1303 | ulog(WARNING, \ 1304 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1305 | } else if (i != argc-1) { 1306 | ulog(INFO,"Comparing EEPROM to File: %s", argv[i+1]); 1307 | options->action = COMPARE_FILE_TO_ROM; 1308 | options->filename = argv[i+1]; 1309 | i++; 1310 | } else { 1311 | ulog(ERROR,"%s Flag must have a filename specified", argv[i]); 1312 | return -1; 1313 | } 1314 | } 1315 | 1316 | // --chipname 1317 | if (!strcmp(argv[i], "--chipname")){ 1318 | if (i != argc-1) { 1319 | ulog(INFO,"Setting Chipname to %s", argv[i+1]); 1320 | options->chipname = argv[i+1]; 1321 | i++; 1322 | } else { 1323 | ulog(ERROR,"%s Flag must have a chipname specified", argv[i]); 1324 | return -1; 1325 | } 1326 | } 1327 | 1328 | // -d --dump || -r --read 1329 | if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--dump") || !strcmp(argv[i], "-r") || !strcmp(argv[i], "--read")){ 1330 | if (options->action != DUMP_ROM && options->action != NOTHING){ 1331 | ulog(WARNING, \ 1332 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1333 | } else { 1334 | ulog(INFO,"Dumping EEPROM to standard out"); 1335 | int format = PRETTY_WITH_ASCII; 1336 | if (i != argc-1) { 1337 | format = str2num(argv[i+1]); 1338 | i++; 1339 | } 1340 | if(format == -1 || format > 4){ 1341 | options->dumpFormat = PRETTY_WITH_ASCII; 1342 | } else { 1343 | options->dumpFormat = format; 1344 | } 1345 | options->action = DUMP_ROM; 1346 | } 1347 | } 1348 | 1349 | // -e --erase 1350 | if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--erase")){ 1351 | if (options->action != ERASE_ROM && options->action != NOTHING){ 1352 | ulog(WARNING, \ 1353 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1354 | } else { 1355 | if (i != argc-1) { 1356 | options->eraseByte = str2num(argv[i+1]); 1357 | i++; 1358 | } 1359 | ulog(INFO,"Erasing EEPROM with 0x%02x", options->eraseByte ); 1360 | options->action = ERASE_ROM; 1361 | } 1362 | } 1363 | 1364 | // -f --force 1365 | if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--force")){ 1366 | ulog(INFO,"Forcing all writes even if value is already present."); 1367 | options->force = 1; 1368 | } 1369 | 1370 | // -id --i2c-device-id 1371 | if (!strcmp(argv[i], "-id") || !strcmp(argv[i], "--i2c-device-id")){ 1372 | if (i != argc-1) { 1373 | options->i2cId = str2num(argv[i+1]); 1374 | if ( options->i2cId == (char)-1){ 1375 | ulog(ERROR,"Unsupported I2C id value"); 1376 | return -1; 1377 | } else { 1378 | ulog(INFO,"Setting I2C id to %i",options->i2cId); 1379 | } 1380 | i++; 1381 | } else { 1382 | ulog(ERROR,"%s Flag must have an id specified", argv[i]); 1383 | return -1; 1384 | } 1385 | } 1386 | 1387 | // -l --limit 1388 | if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--limit")){ 1389 | if (i != argc-1) { 1390 | ulog(INFO,"Setting limit to %s", argv[i+1]); 1391 | options->limit = str2num(argv[i+1]); 1392 | if ( options->limit== -1){ 1393 | ulog(ERROR,"Unsupported limit value"); 1394 | return -1; 1395 | } 1396 | } else { 1397 | ulog(ERROR,"%s Flag must have a value specified", argv[i]); 1398 | return -1; 1399 | } 1400 | } 1401 | 1402 | // -m --model 1403 | if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--model")){ 1404 | if (i != argc-1) { 1405 | options->eepromModel = 0; 1406 | while(options->eepromModel < END && strcmp(argv[i+1], EEPROM_MODEL_STRINGS[options->eepromModel])){ 1407 | options->eepromModel++; 1408 | } 1409 | if(options->eepromModel == END){ 1410 | ulog(INFO,"Supported EEPROM Models:"); 1411 | for(int i=0; i < END-1;i++){ 1412 | ulog(INFO,"\t%s",EEPROM_MODEL_STRINGS[i]); 1413 | } 1414 | ulog(FATAL,"Unsupported EEPROM Model"); 1415 | return -1; 1416 | } 1417 | ulog(INFO,"Setting EEPROM model to %s", EEPROM_MODEL_STRINGS[options->eepromModel]); 1418 | } else { 1419 | ulog(ERROR,"%s Flag must have a model specified", argv[i]); 1420 | return -1; 1421 | } 1422 | } 1423 | 1424 | // --no-validate-write 1425 | if (!strcmp(argv[i], "--no-validate-write")){ 1426 | ulog(WARNING,"Disabling write verification"); 1427 | options->validateWrite = 0; 1428 | } 1429 | 1430 | // -rb --read-byte 1431 | if (!strcmp(argv[i], "-rb") || !strcmp(argv[i], "--read-byte")){ 1432 | if (options->action != READ_SINGLE_BYTE_FROM_ROM && options->action != NOTHING){ 1433 | ulog(WARNING, \ 1434 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1435 | } else if (i != argc-1) { 1436 | options->addressParam = str2num(argv[i+1]); 1437 | if ( options->addressParam == -1) { 1438 | ulog(ERROR,"Invalid number. Exiting."); 1439 | return -1; 1440 | } 1441 | ulog(INFO,"Reading single byte from Address %s", argv[i+1]); 1442 | options->action = READ_SINGLE_BYTE_FROM_ROM; 1443 | }else { 1444 | ulog(ERROR,"%s Flag must have a value specified", argv[i]); 1445 | return -1; 1446 | } 1447 | } 1448 | 1449 | // -q --quick 1450 | if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quick")){ 1451 | ulog(INFO,"Using quick operations. Implied --force and --no-validate-write."); 1452 | options->quick = 1; 1453 | if (i != argc-1) { 1454 | options->readChunk = str2num(argv[i+1]); 1455 | if(options->readChunk > 8192){ 1456 | ulog(WARNING,"Read Chunk set above maximum: 8192. Setting to 8192."); 1457 | options->readChunk = 8192; 1458 | } 1459 | if ( options->readChunk != -1 && options->readChunk != 0 ){ 1460 | ulog(INFO,"Setting read chunk to %i", options->readChunk ); 1461 | } 1462 | } 1463 | } 1464 | 1465 | // -s --start 1466 | if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--start")){ 1467 | if (i != argc-1) { 1468 | options->startValue = str2num(argv[i+1]); 1469 | ulog(INFO,"Setting starting value to %i", options->startValue); 1470 | if ( options->startValue == -1){ 1471 | ulog(FATAL,"Unsupported starting value"); 1472 | return -1; 1473 | } 1474 | } else { 1475 | ulog(ERROR,"%s Flag must have a value specified", argv[i]); 1476 | return -1; 1477 | } 1478 | } 1479 | 1480 | // -t --text 1481 | if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--text")){ 1482 | ulog(INFO,"Setting filetype to text"); 1483 | options->fileType = TEXT_FILE; 1484 | } 1485 | 1486 | // -w --write 1487 | if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--write")){ 1488 | if (options->action != WRITE_FILE_TO_ROM && options->action != NOTHING){ 1489 | ulog(WARNING, \ 1490 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1491 | } else if (i != argc-1) { 1492 | ulog(INFO,"Writing File to EEPROM"); 1493 | options->action = WRITE_FILE_TO_ROM; 1494 | options->filename = argv[i+1]; 1495 | ulog(INFO,"Setting filename to %s", options->filename); 1496 | } else { 1497 | ulog(ERROR,"%s Flag must have a filename specified", argv[i]); 1498 | return -1; 1499 | } 1500 | } 1501 | 1502 | // -wd --write-delay 1503 | if (!strcmp(argv[i], "-wd") || !strcmp(argv[i], "--write-delay")){ 1504 | ulog(INFO,"Using Write Cycle Delay instead of Polling"); 1505 | options->useWriteCyclePolling = 0; 1506 | if (i != argc-1) { 1507 | options->writeCycleUSec = str2num(argv[i+1]); 1508 | if ( options->writeCycleUSec != -1 && options->writeCycleUSec != 0 ){ 1509 | ulog(INFO,"Setting write cycle delay time to %i", options->writeCycleUSec); 1510 | } 1511 | } 1512 | } 1513 | 1514 | // -wb --write-byte 1515 | if (!strcmp(argv[i], "-wb") || !strcmp(argv[i], "--write-byte")){ 1516 | if (options->action != WRITE_SINGLE_BYTE_TO_ROM && options->action != NOTHING){ 1517 | ulog(WARNING, \ 1518 | "%s flag specified but another action has already be set. Ignoring %s flag.", argv[i], argv[i]); 1519 | } else if (i < argc-2) { 1520 | options->addressParam = str2num(argv[i+1]); 1521 | options->dataParam = str2num(argv[i+2]); 1522 | if ( options->addressParam == -1 || options->dataParam == -1) { 1523 | ulog(ERROR,"Invalid number. Exiting."); 1524 | return -1; 1525 | } 1526 | if (options->dataParam > 0xFF){ 1527 | ulog(ERROR,"Data byte too large. Please specify a value less than 256."); 1528 | return -1; 1529 | } 1530 | ulog(INFO,"Writing Byte %s to Address %s", argv[i+2], argv[i+1]); 1531 | options->action = WRITE_SINGLE_BYTE_TO_ROM; 1532 | } else { 1533 | ulog(ERROR,"%s Flag must have an address and data specified", argv[i]); 1534 | return -1; 1535 | } 1536 | } 1537 | 1538 | // -y --yes 1539 | if (!strcmp(argv[i], "-y") || !strcmp(argv[i], "--yes")){ 1540 | ulog(INFO,"Not prompting for writes or erasure."); 1541 | options->promptUser = 0; 1542 | } 1543 | 1544 | } 1545 | return 0; 1546 | } 1547 | --------------------------------------------------------------------------------