├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── blink.spin ├── license.txt ├── spin ├── IP_Loader.spin ├── TV.spin ├── TV_Text.spin ├── fsrw.spin ├── packet_driver.spin ├── safe_spi_c3.spin └── sd_helper.spin ├── src ├── config.c ├── config.h ├── enumcom.c ├── expr.c ├── expr.h ├── fastloader.cpp ├── gpio_sysfs.c ├── gpio_sysfs.h ├── loadelf.c ├── loadelf.h ├── loader.cpp ├── loader.h ├── main.cpp ├── messages.cpp ├── messages.h ├── messages.txt ├── packet.cpp ├── packet.h ├── propconnection.h ├── propimage.cpp ├── propimage.h ├── proploader.h ├── serial.h ├── serial_mingw.c ├── serial_posix.c ├── serialloader.cpp ├── serialpropconnection.cpp ├── serialpropconnection.h ├── sock.h ├── sock_posix.c ├── system.c ├── system.h ├── wifipropconnection.cpp └── wifipropconnection.h ├── toggle.c └── tools ├── bin2c.c └── split.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.binary 3 | *.eeprom 4 | *.elf 5 | *~ 6 | obj/ 7 | bin/ 8 | build-proploader-* 9 | qt/proploader.pro.user* 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 dbetz 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 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # version is MAJOR.minor-commit (date hash) 2 | # if there has not been a commit since the last tag, commit and hash are left out 3 | # MAJOR is the major version number (change when protocol changes) 4 | # minor is the minor version number 5 | # commit is the number of commits since the last tag 6 | # date is the date of the build 7 | # hash is a unique substring of the hash of the last commit 8 | GITDESC=$(shell git describe master) 9 | GITDESC_WORDS=$(subst -, ,$(GITDESC)) 10 | GIT_VERSION=$(word 1,$(GITDESC_WORDS)) 11 | GIT_BUILD=$(word 2,$(GITDESC_WORDS)) 12 | GIT_HASH=$(word 3,$(GITDESC_WORDS)) 13 | ifeq ($(GIT_BUILD),) 14 | GIT_BUILD_SUFFIX= 15 | GIT_HASH_SUFFIX= 16 | else 17 | GIT_BUILD_SUFFIX=-$(GIT_BUILD) 18 | GIT_HASH_SUFFIX=$(subst -, ,-$(GIT_HASH)) 19 | endif 20 | VERSION=$(GIT_VERSION)$(GIT_BUILD_SUFFIX) ($(shell date "+%Y-%m-%d %H:%M:%S")$(GIT_HASH_SUFFIX)) 21 | $(info VERSION $(VERSION)) 22 | 23 | MKDIR=mkdir 24 | TOUCH=touch 25 | RM=rm -r -f 26 | 27 | ifeq ($(CROSS),) 28 | PREFIX= 29 | else 30 | ifeq ($(CROSS),win32) 31 | PREFIX=i686-w64-mingw32- 32 | OS=msys 33 | else 34 | ifeq ($(CROSS),rpi) 35 | PREFIX=arm-linux-gnueabihf- 36 | OS=raspberrypi 37 | else 38 | $(error Unknown cross compilation selected) 39 | endif 40 | endif 41 | endif 42 | 43 | CC=$(PREFIX)gcc 44 | CPP=$(PREFIX)g++ 45 | SPINCMP=openspin 46 | TOOLCC=gcc 47 | 48 | CFLAGS=-Wall -DVERSION=\""$(VERSION)"\" 49 | LDFLAGS= 50 | 51 | ifeq ($(OS),Windows_NT) 52 | OS=msys 53 | endif 54 | 55 | ifeq ($(OS),linux) 56 | CFLAGS+=-DLINUX 57 | EXT= 58 | OSINT=$(OBJDIR)/sock_posix.o $(OBJDIR)/serial_posix.o 59 | LIBS= 60 | 61 | else ifeq ($(OS),raspberrypi) 62 | CFLAGS+=-DLINUX -DRASPBERRY_PI 63 | EXT= 64 | OSINT=$(OBJDIR)/sock_posix.o $(OBJDIR)/serial_posix.o $(OBJDIR)/gpio_sysfs.o 65 | LIBS= 66 | 67 | else ifeq ($(OS),msys) 68 | CFLAGS+=-DMINGW 69 | LDFLAGS=-static 70 | EXT=.exe 71 | OSINT=$(OBJDIR)/serial_mingw.o $(OBJDIR)/sock_posix.o $(OBJDIR)/enumcom.o 72 | LIBS=-lws2_32 -liphlpapi -lsetupapi 73 | 74 | else ifeq ($(OS),macosx) 75 | CFLAGS+=-DMACOSX 76 | EXT= 77 | OSINT=$(OBJDIR)/serial_posix.o $(OBJDIR)/sock_posix.o 78 | LIBS= 79 | 80 | else ifeq ($(OS),) 81 | $(error OS not set) 82 | 83 | else 84 | $(error Unknown OS $(OS)) 85 | endif 86 | 87 | BUILD=$(realpath ..)/proploader-$(OS)-build 88 | 89 | SRCDIR=src 90 | OBJDIR=$(BUILD)/obj 91 | BINDIR=$(BUILD)/bin 92 | SPINDIR=spin 93 | TOOLDIR=tools 94 | 95 | OBJS=\ 96 | $(OBJDIR)/main.o \ 97 | $(OBJDIR)/loader.o \ 98 | $(OBJDIR)/fastloader.o \ 99 | $(OBJDIR)/propimage.o \ 100 | $(OBJDIR)/packet.o \ 101 | $(OBJDIR)/serialpropconnection.o \ 102 | $(OBJDIR)/serialloader.o \ 103 | $(OBJDIR)/wifipropconnection.o \ 104 | $(OBJDIR)/loadelf.o \ 105 | $(OBJDIR)/sd_helper.o \ 106 | $(OBJDIR)/config.o \ 107 | $(OBJDIR)/expr.o \ 108 | $(OBJDIR)/system.o \ 109 | $(OBJDIR)/messages.o \ 110 | $(OSINT) 111 | 112 | CFLAGS+=-I$(OBJDIR) 113 | CPPFLAGS=$(CFLAGS) 114 | 115 | all: $(BINDIR)/proploader$(EXT) $(BUILD)/blink-fast.binary $(BUILD)/blink-slow.binary 116 | 117 | ctests: $(BUILD)/toggle.elf 118 | 119 | $(OBJS): $(OBJDIR)/created $(HDRS) $(OBJDIR)/IP_Loader.h Makefile 120 | 121 | $(BINDIR)/proploader$(EXT): $(BINDIR)/created $(OBJS) 122 | $(CPP) -o $@ $(LDFLAGS) $(OBJS) $(LIBS) -lstdc++ 123 | 124 | $(BUILD)/%.elf: %.c 125 | propeller-elf-gcc -Os -mlmm -o $@ $< 126 | 127 | $(BUILD)/%-fast.binary: %.spin 128 | $(SPINCMP) -o $@ $< 129 | 130 | $(BUILD)/%-slow.binary: %.spin 131 | $(SPINCMP) -DSLOW -o $@ $< 132 | 133 | # This rule doesn't work under Windows because of a bug in OpenSpin. 134 | #$(OBJDIR)/%.binary: $(SPINDIR)/%.spin 135 | # $(SPINCMP) -o $@ $< 136 | 137 | # This is the workaround. 138 | $(OBJDIR)/%.binary: $(SPINDIR)/%.spin 139 | cd $(dir $<); $(SPINCMP) -o $@ $(notdir $<) 140 | 141 | $(OBJDIR)/%.c: $(OBJDIR)/%.binary $(BINDIR)/bin2c$(EXT) 142 | $(BINDIR)/bin2c$(EXT) $< $@ 143 | 144 | $(OBJDIR)/%.o: $(OBJDIR)/%.c 145 | $(CC) $(CFLAGS) -c $< -o $@ 146 | 147 | # This rule doesn't work under Windows because of a bug in OpenSpin. 148 | #$(OBJDIR)/IP_Loader.h: $(SPINDIR)/IP_Loader.spin $(BINDIR)/split$(EXT) 149 | # $(SPINCMP) -o $(OBJDIR)/IP_Loader.binary $< 150 | # $(BINDIR)/split$(EXT) $(OBJDIR)/IP_Loader.binary $(OBJDIR)/IP_Loader.h 151 | 152 | # This is the workaround. 153 | $(OBJDIR)/IP_Loader.h: $(SPINDIR)/IP_Loader.spin $(BINDIR)/split$(EXT) 154 | cd $(dir $<); $(SPINCMP) -o $(OBJDIR)/IP_Loader.binary $(notdir $<) 155 | $(BINDIR)/split$(EXT) $(OBJDIR)/IP_Loader.binary $(OBJDIR)/IP_Loader.h 156 | 157 | setup: $(BUILD)/blink-slow.binary 158 | propeller-load -e $(BUILD)/blink-slow.binary 159 | 160 | run: $(BINDIR)/proploader$(EXT) $(BUILD)/blink-fast.binary 161 | $(BINDIR)/proploader$(EXT) $(BUILD)/blink-fast.binary -t 162 | 163 | runbig: $(BINDIR)/proploader$(EXT) $(BUILD)/toggle.elf 164 | $(BINDIR)/proploader$(EXT) $(BUILD)/toggle.elf -t 165 | 166 | E: $(BINDIR)/proploader$(EXT) $(BUILD)/blink-fast.binary 167 | $(BINDIR)/proploader$(EXT) $(BUILD)/blink-fast.binary -e 168 | 169 | Ebig: $(BINDIR)/proploader$(EXT) $(BUILD)/toggle.elf 170 | $(BINDIR)/proploader$(EXT) $(BUILD)/toggle.elf -e 171 | 172 | P: $(BINDIR)/proploader$(EXT) 173 | $(BINDIR)/proploader$(EXT) -P 174 | 175 | P0: $(BINDIR)/proploader$(EXT) 176 | $(BINDIR)/proploader$(EXT) -P0 177 | 178 | X: $(BINDIR)/proploader$(EXT) 179 | $(BINDIR)/proploader$(EXT) -X 180 | 181 | X0: $(BINDIR)/proploader$(EXT) 182 | $(BINDIR)/proploader$(EXT) -X0 183 | 184 | $(OBJDIR)/%.o: $(SRCDIR)/%.c $(HDRS) 185 | $(CC) $(CFLAGS) -c $< -o $@ 186 | 187 | $(OBJDIR)/%.o: $(SRCDIR)/%.cpp $(HDRS) 188 | $(CPP) $(CPPFLAGS) -c $< -o $@ 189 | 190 | $(BINDIR)/%$(EXT): $(TOOLDIR)/%.c 191 | $(TOOLCC) $(CFLAGS) $< -o $@ 192 | 193 | install: $(BUILD)/bin/proploader$(EXT) 194 | cp $(BUILD)/bin/proploader$(EXT) ~/bin 195 | 196 | clean: 197 | $(RM) $(BUILD) 198 | 199 | %/created: 200 | @$(MKDIR) -p $(@D) 201 | @$(TOUCH) $@ 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a loader for the Parallax Propeller 2 | 3 | It supports loading over a serial link or a WiFi connection to a Parallax WiFi module on the 4 | Propeller Activity Board WX. 5 | 6 | ``` 7 | PropLoader v1.0-43 (2018-12-01 13:55:21 g7445ae2) 8 | 9 | usage: proploader [options] [] 10 | 11 | options: 12 | -b select target board and subtype (default is 'default:default') 13 | -c display numeric message codes 14 | -D var=value define a board configuration variable 15 | -e program eeprom (and halt, unless combined with -r) 16 | -f write a file to the SD card 17 | -i IP address of the Parallax Wi-Fi module 18 | -I add a directory to the include path 19 | -n set the name of a Parallax Wi-Fi module 20 | -p serial port 21 | -P show all serial ports 22 | -r run program after downloading (useful with -e) 23 | -R reset the Propeller 24 | -s do a serial download 25 | -t enter terminal mode after the load is complete 26 | -T enter pst-compatible terminal mode after the load is complete 27 | -v enable verbose debugging output 28 | -W show all discovered wifi modules 29 | -? display a usage message and exit 30 | 31 | file: binary file to load (.elf or .binary) 32 | 33 | Target board type can be either a single identifier like 'propboe' in which case the subtype 34 | defaults to 'default' or it can be of the form : like 'c3:ram'. 35 | 36 | Module names should only include the characters A-Z, a-z, 0-9, or '-' and should not begin or 37 | end with a '-'. They must also be less than 32 characters long. 38 | 39 | Variables that can be set with -D are: 40 | 41 | Used by the loader: 42 | loader reset clkfreq clkmode fast-loader-clkfreq fastloader-clkmode 43 | baudrate loader-baud-rate fast-loader-baud-rate 44 | 45 | Used by the SD file writer: 46 | sdspi-do sdspi-clk sdspi-di sdspi-cs 47 | sdspi-clr sdspi-inc sdspi-start sdspi-width spdspi-addr 48 | sdspi-config1 sdspi-config2 49 | 50 | Value expressions for -D can include: 51 | rcfast rcslow xinput xtal1 xtal2 xtal3 pll1x pll2x pll4x pll8x pll16x k m mhz true false 52 | an integer or two operands with a binary operator + - * / % & | or unary + or - 53 | or a parenthesized expression. 54 | 55 | Examples: 56 | loader=rom to use the ROM loader instead of the fast loader 57 | ``` 58 | 59 | The C++ code should be mostly generic. The platform-specific code is in the C files 60 | sock_posix.c and serial_posix.c. Those will have to be rewritten to work on a different 61 | platform or under a different framework like Qt. If necessary, those interfaces could 62 | also be C++. I left them as C for now because they matched my original code better. 63 | 64 | The files sock.h and serial.h show the interfaces needed to support another platform. 65 | I can easily provide a Windows version of these files to cover running the loader 66 | under Linux, Mac, and Windows. The xxx_posix.c files support both Linux and the Mac. 67 | 68 | In addition to a standard C++ toolset you also need to install OpenSpin and have it 69 | in your path. 70 | 71 | https://github.com/parallaxinc/OpenSpin 72 | 73 | To build the Windows version under Linux you will need the MinGW toolchain installed. 74 | Then type: 75 | 76 | make CROSS=win32 77 | 78 | Output files are placed: 79 | 80 | Macintosh: ../proploader-macosx-build/bin 81 | Linux: ../proploader-linux-build/bin 82 | Windows: ../proploader-msys-build/bin 83 | 84 | To build the C test programs you also need PropGCC installed an in your path. 85 | -------------------------------------------------------------------------------- /blink.spin: -------------------------------------------------------------------------------- 1 | CON 2 | _clkmode = xtal1 + pll16x 3 | _xinfreq = 5_000_000 4 | 5 | LED1 = 26 6 | LED2 = 27 7 | LED3 = 16 8 | LED4 = 17 9 | 10 | #ifdef SLOW 11 | DIVISOR = 2 12 | #else 13 | DIVISOR = 8 14 | #endif 15 | 16 | PUB main : mask 17 | mask := |< LED1 | |< LED2 | |< LED3 | |< LED4 18 | OUTA := |< LED1 | |< LED3 19 | DIRA := mask 20 | repeat 21 | OUTA ^= mask 22 | waitcnt(CNT + CLKFREQ / DIVISOR) 23 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 David Michael Betz 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 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /spin/IP_Loader.spin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parallaxinc/PropLoader/a1b4cd87cedfb1141d9be888335bca130b486425/spin/IP_Loader.spin -------------------------------------------------------------------------------- /spin/TV.spin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parallaxinc/PropLoader/a1b4cd87cedfb1141d9be888335bca130b486425/spin/TV.spin -------------------------------------------------------------------------------- /spin/TV_Text.spin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/parallaxinc/PropLoader/a1b4cd87cedfb1141d9be888335bca130b486425/spin/TV_Text.spin -------------------------------------------------------------------------------- /spin/sd_helper.spin: -------------------------------------------------------------------------------- 1 | ' select debugging output to a TV 2 | '#define TV_DEBUG 3 | 4 | CON 5 | ' these will get overwritten with the correct values before loading 6 | _clkmode = xtal1 + pll16x 7 | _xinfreq = 5_000_000 8 | 9 | TYPE_FILE_WRITE = 0 10 | TYPE_DATA = 1 11 | TYPE_EOF = 2 12 | 13 | ' character codes 14 | CR = $0d 15 | 16 | WRITE_NONE = 0 17 | WRITE_FILE = 1 18 | 19 | OBJ 20 | pkt : "packet_driver" 21 | #ifdef TV_DEBUG 22 | tv : "TV_Text" 23 | #endif 24 | sd : "fsrw" 25 | 26 | VAR 27 | long sd_mounted 28 | long load_address 29 | long write_mode 30 | 31 | PUB start | type, packet, len, ok 32 | 33 | ' allow some time for the PC to change baud rate if necesssary 34 | waitcnt(CLKFREQ / 2 + CNT) 35 | 36 | ' start the packet driver 37 | pkt.start(31, 30, 0, p_baudrate) 38 | 39 | #ifdef TV_DEBUG 40 | tv.start(p_tvpin) 41 | tv.str(string("Serial Helper v0.1", CR)) 42 | #endif 43 | 44 | ' initialize 45 | sd_mounted := 0 46 | write_mode := WRITE_NONE 47 | 48 | ' handle packets 49 | repeat 50 | 51 | ' get the next packet from the PC 52 | ok := pkt.rcv_packet(@type, @packet, @len) 53 | #ifdef TV_DEBUG 54 | if not ok 55 | tv.str(string("Receive packet error", CR)) 56 | #endif 57 | 58 | if ok 59 | case type 60 | TYPE_FILE_WRITE: FILE_WRITE_handler(packet) 61 | TYPE_DATA: DATA_handler(packet, len) 62 | TYPE_EOF: EOF_handler 63 | other: 64 | #ifdef TV_DEBUG 65 | tv.str(string("Bad packet type: ")) 66 | tv.hex(type, 2) 67 | crlf 68 | #endif 69 | pkt.release_packet 70 | 71 | PRI FILE_WRITE_handler(name) | err 72 | mountSD 73 | #ifdef TV_DEBUG 74 | tv.str(string("FILE_WRITE: ")) 75 | tv.str(name) 76 | tv.str(string("...",)) 77 | #endif 78 | err := \sd.popen(name, "w") 79 | #ifdef TV_DEBUG 80 | tv.dec(err) 81 | crlf 82 | #endif 83 | write_mode := WRITE_FILE 84 | load_address := $00000000 85 | 86 | PRI DATA_handler(packet, len) | i, val, err 87 | err := 0 ' assume no error 88 | #ifdef TV_DEBUG 89 | tv.str(string("DATA: ")) 90 | tv.hex(load_address, 8) 91 | tv.out(" ") 92 | tv.dec(len) 93 | tv.str(string("...")) 94 | #endif 95 | case write_mode 96 | WRITE_FILE: 97 | err := \sd.pwrite(packet, len) 98 | load_address += len 99 | other: 100 | #ifdef TV_DEBUG 101 | tv.str(string("bad write_mode: ")) 102 | #endif 103 | #ifdef TV_DEBUG 104 | tv.dec(err) 105 | crlf 106 | #endif 107 | 108 | PRI EOF_handler | err 109 | err := 0 ' assume no errors 110 | #ifdef TV_DEBUG 111 | tv.str(string("EOF...")) 112 | #endif 113 | case write_mode 114 | WRITE_FILE: 115 | err := \sd.pclose 116 | write_mode := WRITE_NONE 117 | WRITE_NONE: 118 | other: 119 | #ifdef TV_DEBUG 120 | tv.str(string("bad write_mode: ")) 121 | #endif 122 | #ifdef TV_DEBUG 123 | tv.dec(err) 124 | crlf 125 | #endif 126 | 127 | PRI mountSD | err 128 | if sd_mounted == 0 129 | repeat 130 | #ifdef TV_DEBUG 131 | tv.str(string("Mounting SD card...", CR)) 132 | #endif 133 | err := \sd.mount_explicit(p_dopin, p_clkpin, p_dipin, p_cspin, p_sel_inc, p_sel_msk, p_sel_addr) 134 | until err == 0 135 | sd_mounted := 1 136 | return 1 137 | 138 | #ifdef TV_DEBUG 139 | PRI crlf 140 | tv.out(CR) 141 | #endif 142 | 143 | DAT 144 | 145 | ' parameters filled in before downloading sd_helper.binary 146 | p_baudrate long 0 147 | p_tvpin byte 0 148 | p_dopin byte 0 149 | p_clkpin byte 0 150 | p_dipin byte 0 151 | p_cspin byte 0 152 | p_sel_addr byte 0 153 | p_sel_inc long 0 154 | p_sel_msk long 0 155 | 156 | -------------------------------------------------------------------------------- /src/config.c: -------------------------------------------------------------------------------- 1 | /* config.c - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "proploader.h" 30 | #include "config.h" 31 | #include "system.h" 32 | #include "expr.h" 33 | 34 | #define MAXLINE 1024 35 | 36 | typedef struct Field Field; 37 | struct Field { 38 | char *tag; 39 | char *value; 40 | Field *next; 41 | }; 42 | 43 | struct BoardConfig { 44 | BoardConfig *parent; 45 | BoardConfig *sibling; 46 | BoardConfig *child; 47 | BoardConfig **pNextChild; 48 | Field *fields; 49 | char name[1]; 50 | }; 51 | 52 | typedef struct { 53 | char lineBuf[MAXLINE]; /* line buffer */ 54 | char *linePtr; /* pointer to the current character */ 55 | int lineNumber; /* current line number */ 56 | } LineBuf; 57 | 58 | typedef struct { 59 | char *name; 60 | int value; 61 | } ConfigSymbol; 62 | 63 | static ConfigSymbol configSymbols[] = { 64 | { "RCFAST", RCFAST }, 65 | { "RCSLOW", RCSLOW }, 66 | { "XINPUT", XINPUT }, 67 | { "XTAL1", XTAL1 }, 68 | { "XTAL2", XTAL2 }, 69 | { "XTAL3", XTAL3 }, 70 | { "PLL1X", PLL1X }, 71 | { "PLL2X", PLL2X }, 72 | { "PLL4X", PLL4X }, 73 | { "PLL8X", PLL8X }, 74 | { "PLL16X", PLL16X }, 75 | { "K", 1024 }, 76 | { "M", 1024*1024 }, 77 | { "MHZ", 1000*1000 }, 78 | { "TRUE", TRUE }, 79 | { "FALSE", FALSE }, 80 | { NULL, 0 } 81 | }; 82 | 83 | static void DumpFields(BoardConfig *config, Field *fields, int indent); 84 | static BoardConfig *GetDefaultConfiguration(void); 85 | static int SkipSpaces(LineBuf *buf); 86 | static char *NextToken(LineBuf *buf, const char *termSet, int *pTerm); 87 | static int FindSymbol(void *cookie, const char *name, int *pValue); 88 | static void ParseError(LineBuf *buf, const char *fmt, ...); 89 | static void Fatal(const char *fmt, ...); 90 | 91 | BoardConfig *NewBoardConfig(BoardConfig *parent, const char *name) 92 | { 93 | BoardConfig *config; 94 | if (!(config = (BoardConfig *)malloc(sizeof(BoardConfig) + strlen(name)))) 95 | Fatal("insufficient memory"); 96 | memset(config, 0, sizeof(BoardConfig)); 97 | strcpy(config->name, name); 98 | config->parent = parent; 99 | config->pNextChild = &config->child; 100 | if (parent) { 101 | *parent->pNextChild = config; 102 | parent->pNextChild = &config->sibling; 103 | } 104 | return config; 105 | } 106 | 107 | /* ParseConfigurationFile - parse a configuration file */ 108 | BoardConfig *ParseConfigurationFile(const char *name) 109 | { 110 | char path[PATH_MAX]; 111 | BoardConfig *baseConfig, *config; 112 | const char *src; 113 | char *tag, *dst; 114 | LineBuf buf; 115 | FILE *fp; 116 | int ch; 117 | 118 | /* make a local copy of the name in lowercase */ 119 | src = name; dst = path; 120 | while ((*dst++ = tolower(*src++)) != '\0') 121 | ; 122 | 123 | /* check for a request for the default configuration */ 124 | if (strcmp(path, DEF_BOARD) == 0) 125 | return GetDefaultConfiguration(); 126 | 127 | /* make the configuration file name */ 128 | strcat(path, ".cfg"); 129 | 130 | /* open the configuration file */ 131 | if (!(fp = xbOpenFileInPath(path, "r"))) 132 | return NULL; 133 | 134 | /* create a new board configuration */ 135 | baseConfig = config = NewBoardConfig(NULL, name); 136 | 137 | /* initialize the line number */ 138 | buf.lineNumber = 0; 139 | 140 | /* process each line in the configuration file */ 141 | while (fgets(buf.lineBuf, sizeof(buf.lineBuf), fp)) { 142 | char *p; 143 | int len; 144 | 145 | /* check for a comment at the end of the line */ 146 | if ((p = strchr(buf.lineBuf, '#')) != NULL) 147 | *p = '\0'; 148 | 149 | /* trim any trailing newline and spaces */ 150 | for (len = strlen(buf.lineBuf); len > 0; --len) 151 | if (!isspace(buf.lineBuf[len-1])) 152 | break; 153 | buf.lineBuf[len] = '\0'; 154 | 155 | /* initialize token parser */ 156 | buf.linePtr = buf.lineBuf; 157 | ++buf.lineNumber; 158 | 159 | /* look for the first token on the line */ 160 | switch (SkipSpaces(&buf)) { 161 | 162 | case '\0': /* blank line */ 163 | case '#': /* comment */ 164 | // ignore blank lines and comments 165 | break; 166 | 167 | case '[': /* configuration tag */ 168 | 169 | /* get the configuration name */ 170 | ++buf.linePtr; 171 | if (!(tag = NextToken(&buf, "]", &ch))) 172 | ParseError(&buf, "missing configuration tag"); 173 | if (ch != ']') { 174 | if (SkipSpaces(&buf) != ']') 175 | ParseError(&buf, "missing close bracket after configuration tag"); 176 | ++buf.linePtr; 177 | } 178 | if (SkipSpaces(&buf) != '\0') 179 | ParseError(&buf, "missing end of line"); 180 | 181 | /* add a new board configuration */ 182 | config = NewBoardConfig(baseConfig, tag); 183 | break; 184 | 185 | default: /* tag:value pair */ 186 | 187 | /* get the tag */ 188 | if (!(tag = NextToken(&buf, ":", &ch))) 189 | ParseError(&buf, "missing tag"); 190 | 191 | /* check for the colon separator */ 192 | if (ch != ':') { 193 | if (SkipSpaces(&buf) != ':') 194 | ParseError(&buf, "missing colon"); 195 | ++buf.linePtr; 196 | } 197 | 198 | /* skip leading spaces before the value */ 199 | SkipSpaces(&buf); 200 | 201 | /* set the configuration value */ 202 | SetConfigField(config, tag, buf.linePtr); 203 | break; 204 | } 205 | } 206 | 207 | /* close the board configuration file */ 208 | fclose(fp); 209 | 210 | /* return the board configuration */ 211 | return baseConfig; 212 | } 213 | 214 | /* DumpBoardConfiguration1 - dump a board configuration */ 215 | static void DumpBoardConfiguration1(BoardConfig *config, int indent) 216 | { 217 | BoardConfig *subconfig; 218 | printf("%*sBoard type '%s'\n", indent, "", config->name); 219 | DumpFields(config, config->fields, indent); 220 | for (subconfig = config->child; subconfig != NULL; subconfig = subconfig->sibling) { 221 | printf("%*s%s\n", indent, "", subconfig->name); 222 | DumpFields(subconfig, subconfig->fields, indent + 2); 223 | } 224 | if (config->parent) { 225 | printf("%*sparent\n", indent, ""); 226 | DumpBoardConfiguration1(config->parent, indent + 2); 227 | } 228 | } 229 | 230 | /* DumpBoardConfiguration - dump a board configuration */ 231 | void DumpBoardConfiguration(BoardConfig *config) 232 | { 233 | DumpBoardConfiguration1(config, 0); 234 | } 235 | 236 | /* DumpFields - dump the fields of a board configuration */ 237 | static void DumpFields(BoardConfig *config, Field *fields, int indent) 238 | { 239 | Field *field; 240 | for (field = fields; field != NULL; field = field->next) { 241 | char *value; 242 | if ((value = GetConfigField(config, field->tag)) != NULL) { 243 | ParseContext c; 244 | int ivalue; 245 | c.findSymbol = FindSymbol; 246 | c.cookie = config; 247 | if (TryParseNumericExpr(&c, value, &ivalue)) 248 | printf("%*s%s: '%s' (%d 0x%08x)\n", indent, "", field->tag, field->value, ivalue, ivalue); 249 | else 250 | printf("%*s%s: '%s'\n", indent, "", field->tag, field->value); 251 | } 252 | } 253 | } 254 | 255 | /* GetConfigSubtype - get a subtype of a board configuration */ 256 | BoardConfig *GetConfigSubtype(BoardConfig *config, const char *name) 257 | { 258 | BoardConfig *subconfig; 259 | for (subconfig = config->child; subconfig != NULL; subconfig = subconfig->sibling) 260 | if (strcasecmp(name, subconfig->name) == 0) 261 | return subconfig; 262 | return strcasecmp(name, DEF_SUBTYPE) == 0 ? config : NULL; 263 | } 264 | 265 | void SetConfigField(BoardConfig *config, const char *tag, const char *value) 266 | { 267 | Field **pNext, *field; 268 | int taglen; 269 | for (pNext = &config->fields; (field = *pNext) != NULL; pNext = &field->next) 270 | if (strcasecmp(tag, field->tag) == 0) { 271 | *pNext = field->next; 272 | free(field); 273 | break; 274 | } 275 | taglen = strlen(tag) + 1; 276 | if (!(field = (Field *)malloc(sizeof(Field) + taglen + strlen(value) + 1))) 277 | Fatal("insufficient memory"); 278 | field->tag = (char *)field + sizeof(Field); 279 | field->value = field->tag + taglen; 280 | strcpy(field->tag, tag); 281 | strcpy(field->value, value); 282 | field->next = *pNext; 283 | *pNext = field; 284 | } 285 | 286 | char *GetConfigField(BoardConfig *config, const char *tag) 287 | { 288 | while (config != NULL) { 289 | Field *field; 290 | for (field = config->fields; field != NULL; field = field->next) 291 | if (strcasecmp(tag, field->tag) == 0) 292 | return field->value; 293 | config = config->parent; 294 | } 295 | return NULL; 296 | } 297 | 298 | int GetNumericConfigField(BoardConfig *config, const char *tag, int *pValue) 299 | { 300 | ParseContext c; 301 | char *value; 302 | if (!(value = GetConfigField(config, tag))) 303 | return FALSE; 304 | c.findSymbol = FindSymbol; 305 | c.cookie = config; 306 | return ParseNumericExpr(&c, value, pValue); 307 | } 308 | 309 | BoardConfig *MergeConfigs(BoardConfig *parent, BoardConfig *child) 310 | { 311 | child->parent = parent; 312 | return child; 313 | } 314 | 315 | /* these default settings match the Parallax ActivityBoard */ 316 | 317 | static BoardConfig *GetDefaultConfiguration(void) 318 | { 319 | static BoardConfig *defaultConfig = NULL; 320 | if (!defaultConfig) { 321 | defaultConfig = NewBoardConfig(NULL, DEF_BOARD); 322 | SetConfigField(defaultConfig, "baudrate", "115200"); 323 | SetConfigField(defaultConfig, "loader-baud-rate", "115200"); 324 | SetConfigField(defaultConfig, "fast-loader-baud-rate", "921600"); 325 | SetConfigField(defaultConfig, "rxpin", "31"); 326 | SetConfigField(defaultConfig, "txpin", "30"); 327 | SetConfigField(defaultConfig, "sdspi-do", "22"); 328 | SetConfigField(defaultConfig, "sdspi-clk", "23"); 329 | SetConfigField(defaultConfig, "sdspi-di", "24"); 330 | SetConfigField(defaultConfig, "sdspi-cs", "25"); 331 | } 332 | return defaultConfig; 333 | } 334 | 335 | static int SkipSpaces(LineBuf *buf) 336 | { 337 | int ch; 338 | while ((ch = *buf->linePtr) != '\0' && isspace(ch)) 339 | ++buf->linePtr; 340 | return *buf->linePtr; 341 | } 342 | 343 | static char *NextToken(LineBuf *buf, const char *termSet, int *pTerm) 344 | { 345 | char *token; 346 | int ch; 347 | 348 | /* skip leading spaces */ 349 | if (!SkipSpaces(buf)) 350 | return NULL; 351 | 352 | /* collect the token */ 353 | token = buf->linePtr; 354 | while ((ch = *buf->linePtr) != '\0' && ch != '\n' && !isspace(ch) && !strchr(termSet, ch)) 355 | ++buf->linePtr; 356 | 357 | /* return the terminator character */ 358 | *pTerm = ch; 359 | 360 | /* terminate the token */ 361 | if (*buf->linePtr != '\0') 362 | *buf->linePtr++ = '\0'; 363 | 364 | /* return the token or NULL if at the end of the line */ 365 | return *token == '\0' ? NULL : token; 366 | } 367 | 368 | static int FindSymbol(void *cookie, const char *name, int *pValue) 369 | { 370 | BoardConfig *config = (BoardConfig *)cookie; 371 | ConfigSymbol *sym; 372 | 373 | /* first check for a configuration variable with the specified name */ 374 | if (config && GetNumericConfigField(config, name, pValue)) 375 | return TRUE; 376 | 377 | /* check for a built-in symbol with the specified name */ 378 | for (sym = configSymbols; sym->name != NULL; ++sym) { 379 | if (strcasecmp(name, sym->name) == 0) { 380 | *pValue = sym->value; 381 | return TRUE; 382 | } 383 | } 384 | 385 | /* symbol not found */ 386 | return FALSE; 387 | } 388 | 389 | static void ParseError(LineBuf *buf, const char *fmt, ...) 390 | { 391 | va_list ap; 392 | va_start(ap, fmt); 393 | printf("error: "); 394 | vprintf(fmt, ap); 395 | putchar('\n'); 396 | if (buf) { 397 | printf(" on line number %d\n", buf->lineNumber); 398 | printf("%s\n", buf->lineBuf); 399 | } 400 | va_end(ap); 401 | exit(1); 402 | } 403 | 404 | static void Fatal(const char *fmt, ...) 405 | { 406 | va_list ap; 407 | va_start(ap, fmt); 408 | printf("error: "); 409 | vprintf(fmt, ap); 410 | putchar('\n'); 411 | va_end(ap); 412 | exit(1); 413 | } 414 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | /* config.h - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #ifndef __CONFIG_H__ 23 | #define __CONFIG_H__ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | #include "system.h" 31 | 32 | #ifdef NO_STDINT 33 | typedef long int32_t; 34 | typedef unsigned long uint32_t; 35 | typedef short int16_t; 36 | typedef unsigned short uint16_t; 37 | typedef signed char int8_t; 38 | typedef unsigned char uint8_t; 39 | #else 40 | #include 41 | #endif 42 | 43 | typedef struct BoardConfig BoardConfig; 44 | 45 | #define DEF_BOARD "default" 46 | #define DEF_SUBTYPE "default" 47 | 48 | BoardConfig *NewBoardConfig(BoardConfig *parent, const char *name); 49 | BoardConfig *ParseConfigurationFile(const char *path); 50 | void DumpBoardConfiguration(BoardConfig *config); 51 | BoardConfig *GetConfigSubtype(BoardConfig *config, const char *name); 52 | BoardConfig *MergeConfigs(BoardConfig *parent, BoardConfig *child); 53 | void SetConfigField(BoardConfig *config, const char *tag, const char *value); 54 | void RemoveConfigField(BoardConfig *config, const char *tag); 55 | char *GetConfigField(BoardConfig *config, const char *tag); 56 | int GetNumericConfigField(BoardConfig *config, const char *tag, int *pValue); 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/enumcom.c: -------------------------------------------------------------------------------- 1 | /************************************************************************* 2 | * Derived from code by Zach Gorman with the following license: 3 | * 4 | * Serial port enumeration routines 5 | * 6 | * The EnumSerialPort function will populate an array of SSerInfo structs, 7 | * each of which contains information about one serial port present in 8 | * the system. Note that this code must be linked with setupapi.lib, 9 | * which is included with the Win32 SDK. 10 | * 11 | * by Zach Gorman 12 | * 13 | * Copyright (c) 2002 Archetype Auction Software, Inc. All rights reserved. 14 | * 15 | * Redistribution and use in source and binary forms, with or without 16 | * modification, are permitted provided that the following condition is 17 | * met: Redistributions of source code must retain the above copyright 18 | * notice, this condition and the following disclaimer. 19 | * 20 | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED 21 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | * DISCLAIMED. IN NO EVENT SHALL ARCHETYPE AUCTION SOFTWARE OR ITS 24 | * AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | ************************************************************************/ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | //#ifndef GUID_CLASS_COMPORT 41 | DEFINE_GUID(GUID_CLASS_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73); 42 | //#endif 43 | 44 | #ifdef MAIN 45 | int CheckPort(const char* port, void* data) 46 | { 47 | printf("Found %s\n", port); 48 | return 0; 49 | } 50 | 51 | int main(void) 52 | { 53 | printf("SerialFind returned %d\n", Serial_Find(NULL, CheckPort, NULL)); 54 | return 0; 55 | } 56 | #endif 57 | 58 | int SerialFind(int (*check)(const char* port, void* data), void* data) 59 | { 60 | GUID *guidDev = (GUID *) &GUID_CLASS_COMPORT; 61 | HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; 62 | SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL; 63 | SP_DEVICE_INTERFACE_DATA ifcData; 64 | DWORD dwDetDataSize; 65 | int rc = -1; 66 | BOOL bOk; 67 | DWORD i; 68 | 69 | hDevInfo = SetupDiGetClassDevs( guidDev, 70 | NULL, 71 | NULL, 72 | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE 73 | ); 74 | 75 | if(hDevInfo == INVALID_HANDLE_VALUE) { 76 | printf("error: SetupDiGetClassDevs failed. (err=%lx)\n", GetLastError()); 77 | goto done; 78 | } 79 | 80 | // Enumerate the serial ports 81 | dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256; 82 | pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA *)malloc(dwDetDataSize); 83 | 84 | ifcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 85 | pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); 86 | 87 | for (i = 0, bOk = TRUE; bOk; ++i) { 88 | 89 | // get the next device 90 | bOk = SetupDiEnumDeviceInterfaces(hDevInfo, NULL, guidDev, i, &ifcData); 91 | if (bOk) { 92 | 93 | // get the device details 94 | SP_DEVINFO_DATA devdata = {sizeof(SP_DEVINFO_DATA)}; 95 | bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo, &ifcData, pDetData, dwDetDataSize, NULL, &devdata); 96 | if (bOk) { 97 | TCHAR fname[256], desc[256]; 98 | 99 | // get some additional info 100 | BOOL bSuccess = SetupDiGetDeviceRegistryProperty(hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL, (PBYTE)fname, sizeof(fname), NULL) 101 | && SetupDiGetDeviceRegistryProperty(hDevInfo, &devdata, SPDRP_DEVICEDESC, NULL, (PBYTE)desc, sizeof(desc), NULL); 102 | if (bSuccess) { 103 | char *comx, *p; 104 | //printf("Device path: %s\n", pDetData->DevicePath); 105 | //printf("Friendly name: %s\n", fname); 106 | //printf("Description: %s\n", desc); 107 | 108 | // handle a friendly name with the device name embedded in the form "(COMnn)" 109 | if ((comx = strstr(fname, "(COM")) != NULL) { 110 | if (isdigit(comx[4])) { 111 | for (p = ++comx + 4; *p != '\0' && *p != ')'; ++p) { 112 | if (!isdigit(*p)) 113 | break; 114 | } 115 | if (*p == ')') { 116 | *p = '\0'; 117 | if ((*check)(comx, data) == 0) { 118 | rc = 0; 119 | goto done; 120 | } 121 | } 122 | } 123 | } 124 | 125 | // handle a friendly name of the form "COMnn" 126 | else if (strncmp(fname, "COM", 3) == 0) { 127 | for (p = fname + 3; *p != '\0'; ++p) { 128 | if (!isdigit(*p)) 129 | break; 130 | } 131 | if (*p == '\0') { 132 | if ((*check)(comx, data) == 0) { 133 | rc = 0; 134 | goto done; 135 | } 136 | } 137 | } 138 | } 139 | } 140 | else { 141 | printf("error: SetupDiGetDeviceInterfaceDetail failed. (err=%lx)\n", GetLastError()); 142 | return 1; 143 | } 144 | } 145 | else { 146 | DWORD err = GetLastError(); 147 | if (err != ERROR_NO_MORE_ITEMS) { 148 | printf("error: SetupDiEnumDeviceInterfaces failed. (err=%lx)\n", err); 149 | goto done; 150 | } 151 | } 152 | } 153 | 154 | done: 155 | if (pDetData != NULL) 156 | free(pDetData); 157 | if (hDevInfo != INVALID_HANDLE_VALUE) 158 | SetupDiDestroyDeviceInfoList(hDevInfo); 159 | 160 | return rc; 161 | } 162 | -------------------------------------------------------------------------------- /src/expr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "expr.h" 8 | 9 | #define TRUE 1 10 | #define FALSE 0 11 | 12 | #define ID_MAX 32 13 | 14 | #define TKN_NONE 0 15 | #define TKN_EOF -1 16 | #define TKN_IDENTIFIER -2 17 | #define TKN_NUMBER -3 18 | #define TKN_SHL -4 19 | #define TKN_SHR -5 20 | #define TKN_LE -6 21 | #define TKN_EQ -7 22 | #define TKN_NE -8 23 | #define TKN_GE -9 24 | #define TKN_AND -10 25 | #define TKN_OR -11 26 | 27 | static void ParseExpr2(ParseContext *c, int *pValue); 28 | static void ParseExpr3(ParseContext *c, int *pValue); 29 | static void ParseExpr4(ParseContext *c, int *pValue); 30 | static void ParseExpr5(ParseContext *c, int *pValue); 31 | static void ParseExpr6(ParseContext *c, int *pValue); 32 | static void ParseExpr7(ParseContext *c, int *pValue); 33 | static void ParseExpr8(ParseContext *c, int *pValue); 34 | static void ParseExpr9(ParseContext *c, int *pValue); 35 | static void ParseExpr10(ParseContext *c, int *pValue); 36 | static void ParseExpr11(ParseContext *c, int *pValue); 37 | static void ParseExpr12(ParseContext *c, int *pValue); 38 | static void ParseExpr13(ParseContext *c, int *pValue); 39 | static void ParsePrimary(ParseContext *c, int *pValue); 40 | static int GetToken(ParseContext *c, int *pValue); 41 | static void ParseIdentifier(ParseContext *c, int *pValue); 42 | static void ParseConfigVariable(ParseContext *c, int *pValue); 43 | static void ParseNumber(ParseContext *c, int *pValue); 44 | static void Error(ParseContext *c, const char *fmt, ...); 45 | 46 | /* ParseNumericExpr - parse a numeric expression */ 47 | int ParseNumericExpr(ParseContext *c, const char *str, int *pValue) 48 | { 49 | if (setjmp(c->errorTarget)) 50 | return FALSE; 51 | c->linePtr = (char *)str; 52 | c->savedTkn = TKN_NONE; 53 | c->showErrors = TRUE; 54 | ParseExpr2(c, pValue); 55 | return TRUE; 56 | } 57 | 58 | /* TryParseNumericExpr - try to parse a numeric expression */ 59 | int TryParseNumericExpr(ParseContext *c, const char *str, int *pValue) 60 | { 61 | if (setjmp(c->errorTarget)) 62 | return FALSE; 63 | c->linePtr = (char *)str; 64 | c->savedTkn = TKN_NONE; 65 | c->showErrors = FALSE; 66 | ParseExpr2(c, pValue); 67 | return TRUE; 68 | } 69 | 70 | /* ParseExpr2 - handle the '?:' operator */ 71 | static void ParseExpr2(ParseContext *c, int *pValue) 72 | { 73 | int value2, value3, tkn; 74 | ParseExpr3(c, pValue); 75 | while ((tkn = GetToken(c, &value2)) == '?') { 76 | ParseExpr3(c, &value2); 77 | if (GetToken(c, &value2) != ':') 78 | Error(c, "expecting a colon"); 79 | ParseExpr3(c, &value3); 80 | *pValue = *pValue ? value2 : value3; 81 | } 82 | c->savedTkn = tkn; 83 | } 84 | 85 | /* ParseExpr3 - handle the '||' operator */ 86 | static void ParseExpr3(ParseContext *c, int *pValue) 87 | { 88 | int value2, tkn; 89 | ParseExpr4(c, pValue); 90 | while ((tkn = GetToken(c, &value2)) == TKN_OR) { 91 | ParseExpr4(c, &value2); 92 | *pValue = *pValue || value2; 93 | } 94 | c->savedTkn = tkn; 95 | } 96 | 97 | /* ParseExpr4 - handle the '&&' operator */ 98 | static void ParseExpr4(ParseContext *c, int *pValue) 99 | { 100 | int value2, tkn; 101 | ParseExpr5(c, pValue); 102 | while ((tkn = GetToken(c, &value2)) == TKN_AND) { 103 | ParseExpr5(c, &value2); 104 | *pValue = *pValue && value2; 105 | } 106 | c->savedTkn = tkn; 107 | } 108 | 109 | /* ParseExpr5 - handle the '|' operator */ 110 | static void ParseExpr5(ParseContext *c, int *pValue) 111 | { 112 | int value2, tkn; 113 | ParseExpr6(c, pValue); 114 | while ((tkn = GetToken(c, &value2)) == '|') { 115 | ParseExpr6(c, &value2); 116 | *pValue |= value2; 117 | } 118 | c->savedTkn = tkn; 119 | } 120 | 121 | /* ParseExpr6 - handle the '^' operator */ 122 | static void ParseExpr6(ParseContext *c, int *pValue) 123 | { 124 | int value2, tkn; 125 | ParseExpr7(c, pValue); 126 | while ((tkn = GetToken(c, &value2)) == '^') { 127 | ParseExpr7(c, &value2); 128 | *pValue ^= value2; 129 | } 130 | c->savedTkn = tkn; 131 | } 132 | 133 | /* ParseExpr7 - handle the '&' operator */ 134 | static void ParseExpr7(ParseContext *c, int *pValue) 135 | { 136 | int value2, tkn; 137 | ParseExpr8(c, pValue); 138 | while ((tkn = GetToken(c, &value2)) == '&') { 139 | ParseExpr8(c, &value2); 140 | *pValue &= value2; 141 | } 142 | c->savedTkn = tkn; 143 | } 144 | 145 | /* ParseExpr8 - handle the '==' and '!=' operators */ 146 | static void ParseExpr8(ParseContext *c, int *pValue) 147 | { 148 | int value2, tkn; 149 | ParseExpr9(c, pValue); 150 | while ((tkn = GetToken(c, &value2)) == TKN_EQ || tkn == TKN_NE) { 151 | ParseExpr9(c, &value2); 152 | switch (tkn) { 153 | case TKN_EQ: 154 | *pValue = *pValue == value2; 155 | break; 156 | case TKN_NE: 157 | *pValue = *pValue != value2; 158 | break; 159 | default: 160 | /* never reached */ 161 | break; 162 | } 163 | } 164 | c->savedTkn = tkn; 165 | } 166 | 167 | /* ParseExpr9 - handle the '<', '<=', '>', and '>=' operators */ 168 | static void ParseExpr9(ParseContext *c, int *pValue) 169 | { 170 | int value2, tkn; 171 | ParseExpr10(c, pValue); 172 | while ((tkn = GetToken(c, &value2)) == '<' || tkn == TKN_LE || tkn == TKN_GE || tkn == '>') { 173 | ParseExpr10(c, &value2); 174 | switch (tkn) { 175 | case '<': 176 | *pValue = *pValue < value2; 177 | break; 178 | case TKN_LE: 179 | *pValue = *pValue <= value2; 180 | break; 181 | case TKN_GE: 182 | *pValue = *pValue >= value2; 183 | break; 184 | case '>': 185 | *pValue = *pValue > value2; 186 | break; 187 | default: 188 | /* never reached */ 189 | break; 190 | } 191 | } 192 | c->savedTkn = tkn; 193 | } 194 | 195 | /* ParseExpr10 - handle the '<<' and '>>' operators */ 196 | static void ParseExpr10(ParseContext *c, int *pValue) 197 | { 198 | int value2, tkn; 199 | ParseExpr11(c, pValue); 200 | while ((tkn = GetToken(c, &value2)) == TKN_SHL || tkn == TKN_SHR) { 201 | ParseExpr11(c, &value2); 202 | switch (tkn) { 203 | case TKN_SHL: 204 | *pValue <<= value2; 205 | break; 206 | case TKN_SHR: 207 | *pValue >>= value2; 208 | break; 209 | default: 210 | /* never reached */ 211 | break; 212 | } 213 | } 214 | c->savedTkn = tkn; 215 | } 216 | 217 | /* ParseExpr11 - handle the '+' and '-' operators */ 218 | static void ParseExpr11(ParseContext *c, int *pValue) 219 | { 220 | int value2, tkn; 221 | ParseExpr12(c, pValue); 222 | while ((tkn = GetToken(c, &value2)) == '+' || tkn == '-') { 223 | ParseExpr12(c, &value2); 224 | switch (tkn) { 225 | case '+': 226 | *pValue += value2; 227 | break; 228 | case '-': 229 | *pValue -= value2; 230 | break; 231 | default: 232 | /* never reached */ 233 | break; 234 | } 235 | } 236 | c->savedTkn = tkn; 237 | } 238 | 239 | /* ParseExpr12 - handle the '*', '/', and '%' operators */ 240 | static void ParseExpr12(ParseContext *c, int *pValue) 241 | { 242 | int value2, tkn; 243 | ParseExpr13(c, pValue); 244 | while ((tkn = GetToken(c, &value2)) == '*' || tkn == '/' || tkn == '%') { 245 | ParseExpr13(c, &value2); 246 | switch (tkn) { 247 | case '*': 248 | *pValue *= value2; 249 | break; 250 | case '/': 251 | if (value2 == 0) 252 | Error(c, "division by zero"); 253 | *pValue /= value2; 254 | break; 255 | case '%': 256 | if (value2 == 0) 257 | Error(c, "division by zero"); 258 | *pValue %= value2; 259 | break; 260 | default: 261 | /* never reached */ 262 | break; 263 | } 264 | } 265 | c->savedTkn = tkn; 266 | } 267 | 268 | /* ParseExpr13 - handle unary operators */ 269 | static void ParseExpr13(ParseContext *c, int *pValue) 270 | { 271 | int tkn; 272 | switch (tkn = GetToken(c, pValue)) { 273 | case '+': 274 | ParsePrimary(c, pValue); 275 | break; 276 | case '-': 277 | ParsePrimary(c, pValue); 278 | *pValue = -*pValue; 279 | break; 280 | case '~': 281 | ParsePrimary(c, pValue); 282 | *pValue = ~*pValue; 283 | break; 284 | case '!': 285 | ParsePrimary(c, pValue); 286 | *pValue = !*pValue; 287 | break; 288 | default: 289 | c->savedTkn = tkn; 290 | ParsePrimary(c, pValue); 291 | break; 292 | } 293 | } 294 | 295 | /* ParsePrimary - parse a primary expression */ 296 | static void ParsePrimary(ParseContext *c, int *pValue) 297 | { 298 | int value2; 299 | switch (GetToken(c, pValue)) { 300 | case '(': 301 | ParseExpr2(c, pValue); 302 | if (GetToken(c, &value2) != ')') 303 | Error(c, "expecting a right paren"); 304 | break; 305 | case '{': 306 | ParseConfigVariable(c, pValue); 307 | if (GetToken(c, &value2) != '}') 308 | Error(c, "expecting a right brace"); 309 | break; 310 | case TKN_NUMBER: 311 | case TKN_IDENTIFIER: 312 | /* nothing else to do */ 313 | break; 314 | default: 315 | Error(c, "expecting a primary expression"); 316 | break; 317 | } 318 | } 319 | 320 | static int GetToken(ParseContext *c, int *pValue) 321 | { 322 | int tkn; 323 | 324 | if ((tkn = c->savedTkn) != TKN_NONE) { 325 | c->savedTkn = TKN_NONE; 326 | return tkn; 327 | } 328 | 329 | /* skip leading spaces */ 330 | while (*c->linePtr != '\0' && isspace(*c->linePtr)) 331 | ++c->linePtr; 332 | 333 | /* check for end of file (string) */ 334 | if (*c->linePtr == '\0') 335 | return TKN_EOF; 336 | 337 | /* check for a number */ 338 | if (isdigit(*c->linePtr)) { 339 | ParseNumber(c, pValue); 340 | tkn = TKN_NUMBER; 341 | } 342 | 343 | /* check for an identifier */ 344 | else if (isalpha(*c->linePtr)) { 345 | ParseIdentifier(c, pValue); 346 | tkn = TKN_IDENTIFIER; 347 | } 348 | 349 | /* handle operators */ 350 | else { 351 | switch (tkn = *c->linePtr) { 352 | case '<': 353 | if (c->linePtr[1] == '<') { 354 | tkn = TKN_SHL; 355 | ++c->linePtr; 356 | } 357 | else if (c->linePtr[1] == '=') { 358 | tkn = TKN_LE; 359 | ++c->linePtr; 360 | } 361 | break; 362 | case '>': 363 | if (c->linePtr[1] == '>') { 364 | tkn = TKN_SHR; 365 | ++c->linePtr; 366 | } 367 | else if (c->linePtr[1] == '=') { 368 | tkn = TKN_GE; 369 | ++c->linePtr; 370 | } 371 | break; 372 | case '!': 373 | if (c->linePtr[1] == '=') { 374 | tkn = TKN_NE; 375 | ++c->linePtr; 376 | } 377 | break; 378 | case '=': 379 | if (c->linePtr[1] == '=') { 380 | tkn = TKN_EQ; 381 | ++c->linePtr; 382 | } 383 | break; 384 | case '&': 385 | if (c->linePtr[1] == '&') { 386 | tkn = TKN_AND; 387 | ++c->linePtr; 388 | } 389 | break; 390 | case '|': 391 | if (c->linePtr[1] == '|') { 392 | tkn = TKN_OR; 393 | ++c->linePtr; 394 | } 395 | break; 396 | default: 397 | /* nothing to do */ 398 | break; 399 | } 400 | ++c->linePtr; 401 | } 402 | 403 | return tkn; 404 | } 405 | 406 | static void ParseNumber(ParseContext *c, int *pValue) 407 | { 408 | int value = (int)strtol(c->linePtr, (char **)&c->linePtr, 0); 409 | switch (*c->linePtr) { 410 | case 'k': 411 | case 'K': 412 | value *= 1024; 413 | ++c->linePtr; 414 | break; 415 | case 'm': 416 | case 'M': 417 | if (strncasecmp(c->linePtr, "mhz", 3) == 0) { 418 | value *= 1000 * 1000; 419 | c->linePtr += 3; 420 | } 421 | else { 422 | value *= 1024 * 1024; 423 | ++c->linePtr; 424 | } 425 | break; 426 | default: 427 | // nothing to do 428 | break; 429 | } 430 | *pValue = value; 431 | } 432 | 433 | static void ParseIdentifier(ParseContext *c, int *pValue) 434 | { 435 | char id[ID_MAX]; 436 | char *p = id; 437 | 438 | while (*c->linePtr != '\0' && (isalnum(*c->linePtr) || *c->linePtr == '_')) { 439 | if (p < id + ID_MAX - 1) 440 | *p++ = *c->linePtr; 441 | ++c->linePtr; 442 | } 443 | *p = '\0'; 444 | 445 | if (!(*c->findSymbol)(c->cookie, id, pValue)) 446 | Error(c, "undefined symbol: %s", id); 447 | } 448 | 449 | static void ParseConfigVariable(ParseContext *c, int *pValue) 450 | { 451 | char id[ID_MAX]; 452 | char *p = id; 453 | 454 | /* skip leading spaces */ 455 | while (*c->linePtr != '\0' && isspace(*c->linePtr)) 456 | ++c->linePtr; 457 | 458 | while (*c->linePtr != '\0' && !isspace(*c->linePtr) && *c->linePtr != '}') { 459 | if (p < id + ID_MAX - 1) 460 | *p++ = *c->linePtr; 461 | ++c->linePtr; 462 | } 463 | *p = '\0'; 464 | 465 | if (!(*c->findSymbol)(c->cookie, id, pValue)) 466 | Error(c, "undefined symbol: %s", id); 467 | } 468 | 469 | static void Error(ParseContext *c, const char *fmt, ...) 470 | { 471 | if (c->showErrors) { 472 | va_list ap; 473 | va_start(ap, fmt); 474 | printf("error: "); 475 | vprintf(fmt, ap); 476 | putchar('\n'); 477 | va_end(ap); 478 | } 479 | longjmp(c->errorTarget, 1); 480 | } 481 | #ifdef MAIN 482 | 483 | typedef struct { 484 | char *name; 485 | int value; 486 | } ConfigSymbol; 487 | 488 | #define RCFAST 0x00 489 | #define RCSLOW 0x01 490 | #define XINPUT 0x22 491 | #define XTAL1 0x2a 492 | #define XTAL2 0x32 493 | #define XTAL3 0x3a 494 | #define PLL1X 0x41 495 | #define PLL2X 0x42 496 | #define PLL4X 0x43 497 | #define PLL8X 0x44 498 | #define PLL16X 0x45 499 | 500 | static ConfigSymbol configSymbols[] = { 501 | { "RCFAST", RCFAST }, 502 | { "RCSLOW", RCSLOW }, 503 | { "XINPUT", XINPUT }, 504 | { "XTAL1", XTAL1 }, 505 | { "XTAL2", XTAL2 }, 506 | { "XTAL3", XTAL3 }, 507 | { "PLL1X", PLL1X }, 508 | { "PLL2X", PLL2X }, 509 | { "PLL4X", PLL4X }, 510 | { "PLL8X", PLL8X }, 511 | { "PLL16X", PLL16X }, 512 | { "K", 1024 }, 513 | { "M", 1024*1024 }, 514 | { "MHZ", 1000*1000 }, 515 | { "TRUE", TRUE }, 516 | { "FALSE", FALSE }, 517 | { "SDSPI-DI", 12 }, 518 | { "SDSPI-CLK", 13 }, 519 | { "SDSPI-DO", 14 }, 520 | { "SDSPI-CS", 15 }, 521 | { NULL, 0 } 522 | }; 523 | 524 | int FindSymbol(void *cookie, const char *name, int *pValue) 525 | { 526 | ConfigSymbol *sym; 527 | for (sym = configSymbols; sym->name != NULL; ++sym) 528 | if (strcasecmp(name, sym->name) == 0) { 529 | *pValue = sym->value; 530 | return TRUE; 531 | } 532 | return FALSE; 533 | } 534 | 535 | int main(int argc, char *argv[]) 536 | { 537 | ParseContext c; 538 | char buf[100]; 539 | int value; 540 | 541 | while (gets(buf)) { 542 | c.findSymbol = FindSymbol; 543 | c.cookie = NULL; 544 | if (ParseNumericExpr(&c, buf, &value)) 545 | printf("%s -> %d\n", buf, value); 546 | } 547 | 548 | return 0; 549 | } 550 | 551 | #endif 552 | -------------------------------------------------------------------------------- /src/expr.h: -------------------------------------------------------------------------------- 1 | #ifndef __EXPR_H__ 2 | #define __EXPR_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | /* parse context */ 11 | typedef struct { 12 | int (*findSymbol)(void *cookie, const char *name, int *pValue); 13 | void *cookie; 14 | jmp_buf errorTarget; 15 | char *linePtr; 16 | int savedTkn; 17 | int showErrors; 18 | } ParseContext; 19 | 20 | /* defined in expr.c */ 21 | int ParseNumericExpr(ParseContext *c, const char *token, int *pValue); 22 | int TryParseNumericExpr(ParseContext *c, const char *str, int *pValue); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/fastloader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "loader.h" 7 | #include "proploader.h" 8 | #include "propimage.h" 9 | 10 | #define MAX_RX_SENSE_ERROR 23 /* Maximum number of cycles by which the detection of a start bit could be off (as affected by the Loader code) */ 11 | 12 | // Offset (in bytes) from end of Loader Image pointing to where most host-initialized values exist. 13 | // Host-Initialized values are: Initial Bit Time, Final Bit Time, 1.5x Bit Time, Failsafe timeout, 14 | // End of Packet timeout, and ExpectedID. In addition, the image checksum at word 5 needs to be 15 | // updated. All these values need to be updated before the download stream is generated. 16 | // NOTE: DAT block data is always placed before the first Spin method 17 | #define RAW_LOADER_INIT_OFFSET_FROM_END (-(10 * 4) - 8) 18 | 19 | // Raw loader image. This is a memory image of a Propeller Application written in PASM that fits into our initial 20 | // download packet. Once started, it assists with the remainder of the download (at a faster speed and with more 21 | // relaxed interstitial timing conducive of Internet Protocol delivery. This memory image isn't used as-is; before 22 | // download, it is first adjusted to contain special values assigned by this host (communication timing and 23 | // synchronization values) and then is translated into an optimized Propeller Download Stream understandable by the 24 | // Propeller ROM-based boot loader. 25 | #include "IP_Loader.h" 26 | 27 | static uint8_t initCallFrame[] = {0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF}; 28 | 29 | static void SetHostInitializedValue(uint8_t *bytes, int offset, int value) 30 | { 31 | for (int i = 0; i < 4; ++i) 32 | bytes[offset + i] = (value >> (i * 8)) & 0xFF; 33 | } 34 | 35 | static int32_t getLong(const uint8_t *buf) 36 | { 37 | return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; 38 | } 39 | 40 | static void setLong(uint8_t *buf, uint32_t value) 41 | { 42 | buf[3] = value >> 24; 43 | buf[2] = value >> 16; 44 | buf[1] = value >> 8; 45 | buf[0] = value; 46 | } 47 | 48 | double ClockSpeed = 80000000.0; 49 | 50 | uint8_t *Loader::generateInitialLoaderImage(int clockSpeed, int clockMode, int packetID, int loaderBaudRate, int fastLoaderBaudRate, int *pLength) 51 | { 52 | int initAreaOffset = sizeof(rawLoaderImage) + RAW_LOADER_INIT_OFFSET_FROM_END; 53 | double floatClockSpeed = (double)clockSpeed; 54 | uint8_t *loaderImage; 55 | int checksum, i; 56 | 57 | // Allocate space for the image 58 | if (!(loaderImage = (uint8_t *)malloc(sizeof(rawLoaderImage)))) 59 | return NULL; 60 | 61 | // Make a copy of the loader template 62 | memcpy(loaderImage, rawLoaderImage, sizeof(rawLoaderImage)); 63 | 64 | // patch the clock frequency and mode 65 | PropImage image(loaderImage, sizeof(rawLoaderImage)); 66 | image.setClkFreq(clockSpeed); 67 | image.setClkMode(clockMode); 68 | 69 | /* 70 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 0, ClockMode and $07); {Booter's clock selection bits} 71 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 4, Round(ClockSpeed / InitialBaud)); {Initial Bit Time} 72 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 8, Round(ClockSpeed / FinalBaud)); {Final Bit Time} 73 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 12, Round(((1.5 * ClockSpeed) / FinalBaud) - MaxRxSenseError)); {1.5x Final Bit Time minus maximum start bit sense error} 74 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 16, 2 * ClockSpeed div (3 * 4)); {Failsafe Timeout (seconds-worth of Loader's Receive loop iterations)} 75 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 20, Round(2 * ClockSpeed / FinalBaud * 10 / 12)); {EndOfPacket Timeout (2 bytes worth of Loader's Receive loop iterations)} 76 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 24, Max(Round(ClockSpeed * SSSHTime), 14)); {Minimum EEPROM Start/Stop Condition setup/hold time (400 KHz = 1/0.6 µS); Minimum 14 cycles} 77 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 28, Max(Round(ClockSpeed * SCLHighTime), 14)); {Minimum EEPROM SCL high time (400 KHz = 1/0.6 µS); Minimum 14 cycles} 78 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 32, Max(Round(ClockSpeed * SCLLowTime), 26)); {Minimum EEPROM SCL low time (400 KHz = 1/1.3 µS); Minimum 26 cycles} 79 | PatchLoaderLongValue(RawSize*4+RawLoaderInitOffset + 36, PacketID); {First Expected Packet ID; total packet count} 80 | */ 81 | 82 | // Clock mode 83 | SetHostInitializedValue(loaderImage, initAreaOffset + 0, clockMode & 0x07); 84 | 85 | // Initial Bit Time. 86 | SetHostInitializedValue(loaderImage, initAreaOffset + 4, (int)trunc(floatClockSpeed / loaderBaudRate + 0.5)); 87 | 88 | // Final Bit Time. 89 | SetHostInitializedValue(loaderImage, initAreaOffset + 8, (int)trunc(floatClockSpeed / fastLoaderBaudRate + 0.5)); 90 | 91 | // 1.5x Final Bit Time minus maximum start bit sense error. 92 | SetHostInitializedValue(loaderImage, initAreaOffset + 12, (int)trunc(1.5 * floatClockSpeed / fastLoaderBaudRate - MAX_RX_SENSE_ERROR + 0.5)); 93 | 94 | // Failsafe Timeout (seconds-worth of Loader's Receive loop iterations). 95 | SetHostInitializedValue(loaderImage, initAreaOffset + 16, (int)trunc(2.0 * floatClockSpeed / (3 * 4) + 0.5)); 96 | 97 | // EndOfPacket Timeout (2 bytes worth of Loader's Receive loop iterations). 98 | SetHostInitializedValue(loaderImage, initAreaOffset + 20, (int)trunc(2.0 * floatClockSpeed / fastLoaderBaudRate * 10.0 / 12.0 + 0.5)); 99 | 100 | const double SSSHTime = 0.0000006; 101 | const double SCLHighTime = 0.0000006; 102 | const double SCLLowTime = 0.0000013; 103 | 104 | // Minimum EEPROM Start/Stop Condition setup/hold time (1/0.6 µs); [Min 14 cycles] 105 | int SSSHTicks = (int)trunc(floatClockSpeed * SSSHTime + 0.5); 106 | if (SSSHTicks < 14) 107 | SSSHTicks = 14; 108 | 109 | // Minimum EEPROM SCL high time (1/0.6 µs); [Min 14 cycles] 110 | int SCLHighTicks = (int)trunc(floatClockSpeed * SCLHighTime + 0.5); 111 | if (SCLHighTicks < 14) 112 | SCLHighTicks = 14; 113 | 114 | // Minimum EEPROM SCL low time (1/1.3 µs); [Min 26 cycles] 115 | int SCLLowTicks = (int)trunc(floatClockSpeed * SCLLowTime + 0.5); 116 | if (SCLLowTicks < 26) 117 | SCLLowTicks = 26; 118 | 119 | // Minimum EEPROM Start/Stop Condition setup/hold time (400 KHz = 1/0.6 µS); Minimum 14 cycles 120 | SetHostInitializedValue(loaderImage, initAreaOffset + 24, SSSHTicks); 121 | 122 | // Minimum EEPROM SCL high time (400 KHz = 1/0.6 µS); Minimum 14 cycles 123 | SetHostInitializedValue(loaderImage, initAreaOffset + 28, SCLHighTicks); 124 | 125 | // Minimum EEPROM SCL low time (400 KHz = 1/1.3 µS); Minimum 26 cycles 126 | SetHostInitializedValue(loaderImage, initAreaOffset + 32, SCLLowTicks); 127 | 128 | // First Expected Packet ID; total packet count. 129 | SetHostInitializedValue(loaderImage, initAreaOffset + 36, packetID); 130 | 131 | // Recalculate and update checksum so low byte of checksum calculates to 0. 132 | checksum = 0; 133 | loaderImage[5] = 0; // start with a zero checksum 134 | for (i = 0; i < (int)sizeof(rawLoaderImage); ++i) 135 | checksum += loaderImage[i]; 136 | for (i = 0; i < (int)sizeof(initCallFrame); ++i) 137 | checksum += initCallFrame[i]; 138 | loaderImage[5] = 256 - (checksum & 0xFF); 139 | 140 | /* return the loader image */ 141 | *pLength = sizeof(rawLoaderImage); 142 | return loaderImage; 143 | } 144 | 145 | int Loader::fastLoadFile(const char *file, LoadType loadType) 146 | { 147 | uint8_t *image; 148 | int imageSize; 149 | int sts; 150 | 151 | /* make sure the image was loaded into memory */ 152 | if (!(image = readFile(file, &imageSize))) { 153 | message("Failed to load image '%s'", file); 154 | return -1; 155 | } 156 | 157 | /* load the file */ 158 | sts = fastLoadImage(image, imageSize, loadType); 159 | free(image); 160 | 161 | /* return load result */ 162 | return sts; 163 | } 164 | 165 | int Loader::fastLoadImage(const uint8_t *image, int imageSize, LoadType loadType) 166 | { 167 | int sts; 168 | 169 | // get the binary clock settings 170 | PropImage img((uint8_t *)image, imageSize); // shouldn't really modify image! 171 | int binaryClockSpeed = img.clkFreq(); 172 | int binaryClockMode = img.clkMode(); 173 | 174 | // get the fast loader and program clock speeds 175 | int fastLoaderClockSpeed, clockSpeed; 176 | int gotFastLoaderClockSpeed = GetNumericConfigField(m_connection->config(), "fast-loader-clkfreq", &fastLoaderClockSpeed); 177 | int gotClockSpeed = GetNumericConfigField(m_connection->config(), "clkfreq", &clockSpeed); 178 | 179 | if (!gotFastLoaderClockSpeed) { 180 | if (gotClockSpeed) 181 | fastLoaderClockSpeed = clockSpeed; 182 | else 183 | fastLoaderClockSpeed = binaryClockSpeed; 184 | } 185 | if (gotClockSpeed) { 186 | img.setClkFreq(clockSpeed); 187 | img.updateChecksum(); 188 | } 189 | 190 | // get the fast loader and program clock modes 191 | int fastLoaderClockMode, clockMode; 192 | int gotFastLoaderClockMode = GetNumericConfigField(m_connection->config(), "fast-loader-clkmode", &fastLoaderClockMode); 193 | int gotClockMode = GetNumericConfigField(m_connection->config(), "clkmode", &clockMode); 194 | 195 | if (!gotFastLoaderClockMode) { 196 | if (gotClockMode) 197 | fastLoaderClockMode = clockMode; 198 | else 199 | fastLoaderClockMode = binaryClockMode; 200 | } 201 | if (gotClockMode) { 202 | img.setClkMode(clockMode); 203 | img.updateChecksum(); 204 | } 205 | 206 | message("fastLoaderClockSpeed %d, fastLoadClockMode %02x, clockSpeed %d, clockMode %02x", 207 | fastLoaderClockSpeed, 208 | fastLoaderClockMode, 209 | gotClockSpeed ? clockSpeed : binaryClockSpeed, 210 | gotClockMode ? clockMode : binaryClockMode); 211 | 212 | // get the loader baudrates 213 | int loaderBaudRate, fastLoaderBaudRate; 214 | if (!GetNumericConfigField(m_connection->config(), "loader-baud-rate", &loaderBaudRate)) 215 | loaderBaudRate = DEF_LOADER_BAUDRATE; 216 | if (!GetNumericConfigField(m_connection->config(), "fast-loader-baud-rate", &fastLoaderBaudRate)) 217 | fastLoaderBaudRate = DEF_FAST_LOADER_BAUDRATE; 218 | 219 | for (;;) { 220 | if ((sts = fastLoadImageHelper(image, imageSize, loadType, fastLoaderClockSpeed, fastLoaderClockMode, loaderBaudRate, fastLoaderBaudRate)) == 0) 221 | return 0; 222 | else if (sts == -2) { 223 | if ((fastLoaderBaudRate /= 2) >= 115200) 224 | nmessage(INFO_STEPPING_DOWN_BAUD_RATE, fastLoaderBaudRate); 225 | else 226 | break; 227 | } 228 | else 229 | return sts; 230 | } 231 | 232 | /* try a slow load if all baud rates failed */ 233 | nmessage(INFO_USING_SINGLE_STAGE_LOADER); 234 | return m_connection->loadImage(image, imageSize, loadType, true); 235 | } 236 | 237 | /* returns: 238 | 0 for success 239 | -1 for fatal errors 240 | -2 for errors where a lower baud rate might help 241 | */ 242 | int Loader::fastLoadImageHelper(const uint8_t *image, int imageSize, LoadType loadType, int clockSpeed, int clockMode, int loaderBaudRate, int fastLoaderBaudRate) 243 | { 244 | uint8_t *loaderImage, response[8]; 245 | int loaderImageSize, remaining, result, sts, i; 246 | int32_t packetID, checksum; 247 | SpinHdr *hdr = (SpinHdr *)image; 248 | 249 | // don't need to load beyond this even for .eeprom images 250 | imageSize = hdr->vbase; 251 | 252 | /* compute the image checksum */ 253 | checksum = 0; 254 | for (i = 0; i < imageSize; ++i) 255 | checksum += image[i]; 256 | for (i = 0; i < (int)sizeof(initCallFrame); ++i) 257 | checksum += initCallFrame[i]; 258 | 259 | /* compute the packet ID (number of packets to be sent) */ 260 | packetID = (imageSize + m_connection->maxDataSize() - 1) / m_connection->maxDataSize(); 261 | 262 | /* generate a loader image */ 263 | loaderImage = generateInitialLoaderImage(clockSpeed, clockMode, packetID, loaderBaudRate, fastLoaderBaudRate, &loaderImageSize); 264 | if (!loaderImage) { 265 | message("generateInitialLoaderImage failed"); 266 | nerror(ERROR_INTERNAL_CODE_ERROR); 267 | return -1; 268 | } 269 | 270 | /* load the second-stage loader using the Propeller ROM protocol */ 271 | message("Delivering second-stage loader"); 272 | result = m_connection->loadImage(loaderImage, loaderImageSize, response, sizeof(response)); 273 | free(loaderImage); 274 | if (result != 0) 275 | return result; 276 | 277 | result = getLong(&response[0]); 278 | if (result != packetID) { 279 | message("Second-stage loader failed to start - packetID %d, result %d", packetID, result); 280 | return -2; 281 | } 282 | 283 | /* switch to the final baud rate */ 284 | m_connection->setBaudRate(fastLoaderBaudRate); 285 | 286 | /* open the transparent serial connection that will be used for the second-stage loader */ 287 | if (m_connection->connect() != 0) { 288 | message("Failed to connect to target"); 289 | nerror(ERROR_COMMUNICATION_LOST); 290 | return -1; 291 | } 292 | 293 | /* transmit the image */ 294 | nmessage(INFO_DOWNLOADING, m_connection->portName()); 295 | remaining = imageSize; 296 | while (remaining > 0) { 297 | int size; 298 | nprogress(INFO_BYTES_REMAINING, (long)remaining); 299 | if ((size = remaining) > m_connection->maxDataSize()) 300 | size = m_connection->maxDataSize(); 301 | if ((sts = transmitPacket(packetID, image, size, &result)) != 0) 302 | return -2; 303 | if (result != packetID - 1) { 304 | message("Unexpected response: expected %d, received %d", packetID - 1, result); 305 | return -2; 306 | } 307 | remaining -= size; 308 | image += size; 309 | --packetID; 310 | } 311 | nmessage(INFO_BYTES_SENT, (long)imageSize); 312 | 313 | /* 314 | When we're doing a download that does not include an EEPROM write, the Packet IDs end up as: 315 | 316 | ltVerifyRAM: zero 317 | ltReadyToLaunch: -Checksum 318 | ltLaunchNow: -Checksum - 1 319 | 320 | ... and when we're doing a download that includes an EEPROM write, the Packet IDs end up as: 321 | 322 | ltVerifyRAM: zero 323 | ltProgramEEPROM: -Checksum 324 | ltReadyToLaunch: -Checksum*2 325 | ltLaunchNow: -Checksum*2 - 1 326 | */ 327 | 328 | /* transmit the RAM verify packet and verify the checksum */ 329 | nmessage(INFO_VERIFYING_RAM); 330 | if ((sts = transmitPacket(packetID, verifyRAM, sizeof(verifyRAM), &result)) != 0) 331 | return sts; 332 | if (result != -checksum) { 333 | nmessage(ERROR_RAM_CHECKSUM_FAILED); 334 | return -1; 335 | } 336 | packetID = -checksum; 337 | 338 | if (loadType & ltDownloadAndProgram) { 339 | nmessage(INFO_PROGRAMMING_EEPROM); 340 | if ((sts = transmitPacket(packetID, programVerifyEEPROM, sizeof(programVerifyEEPROM), &result, 8000)) != 0) 341 | return sts; 342 | if (result != -checksum*2) { 343 | nmessage(ERROR_EEPROM_CHECKSUM_FAILED); 344 | return -1; 345 | } 346 | packetID = -checksum*2; 347 | } 348 | 349 | /* transmit the final launch packets */ 350 | 351 | message("Sending readyToLaunch packet"); 352 | if ((sts = transmitPacket(packetID, readyToLaunch, sizeof(readyToLaunch), &result)) != 0) 353 | return sts; 354 | if (result != packetID - 1) { 355 | message("ReadyToLaunch failed: expected %08x, got %08x", packetID - 1, result); 356 | return -1; 357 | } 358 | --packetID; 359 | 360 | message("Sending launchNow packet"); 361 | if ((sts = transmitPacket(packetID, launchNow, sizeof(launchNow), NULL)) != 0) 362 | return sts; 363 | 364 | /* return successfully */ 365 | return 0; 366 | } 367 | 368 | /* returns: 369 | 0 for success 370 | -1 for fatal errors 371 | -2 for errors where a lower baud rate might help 372 | */ 373 | int Loader::transmitPacket(int id, const uint8_t *payload, int payloadSize, int *pResult, int timeout) 374 | { 375 | int packetSize = 2*sizeof(uint32_t) + payloadSize; 376 | uint8_t *packet, response[8]; 377 | int retries, result; 378 | int32_t tag, rtag; 379 | 380 | /* build the packet to transmit */ 381 | if (!(packet = (uint8_t *)malloc(packetSize))) { 382 | nmessage(ERROR_INSUFFICIENT_MEMORY); 383 | return -1; 384 | } 385 | setLong(&packet[0], id); 386 | memcpy(&packet[8], payload, payloadSize); 387 | 388 | /* send the packet */ 389 | retries = 3; 390 | while (--retries >= 0) { 391 | 392 | /* setup the packet header */ 393 | #ifdef __MINGW32__ 394 | tag = (int32_t)rand() | ((int32_t)rand() << 16); 395 | #else 396 | tag = (int32_t)rand(); 397 | #endif 398 | setLong(&packet[4], tag); 399 | //printf("transmit packet %d - tag %08x, size %d\n", id, tag, packetSize); 400 | if (m_connection->sendData(packet, packetSize) != packetSize) { 401 | nmessage(ERROR_INTERNAL_CODE_ERROR); 402 | free(packet); 403 | return -1; 404 | } 405 | 406 | /* receive the response */ 407 | if (pResult) { 408 | if (m_connection->receiveDataExactTimeout(response, sizeof(response), timeout) != sizeof(response)) 409 | message("transmitPacket %d failed - receiveDataExactTimeout", id); 410 | else if ((rtag = getLong(&response[4])) == tag) { 411 | if ((result = getLong(&response[0])) == id) 412 | message("transmitPacket %d failed: duplicate id", id); 413 | else { 414 | *pResult = result; 415 | free(packet); 416 | return 0; 417 | } 418 | } 419 | else 420 | message("transmitPacket %d failed: wrong tag %08x - expected %08x", id, rtag, tag); 421 | } 422 | 423 | /* don't wait for a result */ 424 | else { 425 | free(packet); 426 | return 0; 427 | } 428 | message("transmitPacket %d failed - retrying", id); 429 | } 430 | 431 | /* free the packet */ 432 | free(packet); 433 | 434 | /* return timeout */ 435 | message("transmitPacket %d failed - timeout", id); 436 | return -1; 437 | } 438 | 439 | -------------------------------------------------------------------------------- /src/gpio_sysfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * gpio_sysfs.c 3 | * 4 | * Raspberry Pi GPIO handling using sysfs interface. 5 | * Guillermo A. Amaral B. 6 | * 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "gpio_sysfs.h" 17 | 18 | #define IN 0 19 | #define OUT 1 20 | 21 | #define LOW 0 22 | #define HIGH 1 23 | 24 | #define PIN 24 /* P1-18 */ 25 | #define POUT 4 /* P1-07 */ 26 | 27 | 28 | /** 29 | * Export GPIO pin for use. 30 | * @param pin - The pin number to be used. 31 | * @returns Zero on success -1 on failure. 32 | */ 33 | int gpio_export(int pin) 34 | { 35 | #define BUFFER_MAX 3 36 | char buffer[BUFFER_MAX]; 37 | ssize_t bytes_written; 38 | int fd; 39 | 40 | fd = open("/sys/class/gpio/export", O_WRONLY); 41 | if (-1 == fd) { 42 | fprintf(stderr, "Failed to open export for writing!\n"); 43 | return(-1); 44 | } 45 | 46 | bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); 47 | write(fd, buffer, bytes_written); 48 | close(fd); 49 | return(0); 50 | } 51 | 52 | /** 53 | * Unexport GPIO pin for use. 54 | * @param pin - The pin number to be used. 55 | * @returns Zero on success -1 on failure. 56 | */ 57 | int gpio_unexport(int pin) 58 | { 59 | char buffer[BUFFER_MAX]; 60 | ssize_t bytes_written; 61 | int fd; 62 | 63 | fd = open("/sys/class/gpio/unexport", O_WRONLY); 64 | if (-1 == fd) { 65 | fprintf(stderr, "Failed to open unexport for writing!\n"); 66 | return(-1); 67 | } 68 | 69 | bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin); 70 | write(fd, buffer, bytes_written); 71 | close(fd); 72 | return(0); 73 | } 74 | 75 | /** 76 | * Set GPIO pin direction. 77 | * @param pin - The pin number to be used. 78 | * @param dir - The desired pin directio, 1 = out, 0 = in. 79 | * @returns Zero on success -1 on failure. 80 | */ 81 | int gpio_direction(int pin, int dir) 82 | { 83 | static const char s_directions_str[] = "in\0out"; 84 | 85 | #define DIRECTION_MAX 35 86 | char path[DIRECTION_MAX]; 87 | int fd; 88 | 89 | snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin); 90 | fd = open(path, O_WRONLY); 91 | if (-1 == fd) { 92 | fprintf(stderr, "Failed to open gpio direction for writing!\n"); 93 | return(-1); 94 | } 95 | 96 | if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) { 97 | fprintf(stderr, "Failed to set direction!\n"); 98 | return(-1); 99 | } 100 | 101 | close(fd); 102 | return(0); 103 | } 104 | 105 | /** 106 | * Read GPIO pin state. 107 | * @param pin - The pin number to be read. 108 | * @returns The pin state 0 or 1. 109 | */ 110 | int gpio_read(int pin) 111 | { 112 | #define VALUE_MAX 30 113 | char path[VALUE_MAX]; 114 | char value_str[3]; 115 | int fd; 116 | 117 | snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin); 118 | fd = open(path, O_RDONLY); 119 | if (-1 == fd) { 120 | fprintf(stderr, "Failed to open gpio value for reading!\n"); 121 | return(-1); 122 | } 123 | 124 | if (-1 == read(fd, value_str, 3)) { 125 | fprintf(stderr, "Failed to read value!\n"); 126 | return(-1); 127 | } 128 | 129 | close(fd); 130 | 131 | return(atoi(value_str)); 132 | } 133 | 134 | /** 135 | * Write GPIO pin state. 136 | * @param pin - The pin number to be written to. 137 | * @returns The desired pin state 0 or 1. 138 | */ 139 | int gpio_write(int pin, int value) 140 | { 141 | static const char s_values_str[] = "01"; 142 | 143 | char path[VALUE_MAX]; 144 | int fd; 145 | 146 | snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin); 147 | fd = open(path, O_WRONLY); 148 | if (-1 == fd) { 149 | fprintf(stderr, "Failed to open gpio value for writing!\n"); 150 | return(-1); 151 | } 152 | 153 | if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) { 154 | fprintf(stderr, "Failed to write value!\n"); 155 | return(-1); 156 | } 157 | 158 | close(fd); 159 | return(0); 160 | } 161 | 162 | #ifdef TEST_GPIO_SYSFS 163 | 164 | /* This test blinks GPIO 4 (P1-07) while reading GPIO 24 (P1_18) */ 165 | int main(int argc, char *argv[]) 166 | { 167 | int repeat = 10; 168 | 169 | // Enable GPIO pins 170 | if (-1 == gpio_export(POUT) || -1 == gpio_export(PIN)) 171 | return(1); 172 | 173 | // Set GPIO directions 174 | if (-1 == gpio_direction(POUT, OUT) || -1 == gpio_direction(PIN, IN)) 175 | return(2); 176 | 177 | do { 178 | // Write GPIO value 179 | if (-1 == gpio_write(POUT, repeat % 2)) 180 | return(3); 181 | 182 | // Read GPIO value 183 | printf("I'm reading %d in GPIO %d\n", gpio_read(PIN), PIN); 184 | 185 | usleep(500 * 1000); 186 | } 187 | while (repeat--); 188 | 189 | // Disable GPIO pins 190 | if (-1 == gpio_unexport(POUT) || -1 == gpio_unexport(PIN)) 191 | return(4); 192 | 193 | return(0); 194 | } 195 | 196 | #endif // TEST_GPIO_SYSFS 197 | 198 | -------------------------------------------------------------------------------- /src/gpio_sysfs.h: -------------------------------------------------------------------------------- 1 | #ifndef GPIO_SYSFS_H 2 | #define GPIO_SYSFS_H 3 | 4 | #define IN 0 5 | #define OUT 1 6 | 7 | #define LOW 0 8 | #define HIGH 1 9 | 10 | int gpio_export (int pin); 11 | 12 | int gpio_unexport (int pin); 13 | 14 | int gpio_direction (int pin, int dir); 15 | 16 | int gpio_read (int pin); 17 | 18 | int gpio_write (int pin, int value); 19 | 20 | #endif // GPIO_SYSFS_H 21 | 22 | -------------------------------------------------------------------------------- /src/loadelf.c: -------------------------------------------------------------------------------- 1 | /* loadelf.c - an elf loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "loadelf.h" 27 | #include "proploader.h" 28 | 29 | #ifndef TRUE 30 | #define TRUE 1 31 | #define FALSE 0 32 | #endif 33 | 34 | #define IDENT_SIGNIFICANT_BYTES 9 35 | 36 | static uint8_t ident[] = { 37 | 0x7f, 'E', 'L', 'F', // magic number 38 | 0x01, // class 39 | 0x01, // data 40 | 0x01, // version 41 | 0x00, // os / abi identification 42 | 0x00, // abi version 43 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // padding 44 | }; 45 | 46 | static int FindProgramTableEntry(ElfContext *c, ElfSectionHdr *section, ElfProgramHdr *program); 47 | static void ShowSectionHdr(ElfSectionHdr *section); 48 | static void ShowProgramHdr(ElfProgramHdr *program); 49 | 50 | int ReadAndCheckElfHdr(FILE *fp, ElfHdr *hdr) 51 | { 52 | if (fread(hdr, 1, sizeof(ElfHdr), fp) != sizeof(ElfHdr)) 53 | return FALSE; 54 | return memcmp(ident, hdr->ident, IDENT_SIGNIFICANT_BYTES) == 0; 55 | } 56 | 57 | ElfContext *OpenElfFile(FILE *fp, ElfHdr *hdr) 58 | { 59 | ElfSectionHdr section; 60 | ElfContext *c; 61 | 62 | /* allocate and initialize a context structure */ 63 | if (!(c = (ElfContext *)malloc(sizeof(ElfContext)))) 64 | return NULL; 65 | memset(c, 0, sizeof(ElfContext)); 66 | c->hdr = *hdr; 67 | c->fp = fp; 68 | 69 | /* get the string section offset */ 70 | if (!LoadSectionTableEntry(c, c->hdr.shstrndx, §ion)) { 71 | free(c); 72 | return NULL; 73 | } 74 | c->stringOff = section.offset; 75 | 76 | /* get the symbol table section offset */ 77 | if (FindSectionTableEntry(c, ".symtab", §ion)) { 78 | c->symbolOff = section.offset; 79 | c->symbolCnt = section.size / sizeof(ElfSymbol); 80 | if (FindSectionTableEntry(c, ".strtab", §ion)) 81 | c->symbolStringOff = section.offset; 82 | else { 83 | c->symbolOff = 0; 84 | c->symbolCnt = 0; 85 | } 86 | } 87 | 88 | /* return the context */ 89 | return c; 90 | } 91 | 92 | void FreeElfContext(ElfContext *c) 93 | { 94 | free(c); 95 | } 96 | 97 | int GetProgramSize(ElfContext *c, uint32_t *pStart, uint32_t *pSize, uint32_t *pCogImagesSize) 98 | { 99 | ElfProgramHdr program; 100 | uint32_t start = 0xffffffff; 101 | uint32_t end = 0; 102 | uint32_t cogImagesStart = 0xffffffff; 103 | uint32_t cogImagesEnd = 0; 104 | int cogImagesFound = FALSE; 105 | int i; 106 | for (i = 0; i < c->hdr.phnum; ++i) { 107 | if (!LoadProgramTableEntry(c, i, &program)) { 108 | message("Can't read ELF program header %d", i); 109 | return FALSE; 110 | } 111 | if (program.paddr < COG_DRIVER_IMAGE_BASE) { 112 | if (program.paddr < start) 113 | start = program.paddr; 114 | if (program.paddr + program.filesz > end) 115 | end = program.paddr + program.filesz; 116 | } 117 | else { 118 | if (program.paddr < cogImagesStart) 119 | cogImagesStart = program.paddr; 120 | if (program.paddr + program.filesz > cogImagesEnd) 121 | cogImagesEnd = program.paddr + program.filesz; 122 | cogImagesFound = TRUE; 123 | } 124 | } 125 | *pStart = start; 126 | *pSize = end - start; 127 | *pCogImagesSize = cogImagesFound ? cogImagesEnd - cogImagesStart : 0; 128 | return TRUE; 129 | } 130 | 131 | int FindProgramSegment(ElfContext *c, const char *name, ElfProgramHdr *program) 132 | { 133 | ElfSectionHdr section; 134 | if (!FindSectionTableEntry(c, name, §ion)) 135 | return -1; 136 | return FindProgramTableEntry(c, §ion, program); 137 | } 138 | 139 | uint8_t *LoadProgramSegment(ElfContext *c, ElfProgramHdr *program) 140 | { 141 | uint8_t *buf; 142 | if (!(buf = (uint8_t *)malloc(program->filesz))) 143 | return NULL; 144 | if (fseek(c->fp, program->offset, SEEK_SET) != 0) { 145 | free(buf); 146 | return NULL; 147 | } 148 | if (fread(buf, 1, program->filesz, c->fp) != program->filesz) { 149 | free(buf); 150 | return NULL; 151 | } 152 | return buf; 153 | } 154 | 155 | int FindSectionTableEntry(ElfContext *c, const char *name, ElfSectionHdr *section) 156 | { 157 | int i; 158 | for (i = 0; i < c->hdr.shnum; ++i) { 159 | char thisName[ELFNAMEMAX], *p = thisName; 160 | int cnt, ch; 161 | if (!LoadSectionTableEntry(c, i, section)) { 162 | message("Can't read ELF section header %d", i); 163 | return 1; 164 | } 165 | fseek(c->fp, c->stringOff + section->name, SEEK_SET); 166 | for (cnt = 0; ++cnt < ELFNAMEMAX && (ch = getc(c->fp)) != '\0'; ) 167 | *p++ = ch; 168 | *p = '\0'; 169 | if (strcmp(name, thisName) == 0) 170 | return TRUE; 171 | } 172 | return FALSE; 173 | } 174 | 175 | int LoadSectionTableEntry(ElfContext *c, int i, ElfSectionHdr *section) 176 | { 177 | return fseek(c->fp, c->hdr.shoff + i * c->hdr.shentsize, SEEK_SET) == 0 178 | && fread(section, 1, sizeof(ElfSectionHdr), c->fp) == sizeof(ElfSectionHdr); 179 | } 180 | 181 | static int FindProgramTableEntry(ElfContext *c, ElfSectionHdr *section, ElfProgramHdr *program) 182 | { 183 | int i; 184 | for (i = 0; i < c->hdr.shnum; ++i) { 185 | if (!LoadProgramTableEntry(c, i, program)) { 186 | message("Can't read ELF program header %d", i); 187 | return -1; 188 | } 189 | if (SectionInProgramSegment(section, program)) 190 | return i; 191 | } 192 | return -1; 193 | } 194 | 195 | int LoadProgramTableEntry(ElfContext *c, int i, ElfProgramHdr *program) 196 | { 197 | return fseek(c->fp, c->hdr.phoff + i * c->hdr.phentsize, SEEK_SET) == 0 198 | && fread(program, 1, sizeof(ElfProgramHdr), c->fp) == sizeof(ElfProgramHdr); 199 | } 200 | 201 | int FindElfSymbol(ElfContext *c, const char *name, ElfSymbol *symbol) 202 | { 203 | int i; 204 | for (i = 1; i < c->symbolCnt; ++i) { 205 | char thisName[ELFNAMEMAX]; 206 | if (LoadElfSymbol(c, i, thisName, symbol) == 0 && strcmp(name, thisName) == 0) 207 | return TRUE; 208 | } 209 | return FALSE; 210 | } 211 | 212 | int LoadElfSymbol(ElfContext *c, int i, char *name, ElfSymbol *symbol) 213 | { 214 | char *p = name; 215 | if (fseek(c->fp, c->symbolOff + i * sizeof(ElfSymbol), SEEK_SET) != 0 216 | || fread(symbol, 1, sizeof(ElfSymbol), c->fp) != sizeof(ElfSymbol)) 217 | return -1; 218 | if (symbol->name) { 219 | int cnt, ch; 220 | fseek(c->fp, c->symbolStringOff + symbol->name, SEEK_SET); 221 | for (cnt = 0; ++cnt < ELFNAMEMAX && (ch = getc(c->fp)) != '\0'; ) 222 | *p++ = ch; 223 | } 224 | *p = '\0'; 225 | return 0; 226 | } 227 | 228 | void ShowElfFile(ElfContext *c) 229 | { 230 | ElfSectionHdr section; 231 | ElfProgramHdr program; 232 | int i; 233 | 234 | /* show file header */ 235 | printf("ELF Header:\n"); 236 | printf(" ident: "); 237 | for (i = 0; i < sizeof(c->hdr.ident); ++i) 238 | printf(" %02x", c->hdr.ident[i]); 239 | putchar('\n'); 240 | printf(" type: %04x\n", c->hdr.type); 241 | printf(" machine: %04x\n", c->hdr.machine); 242 | printf(" version: %08x\n", c->hdr.version); 243 | printf(" entry: %08x\n", c->hdr.entry); 244 | printf(" phoff: %08x\n", c->hdr.phoff); 245 | printf(" shoff: %08x\n", c->hdr.shoff); 246 | printf(" flags: %08x\n", c->hdr.flags); 247 | printf(" ehsize: %d\n", c->hdr.entry); 248 | printf(" phentsize: %d\n", c->hdr.phentsize); 249 | printf(" phnum: %d\n", c->hdr.phnum); 250 | printf(" shentsize: %d\n", c->hdr.shentsize); 251 | printf(" shnum: %d\n", c->hdr.shnum); 252 | printf(" shstrndx: %d\n", c->hdr.shstrndx); 253 | 254 | /* show the section table */ 255 | for (i = 0; i < c->hdr.shnum; ++i) { 256 | char name[ELFNAMEMAX], *p = name; 257 | int cnt, ch; 258 | if (!LoadSectionTableEntry(c, i, §ion)) { 259 | printf("error: can't read section header %d\n", i); 260 | return; 261 | } 262 | fseek(c->fp, c->stringOff + section.name, SEEK_SET); 263 | for (cnt = 0; ++cnt < ELFNAMEMAX && (ch = getc(c->fp)) != '\0'; ) 264 | *p++ = ch; 265 | *p = '\0'; 266 | printf("SectionHdr %d:\n", i); 267 | printf(" name: %08x %s\n", section.name, name); 268 | ShowSectionHdr(§ion); 269 | } 270 | 271 | /* show the program table */ 272 | for (i = 0; i < c->hdr.phnum; ++i) { 273 | if (!LoadProgramTableEntry(c, i, &program)) { 274 | printf("error: can't read program header %d\n", i); 275 | return; 276 | } 277 | printf("ProgramHdr %d:\n", i); 278 | ShowProgramHdr(&program); 279 | } 280 | 281 | /* show the symbol table */ 282 | for (i = 1; i < c->symbolCnt; ++i) { 283 | char name[ELFNAMEMAX]; 284 | ElfSymbol symbol; 285 | if (LoadElfSymbol(c, i, name, &symbol) == 0 && symbol.name && INFO_BIND(symbol.info) == STB_GLOBAL) 286 | printf(" %08x %s: %08x\n", symbol.name, name, symbol.value); 287 | } 288 | } 289 | 290 | static void ShowSectionHdr(ElfSectionHdr *section) 291 | { 292 | printf(" type: %08x\n", section->type); 293 | printf(" flags: %08x\n", section->flags); 294 | printf(" addr: %08x\n", section->addr); 295 | printf(" offset: %08x\n", section->offset); 296 | printf(" size: %08x\n", section->size); 297 | printf(" link: %08x\n", section->link); 298 | printf(" info: %08x\n", section->info); 299 | printf(" addralign: %08x\n", section->addralign); 300 | printf(" entsize: %08x\n", section->entsize); 301 | } 302 | 303 | static void ShowProgramHdr(ElfProgramHdr *program) 304 | { 305 | printf(" type: %08x\n", program->type); 306 | printf(" offset: %08x\n", program->offset); 307 | printf(" vaddr: %08x\n", program->vaddr); 308 | printf(" paddr: %08x\n", program->paddr); 309 | printf(" filesz: %08x\n", program->filesz); 310 | printf(" memsz: %08x\n", program->memsz); 311 | printf(" flags: %08x\n", program->flags); 312 | printf(" align: %08x\n", program->align); 313 | } 314 | 315 | #ifdef MAIN 316 | 317 | int main(int argc, char *argv[]) 318 | { 319 | ElfContext *c; 320 | ElfHdr hdr; 321 | FILE *fp; 322 | 323 | /* check the arguments */ 324 | if (argc != 2) { 325 | printf("usage: loadelf \n"); 326 | return 1; 327 | } 328 | 329 | /* open the image file */ 330 | if (!(fp = fopen(argv[1], "rb"))) { 331 | printf("error: opening '%s'\n", argv[1]); 332 | return 1; 333 | } 334 | 335 | /* make sure it's an elf file */ 336 | if (!ReadAndCheckElfHdr(fp, &hdr)) { 337 | printf("error: not an elf file"); 338 | return 1; 339 | } 340 | 341 | /* open the elf file */ 342 | if (!(c = OpenElfFile(fp, &hdr))) { 343 | printf("error: opening elf file\n"); 344 | return 1; 345 | } 346 | 347 | /* show the contents of the elf file */ 348 | ShowElfFile(c); 349 | 350 | /* close the elf file */ 351 | CloseElfFile(c); 352 | 353 | return 0; 354 | } 355 | 356 | #endif 357 | 358 | -------------------------------------------------------------------------------- /src/loadelf.h: -------------------------------------------------------------------------------- 1 | /* loadelf.h - an elf loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #ifndef __LOADELF_H__ 23 | #define __LOADELF_H__ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | 31 | /* base address of cog driver overlays to be loaded into eeprom */ 32 | #define COG_DRIVER_IMAGE_BASE 0xc0000000 33 | 34 | #define ST_NULL 0 35 | #define ST_PROGBITS 1 36 | #define ST_SYMTAB 2 37 | #define ST_STRTAB 3 38 | #define ST_RELA 4 39 | #define ST_HASH 5 40 | #define ST_DYNAMIC 6 41 | #define ST_NOTE 7 42 | #define ST_NOBITS 8 43 | #define ST_REL 9 44 | #define ST_SHLIB 10 45 | #define ST_DYNSYM 11 46 | 47 | #define SF_WRITE 1 48 | #define SF_ALLOC 2 49 | #define SF_EXECUTE 4 50 | 51 | #define ELFNAMEMAX 128 52 | 53 | typedef struct { 54 | uint8_t ident[16]; 55 | uint16_t type; 56 | uint16_t machine; 57 | uint32_t version; 58 | uint32_t entry; 59 | uint32_t phoff; 60 | uint32_t shoff; 61 | uint32_t flags; 62 | uint16_t ehsize; 63 | uint16_t phentsize; 64 | uint16_t phnum; 65 | uint16_t shentsize; 66 | uint16_t shnum; 67 | uint16_t shstrndx; 68 | } ElfHdr; 69 | 70 | typedef struct { 71 | uint32_t name; 72 | uint32_t type; 73 | uint32_t flags; 74 | uint32_t addr; 75 | uint32_t offset; 76 | uint32_t size; 77 | uint32_t link; 78 | uint32_t info; 79 | uint32_t addralign; 80 | uint32_t entsize; 81 | } ElfSectionHdr; 82 | 83 | typedef struct { 84 | uint32_t type; 85 | uint32_t offset; 86 | uint32_t vaddr; 87 | uint32_t paddr; 88 | uint32_t filesz; 89 | uint32_t memsz; 90 | uint32_t flags; 91 | uint32_t align; 92 | } ElfProgramHdr; 93 | 94 | typedef struct { 95 | uint32_t name; 96 | uint32_t value; 97 | uint32_t size; 98 | uint8_t info; 99 | uint8_t other; 100 | uint16_t shndx; 101 | } ElfSymbol; 102 | 103 | #define INFO_BIND(i) ((i) >> 4) 104 | #define INFO_TYPE(i) ((i) & 0x0f) 105 | 106 | #define STB_LOCAL 0 107 | #define STB_GLOBAL 1 108 | #define STB_WEAK 2 109 | 110 | typedef struct { 111 | ElfHdr hdr; 112 | uint32_t stringOff; 113 | uint32_t symbolOff; 114 | uint32_t symbolStringOff; 115 | uint32_t symbolCnt; 116 | FILE *fp; 117 | } ElfContext; 118 | 119 | #define SectionInProgramSegment(s, p) \ 120 | ((s)->offset >= (p)->offset && (s)->offset < (p)->offset + (p)->filesz \ 121 | && (s)->addr >= (p)->vaddr && (s)->addr < (p)->vaddr + (p)->memsz) 122 | 123 | #define ProgramSegmentsMatch(p1, p2) \ 124 | ((p1)->offset == (p2)->offset && (p1)->vaddr == (p2)->vaddr) 125 | 126 | int ReadAndCheckElfHdr(FILE *fp, ElfHdr *hdr); 127 | ElfContext *OpenElfFile(FILE *fp, ElfHdr *hdr); 128 | void FreeElfContext(ElfContext *c); 129 | int GetProgramSize(ElfContext *c, uint32_t *pStart, uint32_t *pSize, uint32_t *pCogImagesSize); 130 | int FindSectionTableEntry(ElfContext *c, const char *name, ElfSectionHdr *section); 131 | int FindProgramSegment(ElfContext *c, const char *name, ElfProgramHdr *program); 132 | uint8_t *LoadProgramSegment(ElfContext *c, ElfProgramHdr *program); 133 | int LoadSectionTableEntry(ElfContext *c, int i, ElfSectionHdr *section); 134 | int LoadProgramTableEntry(ElfContext *c, int i, ElfProgramHdr *program); 135 | int FindElfSymbol(ElfContext *c, const char *name, ElfSymbol *symbol); 136 | int LoadElfSymbol(ElfContext *c, int i, char *name, ElfSymbol *symbol); 137 | void ShowElfFile(ElfContext *c); 138 | 139 | #ifdef __cplusplus 140 | } 141 | #endif 142 | 143 | #endif 144 | -------------------------------------------------------------------------------- /src/loader.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "loader.h" 7 | #include "loadelf.h" 8 | #include "propimage.h" 9 | #include "proploader.h" 10 | 11 | int Loader::loadFile(const char *file, LoadType loadType) 12 | { 13 | uint8_t *image; 14 | int imageSize; 15 | int sts; 16 | 17 | /* make sure the image was loaded into memory */ 18 | if (!(image = readFile(file, &imageSize))) 19 | return -1; 20 | 21 | /* load the file */ 22 | sts = loadImage(image, imageSize, loadType); 23 | free(image); 24 | 25 | /* return load result */ 26 | return sts; 27 | } 28 | 29 | int Loader::loadImage(const uint8_t *image, int imageSize, LoadType loadType) 30 | { 31 | // get the binary clock settings 32 | PropImage img((uint8_t *)image, imageSize); // shouldn't really modify image! 33 | 34 | // get the fast loader and program clock speeds 35 | int clockSpeed; 36 | int gotClockSpeed = GetNumericConfigField(m_connection->config(), "clkfreq", &clockSpeed); 37 | if (gotClockSpeed) { 38 | img.setClkFreq(clockSpeed); 39 | img.updateChecksum(); 40 | } 41 | 42 | // get the fast loader and program clock modes 43 | int clockMode; 44 | int gotClockMode = GetNumericConfigField(m_connection->config(), "clkmode", &clockMode); 45 | if (gotClockMode) { 46 | img.setClkMode(clockMode); 47 | img.updateChecksum(); 48 | } 49 | 50 | nmessage(INFO_DOWNLOADING, m_connection->portName()); 51 | return m_connection->loadImage(image, imageSize, loadType); 52 | } 53 | 54 | uint8_t *Loader::readFile(const char *file, int *pImageSize) 55 | { 56 | uint8_t *image; 57 | int imageSize; 58 | ElfHdr elfHdr; 59 | FILE *fp; 60 | 61 | /* open the binary file */ 62 | if (!(fp = fopen(file, "rb"))) 63 | return NULL; 64 | 65 | /* check for an elf file */ 66 | if (ReadAndCheckElfHdr(fp, &elfHdr)) 67 | image = readElfFile(fp, &elfHdr, &imageSize); 68 | 69 | /* otherwise, assume a Spin binary */ 70 | else 71 | image = readSpinBinaryFile(fp, &imageSize); 72 | 73 | /* close the binary file */ 74 | fclose(fp); 75 | 76 | /* return the image */ 77 | if (image) *pImageSize = imageSize; 78 | return image; 79 | } 80 | 81 | uint8_t *Loader::readSpinBinaryFile(FILE *fp, int *pImageSize) 82 | { 83 | uint8_t *image; 84 | int imageSize; 85 | 86 | /* get the size of the binary file */ 87 | fseek(fp, 0, SEEK_END); 88 | imageSize = (int)ftell(fp); 89 | fseek(fp, 0, SEEK_SET); 90 | 91 | /* allocate space for the file */ 92 | if (!(image = (uint8_t *)malloc(imageSize))) 93 | return NULL; 94 | 95 | /* read the entire image into memory */ 96 | if ((int)fread(image, 1, imageSize, fp) != imageSize) { 97 | free(image); 98 | return NULL; 99 | } 100 | 101 | /* return the buffer containing the file contents */ 102 | *pImageSize = imageSize; 103 | return image; 104 | } 105 | 106 | uint8_t *Loader::readElfFile(FILE *fp, ElfHdr *hdr, int *pImageSize) 107 | { 108 | uint32_t start, imageSize, cogImagesSize; 109 | ElfProgramHdr program; 110 | uint8_t *image = NULL, *buf; 111 | SpinHdr *spinHdr; 112 | ElfContext *c; 113 | int i; 114 | 115 | /* open the elf file */ 116 | if (!(c = OpenElfFile(fp, hdr))) 117 | return NULL; 118 | 119 | /* get the total size of the program */ 120 | if (!GetProgramSize(c, &start, &imageSize, &cogImagesSize)) 121 | goto fail; 122 | 123 | /* cog images in eeprom are not allowed */ 124 | if (cogImagesSize > 0) 125 | goto fail; 126 | 127 | /* allocate a buffer big enough for the entire image */ 128 | if (!(image = (uint8_t *)malloc(imageSize))) 129 | goto fail; 130 | memset(image, 0, imageSize); 131 | 132 | /* load each program section */ 133 | for (i = 0; i < c->hdr.phnum; ++i) { 134 | if (!LoadProgramTableEntry(c, i, &program) 135 | || !(buf = LoadProgramSegment(c, &program))) { 136 | free(image); 137 | goto fail; 138 | } 139 | if (program.paddr < COG_DRIVER_IMAGE_BASE) 140 | memcpy(&image[program.paddr - start], buf, program.filesz); 141 | } 142 | 143 | /* free the elf file context */ 144 | FreeElfContext(c); 145 | 146 | /* fixup the spin binary header */ 147 | spinHdr = (SpinHdr *)image; 148 | spinHdr->vbase = imageSize; 149 | spinHdr->dbase = imageSize + 2 * sizeof(uint32_t); // stack markers 150 | spinHdr->dcurr = spinHdr->dbase + sizeof(uint32_t); 151 | 152 | /* update the checksum */ 153 | PropImage::updateChecksum(image, imageSize); 154 | 155 | /* return the image */ 156 | *pImageSize = imageSize; 157 | return image; 158 | 159 | fail: 160 | /* return failure */ 161 | if (image) 162 | free(image); 163 | FreeElfContext(c); 164 | return NULL; 165 | } 166 | 167 | -------------------------------------------------------------------------------- /src/loader.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOADER_H__ 2 | #define __LOADER_H__ 3 | 4 | #include 5 | #include 6 | #include "propconnection.h" 7 | #include "loadelf.h" 8 | 9 | class Loader { 10 | public: 11 | Loader() : m_connection(0) {} 12 | Loader(PropConnection *connection) : m_connection(connection) {} 13 | ~Loader() {} 14 | void setConnection(PropConnection *connection) { m_connection = connection; } 15 | int identify(int *pVersion); 16 | int loadFile(const char *file, LoadType loadType = ltDownloadAndRun); 17 | int fastLoadFile(const char *file, LoadType loadType = ltDownloadAndRun); 18 | int loadImage(const uint8_t *image, int imageSize, LoadType loadType = ltDownloadAndRun); 19 | int fastLoadImage(const uint8_t *image, int imageSize, LoadType loadType = ltDownloadAndRun); 20 | static uint8_t *readFile(const char *file, int *pImageSize); 21 | private: 22 | int fastLoadImageHelper(const uint8_t *image, int imageSize, LoadType loadType, int clockSpeed, int clockMode, int loaderBaudRate, int fastLoaderBaudRate); 23 | uint8_t *generateInitialLoaderImage(int clockSpeed, int clockMode, int packetID, int loaderBaudRate, int fastLoaderBaudRate, int *pLength); 24 | int transmitPacket(int id, const uint8_t *payload, int payloadSize, int *pResult, int timeout = 2000); 25 | static uint8_t *readSpinBinaryFile(FILE *fp, int *pImageSize); 26 | static uint8_t *readElfFile(FILE *fp, ElfHdr *hdr, int *pImageSize); 27 | PropConnection *m_connection; 28 | }; 29 | 30 | inline void msleep(int ms) 31 | { 32 | usleep(ms * 1000); 33 | } 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /src/messages.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "messages.h" 5 | 6 | /* 7 | 8 | This is a list of messages generated by PropLoader. There are two command line options that affect the message output; -c and -v (code and verbose, respectively). 9 | By default (ie: when no -c or -v option is active) a limited set of messages appears and contain just the text of the message. 10 | 11 | With -c, a numeric code is prepended to each message in the format ###-. 12 | With -v, verbose (debugging) messages appear along with normal messages. These are messages meant only for PropLoader developers to diagnose problems. 13 | Both -c and -v can be used individually or together. 14 | 15 | There are three categories of messages and message codes: 16 | 17 | * Status - These express state/progress/event information and are given codes 001 through 099. 18 | 19 | * Error - These express fatal problems and are given codes 100 and beyond. 20 | 21 | * Verbose - These are for seasoned developers. They may express state information or specific deep error information that is usually only helpful to a small 22 | set of users, thus, they are never shown without an active -v option on the command line. These are all automatically given the code 000; tools that parse 23 | them should ignore all 000-coded messages. 24 | 25 | Code numbers ARE NEVER REUSED for a condition that means something different than what was first intended by a message. When a new State or Error message 26 | is created, it simply takes on the next available code number even if it's logically related to another far away message. 27 | 28 | */ 29 | 30 | // message codes 1-99 -- must be in the same order as the INFO_xxx enum values in messages.h 31 | static const char *infoText[] = { 32 | "Opening file '%s'", 33 | "Downloading file to port %s", 34 | "Verifying RAM", 35 | "Programming EEPROM", 36 | "Download successful!", 37 | "[ Entering terminal mode. Type ESC or Control-C to exit. ]", 38 | "Writing '%s' to the SD card", 39 | "%ld bytes remaining ", 40 | "%ld bytes sent ", 41 | "Setting module name to '%s'", 42 | "Using port %s instead of port %s", 43 | "Stepping down to %d baud", 44 | "Using single-stage download", 45 | "Verifying EEPROM" 46 | }; 47 | 48 | // message codes 100 and up -- must be in the same order as the ERROR_xxx enum values in messsages.h 49 | static const char *errorText[] = { 50 | "Option -n can only be used to name wifi modules", 51 | "Invalid address: %s", 52 | "Download failed", 53 | "Can't open file '%s'", 54 | "Propeller not found on %s", 55 | "Failed to enter terminal mode", 56 | "Unrecognized wi-fi module firmware\n\ 57 | Version is %s but expected %s.\n\ 58 | Recommended action: update firmware and/or PropLoader to latest version(s).", 59 | "Failed to write SD card file '%s'", 60 | "Invalid module name", 61 | "Failed to set module name", 62 | "File is truncated or not a Propeller application image", 63 | "File is corrupt or not a Propeller application", 64 | "Can't read Propeller application file '%s'", 65 | "Wifi module discovery failed", 66 | "No wifi modules found", 67 | "Serial port discovery failed", 68 | "No serial ports found", 69 | "Unable to connect to port %s", 70 | "Unable to connect to module at %s", 71 | "Failed to set baud rate", 72 | "Internal error", 73 | "Insufficient memory", 74 | "No reset method '%s'", 75 | "Reset failed", 76 | "Wrong Propeller version: got %d, expected 1", 77 | "RAM checksum failed", 78 | "EEPROM checksum failed", 79 | "EEPROM verify failed", 80 | "Communication lost", 81 | "Load image failed" 82 | }; 83 | 84 | static void vmessage(const char *fmt, va_list ap, int eol); 85 | static void vnmessage(int code, const char *fmt, va_list ap, int eol); 86 | 87 | static const char *messageText(int code) 88 | { 89 | const char *fmt; 90 | if (code >= MIN_INFO && code < MAX_INFO) 91 | fmt = infoText[code - MIN_INFO]; 92 | else if (code >= MIN_ERROR && code < MAX_ERROR) 93 | fmt = errorText[code - MIN_ERROR]; 94 | else 95 | fmt = "Internal error"; 96 | return fmt; 97 | } 98 | 99 | int verbose = 0; 100 | int showMessageCodes = false; 101 | 102 | int error(const char *fmt, ...) 103 | { 104 | va_list ap; 105 | va_start(ap, fmt); 106 | vmessage(fmt, ap, '\n'); 107 | va_end(ap); 108 | return -1; 109 | } 110 | 111 | int nerror(int code, ...) 112 | { 113 | va_list ap; 114 | va_start(ap, code); 115 | vnmessage(code, messageText(code), ap, '\n'); 116 | va_end(ap); 117 | return -1; 118 | } 119 | 120 | void message(const char *fmt, ...) 121 | { 122 | va_list ap; 123 | va_start(ap, fmt); 124 | vmessage(fmt, ap, '\n'); 125 | va_end(ap); 126 | } 127 | 128 | void nmessage(int code, ...) 129 | { 130 | va_list ap; 131 | va_start(ap, code); 132 | vnmessage(code, messageText(code), ap, '\n'); 133 | va_end(ap); 134 | } 135 | 136 | void nprogress(int code, ...) 137 | { 138 | va_list ap; 139 | va_start(ap, code); 140 | vnmessage(code, messageText(code), ap, '\r'); 141 | va_end(ap); 142 | } 143 | 144 | static void vmessage(const char *fmt, va_list ap, int eol) 145 | { 146 | const char *p = fmt; 147 | int code = 0; 148 | 149 | /* check for and parse the numeric message code */ 150 | if (*p && isdigit(*p)) { 151 | while (*p && isdigit(*p)) 152 | code = code * 10 + *p++ - '0'; 153 | if (*p == '-') 154 | fmt = ++p; 155 | } 156 | 157 | /* display messages in verbose mode or when the code is > 0 */ 158 | if (verbose || code > 0) { 159 | if (showMessageCodes) 160 | printf("%03d-", code); 161 | if (code > 99) 162 | printf("ERROR: "); 163 | vprintf(fmt, ap); 164 | putchar(eol); 165 | if (eol == '\r') 166 | fflush(stdout); 167 | } 168 | } 169 | 170 | static void vnmessage(int code, const char *fmt, va_list ap, int eol) 171 | { 172 | /* display messages in verbose mode or when the code is > 0 */ 173 | if (verbose || code > 0) { 174 | if (showMessageCodes) 175 | printf("%03d-", code); 176 | if (code > 99) 177 | printf("ERROR: "); 178 | vprintf(fmt, ap); 179 | putchar(eol); 180 | if (eol == '\r') 181 | fflush(stdout); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/messages.h: -------------------------------------------------------------------------------- 1 | #ifndef __MESSAGES_H__ 2 | #define __MESSAGES_H__ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /* 11 | 12 | This is a list of messages generated by PropLoader. There are two command line options that affect the message output; -c and -v (code and verbose, respectively). 13 | By default (ie: when no -c or -v option is active) a limited set of messages appears and contain just the text of the message. 14 | 15 | With -c, a numeric code is prepended to each message in the format ###-. 16 | With -v, verbose (debugging) messages appear along with normal messages. These are messages meant only for PropLoader developers to diagnose problems. 17 | Both -c and -v can be used individually or together. 18 | 19 | There are three categories of messages and message codes: 20 | 21 | * Status - These express state/progress/event information and are given codes 001 through 099. 22 | 23 | * Error - These express fatal problems and are given codes 100 and beyond. 24 | 25 | * Verbose - These are for seasoned developers. They may express state information or specific deep error information that is usually only helpful to a small 26 | set of users, thus, they are never shown without an active -v option on the command line. These are all automatically given the code 000; tools that parse 27 | them should ignore all 000-coded messages. 28 | 29 | Code numbers ARE NEVER REUSED for a condition that means something different than what was first intended by a message. When a new State or Error message 30 | is created, it simply takes on the next available code number even if it's logically related to another far away message. 31 | 32 | */ 33 | 34 | enum { 35 | /* 000 */ DEBUG_MESSAGE, 36 | 37 | MIN_INFO = 1, 38 | /* 001 */ INFO_OPENING_FILE = MIN_INFO, 39 | /* 002 */ INFO_DOWNLOADING, 40 | /* 003 */ INFO_VERIFYING_RAM, 41 | /* 004 */ INFO_PROGRAMMING_EEPROM, 42 | /* 005 */ INFO_DOWNLOAD_SUCCESSFUL, 43 | /* 006 */ INFO_TERMINAL_MODE, 44 | /* 007 */ INFO_WRITING_TO_SD_CARD, 45 | /* 008 */ INFO_BYTES_REMAINING, 46 | /* 009 */ INFO_BYTES_SENT, 47 | /* 010 */ INFO_SETTING_MODULE_NAME, 48 | /* 011 */ INFO_USING_ALTERNATE_PORT, 49 | /* 012 */ INFO_STEPPING_DOWN_BAUD_RATE, 50 | /* 013 */ INFO_USING_SINGLE_STAGE_LOADER, 51 | /* 014 */ INFO_VERIFYING_EEPROM, 52 | MAX_INFO, 53 | 54 | MIN_ERROR = 100, 55 | /* 100 */ ERROR_CAN_ONLY_NAME_WIFI_MODULES = MIN_ERROR, 56 | /* 101 */ ERROR_INVALID_MODULE_ADDRESS, 57 | /* 102 */ ERROR_DOWNLOAD_FAILED, 58 | /* 103 */ ERROR_CANT_OPEN_FILE, 59 | /* 104 */ ERROR_PROPELLER_NOT_FOUND, 60 | /* 105 */ ERROR_FAILED_TO_ENTER_TERMINAL_MODE, 61 | /* 106 */ ERROR_WRONG_WIFI_MODULE_FIRMWARE, 62 | /* 107 */ ERROR_FAILED_TO_WRITE_TO_SD_CARD, 63 | /* 108 */ ERROR_INVALID_MODULE_NAME, 64 | /* 109 */ ERROR_FAILED_TO_SET_MODULE_NAME, 65 | /* 110 */ ERROR_FILE_TRUNCATED, 66 | /* 111 */ ERROR_FILE_CORRUPTED, 67 | /* 112 */ ERROR_CANT_READ_PROPELLER_APP_FILE, 68 | /* 113 */ ERROR_WIFI_MODULE_DISCOVERY_FAILED, 69 | /* 114 */ ERROR_NO_WIFI_MODULES_FOUND, 70 | /* 115 */ ERROR_SERIAL_PORT_DISCOVERY_FAILED, 71 | /* 116 */ ERROR_NO_SERIAL_PORTS_FOUND, 72 | /* 117 */ ERROR_UNABLE_TO_CONNECT_TO_PORT, 73 | /* 118 */ ERROR_UNABLE_TO_CONNECT_TO_MODULE, 74 | /* 119 */ ERROR_FAILED_TO_SET_BAUD_RATE, 75 | /* 120 */ ERROR_INTERNAL_CODE_ERROR, 76 | /* 121 */ ERROR_INSUFFICIENT_MEMORY, 77 | /* 122 */ ERROR_NO_RESET_METHOD, 78 | /* 123 */ ERROR_RESET_FAILED, 79 | /* 124 */ ERROR_WRONG_PROPELLER_VERSION, 80 | /* 125 */ ERROR_RAM_CHECKSUM_FAILED, 81 | /* 126 */ ERROR_EEPROM_CHECKSUM_FAILED, 82 | /* 127 */ ERROR_EEPROM_VERIFY_FAILED, 83 | /* 128 */ ERROR_COMMUNICATION_LOST, 84 | /* 129 */ ERROR_LOAD_IMAGE_FAILED, 85 | MAX_ERROR 86 | }; 87 | 88 | extern int showMessageCodes; 89 | extern int verbose; 90 | 91 | int error(const char *fmt, ...); 92 | int nerror(int code, ...); 93 | void message(const char *fmt, ...); 94 | void nmessage(int code, ...); 95 | void nprogress(int code, ...); 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /src/messages.txt: -------------------------------------------------------------------------------- 1 | This is a list of messages generated by PropLoader. There are two command line options that affect the message output; -c and -v (code and verbose, respectively). 2 | By default (ie: when no -c or -v option is active) a limited set of messages appears and contain just the text of the message. 3 | 4 | With -c, a numeric code is prepended to each message in the format ###-. 5 | With -v, verbose (debugging) messages appear along with normal messages. These are messages meant only for PropLoader developers to diagnose problems. 6 | Both -c and -v can be used individually or together. 7 | 8 | There are three categories of messages and message codes: 9 | 10 | * Status - These express state/progress/event information and are given codes 001 through 099. 11 | 12 | * Error - These express fatal problems and are given codes 100 and beyond. 13 | 14 | * Verbose - These are for seasoned developers. They may express state information or specific deep error information that is usually only helpful to a small 15 | set of users, thus, they are never shown without an active -v option on the command line. These are all automatically given the code 000; tools that parse 16 | them should ignore all 000-coded messages. 17 | 18 | Code numbers ARE NEVER REUSED for a condition that means something different than what was first intended by a message. When a new State or Error message 19 | is created, it simply takes on the next available code number even if it's logically related to another far away message. 20 | 21 | STATE MESSAGES 22 | "001-Opening file %s", file 23 | "002-Downloading to Propeller" 24 | "003-Verifying RAM" 25 | "004-Programming EEPROM" 26 | "005-Download successful!" 27 | "006-[ Entering terminal mode. Type ESC or Control-C to exit. ]" 28 | "007-Writing %s to the SD card", file 29 | "008-%ld bytes remaining" 30 | "009-%ld bytes sent" 31 | "010-Setting module name to %s", name 32 | 33 | ERROR MESSAGES 34 | "100-Option -n can only be used to name wifi modules" 35 | "101-Invalid address: %s", ip_address 36 | "102-Download failed: %d", sts 37 | "103-Can't open file '%s'", file 38 | "104-Propeller not found on port %s", port_or_ip_address 39 | "105-Failed to enter terminal mode" 40 | "106-Unrecognized wi-fi module firmware\n\ 41 | " Version is %s but expected %s.x...\n", wifiConnection->version(), WIFI_REQUIRED_MAJOR_VERSION\n\ 42 | " Recommended action: update firmware and/or PropLoader to latest version(s)." 43 | "107-Failed to write SD card file: '%s'", file 44 | "108-Invalid module name" 45 | "109-Failed to set module name" 46 | "110-File is truncated or not a Propeller application image" 47 | "111-File is corrupt or not a Propeller application" 48 | "112-Can't read Propeller application file '%s'" 49 | "113-Wi-fi module discovery failed" 50 | "114-No wi-fi modules found" 51 | "115-Serial port discovery failed" 52 | "116-No serial ports found" 53 | "117-Unable to connect to port %s" 54 | "118-Unable to connect to module at %s" 55 | "119-Failed to set baud rate" 56 | 57 | USE-CASE ORGANIZED MESSAGE EXAMPLES 58 | The list below contains State, Error, and Verbose messages arranged by use-case so context is more obvious. It does not necessarily contains every possible 59 | message. Those with an "X" in the -v column only appear when the -v (verbose) option is used. 60 | 61 | -v Message and notes 62 | --- ---------------------------------------------------------------------------------------------------------------------------------------------------------- 63 | // Command verification 64 | "100-Option -n can only be used to name wifi modules\n" 65 | "101-Invalid address: %s\n", ip_address 66 | 67 | 68 | // Downloading Propeller Image 69 | "001-Opening file %s", file NOTE: This is printed prior to any file open attempt 70 | "102-Download failed: %d\n", sts NOTE: This should not occur after file open error 71 | "103-Can't open file '%s'\n", file 72 | X "failed to read ELF program header %d\n", i 73 | X "failed to read ELF section header %d\n", i 74 | X "failed to set baud rate\n" 75 | X "set baud-rate request failed\n" 76 | X "set baud-rate returned %d\n", result 77 | "104-Propeller not found on port %s\n", port_or_ip_address 78 | X "reset request failed\n" 79 | X "reset returned %d\n", result 80 | X "delivering second-stage loader" 81 | "002-Downloading to Propeller\n" 82 | "003-Verifying RAM" 83 | "004-Programming EEPROM" 84 | X "sending readyToLaunch packet" 85 | X "sending launchNow packet" 86 | "005-Download successful!" 87 | "006-[ Entering terminal mode. Type ESC or Control-C to exit. ]" 88 | 89 | 90 | // Opening a telnet connection for terminal mode 91 | X "can't open connection to target\n" 92 | "105-Failed to enter terminal mode\n" 93 | 94 | 95 | // Serial loads 96 | X "failed to generate identify packet\n" 97 | X "handshake failed\n" 98 | X "wrong propeller version\n" 99 | X "timeout waiting for checksum\n" 100 | X "loader checksum failed: %02x\n", packet2[0] 101 | 102 | 103 | // Wi-fi loads 104 | "106-Unrecognized wi-fi module firmware\n" 105 | " Version is %s but expected %s.x...\n", wifiConnection->version(), WIFI_REQUIRED_MAJOR_VERSION 106 | " Recommended action: update firmware and/or PropLoader to latest version(s)." 107 | X "load request failed\n" 108 | X "load returned %d\n", result 109 | X "get version failed\n" 110 | X "get version returned %d\n", result 111 | X "no version string\n" 112 | 113 | 114 | // Writing a file to the SD card 115 | "001-Opening file %s" NOTE: Reused message from Downloading Propeller Image section 116 | "103-Can't open file '%s'\n" NOTE: Reused message from Downloading Propeller Image section 117 | "007-Writing %s to the SD card", file 118 | "107-Failed to write SD card file: '%s'\n", file 119 | X "loading SD helper" NOTE: Appears before loading the SD helper program into the target board 120 | X "failed to connect to helper" 121 | X "sendPacket FILE_WRITE failed" 122 | X "sendPacket DATA failed" 123 | X "sendPacket EOF failed" 124 | X "second SendPacket EOF failed" 125 | X "missing sdspi-do pin configuration" 126 | X "missing sdspi-clk pin configuration" 127 | X "missing sdspi-di pin configuration" 128 | X "missing sdspi-cs or sdspi-clr pin configuration" 129 | X "helper load failed" 130 | "008-%ld bytes remaining" NOTE: Appears during the writing of file data to the SD card to indicate progress 131 | "009-%ld bytes sent" NOTE: Appears after finishing write of a file to the SD card 132 | 133 | 134 | // Setting module name 135 | "010-Setting module name to %s" NOTE: Appears only when the given module name contains prohibited characters that had to be stripped out 136 | "108-Invalid module name\n" 137 | "109-Failed to set module name\n" 138 | X "module-name update request failed\n" 139 | X "module-name update returned %d\n", result 140 | X "save settings request failed\n" 141 | X "save settings returned %d\n", result 142 | 143 | 144 | // Wi-fi discovery 145 | X "getInterfaceAddresses failed\n" 146 | X "openBroadcastSocket failed\n" 147 | X "receiveSocketData failed\n" 148 | 149 | 150 | // Any HTTP request 151 | X "connect failed\n" 152 | X "send request failed\n" 153 | X "receive response failed\n" 154 | 155 | 156 | -------------------------------------------------------------------------------- /src/packet.cpp: -------------------------------------------------------------------------------- 1 | /* packet.c - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include "packet.h" 25 | #include "proploader.h" 26 | 27 | #ifndef TRUE 28 | #define TRUE 1 29 | #define FALSE 0 30 | #endif 31 | 32 | /* timeouts for waiting for ACK/NAK */ 33 | #define INITIAL_TIMEOUT 10000 // 10 seconds 34 | #define PACKET_TIMEOUT 10000 // 10 seconds - this is long because SD cards may take a file to scan the FAT 35 | 36 | /* packet format: SOH pkt# type length-lo length-hi hdrchk length*data crc1 crc2 */ 37 | #define HDR_SOH 0 38 | #define HDR_TYPE 1 39 | #define HDR_LEN_HI 2 40 | #define HDR_LEN_LO 3 41 | #define HDR_CHK 4 42 | 43 | /* packet header and crc lengths */ 44 | #define PKTHDRLEN 5 45 | #define PKTCRCLEN 2 46 | 47 | /* maximum length of a frame */ 48 | #define FRAMELEN (PKTHDRLEN + PKTMAXLEN + PKTCRCLEN) 49 | 50 | /* protocol characters */ 51 | #define SOH 0x01 /* start of a packet */ 52 | #define ACK 0x06 /* positive acknowledgement */ 53 | #define NAK 0x15 /* negative acknowledgement */ 54 | #define ESC 0x1b /* escape from terminal mode */ 55 | 56 | #define updcrc(crc, ch) (crctab[((crc) >> 8) & 0xff] ^ ((crc) << 8) ^ (ch)) 57 | 58 | static const uint16_t crctab[256] = { 59 | 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 60 | 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 61 | 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 62 | 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 63 | 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 64 | 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 65 | 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 66 | 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 67 | 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 68 | 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 69 | 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 70 | 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 71 | 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 72 | 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 73 | 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 74 | 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 75 | 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 76 | 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 77 | 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 78 | 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 79 | 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 80 | 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 81 | 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, 82 | 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 83 | 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 84 | 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 85 | 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 86 | 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 87 | 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 88 | 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 89 | 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 90 | 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 91 | }; 92 | 93 | int PacketDriver::waitForInitialAck(void) 94 | { 95 | return waitForAckNak(INITIAL_TIMEOUT) == ACK; 96 | } 97 | 98 | int PacketDriver::sendPacket(int type, uint8_t *buf, int len) 99 | { 100 | uint8_t hdr[PKTHDRLEN], crc[PKTCRCLEN], *p; 101 | uint16_t crc16 = 0; 102 | int cnt, ch; 103 | 104 | /* setup the frame header */ 105 | hdr[HDR_SOH] = SOH; /* SOH */ 106 | hdr[HDR_TYPE] = type; /* type type */ 107 | hdr[HDR_LEN_HI] = (uint8_t)(len >> 8); /* data length - high byte */ 108 | hdr[HDR_LEN_LO] = (uint8_t)len; /* data length - low byte */ 109 | hdr[HDR_CHK] = hdr[1] + hdr[2] + hdr[3]; /* header checksum */ 110 | 111 | /* compute the crc */ 112 | for (p = buf, cnt = len; --cnt >= 0; ++p) 113 | crc16 = updcrc(crc16, *p); 114 | crc16 = updcrc(crc16, '\0'); 115 | crc16 = updcrc(crc16, '\0'); 116 | 117 | /* add the crc to the frame */ 118 | crc[0] = (uint8_t)(crc16 >> 8); 119 | crc[1] = (uint8_t)crc16; 120 | 121 | /* send the packet */ 122 | m_connection.sendData(hdr, PKTHDRLEN); 123 | if (len > 0) 124 | m_connection.sendData(buf, len); 125 | m_connection.sendData(crc, PKTCRCLEN); 126 | 127 | /* wait for an ACK/NAK */ 128 | if ((ch = waitForAckNak(PACKET_TIMEOUT)) < 0) { 129 | message("Timeout waiting for ACK/NAK"); 130 | ch = NAK; 131 | } 132 | 133 | /* return status */ 134 | return ch == ACK; 135 | } 136 | 137 | int PacketDriver::receivePacket(int *pType, uint8_t *buf, int len, int timeout) 138 | { 139 | uint8_t hdr[PKTHDRLEN], crc[PKTCRCLEN]; 140 | int actual_len, chk; 141 | uint16_t crc16 = 0; 142 | 143 | /* look for start of packet */ 144 | do { 145 | if (m_connection.receiveDataExactTimeout(&hdr[HDR_SOH], 1, timeout) == -1) 146 | return -1; 147 | } while (hdr[HDR_SOH] != SOH); 148 | 149 | /* receive the rest of the header */ 150 | if (m_connection.receiveDataExactTimeout(&hdr[HDR_TYPE], PKTHDRLEN - 1, timeout) == -1) 151 | return -1; 152 | 153 | /* check the header checksum */ 154 | chk = (hdr[1] + hdr[2] + hdr[3]) & 0xff; 155 | if (hdr[HDR_CHK] != chk) 156 | return -1; 157 | 158 | /* make sure the buffer is big enough for the payload */ 159 | actual_len = hdr[HDR_LEN_HI] << 8 | hdr[HDR_LEN_LO]; 160 | if (actual_len > len) 161 | return -1; 162 | 163 | /* receive the packet payload */ 164 | if (m_connection.receiveDataExactTimeout(buf, actual_len, timeout) == -1) 165 | return -1; 166 | 167 | /* compute the crc */ 168 | for (len = actual_len; --len >= 0; ) 169 | crc16 = updcrc(crc16, *buf++); 170 | 171 | /* receive the crc */ 172 | if (m_connection.receiveDataExactTimeout(crc, PKTCRCLEN, timeout) == -1) 173 | return-1; 174 | 175 | /* check the crc */ 176 | crc16 = updcrc(crc16, crc[0]); 177 | crc16 = updcrc(crc16, crc[1]); 178 | if (crc16 != 0) 179 | return -1; 180 | 181 | /* return packet type and the length of the payload */ 182 | *pType = hdr[HDR_TYPE]; 183 | return actual_len; 184 | } 185 | 186 | int PacketDriver::waitForAckNak(int timeout) 187 | { 188 | uint8_t buf[1]; 189 | return m_connection.receiveDataExactTimeout(buf, 1, timeout) == 1 ? buf[0] : -1; 190 | } 191 | -------------------------------------------------------------------------------- /src/packet.h: -------------------------------------------------------------------------------- 1 | /* packet.h - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #ifndef __PACKET_H__ 23 | #define __PACKET_H__ 24 | 25 | #include "propconnection.h" 26 | 27 | #define PKTMAXLEN 1024 28 | 29 | class PacketDriver { 30 | public: 31 | PacketDriver(PropConnection &connection) : m_connection(connection) {} 32 | int waitForInitialAck(void); 33 | int sendPacket(int type, uint8_t *buf, int len); 34 | int receivePacket(int *pType, uint8_t *buf, int len, int timeout); 35 | private: 36 | int waitForAckNak(int timeout); 37 | 38 | PropConnection &m_connection; 39 | }; 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /src/propconnection.h: -------------------------------------------------------------------------------- 1 | #ifndef PROPCONNECTION_H 2 | #define PROPCONNECTION_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "config.h" 8 | 9 | typedef enum { 10 | ltShutdown = 0, 11 | ltDownloadAndRun = 1 << 0, 12 | ltDownloadAndProgram = 1 << 1, 13 | ltDownloadAndProgramAndRun = ltDownloadAndRun | ltDownloadAndProgram 14 | } LoadType; 15 | 16 | class PropConnection 17 | { 18 | public: 19 | PropConnection() : m_config(NULL), m_portName(NULL) {} 20 | ~PropConnection() { 21 | if (m_portName) 22 | free(m_portName); 23 | } 24 | virtual bool isOpen() = 0; 25 | virtual int close() = 0; 26 | virtual int connect() = 0; 27 | virtual int disconnect() = 0; 28 | virtual int setResetMethod(const char *method) = 0; 29 | virtual int generateResetSignal() = 0; 30 | virtual int identify(int *pVersion) = 0; 31 | virtual int loadImage(const uint8_t *image, int imageSize, uint8_t *response, int responseSize) = 0; 32 | virtual int loadImage(const uint8_t *image, int imageSize, LoadType loadType = ltDownloadAndRun, int info = false) = 0; 33 | virtual int sendData(const uint8_t *buf, int len) = 0; 34 | virtual int receiveDataTimeout(uint8_t *buf, int len, int timeout) = 0; 35 | virtual int receiveDataExactTimeout(uint8_t *buf, int len, int timeout) = 0; 36 | virtual int setBaudRate(int baudRate) = 0; 37 | virtual int maxDataSize() = 0; 38 | virtual int terminal(bool checkForExit, bool pstMode) = 0; 39 | const char *portName() { return m_portName ? m_portName : ""; } 40 | void setPortName(const char *portName) { 41 | if (m_portName) 42 | free(m_portName); 43 | if ((m_portName = (char *)malloc(strlen(portName) + 1)) != NULL) 44 | strcpy(m_portName, portName); 45 | } 46 | void setConfig(BoardConfig *config) { m_config = config; } 47 | BoardConfig *config() { return m_config; } 48 | protected: 49 | BoardConfig *m_config; 50 | char *m_portName; 51 | int m_baudRate; 52 | }; 53 | 54 | #endif // PROPCONNECTION_H 55 | -------------------------------------------------------------------------------- /src/propimage.cpp: -------------------------------------------------------------------------------- 1 | #include "propimage.h" 2 | #include "proploader.h" 3 | 4 | static uint8_t initialCallFrame[] = {0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF}; 5 | 6 | PropImage::PropImage() 7 | : m_imageData(NULL) 8 | { 9 | } 10 | 11 | PropImage::PropImage(uint8_t *imageData, int imageSize) 12 | : m_imageData(NULL) 13 | { 14 | setImage(imageData, imageSize); 15 | } 16 | 17 | PropImage::~PropImage() 18 | { 19 | } 20 | 21 | void PropImage::setImage(uint8_t *imageData, int imageSize) 22 | { 23 | m_imageData = imageData; 24 | m_imageSize = imageSize; 25 | } 26 | 27 | uint32_t PropImage::clkFreq() 28 | { 29 | return getLong((uint8_t *)&((SpinHdr *)m_imageData)->clkfreq); 30 | } 31 | 32 | void PropImage::setClkFreq(uint32_t clkFreq) 33 | { 34 | setLong((uint8_t *)&((SpinHdr *)m_imageData)->clkfreq, clkFreq); 35 | } 36 | 37 | uint8_t PropImage::clkMode() 38 | { 39 | return *(uint8_t *)&((SpinHdr *)m_imageData)->clkmode; 40 | } 41 | 42 | void PropImage::setClkMode(uint8_t clkMode) 43 | { 44 | *(uint8_t *)&((SpinHdr *)m_imageData)->clkmode = clkMode; 45 | } 46 | 47 | int PropImage::validate() 48 | { 49 | SpinHdr *hdr = (SpinHdr *)m_imageData; 50 | uint8_t fullImage[MAX_IMAGE_SIZE]; 51 | 52 | // make sure the image is at least the size of a Spin header 53 | if (m_imageSize <= (int)sizeof(SpinHdr)) 54 | return IMAGE_TRUNCATED; 55 | 56 | // make sure the file is big enough to contain all of the code 57 | if (m_imageSize < hdr->vbase) 58 | return IMAGE_TRUNCATED; 59 | 60 | // make sure the image isn't too large 61 | if (m_imageSize > MAX_IMAGE_SIZE) 62 | return IMAGE_TOO_LARGE; 63 | 64 | // make sure the code starts in the right place 65 | if (hdr->pbase != 0x0010) 66 | return IMAGE_CORRUPTED; 67 | 68 | // make a local full-sized copy of the image 69 | memset(fullImage, 0, sizeof(fullImage)); 70 | memcpy(fullImage, m_imageData, m_imageSize); 71 | 72 | // make sure there is space for the initial call frame 73 | if (hdr->dbase > MAX_IMAGE_SIZE) 74 | return IMAGE_TOO_LARGE; 75 | 76 | // setup the initial call frame 77 | int callFrameStart = hdr->dbase - sizeof(initialCallFrame); 78 | memcpy(&fullImage[callFrameStart], initialCallFrame, sizeof(initialCallFrame)); 79 | 80 | // verify the checksum 81 | uint8_t *p = fullImage; 82 | uint8_t chksum = 0; 83 | for (int cnt = MAX_IMAGE_SIZE; --cnt >= 0; ) 84 | chksum += *p++; 85 | if (chksum != 0) 86 | return IMAGE_CORRUPTED; 87 | 88 | // make sure there is no data after the code 89 | uint16_t idx = hdr->vbase; 90 | while (idx < m_imageSize && (fullImage[idx] == 0 || (idx >= hdr->dbase - sizeof(initialCallFrame) && idx < hdr->dbase && fullImage[idx] == initialCallFrame[idx - (hdr->dbase - sizeof(initialCallFrame))]))) 91 | ++idx; 92 | if (idx < m_imageSize) 93 | return IMAGE_CORRUPTED; 94 | 95 | // image is okay 96 | return 0; 97 | } 98 | 99 | int PropImage::validate(uint8_t *imageData, int imageSize) 100 | { 101 | PropImage image(imageData, imageSize); 102 | return image.validate(); 103 | } 104 | 105 | void PropImage::updateChecksum() 106 | { 107 | SpinHdr *spinHdr = (SpinHdr *)m_imageData; 108 | uint8_t *p = m_imageData; 109 | uint8_t chksum; 110 | int cnt; 111 | spinHdr->chksum = 0; 112 | chksum = SPIN_STACK_FRAME_CHECKSUM; 113 | for (cnt = m_imageSize; --cnt >= 0; ) 114 | chksum += *p++; 115 | spinHdr->chksum = -chksum; 116 | } 117 | 118 | void PropImage::updateChecksum(uint8_t *imageData, int imageSize) 119 | { 120 | PropImage image(imageData, imageSize); 121 | image.updateChecksum(); 122 | } 123 | 124 | uint16_t PropImage::getWord(const uint8_t *buf) 125 | { 126 | return (buf[1] << 8) | buf[0]; 127 | } 128 | 129 | void PropImage::setWord(uint8_t *buf, uint16_t value) 130 | { 131 | buf[1] = value >> 8; 132 | buf[0] = value; 133 | } 134 | 135 | uint32_t PropImage::getLong(const uint8_t *buf) 136 | { 137 | return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; 138 | } 139 | 140 | void PropImage::setLong(uint8_t *buf, uint32_t value) 141 | { 142 | buf[3] = value >> 24; 143 | buf[2] = value >> 16; 144 | buf[1] = value >> 8; 145 | buf[0] = value; 146 | } 147 | -------------------------------------------------------------------------------- /src/propimage.h: -------------------------------------------------------------------------------- 1 | #ifndef PROPELLERIMAGE_H 2 | #define PROPELLERIMAGE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "loadelf.h" 9 | 10 | /* initial stack frame checksum for a binary file */ 11 | /* this value is the sum of the bytes in the initial stack frame which are not included in the .binary file. 12 | 0xEC = (0xFF + 0xFF + 0xF9 + 0xFF + 0xFF + 0xFF + 0xF9 + 0xFF) & 0xFF 13 | */ 14 | #define SPIN_STACK_FRAME_CHECKSUM 0xEC 15 | 16 | #define MAX_IMAGE_SIZE 32768 17 | 18 | /* spin object file header */ 19 | typedef struct { // word offsets 20 | uint32_t clkfreq; // 0 21 | uint8_t clkmode; // 2 22 | uint8_t chksum; 23 | uint16_t pbase; // 3 24 | uint16_t vbase; // 4 25 | uint16_t dbase; // 5 26 | uint16_t pcurr; // 6 27 | uint16_t dcurr; // 7 28 | } SpinHdr; 29 | 30 | /* spin object */ 31 | typedef struct { 32 | uint16_t next; 33 | uint8_t pubcnt; 34 | uint8_t objcnt; 35 | uint16_t pcurr; 36 | uint16_t numlocals; 37 | } SpinObj; 38 | 39 | class PropImage 40 | { 41 | public: 42 | enum { 43 | SUCCESS = 0, 44 | IMAGE_TRUNCATED = -1, 45 | IMAGE_CORRUPTED = -2, 46 | IMAGE_TOO_LARGE = -3 47 | }; 48 | PropImage(); 49 | PropImage(uint8_t *imageData, int imageSize); 50 | ~PropImage(); 51 | void setImage(uint8_t *imageData, int imageSize); 52 | int validate(); 53 | void updateChecksum(); 54 | uint8_t *imageData() { return m_imageData; } 55 | int imageSize() { return m_imageSize; } 56 | uint32_t clkFreq(); 57 | void setClkFreq(uint32_t clkFreq); 58 | uint8_t clkMode(); 59 | void setClkMode(uint8_t clkMode); 60 | static int validate(uint8_t *imageData, int imageSize); 61 | static void updateChecksum(uint8_t *imageData, int imageSize); 62 | 63 | private: 64 | int loadSpinBinaryFile(FILE *fp); 65 | int loadElfFile(FILE *fp, ElfHdr *hdr); 66 | static uint16_t getWord(const uint8_t *buf); 67 | static void setWord(uint8_t *buf, uint16_t value); 68 | static uint32_t getLong(const uint8_t *buf); 69 | static void setLong(uint8_t *buf, uint32_t value); 70 | 71 | uint8_t *m_imageData; 72 | int m_imageSize; 73 | }; 74 | 75 | #endif // PROPELLERIMAGE_H 76 | -------------------------------------------------------------------------------- /src/proploader.h: -------------------------------------------------------------------------------- 1 | #ifndef __PROPLOADER_H__ 2 | #define __PROPLOADER_H__ 3 | 4 | #include "messages.h" 5 | 6 | #define RCFAST 0x00 7 | #define RCSLOW 0x01 8 | #define XINPUT 0x22 9 | #define XTAL1 0x2a 10 | #define XTAL2 0x32 11 | #define XTAL3 0x3a 12 | #define PLL1X 0x41 13 | #define PLL2X 0x42 14 | #define PLL4X 0x43 15 | #define PLL8X 0x44 16 | #define PLL16X 0x45 17 | 18 | #define DEF_LOADER_BAUDRATE 115200 19 | #define DEF_FAST_LOADER_BAUDRATE 921600 20 | #define DEF_TERMINAL_BAUDRATE 115200 21 | #define DEF_CLOCK_SPEED 80000000 22 | #define DEF_CLOCK_MODE (XTAL1+PLL16X) 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /src/serial.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file osint.h 3 | * 4 | * Serial I/O functions used by PLoadLib.c 5 | * 6 | * Copyright (c) 2009 by John Steven Denson 7 | * Modified in 2011 by David Michael Betz 8 | * 9 | * MIT License 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to deal 13 | * in the Software without restriction, including without limitation the rights 14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | * copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, 26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | * THE SOFTWARE. 28 | * 29 | */ 30 | #ifndef __SERIAL_IO_H__ 31 | #define __SERIAL_IO_H__ 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | /* Method of issuing reset to the Propeller chip. */ 38 | typedef enum { 39 | RESET_WITH_RTS, 40 | RESET_WITH_DTR, 41 | RESET_WITH_GPIO 42 | } reset_method_t; 43 | 44 | typedef struct SERIAL SERIAL; 45 | 46 | int SerialUseResetMethod(SERIAL *serial, const char *method); 47 | int OpenSerial(const char *port, int baud, SERIAL **pSerial); 48 | void CloseSerial(SERIAL *serial); 49 | int SetSerialBaud(SERIAL *serial, int baud); 50 | int SerialGenerateResetSignal(SERIAL *serial); 51 | int SendSerialData(SERIAL *serial, const void *buf, int len); 52 | int FlushSerialData(SERIAL *serial); 53 | int ReceiveSerialData(SERIAL *serial, void *buf, int len); 54 | int ReceiveSerialDataTimeout(SERIAL *serial, void *buf, int len, int timeout); 55 | int ReceiveSerialDataExactTimeout(SERIAL *serial, void *buf, int len, int timeout); 56 | int SerialFind(int (*check)(const char *port, void *data), void *data); 57 | void SerialTerminal(SERIAL *serial, int check_for_exit, int pst_mode); 58 | 59 | #ifdef __cplusplus 60 | } 61 | #endif 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /src/serial_mingw.c: -------------------------------------------------------------------------------- 1 | /* 2 | * osint_mingw.c - serial i/o routines for win32api via mingw 3 | * 4 | * Based on: Serial I/O functions used by PLoadLib.c 5 | * 6 | * Copyright (c) 2009 by John Steven Denson 7 | * Modified in 2011 by David Michael Betz 8 | * Modified in 2015 by David Michael Betz 9 | * 10 | * MIT License 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | * 30 | */ 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "serial.h" 39 | 40 | static void ShowLastError(void); 41 | 42 | struct SERIAL { 43 | COMMTIMEOUTS originalTimeouts; 44 | COMMTIMEOUTS timeouts; 45 | reset_method_t resetMethod; 46 | HANDLE hSerial; 47 | }; 48 | 49 | int SerialUseResetMethod(SERIAL *serial, const char *method) 50 | { 51 | if (strcasecmp(method, "dtr") == 0) 52 | serial->resetMethod = RESET_WITH_DTR; 53 | else if (strcasecmp(method, "rts") == 0) 54 | serial->resetMethod = RESET_WITH_RTS; 55 | else 56 | return -1; 57 | return 0; 58 | } 59 | 60 | int OpenSerial(const char *port, int baud, SERIAL **pSerial) 61 | { 62 | char fullPort[20]; 63 | SERIAL *serial; 64 | DCB state; 65 | int sts; 66 | 67 | /* allocate a serial state structure */ 68 | if (!(serial = (SERIAL *)malloc(sizeof(SERIAL)))) 69 | return -1; 70 | 71 | /* initialize the state structure */ 72 | memset(serial, 0, sizeof(SERIAL)); 73 | serial->resetMethod = RESET_WITH_DTR; 74 | 75 | sprintf(fullPort, "\\\\.\\%s", port); 76 | 77 | serial->hSerial = CreateFile( 78 | fullPort, 79 | GENERIC_READ | GENERIC_WRITE, 80 | 0, 81 | NULL, 82 | OPEN_EXISTING, 83 | 0, 84 | NULL); 85 | 86 | if (serial->hSerial == INVALID_HANDLE_VALUE) { 87 | free(serial); 88 | return -1; 89 | } 90 | 91 | /* set the baud rate */ 92 | if ((sts = SetSerialBaud(serial, baud)) != 0) { 93 | CloseHandle(serial->hSerial); 94 | free(serial); 95 | return sts; 96 | } 97 | 98 | GetCommState(serial->hSerial, &state); 99 | state.ByteSize = 8; 100 | state.Parity = NOPARITY; 101 | state.StopBits = ONESTOPBIT; 102 | state.fOutxDsrFlow = FALSE; 103 | state.fDtrControl = DTR_CONTROL_DISABLE; 104 | state.fOutxCtsFlow = FALSE; 105 | state.fRtsControl = RTS_CONTROL_DISABLE; 106 | state.fInX = FALSE; 107 | state.fOutX = FALSE; 108 | state.fBinary = TRUE; 109 | state.fParity = FALSE; 110 | state.fDsrSensitivity = FALSE; 111 | state.fTXContinueOnXoff = TRUE; 112 | state.fNull = FALSE; 113 | state.fAbortOnError = FALSE; 114 | SetCommState(serial->hSerial, &state); 115 | 116 | GetCommTimeouts(serial->hSerial, &serial->originalTimeouts); 117 | serial->timeouts = serial->originalTimeouts; 118 | serial->timeouts.ReadIntervalTimeout = MAXDWORD; 119 | serial->timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; 120 | serial->timeouts.WriteTotalTimeoutMultiplier = 0; 121 | serial->timeouts.WriteTotalTimeoutConstant = 0; 122 | SetCommTimeouts(serial->hSerial, &serial->timeouts); 123 | 124 | /* setup device buffers */ 125 | SetupComm(serial->hSerial, 10000, 10000); 126 | 127 | /* purge any information in the buffer */ 128 | PurgeComm(serial->hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); 129 | 130 | /* return the serial state structure */ 131 | *pSerial = serial; 132 | return 0; 133 | } 134 | 135 | void CloseSerial(SERIAL *serial) 136 | { 137 | if (serial->hSerial != INVALID_HANDLE_VALUE) { 138 | FlushFileBuffers(serial->hSerial); 139 | CloseHandle(serial->hSerial); 140 | } 141 | free(serial); 142 | } 143 | 144 | int SetSerialBaud(SERIAL *serial, int baud) 145 | { 146 | DCB state; 147 | 148 | GetCommState(serial->hSerial, &state); 149 | switch (baud) { 150 | case 9600: 151 | state.BaudRate = CBR_9600; 152 | break; 153 | case 19200: 154 | state.BaudRate = CBR_19200; 155 | break; 156 | case 38400: 157 | state.BaudRate = CBR_38400; 158 | break; 159 | case 57600: 160 | state.BaudRate = CBR_57600; 161 | break; 162 | case 115200: 163 | state.BaudRate = CBR_115200; 164 | break; 165 | case 128000: 166 | state.BaudRate = CBR_128000; 167 | break; 168 | case 256000: 169 | state.BaudRate = CBR_256000; 170 | break; 171 | default: 172 | /* just try the number the user entered */ 173 | state.BaudRate = baud; 174 | break; 175 | } 176 | SetCommState(serial->hSerial, &state); 177 | 178 | return 0; 179 | } 180 | 181 | int SerialGenerateResetSignal(SERIAL *serial) 182 | { 183 | EscapeCommFunction(serial->hSerial, serial->resetMethod == RESET_WITH_RTS ? SETRTS : SETDTR); 184 | Sleep(25); 185 | EscapeCommFunction(serial->hSerial, serial->resetMethod == RESET_WITH_RTS ? CLRRTS : CLRDTR); 186 | Sleep(90); 187 | // Purge here after reset helps to get rid of buffered data. 188 | PurgeComm(serial->hSerial, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); 189 | return 0; 190 | } 191 | 192 | int SendSerialData(SERIAL *serial, const void *buf, int len) 193 | { 194 | DWORD dwBytes = 0; 195 | if (!WriteFile(serial->hSerial, buf, len, &dwBytes, NULL)) { 196 | printf("Error writing port\n"); 197 | ShowLastError(); 198 | return -1; 199 | } 200 | return dwBytes; 201 | } 202 | 203 | int FlushSerialData(SERIAL *serial) 204 | { 205 | return FlushFileBuffers(serial->hSerial) ? 0 : -1; 206 | } 207 | 208 | int ReceiveSerialData(SERIAL *serial, void *buf, int len) 209 | { 210 | DWORD dwBytes = 0; 211 | FlushFileBuffers(serial->hSerial); 212 | serial->timeouts.ReadTotalTimeoutConstant = 0; 213 | SetCommTimeouts(serial->hSerial, &serial->timeouts); 214 | if (!ReadFile(serial->hSerial, buf, len, &dwBytes, NULL)) { 215 | printf("Error reading port\n"); 216 | ShowLastError(); 217 | return -1; 218 | } 219 | return dwBytes; 220 | } 221 | 222 | int ReceiveSerialDataTimeout(SERIAL *serial, void *buf, int len, int timeout) 223 | { 224 | DWORD dwBytes = 0; 225 | FlushFileBuffers(serial->hSerial); 226 | serial->timeouts.ReadTotalTimeoutConstant = timeout; 227 | SetCommTimeouts(serial->hSerial, &serial->timeouts); 228 | if (!ReadFile(serial->hSerial, buf, len, &dwBytes, NULL)) { 229 | printf("Error reading port\n"); 230 | ShowLastError(); 231 | return -1; 232 | } 233 | 234 | if (dwBytes == 0) { 235 | //printf("Timeout 1\n"); 236 | return -1; 237 | } 238 | 239 | return dwBytes; 240 | } 241 | 242 | int ReceiveSerialDataExactTimeout(SERIAL *serial, void *buf, int len, int timeout) 243 | { 244 | uint8_t *ptr = (uint8_t *)buf; 245 | int remaining = len; 246 | DWORD dwBytes = 0; 247 | 248 | FlushFileBuffers(serial->hSerial); 249 | 250 | serial->timeouts.ReadTotalTimeoutConstant = timeout; 251 | SetCommTimeouts(serial->hSerial, &serial->timeouts); 252 | 253 | /* return only when the buffer contains the exact amount of data requested */ 254 | while (remaining > 0) { 255 | 256 | /* read the next bit of data */ 257 | if (!ReadFile(serial->hSerial, ptr, remaining, &dwBytes, NULL)) { 258 | printf("Error reading port\n"); 259 | ShowLastError(); 260 | return -1; 261 | } 262 | 263 | /* check for a timeout */ 264 | if (dwBytes == 0) { 265 | //printf("Timeout %d %d\n", len, remaining); 266 | return -1; 267 | } 268 | 269 | /* update the buffer pointer */ 270 | remaining -= dwBytes; 271 | ptr += dwBytes; 272 | } 273 | 274 | /* return the full size of the buffer */ 275 | return len; 276 | } 277 | 278 | static void ShowLastError(void) 279 | { 280 | LPVOID lpMsgBuf; 281 | FormatMessage( 282 | FORMAT_MESSAGE_ALLOCATE_BUFFER | 283 | FORMAT_MESSAGE_FROM_SYSTEM | 284 | FORMAT_MESSAGE_IGNORE_INSERTS, 285 | NULL, 286 | GetLastError(), 287 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 288 | (LPTSTR)&lpMsgBuf, 289 | 0, NULL); 290 | printf(" %s\n", (char *)lpMsgBuf); 291 | LocalFree(lpMsgBuf); 292 | } 293 | 294 | /* escape from terminal mode */ 295 | #define ESC 0x1b 296 | 297 | /* 298 | * if "check_for_exit" is true, then 299 | * a sequence EXIT_CHAR 00 nn indicates that we should exit 300 | */ 301 | #define EXIT_CHAR 0xff 302 | 303 | void SerialTerminal(SERIAL *serial, int check_for_exit, int pst_mode) 304 | { 305 | int sawexit_char = 0; 306 | int sawexit_valid = 0; 307 | int exitcode = 0; 308 | int continue_terminal = 1; 309 | 310 | while (continue_terminal) { 311 | uint8_t buf[1]; 312 | if (ReceiveSerialDataTimeout(serial, buf, 1, 0) != -1) { 313 | if (sawexit_valid) { 314 | exitcode = buf[0]; 315 | continue_terminal = 0; 316 | } 317 | else if (sawexit_char) { 318 | if (buf[0] == 0) { 319 | sawexit_valid = 1; 320 | } else { 321 | putchar(EXIT_CHAR); 322 | putchar(buf[0]); 323 | fflush(stdout); 324 | } 325 | } 326 | else if (check_for_exit && buf[0] == EXIT_CHAR) { 327 | sawexit_char = 1; 328 | } 329 | else { 330 | putchar(buf[0]); 331 | if (pst_mode && buf[0] == '\r') 332 | putchar('\n'); 333 | fflush(stdout); 334 | } 335 | } 336 | else if (kbhit()) { 337 | if ((buf[0] = getch()) == ESC) 338 | break; 339 | SendSerialData(serial, buf, 1); 340 | } 341 | } 342 | 343 | if (check_for_exit && sawexit_valid) { 344 | exit(exitcode); 345 | } 346 | } 347 | 348 | #if 0 349 | 350 | HANDLE hComm; 351 | hComm = CreateFile( gszPort, 352 | GENERIC_READ | GENERIC_WRITE, 353 | 0, 354 | 0, 355 | OPEN_EXISTING, 356 | FILE_FLAG_OVERLAPPED, 357 | 0); 358 | if (hComm == INVALID_HANDLE_VALUE) 359 | // error opening port; abort 360 | 361 | DWORD dwRead; 362 | BOOL fWaitingOnRead = FALSE; 363 | OVERLAPPED osReader = {0}; 364 | 365 | // Create the overlapped event. Must be closed before exiting 366 | // to avoid a handle leak. 367 | osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 368 | 369 | if (osReader.hEvent == NULL) 370 | // Error creating overlapped event; abort. 371 | 372 | if (!fWaitingOnRead) { 373 | // Issue read operation. 374 | if (!ReadFile(hComm, lpBuf, READ_BUF_SIZE, &dwRead, &osReader)) { 375 | if (GetLastError() != ERROR_IO_PENDING) // read not delayed? 376 | // Error in communications; report it. 377 | else 378 | fWaitingOnRead = TRUE; 379 | } 380 | else { 381 | // read completed immediately 382 | HandleASuccessfulRead(lpBuf, dwRead); 383 | } 384 | } 385 | 386 | #define READ_TIMEOUT 500 // milliseconds 387 | 388 | DWORD dwRes; 389 | 390 | if (fWaitingOnRead) { 391 | dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT); 392 | switch(dwRes) 393 | { 394 | // Read completed. 395 | case WAIT_OBJECT_0: 396 | if (!GetOverlappedResult(hComm, &osReader, &dwRead, FALSE)) 397 | // Error in communications; report it. 398 | else 399 | // Read completed successfully. 400 | HandleASuccessfulRead(lpBuf, dwRead); 401 | 402 | // Reset flag so that another opertion can be issued. 403 | fWaitingOnRead = FALSE; 404 | break; 405 | 406 | case WAIT_TIMEOUT: 407 | // Operation isn't complete yet. fWaitingOnRead flag isn't 408 | // changed since I'll loop back around, and I don't want 409 | // to issue another read until the first one finishes. 410 | // 411 | // This is a good time to do some background work. 412 | break; 413 | 414 | default: 415 | // Error in the WaitForSingleObject; abort. 416 | // This indicates a problem with the OVERLAPPED structure's 417 | // event handle. 418 | break; 419 | } 420 | } 421 | 422 | #endif 423 | -------------------------------------------------------------------------------- /src/serial_posix.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file osint_linux.c 3 | * 4 | * Based on: Serial I/O functions used by PLoadLib.c 5 | * 6 | * Copyright (c) 2009 by John Steven Denson 7 | * Modified in 2011 by David Michael Betz 8 | * Modified in 2015 by David Michael Betz 9 | * 10 | * MIT License 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | * 30 | */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | 49 | #include "serial.h" 50 | #include "proploader.h" 51 | #ifdef RASPBERRY_PI 52 | #include "gpio_sysfs.h" 53 | #define DEFAULT_GPIO_PIN 17 54 | #define DEFAULT_GPIO_LEVEL 0 55 | #endif 56 | struct SERIAL { 57 | struct termios oldParams; 58 | reset_method_t resetMethod; 59 | #ifdef RASPBERRY_PI 60 | int resetGpioPin; 61 | int resetGpioLevel; 62 | #endif 63 | int fd; 64 | }; 65 | 66 | static void msleep(int ms) 67 | { 68 | usleep(ms * 1000); 69 | } 70 | 71 | static void chk(char *fun, int sts) 72 | { 73 | if (sts != 0) 74 | message("%s failed", fun); 75 | } 76 | 77 | int SerialUseResetMethod(SERIAL *serial, const char *method) 78 | { 79 | if (strcasecmp(method, "dtr") == 0) 80 | serial->resetMethod = RESET_WITH_DTR; 81 | else if (strcasecmp(method, "rts") == 0) 82 | serial->resetMethod = RESET_WITH_RTS; 83 | #ifdef RASPBERRY_PI 84 | else if (strncasecmp(method, "gpio", 4) == 0) 85 | { 86 | serial->resetMethod = RESET_WITH_GPIO; 87 | serial->resetGpioPin = DEFAULT_GPIO_PIN; 88 | serial->resetGpioLevel = DEFAULT_GPIO_LEVEL; 89 | 90 | char *writeableMethod = strdup(method); 91 | if (!writeableMethod) 92 | { 93 | printf("insufficient memory to parse gpio option\n"); 94 | return -1; 95 | } 96 | 97 | char *token; 98 | if (isdigit(writeableMethod[4])) 99 | { 100 | serial->resetGpioPin = atoi(&writeableMethod[4]); 101 | token = strtok(writeableMethod, ","); // skip over 'gpio' 102 | if (token) 103 | { 104 | token = strtok(NULL, ""); 105 | } 106 | } 107 | else { 108 | token = strtok(writeableMethod, ","); // skip over 'gpio' 109 | if (token) 110 | { 111 | token = strtok(NULL, ","); 112 | if (token) 113 | { 114 | serial->resetGpioPin = atoi(token); 115 | token = strtok(NULL, ""); 116 | } 117 | } 118 | } 119 | 120 | if (token) 121 | { 122 | serial->resetGpioLevel = atoi(token); 123 | } 124 | 125 | free(writeableMethod); 126 | 127 | printf ("Using GPIO pin %d as Propeller reset, active %s\n", serial->resetGpioPin, serial->resetGpioLevel ? "HIGH" : "LOW"); 128 | gpio_export(serial->resetGpioPin); 129 | gpio_write(serial->resetGpioPin, serial->resetGpioLevel ^ 1); 130 | gpio_direction(serial->resetGpioPin, 1); 131 | } 132 | #endif 133 | else { 134 | return -1; 135 | } 136 | return 0; 137 | } 138 | 139 | int OpenSerial(const char *port, int baud, SERIAL **pSerial) 140 | { 141 | struct termios sparams; 142 | SERIAL *serial; 143 | int sts; 144 | 145 | /* allocate a serial state structure */ 146 | if (!(serial = (SERIAL *)malloc(sizeof(SERIAL)))) 147 | return -2; 148 | 149 | /* initialize the state structure */ 150 | memset(serial, 0, sizeof(SERIAL)); 151 | #ifdef RASPBERRY_PI 152 | serial->resetMethod = RESET_WITH_GPIO; 153 | serial->resetGpioPin = DEFAULT_GPIO_PIN; 154 | serial->resetGpioLevel = DEFAULT_GPIO_LEVEL; 155 | #else 156 | serial->resetMethod = RESET_WITH_DTR; 157 | #endif 158 | serial->fd = -1; 159 | 160 | /* open the port */ 161 | #ifdef MACOSX 162 | serial->fd = open(port, O_RDWR | O_NOCTTY | O_NONBLOCK); 163 | #else 164 | serial->fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); 165 | #endif 166 | if(serial->fd == -1) { 167 | message("Can't open '%s' -- %s", port, strerror(errno)); 168 | free(serial); 169 | return -3; 170 | } 171 | 172 | #if 0 173 | signal(SIGINT, sigint_handler); 174 | #endif 175 | 176 | /* set the terminal to exclusive mode */ 177 | if (ioctl(serial->fd, TIOCEXCL) != 0) { 178 | message("Can't open terminal for exclusive access"); 179 | close(serial->fd); 180 | free(serial); 181 | return -4; 182 | } 183 | 184 | fcntl(serial->fd, F_SETFL, 0); 185 | 186 | /* set the baud rate */ 187 | if ((sts = SetSerialBaud(serial, baud)) != 0) { 188 | close(serial->fd); 189 | free(serial); 190 | return sts; 191 | } 192 | 193 | /* get the current options */ 194 | chk("tcgetattr", tcgetattr(serial->fd, &serial->oldParams)); 195 | sparams = serial->oldParams; 196 | 197 | /* set raw input */ 198 | #ifdef MACOSX 199 | cfmakeraw(&sparams); 200 | sparams.c_cc[VTIME] = 0; 201 | sparams.c_cc[VMIN] = 1; 202 | #else 203 | memset(&sparams, 0, sizeof(sparams)); 204 | sparams.c_cflag = CS8 | CLOCAL | CREAD; 205 | sparams.c_lflag = 0; // &= ~(ICANON | ECHO | ECHOE | ISIG); 206 | sparams.c_oflag = 0; // &= ~OPOST; 207 | 208 | sparams.c_iflag = IGNPAR | IGNBRK; 209 | sparams.c_cc[VTIME] = 0; 210 | sparams.c_cc[VMIN] = 1; 211 | #endif 212 | 213 | /* set the options */ 214 | chk("tcflush", tcflush(serial->fd, TCIFLUSH)); 215 | chk("tcsetattr", tcsetattr(serial->fd, TCSANOW, &sparams)); 216 | 217 | /* return the serial state structure */ 218 | *pSerial = serial; 219 | return 0; 220 | } 221 | 222 | void CloseSerial(SERIAL *serial) 223 | { 224 | if (serial->fd != -1) { 225 | tcflush(serial->fd, TCIOFLUSH); 226 | tcsetattr(serial->fd, TCSANOW, &serial->oldParams); 227 | ioctl(serial->fd, TIOCNXCL); 228 | close(serial->fd); 229 | } 230 | free(serial); 231 | } 232 | 233 | int SetSerialBaud(SERIAL *serial, int baud) 234 | { 235 | struct termios sparams; 236 | int tbaud = 0; 237 | 238 | switch(baud) { 239 | case 0: // default 240 | tbaud = B115200; 241 | break; 242 | #ifdef B921600 243 | case 921600: 244 | tbaud = B921600; 245 | break; 246 | #endif 247 | #ifdef B576000 248 | case 576000: 249 | tbaud = B576000; 250 | break; 251 | #endif 252 | #ifdef B500000 253 | case 500000: 254 | tbaud = B500000; 255 | break; 256 | #endif 257 | #ifdef B460800 258 | case 460800: 259 | tbaud = B460800; 260 | break; 261 | #endif 262 | #ifdef B230400 263 | case 230400: 264 | tbaud = B230400; 265 | break; 266 | #endif 267 | case 115200: 268 | tbaud = B115200; 269 | break; 270 | case 57600: 271 | tbaud = B57600; 272 | break; 273 | case 38400: 274 | tbaud = B38400; 275 | break; 276 | case 19200: 277 | tbaud = B19200; 278 | break; 279 | case 9600: 280 | tbaud = B9600; 281 | break; 282 | default: 283 | tbaud = baud; break; 284 | printf("Unsupported baudrate. Use "); 285 | #ifdef B921600 286 | printf("921600, "); 287 | #endif 288 | #ifdef B576000 289 | printf("576000, "); 290 | #endif 291 | #ifdef B500000 292 | printf("500000, "); 293 | #endif 294 | #ifdef B460800 295 | printf("460800, "); 296 | #endif 297 | #ifdef B230400 298 | printf("230400, "); 299 | #endif 300 | printf("115200, 57600, 38400, 19200, or 9600\n"); 301 | return -1; 302 | } 303 | 304 | /* get the current options */ 305 | chk("tcgetattr", tcgetattr(serial->fd, &sparams)); 306 | 307 | /* set raw input */ 308 | #ifdef MACOSX 309 | chk("cfsetspeed", cfsetspeed(&sparams, tbaud)); 310 | #else 311 | chk("cfsetispeed", cfsetispeed(&sparams, tbaud)); 312 | chk("cfsetospeed", cfsetospeed(&sparams, tbaud)); 313 | #endif 314 | 315 | /* set the options */ 316 | chk("tcflush", tcflush(serial->fd, TCIFLUSH)); 317 | chk("tcsetattr", tcsetattr(serial->fd, TCSANOW, &sparams)); 318 | 319 | return 0; 320 | } 321 | 322 | int SerialGenerateResetSignal(SERIAL *serial) 323 | { 324 | int cmd; 325 | 326 | /* assert the reset signal */ 327 | switch (serial->resetMethod) { 328 | case RESET_WITH_DTR: 329 | cmd = TIOCM_DTR; 330 | ioctl(serial->fd, TIOCMBIS, &cmd); /* set bit */ 331 | break; 332 | case RESET_WITH_RTS: 333 | cmd = TIOCM_RTS; 334 | ioctl(serial->fd, TIOCMBIS, &cmd); /* set bit */ 335 | break; 336 | #ifdef RASPBERRY_PI 337 | case RESET_WITH_GPIO: 338 | gpio_write(serial->resetGpioPin, serial->resetGpioLevel); 339 | break; 340 | #endif 341 | default: 342 | // should be reached 343 | break; 344 | } 345 | 346 | msleep(10); 347 | 348 | /* deassert the reset signal */ 349 | switch (serial->resetMethod) { 350 | case RESET_WITH_DTR: 351 | cmd = TIOCM_DTR; 352 | ioctl(serial->fd, TIOCMBIC, &cmd); /* clear bit */ 353 | break; 354 | case RESET_WITH_RTS: 355 | cmd = TIOCM_RTS; 356 | ioctl(serial->fd, TIOCMBIC, &cmd); /* clear bit */ 357 | break; 358 | #ifdef RASPBERRY_PI 359 | case RESET_WITH_GPIO: 360 | gpio_write(serial->resetGpioPin, serial->resetGpioLevel ^ 1); 361 | break; 362 | #endif 363 | default: 364 | // should be reached 365 | break; 366 | } 367 | 368 | msleep(100); 369 | 370 | /* flush any pending input */ 371 | tcflush(serial->fd, TCIFLUSH); 372 | 373 | return 0; 374 | } 375 | 376 | int SendSerialData(SERIAL *serial, const void *buf, int len) 377 | { 378 | int cnt; 379 | cnt = write(serial->fd, buf, len); 380 | if (cnt != len) { 381 | message("Error writing port"); 382 | return -1; 383 | } 384 | return cnt; 385 | } 386 | 387 | int FlushSerialData(SERIAL *serial) 388 | { 389 | return tcdrain(serial->fd); 390 | } 391 | 392 | int ReceiveSerialData(SERIAL *serial, void *buf, int len) 393 | { 394 | int cnt = read(serial->fd, buf, len); 395 | if (cnt < 1) { 396 | message("Error reading port"); 397 | return -1; 398 | } 399 | return cnt; 400 | } 401 | 402 | int ReceiveSerialDataTimeout(SERIAL *serial, void *buf, int len, int timeout) 403 | { 404 | ssize_t bytes = 0; 405 | struct timeval toval; 406 | fd_set set; 407 | 408 | /* initialize the socket set */ 409 | FD_ZERO(&set); 410 | FD_SET(serial->fd, &set); 411 | 412 | /* setup the timeout */ 413 | toval.tv_sec = timeout / 1000; 414 | toval.tv_usec = (timeout % 1000) * 1000; 415 | 416 | /* wait for data to be available on the port */ 417 | if (select(serial->fd + 1, &set, NULL, NULL, &toval) <= 0) 418 | return -1; 419 | 420 | /* read the incoming data */ 421 | if (FD_ISSET(serial->fd, &set)) 422 | bytes = read(serial->fd, buf, len); 423 | 424 | return (int)(bytes > 0 ? bytes : -1); 425 | } 426 | 427 | int ReceiveSerialDataExactTimeout(SERIAL *serial, void *buf, int len, int timeout) 428 | { 429 | uint8_t *ptr = (uint8_t *)buf; 430 | int remaining = len; 431 | int cnt = 0; 432 | struct timeval toval; 433 | fd_set set; 434 | 435 | /* return only when the buffer contains the exact amount of data requested */ 436 | while (remaining > 0) { 437 | 438 | /* initialize the socket set */ 439 | FD_ZERO(&set); 440 | FD_SET(serial->fd, &set); 441 | 442 | /* setup the timeout */ 443 | toval.tv_sec = timeout / 1000; 444 | toval.tv_usec = (timeout % 1000) * 1000; 445 | 446 | /* wait for data to be available on the port */ 447 | if (select(serial->fd + 1, &set, NULL, NULL, &toval) <= 0) 448 | return -1; 449 | 450 | /* read the incoming data */ 451 | if (FD_ISSET(serial->fd, &set)) { 452 | 453 | /* read the next bit of data */ 454 | if ((cnt = read(serial->fd, ptr, remaining)) < 0) 455 | return -1; 456 | 457 | /* update the buffer pointer */ 458 | remaining -= cnt; 459 | ptr += cnt; 460 | } 461 | } 462 | 463 | /* return the full size of the buffer */ 464 | return len; 465 | } 466 | 467 | static int CheckPrefix(const char *prefix) 468 | { 469 | #if defined(LINUX) 470 | #ifdef RASPBERRY_PI 471 | return strncmp(prefix, "serial", 6) == 0; 472 | #else 473 | return strncmp(prefix, "ttyUSB", 6) == 0; 474 | #endif 475 | #elif defined(MACOSX) 476 | return strncmp(prefix, "cu.", 3) == 0 && strstr(prefix, "Bluetooth") == NULL; 477 | #else 478 | return 1; 479 | #endif 480 | } 481 | 482 | int SerialFind(int (*check)(const char *port, void *data), void *data) 483 | { 484 | char path[PATH_MAX]; 485 | struct dirent *entry; 486 | DIR *dirp; 487 | 488 | if (!(dirp = opendir("/dev"))) 489 | return -1; 490 | 491 | while ((entry = readdir(dirp)) != NULL) { 492 | if (CheckPrefix(entry->d_name)) { 493 | sprintf(path, "/dev/%s", entry->d_name); 494 | if ((*check)(path, data) == 0) { 495 | closedir(dirp); 496 | return 0; 497 | } 498 | } 499 | } 500 | 501 | closedir(dirp); 502 | return -1; 503 | } 504 | 505 | #if 0 506 | 507 | static void sigint_handler(int signum) 508 | { 509 | serial_done(); 510 | exit(1); 511 | } 512 | 513 | #endif 514 | 515 | #define ESC 0x1b /* escape from terminal mode */ 516 | 517 | /** 518 | * simple terminal emulator 519 | */ 520 | void SerialTerminal(SERIAL *serial, int check_for_exit, int pst_mode) 521 | { 522 | struct termios oldt, newt; 523 | char buf[128], realbuf[256]; // double in case buf is filled with \r in PST mode 524 | ssize_t cnt; 525 | fd_set set; 526 | int exit_char = 0xdead; /* not a valid character */ 527 | int sawexit_char = 0; 528 | int sawexit_valid = 0; 529 | int exitcode = 0; 530 | int continue_terminal = 1; 531 | 532 | tcgetattr(STDIN_FILENO, &oldt); 533 | newt = oldt; 534 | newt.c_lflag &= ~(ICANON | ECHO | ISIG); 535 | newt.c_iflag &= ~(ICRNL | INLCR); 536 | newt.c_oflag &= ~OPOST; 537 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 538 | 539 | if (check_for_exit) 540 | exit_char = 0xff; 541 | 542 | #if 0 543 | /* make it possible to detect breaks */ 544 | tcgetattr(serial->fd, &newt); 545 | newt.c_iflag &= ~IGNBRK; 546 | newt.c_iflag |= PARMRK; 547 | tcsetattr(serial->fd, TCSANOW, &newt); 548 | #endif 549 | 550 | do { 551 | FD_ZERO(&set); 552 | FD_SET(serial->fd, &set); 553 | FD_SET(STDIN_FILENO, &set); 554 | if (select(serial->fd + 1, &set, NULL, NULL, NULL) > 0) { 555 | if (FD_ISSET(serial->fd, &set)) { 556 | if ((cnt = read(serial->fd, buf, sizeof(buf))) > 0) { 557 | int i; 558 | // check for breaks 559 | ssize_t realbytes = 0; 560 | for (i = 0; i < cnt; i++) { 561 | if (sawexit_valid) 562 | { 563 | exitcode = buf[i]; 564 | continue_terminal = 0; 565 | } 566 | else if (sawexit_char) { 567 | if (buf[i] == 0) { 568 | sawexit_valid = 1; 569 | } else { 570 | realbuf[realbytes++] = exit_char; 571 | realbuf[realbytes++] = buf[i]; 572 | sawexit_char = 0; 573 | } 574 | } else if (((int)buf[i] & 0xff) == exit_char) { 575 | sawexit_char = 1; 576 | } else { 577 | realbuf[realbytes++] = buf[i]; 578 | if (pst_mode && buf[i] == '\r') 579 | realbuf[realbytes++] = '\n'; 580 | } 581 | } 582 | write(fileno(stdout), realbuf, realbytes); 583 | } 584 | } 585 | if (FD_ISSET(STDIN_FILENO, &set)) { 586 | if ((cnt = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { 587 | int i; 588 | for (i = 0; i < cnt; ++i) { 589 | if (buf[i] == ESC) 590 | goto done; 591 | } 592 | write(serial->fd, buf, cnt); 593 | } 594 | } 595 | } 596 | } while (continue_terminal); 597 | 598 | done: 599 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 600 | 601 | if (sawexit_valid) 602 | exit(exitcode); 603 | } 604 | -------------------------------------------------------------------------------- /src/serialpropconnection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "serialpropconnection.h" 3 | #include "messages.h" 4 | 5 | #define CALIBRATE_DELAY 10 6 | 7 | SerialPropConnection::SerialPropConnection() 8 | : m_serialPort(NULL) 9 | { 10 | } 11 | 12 | SerialPropConnection::~SerialPropConnection() 13 | { 14 | close(); 15 | } 16 | 17 | // this is in serialloader.cpp 18 | // int SerialPropConnection::loadImage(const uint8_t *image, int imageSize, LoadType loadType); 19 | 20 | struct FindState { 21 | SerialInfoList *list; 22 | int count; 23 | }; 24 | 25 | int SerialPropConnection::addPort(const char *port, void *data) 26 | { 27 | FindState *state = (FindState *)data; 28 | 29 | SerialInfo info(port); 30 | state->list->push_back(info); 31 | 32 | return state->count < 0 || --state->count > 0; 33 | } 34 | 35 | int SerialPropConnection::findPorts(bool check, SerialInfoList &list, int count) 36 | { 37 | FindState state; 38 | state.list = &list; 39 | state.count = count; 40 | SerialFind(addPort, &state); 41 | return 0; 42 | } 43 | 44 | int SerialPropConnection::open(const char *port, int baudRate) 45 | { 46 | if (isOpen()) 47 | return -1; 48 | 49 | if (OpenSerial(port, baudRate, &m_serialPort) != 0) 50 | return -1; 51 | 52 | setPortName(port); 53 | m_baudRate = baudRate; 54 | 55 | return 0; 56 | } 57 | 58 | bool SerialPropConnection::isOpen() 59 | { 60 | return m_serialPort ? true : false; 61 | } 62 | 63 | int SerialPropConnection::close() 64 | { 65 | if (!isOpen()) 66 | return -1; 67 | 68 | CloseSerial(m_serialPort); 69 | m_serialPort = NULL; 70 | 71 | return 0; 72 | } 73 | 74 | int SerialPropConnection::connect() 75 | { 76 | return isOpen() ? 0 : -1; 77 | } 78 | 79 | int SerialPropConnection::disconnect() 80 | { 81 | return 0; 82 | } 83 | 84 | int SerialPropConnection::setResetMethod(const char *method) 85 | { 86 | if (SerialUseResetMethod(m_serialPort, method) != 0) 87 | return -1; 88 | return 0; 89 | } 90 | 91 | int SerialPropConnection::generateResetSignal() 92 | { 93 | if (!isOpen()) 94 | return -1; 95 | SerialGenerateResetSignal(m_serialPort); 96 | return 0; 97 | } 98 | 99 | int SerialPropConnection::sendData(const uint8_t *buf, int len) 100 | { 101 | if (!isOpen()) 102 | return -1; 103 | return SendSerialData(m_serialPort, buf, len); 104 | } 105 | 106 | int SerialPropConnection::receiveDataTimeout(uint8_t *buf, int len, int timeout) 107 | { 108 | if (!isOpen()) 109 | return -1; 110 | return ReceiveSerialDataTimeout(m_serialPort, buf, len, timeout); 111 | } 112 | 113 | int SerialPropConnection::receiveDataExactTimeout(uint8_t *buf, int len, int timeout) 114 | { 115 | if (!isOpen()) 116 | return -1; 117 | return ReceiveSerialDataExactTimeout(m_serialPort, buf, len, timeout); 118 | } 119 | 120 | int SerialPropConnection::receiveChecksumAck(int byteCount, int delay) 121 | { 122 | static uint8_t calibrate[1] = { 0xF9 }; 123 | int msSendTime = (byteCount * 10 * 1000) / m_baudRate; 124 | int retries = (msSendTime / CALIBRATE_DELAY) | (delay / CALIBRATE_DELAY); 125 | uint8_t buf[1]; 126 | int cnt; 127 | 128 | do { 129 | sendData(calibrate, sizeof(calibrate)); 130 | cnt = receiveDataExactTimeout(buf, 1, CALIBRATE_DELAY); 131 | if (cnt == 1) 132 | return buf[0] == 0xFE ? 0 : -1; 133 | } while (--retries >= 0); 134 | 135 | return -1; 136 | } 137 | 138 | int SerialPropConnection::setBaudRate(int baudRate) 139 | { 140 | if (baudRate != m_baudRate) { 141 | FlushSerialData(m_serialPort); 142 | if (SetSerialBaud(m_serialPort, baudRate) != 0) 143 | return -1; 144 | m_baudRate = baudRate; 145 | } 146 | return 0; 147 | } 148 | 149 | int SerialPropConnection::terminal(bool checkForExit, bool pstMode) 150 | { 151 | SerialTerminal(m_serialPort, checkForExit, pstMode); 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /src/serialpropconnection.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIALPROPCONNECTION_H 2 | #define SERIALPROPCONNECTION_H 3 | 4 | #include 5 | #include 6 | #include "propconnection.h" 7 | #include "serial.h" 8 | 9 | class SerialInfo { 10 | public: 11 | SerialInfo() {} 12 | SerialInfo(std::string port) : m_port(port) {} 13 | const char *port() { return m_port.c_str(); } 14 | private: 15 | std::string m_port; 16 | }; 17 | 18 | typedef std::list SerialInfoList; 19 | 20 | class SerialPropConnection : public PropConnection 21 | { 22 | public: 23 | SerialPropConnection(); 24 | ~SerialPropConnection(); 25 | int open(const char *port, int baudRate); 26 | bool isOpen(); 27 | int close(); 28 | int connect(); 29 | int disconnect(); 30 | int setResetMethod(const char *method); 31 | int generateResetSignal(); 32 | int identify(int *pVersion); 33 | int loadImage(const uint8_t *image, int imageSize, uint8_t *response, int responseSize); 34 | int loadImage(const uint8_t *image, int imageSize, LoadType loadType = ltDownloadAndRun, int info = false); 35 | int sendData(const uint8_t *buf, int len); 36 | int receiveDataTimeout(uint8_t *buf, int len, int timeout); 37 | int receiveDataExactTimeout(uint8_t *buf, int len, int timeout); 38 | int setBaudRate(int baudRate); 39 | int maxDataSize() { return 1024; } 40 | int terminal(bool checkForExit, bool pstMode); 41 | static int findPorts(bool check, SerialInfoList &list, int count = -1); 42 | private: 43 | int receiveChecksumAck(int byteCount, int delay); 44 | static int addPort(const char *port, void *data); 45 | SERIAL *m_serialPort; 46 | }; 47 | 48 | #endif // SERIALPROPELLERCONNECTION_H 49 | -------------------------------------------------------------------------------- /src/sock.h: -------------------------------------------------------------------------------- 1 | #ifndef __SOCK_H__ 2 | #define __SOCK_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | /* for windows builds */ 9 | #ifdef __MINGW32__ 10 | #include 11 | #include 12 | #include 13 | typedef unsigned long in_addr_t; 14 | 15 | /* for linux and mac builds */ 16 | #else 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #define IN_ADDR struct in_addr 27 | #define SOCKADDR_IN struct sockaddr_in 28 | #define SOCKADDR struct sockaddr 29 | #define HOSTENT struct hostent 30 | #define closesocket(x) close(x) 31 | typedef int SOCKET; 32 | #define INVALID_SOCKET -1 33 | #endif 34 | 35 | typedef struct { 36 | SOCKADDR_IN addr; 37 | SOCKADDR_IN mask; 38 | SOCKADDR_IN bcast; 39 | } IFADDR; 40 | 41 | int GetInterfaceAddresses(IFADDR *addrs, int max); 42 | int GetInternetAddress(const char *hostName, short port, SOCKADDR_IN *addr); 43 | const char *AddrToString(uint32_t addr); 44 | int StringToAddr(const char *addr, uint32_t *pAddr); 45 | int OpenBroadcastSocket(short port, SOCKET *pSocket); 46 | int ConnectSocket(SOCKADDR_IN *addr, SOCKET *pSocket); 47 | int ConnectSocketTimeout(SOCKADDR_IN *addr, int timeout, SOCKET *pSocket); 48 | int BindSocket(short port, SOCKET *pSocket); 49 | void CloseSocket(SOCKET sock); 50 | int SocketDataAvailableP(SOCKET sock, int timeout); 51 | int SendSocketData(SOCKET sock, const void *buf, int len); 52 | int ReceiveSocketData(SOCKET sock, void *buf, int len); 53 | int ReceiveSocketDataTimeout(SOCKET sock, void *buf, int len, int timeout); 54 | int ReceiveSocketDataExactTimeout(SOCKET sock, void *buf, int len, int timeout); 55 | int SendSocketDataTo(SOCKET sock, const void *buf, int len, SOCKADDR_IN *addr); 56 | int ReceiveSocketDataFrom(SOCKET sock, void *buf, int len, SOCKADDR_IN *addr); 57 | int ReceiveSocketDataAndAddress(SOCKET sock, void *buf, int len, SOCKADDR_IN *addr); 58 | const char *AddressToString(SOCKADDR_IN *addr); 59 | void SocketTerminal(SOCKET sock, int check_for_exit, int pst_mode); 60 | 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/sock_posix.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #ifdef __MINGW32__ 9 | #include 10 | #include 11 | #include 12 | #else 13 | #include 14 | #include 15 | #endif 16 | 17 | #include "sock.h" 18 | 19 | #ifdef __MINGW32__ 20 | 21 | static int socketsInitialized = FALSE; 22 | 23 | static int InitWinSock(void) 24 | { 25 | WSADATA WsaData; 26 | 27 | /* initialize winsock */ 28 | if (!socketsInitialized) { 29 | if (WSAStartup(0x0101, &WsaData) != 0) 30 | return -1; 31 | socketsInitialized = TRUE; 32 | } 33 | 34 | return 0; 35 | } 36 | 37 | #endif 38 | 39 | /* GetInternetAddress - get an internet address */ 40 | int GetInternetAddress(const char *hostName, short port, SOCKADDR_IN *addr) 41 | { 42 | HOSTENT *hostEntry; 43 | 44 | #ifdef __MINGW32__ 45 | if (InitWinSock() != 0) 46 | return -1; 47 | #endif 48 | 49 | /* setup the address */ 50 | memset(addr, 0, sizeof(SOCKADDR_IN)); 51 | addr->sin_family = AF_INET; 52 | addr->sin_port = htons(port); 53 | 54 | /* get the host address by address in dot notation */ 55 | if (isdigit(hostName[0])) { 56 | unsigned long hostAddr = inet_addr(hostName); 57 | addr->sin_addr = *(IN_ADDR *)&hostAddr; 58 | } 59 | 60 | /* get the host address by name */ 61 | else { 62 | if ((hostEntry = gethostbyname(hostName)) == NULL) 63 | return -1; 64 | addr->sin_addr = *(IN_ADDR *)*hostEntry->h_addr_list; 65 | } 66 | 67 | /* return successfully */ 68 | return 0; 69 | } 70 | 71 | /* OpenBroadcastSocket - open a broadcast socket */ 72 | int OpenBroadcastSocket(short port, SOCKET *pSocket) 73 | { 74 | int broadcast = 1; 75 | SOCKADDR_IN addr; 76 | SOCKET sock; 77 | 78 | #ifdef __MINGW32__ 79 | if (InitWinSock() != 0) 80 | return INVALID_SOCKET; 81 | #endif 82 | 83 | /* create the socket */ 84 | if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 85 | return -1; 86 | 87 | /* enable broadcasting */ 88 | if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (void *)&broadcast, sizeof(broadcast)) != 0) { 89 | CloseSocket(sock); 90 | return -1; 91 | } 92 | 93 | /* setup the address */ 94 | memset(&addr, 0, sizeof(addr)); 95 | addr.sin_family = AF_INET; 96 | addr.sin_addr.s_addr = INADDR_ANY; 97 | addr.sin_port = htons(port); 98 | 99 | /* bind the socket to the port */ 100 | if (bind(sock, (SOCKADDR *)&addr, sizeof(addr)) != 0) { 101 | closesocket(sock); 102 | return -1; 103 | } 104 | 105 | /* return the socket */ 106 | *pSocket = sock; 107 | return 0; 108 | } 109 | 110 | /* ConnectSocket - connect to a server */ 111 | int ConnectSocket(SOCKADDR_IN *addr, SOCKET *pSocket) 112 | { 113 | SOCKET sock; 114 | 115 | #ifdef __MINGW32__ 116 | if (InitWinSock() != 0) 117 | return INVALID_SOCKET; 118 | #endif 119 | 120 | /* create the socket */ 121 | if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 122 | return -1; 123 | 124 | /* connect to the server */ 125 | if (connect(sock, (SOCKADDR *)addr, sizeof(*addr)) != 0) { 126 | closesocket(sock); 127 | return -1; 128 | } 129 | 130 | /* return the socket */ 131 | *pSocket = sock; 132 | return 0; 133 | } 134 | 135 | /* ConnectSocketTimeout - connect to a server with a timeout */ 136 | int ConnectSocketTimeout(SOCKADDR_IN *addr, int timeout, SOCKET *pSocket) 137 | { 138 | #ifdef __MINGW32__ 139 | return ConnectSocket(addr, pSocket); 140 | #else 141 | int flags, err; 142 | SOCKET sock; 143 | 144 | #ifdef __MINGW32__ 145 | if (InitWinSock() != 0) 146 | return INVALID_SOCKET; 147 | #endif 148 | 149 | /* create the socket */ 150 | if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 151 | return -1; 152 | 153 | /* set the socket to non-blocking mode */ 154 | flags = fcntl(sock, F_GETFL, 0); 155 | if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) != 0) { 156 | closesocket(sock); 157 | return -1; 158 | } 159 | 160 | /* connect to the server */ 161 | if (connect(sock, (SOCKADDR *)addr, sizeof(*addr)) != 0) { 162 | struct timeval timeVal; 163 | socklen_t optLen; 164 | fd_set sockets; 165 | 166 | /* fail on any error other than "in progress" */ 167 | if (errno != EINPROGRESS) { 168 | closesocket(sock); 169 | return -1; 170 | } 171 | 172 | /* setup the read socket set */ 173 | FD_ZERO(&sockets); 174 | FD_SET(sock, &sockets); 175 | 176 | /* setup the timeout */ 177 | timeVal.tv_sec = timeout / 1000; 178 | timeVal.tv_usec = (timeout % 1000) * 1000; 179 | 180 | /* wait for the connect to complete or a timeout */ 181 | if (select(sock + 1, NULL, &sockets, NULL, &timeVal) <= 0 || !FD_ISSET(sock, &sockets)) { 182 | closesocket(sock); 183 | return -1; 184 | } 185 | 186 | /* make sure the connection was successful */ 187 | optLen = sizeof(err); 188 | if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &optLen) != 0 || err != 0) { 189 | closesocket(sock); 190 | return -1; 191 | } 192 | } 193 | 194 | /* set the socket back to blocking mode */ 195 | if (fcntl(sock, F_SETFL, flags) != 0) { 196 | closesocket(sock); 197 | return -1; 198 | } 199 | 200 | /* return the socket */ 201 | *pSocket = sock; 202 | return 0; 203 | #endif 204 | } 205 | 206 | /* BindSocket - bind a socket to a port */ 207 | int BindSocket(short port, SOCKET *pSocket) 208 | { 209 | SOCKADDR_IN addr; 210 | SOCKET sock; 211 | 212 | #ifdef __MINGW32__ 213 | if (InitWinSock() != 0) 214 | return INVALID_SOCKET; 215 | #endif 216 | 217 | /* create the socket */ 218 | if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) 219 | return -1; 220 | 221 | /* setup the address */ 222 | memset(&addr, 0, sizeof(addr)); 223 | addr.sin_family = AF_INET; 224 | addr.sin_addr.s_addr = INADDR_ANY; 225 | addr.sin_port = htons(port); 226 | 227 | /* bind the socket to the port */ 228 | if (bind(sock, (SOCKADDR *)&addr, sizeof(addr)) != 0) { 229 | closesocket(sock); 230 | return -1; 231 | } 232 | 233 | /* return the socket */ 234 | *pSocket = sock; 235 | return 0; 236 | } 237 | 238 | /* CloseSocket - close a socket */ 239 | void CloseSocket(SOCKET sock) 240 | { 241 | struct timeval tv; 242 | fd_set sockets; 243 | char buf[512]; 244 | 245 | tv.tv_sec = 0; 246 | tv.tv_usec = 1000; 247 | 248 | /* wait for the close to complete */ 249 | for (;;) { 250 | FD_ZERO(&sockets); 251 | FD_SET(sock, &sockets); 252 | if (select(sock + 1, &sockets, NULL, NULL, &tv) > 0) { 253 | if (FD_ISSET(sock, &sockets)) 254 | if (recv(sock, buf, sizeof(buf), 0) == 0) 255 | break; 256 | } 257 | else 258 | break; 259 | } 260 | 261 | /* close the socket */ 262 | closesocket(sock); 263 | } 264 | 265 | /* SocketDataAvailableP - check for data being available on a socket */ 266 | int SocketDataAvailableP(SOCKET sock, int timeout) 267 | { 268 | struct timeval timeVal; 269 | fd_set sockets; 270 | int cnt; 271 | 272 | /* setup the read socket set */ 273 | FD_ZERO(&sockets); 274 | FD_SET(sock, &sockets); 275 | 276 | timeVal.tv_sec = timeout / 1000; 277 | timeVal.tv_usec = (timeout % 1000) * 1000; 278 | 279 | /* check for data available */ 280 | cnt = select(sock + 1, &sockets, NULL, NULL, timeout < 0 ? NULL : &timeVal); 281 | 282 | /* return whether data is available */ 283 | return cnt > 0 && FD_ISSET(sock, &sockets); 284 | } 285 | 286 | /* SendSocketData - send socket data */ 287 | int SendSocketData(SOCKET sock, const void *buf, int len) 288 | { 289 | return send(sock, buf, len, 0); 290 | } 291 | 292 | /* SendSocketDataTo - send socket data to a specified address */ 293 | int SendSocketDataTo(SOCKET sock, const void *buf, int len, SOCKADDR_IN *addr) 294 | { 295 | return sendto(sock, buf, len, 0, (SOCKADDR *)addr, sizeof(SOCKADDR)); 296 | } 297 | 298 | /* ReceiveSocketData - receive socket data */ 299 | int ReceiveSocketData(SOCKET sock, void *buf, int len) 300 | { 301 | return recv(sock, buf, len, 0); 302 | } 303 | 304 | /* ReceiveSocketDataTimeout - receive socket data */ 305 | int ReceiveSocketDataTimeout(SOCKET sock, void *buf, int len, int timeout) 306 | { 307 | struct timeval toval; 308 | fd_set set; 309 | 310 | FD_ZERO(&set); 311 | FD_SET(sock, &set); 312 | 313 | toval.tv_sec = timeout / 1000; 314 | toval.tv_usec = (timeout % 1000) * 1000; 315 | 316 | if (select(sock + 1, &set, NULL, NULL, &toval) > 0) { 317 | if (FD_ISSET(sock, &set)) { 318 | int bytes = (int)recv(sock, buf, len, 0); 319 | return bytes; 320 | } 321 | } 322 | 323 | return -1; 324 | } 325 | 326 | /* ReceiveSocketDataExactTimeout - receive an exact amount of socket data */ 327 | int ReceiveSocketDataExactTimeout(SOCKET sock, void *buf, int len, int timeout) 328 | { 329 | return ReceiveSocketDataTimeout(sock, buf, len, timeout); 330 | } 331 | 332 | /* ReceiveSocketDataAndAddress - receive socket data and sender's address */ 333 | int ReceiveSocketDataAndAddress(SOCKET sock, void *buf, int len, SOCKADDR_IN *addr) 334 | { 335 | SOCKADDR x; 336 | #ifdef __MINGW32__ 337 | int xlen = sizeof(x); 338 | #else 339 | unsigned int xlen = sizeof(x); 340 | #endif 341 | int cnt; 342 | if ((cnt = recvfrom(sock, buf, len, 0, &x, &xlen)) < 0 || xlen != sizeof(SOCKADDR_IN)) 343 | return cnt; 344 | *addr = *(SOCKADDR_IN *)&x; 345 | return cnt; 346 | } 347 | 348 | const char *AddressToString(SOCKADDR_IN *addr) 349 | { 350 | return inet_ntoa(addr->sin_addr); 351 | } 352 | 353 | /* escape from terminal mode */ 354 | #define ESC 0x1b 355 | 356 | /* 357 | * if "check_for_exit" is true, then 358 | * a sequence EXIT_CHAR 00 nn indicates that we should exit 359 | */ 360 | #define EXIT_CHAR 0xff 361 | 362 | void SocketTerminal(SOCKET sock, int check_for_exit, int pst_mode) 363 | { 364 | #ifdef __MINGW32__ 365 | int sawexit_char = 0; 366 | int sawexit_valid = 0; 367 | int exitcode = 0; 368 | int continue_terminal = 1; 369 | 370 | while (continue_terminal) { 371 | uint8_t buf[1]; 372 | if (ReceiveSocketDataTimeout(sock, buf, 1, 0) != -1) { 373 | if (sawexit_valid) { 374 | exitcode = buf[0]; 375 | continue_terminal = 0; 376 | } 377 | else if (sawexit_char) { 378 | if (buf[0] == 0) { 379 | sawexit_valid = 1; 380 | } else { 381 | putchar(EXIT_CHAR); 382 | putchar(buf[0]); 383 | fflush(stdout); 384 | } 385 | } 386 | else if (check_for_exit && buf[0] == EXIT_CHAR) { 387 | sawexit_char = 1; 388 | } 389 | else { 390 | putchar(buf[0]); 391 | if (pst_mode && buf[0] == '\r') 392 | putchar('\n'); 393 | fflush(stdout); 394 | } 395 | } 396 | else if (kbhit()) { 397 | if ((buf[0] = getch()) == ESC) 398 | break; 399 | SendSocketData(sock, buf, 1); 400 | } 401 | } 402 | 403 | if (check_for_exit && sawexit_valid) { 404 | exit(exitcode); 405 | } 406 | #else 407 | struct termios oldt, newt; 408 | char buf[128], realbuf[256]; // double in case buf is filled with \r in PST mode 409 | ssize_t cnt; 410 | fd_set set; 411 | int exit_char = 0xdead; /* not a valid character */ 412 | int sawexit_char = 0; 413 | int sawexit_valid = 0; 414 | int exitcode = 0; 415 | int continue_terminal = 1; 416 | 417 | tcgetattr(STDIN_FILENO, &oldt); 418 | newt = oldt; 419 | newt.c_lflag &= ~(ICANON | ECHO | ISIG); 420 | newt.c_iflag &= ~(ICRNL | INLCR); 421 | newt.c_oflag &= ~OPOST; 422 | tcsetattr(STDIN_FILENO, TCSANOW, &newt); 423 | 424 | if (check_for_exit) 425 | exit_char = EXIT_CHAR; 426 | 427 | #if 0 428 | /* make it possible to detect breaks */ 429 | tcgetattr(sock, &newt); 430 | newt.c_iflag &= ~IGNBRK; 431 | newt.c_iflag |= PARMRK; 432 | tcsetattr(sock, TCSANOW, &newt); 433 | #endif 434 | 435 | do { 436 | FD_ZERO(&set); 437 | FD_SET(sock, &set); 438 | FD_SET(STDIN_FILENO, &set); 439 | if (select(sock + 1, &set, NULL, NULL, NULL) > 0) { 440 | if (FD_ISSET(sock, &set)) { 441 | if ((cnt = recv(sock, buf, sizeof(buf), 0)) > 0) { 442 | int i; 443 | // check for breaks 444 | ssize_t realbytes = 0; 445 | for (i = 0; i < cnt; i++) { 446 | if (sawexit_valid) 447 | { 448 | exitcode = buf[i]; 449 | continue_terminal = 0; 450 | } 451 | else if (sawexit_char) { 452 | if (buf[i] == 0) { 453 | sawexit_valid = 1; 454 | } else { 455 | realbuf[realbytes++] = exit_char; 456 | realbuf[realbytes++] = buf[i]; 457 | sawexit_char = 0; 458 | } 459 | } else if (((int)buf[i] & 0xff) == exit_char) { 460 | sawexit_char = 1; 461 | } else { 462 | realbuf[realbytes++] = buf[i]; 463 | if (pst_mode && buf[i] == '\r') 464 | realbuf[realbytes++] = '\n'; 465 | } 466 | } 467 | write(fileno(stdout), realbuf, realbytes); 468 | } 469 | } 470 | if (FD_ISSET(STDIN_FILENO, &set)) { 471 | if ((cnt = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { 472 | int i; 473 | for (i = 0; i < cnt; ++i) { 474 | if (buf[i] == ESC) 475 | goto done; 476 | } 477 | write(sock, buf, cnt); 478 | } 479 | } 480 | } 481 | } while (continue_terminal); 482 | 483 | done: 484 | tcsetattr(STDIN_FILENO, TCSANOW, &oldt); 485 | 486 | if (sawexit_valid) 487 | exit(exitcode); 488 | #endif 489 | } 490 | 491 | /* GetInterfaceAddresses - get the addresses of all IPv4 interfaces */ 492 | int GetInterfaceAddresses(IFADDR *addrs, int max) 493 | { 494 | #ifdef __MINGW32__ 495 | // adapted from an example in the Microsoft documentation 496 | PMIB_IPADDRTABLE pIPAddrTable; 497 | DWORD dwSize = 0; 498 | DWORD dwRetVal = 0; 499 | int cnt, i; 500 | 501 | // Before calling AddIPAddress we use GetIpAddrTable to get 502 | // an adapter to which we can add the IP. 503 | pIPAddrTable = (MIB_IPADDRTABLE *)malloc(sizeof (MIB_IPADDRTABLE)); 504 | if (!pIPAddrTable) 505 | return -1; 506 | 507 | // Make an initial call to GetIpAddrTable to get the 508 | // necessary size into the dwSize variable 509 | if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) { 510 | free(pIPAddrTable); 511 | pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize); 512 | if (!pIPAddrTable) 513 | return -1; 514 | } 515 | 516 | // Make a second call to GetIpAddrTable to get the actual data we want 517 | if ((dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 )) != NO_ERROR) 518 | return -1; 519 | 520 | cnt = 0; 521 | for (i = 0; cnt < max && i < (int)pIPAddrTable->dwNumEntries; ++i) { 522 | addrs->addr.sin_family = AF_INET; 523 | addrs->addr.sin_addr.s_addr = (uint32_t)pIPAddrTable->table[i].dwAddr; 524 | addrs->mask.sin_family = AF_INET; 525 | addrs->mask.sin_addr.s_addr = (uint32_t)pIPAddrTable->table[i].dwMask; 526 | addrs->bcast.sin_family = AF_INET; 527 | addrs->bcast.sin_addr.s_addr = addrs->addr.sin_addr.s_addr | ~addrs->mask.sin_addr.s_addr; 528 | ++addrs; 529 | ++cnt; 530 | } 531 | 532 | free(pIPAddrTable); 533 | 534 | return cnt; 535 | #else 536 | struct ifaddrs *list, *entry; 537 | int cnt; 538 | 539 | if (getifaddrs(&list) != 0) 540 | return -1; 541 | 542 | cnt = 0; 543 | for (entry = list; cnt < max && entry != NULL; entry = entry->ifa_next) { 544 | if (entry->ifa_addr->sa_family == AF_INET && !(entry->ifa_flags & IFF_LOOPBACK)) { 545 | addrs->addr = *(SOCKADDR_IN *)entry->ifa_addr; 546 | addrs->mask = *(SOCKADDR_IN *)entry->ifa_netmask; 547 | addrs->bcast = *(SOCKADDR_IN *)entry->ifa_broadaddr; 548 | ++addrs; 549 | ++cnt; 550 | } 551 | } 552 | 553 | freeifaddrs(list); 554 | 555 | return cnt; 556 | #endif 557 | } 558 | 559 | const char *AddrToString(uint32_t addr) 560 | { 561 | IN_ADDR inaddr; 562 | inaddr.s_addr = addr; 563 | return inet_ntoa(inaddr); 564 | } 565 | 566 | int StringToAddr(const char *addr, uint32_t *pAddr) 567 | { 568 | in_addr_t inaddr; 569 | if ((inaddr = inet_addr(addr)) == (in_addr_t)(-1)) 570 | return -1; 571 | *pAddr = inaddr; 572 | return 0; 573 | } 574 | 575 | -------------------------------------------------------------------------------- /src/system.c: -------------------------------------------------------------------------------- 1 | /* system.c - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "system.h" 27 | 28 | #if defined(WIN32) 29 | #include 30 | #include 31 | #endif 32 | 33 | #if defined(MACOSX) 34 | #include 35 | #endif 36 | 37 | #if defined(LINUX) 38 | #include 39 | #endif 40 | 41 | typedef struct PathEntry PathEntry; 42 | struct PathEntry { 43 | PathEntry *next; 44 | char path[1]; 45 | }; 46 | 47 | static PathEntry *path = NULL; 48 | static PathEntry **pNextPathEntry = &path; 49 | 50 | static const char *MakePath(PathEntry *entry, const char *name); 51 | 52 | FILE *xbOpenFileInPath(const char *name, const char *mode) 53 | { 54 | PathEntry *entry; 55 | FILE *fp; 56 | 57 | if (!(fp = fopen(name, mode))) { 58 | for (entry = path; entry != NULL; entry = entry->next) 59 | if ((fp = fopen(MakePath(entry, name), mode)) != NULL) 60 | break; 61 | } 62 | return fp; 63 | } 64 | 65 | int xbAddPath(const char *path) 66 | { 67 | PathEntry *entry = malloc(sizeof(PathEntry) + strlen(path)); 68 | if (!(entry)) 69 | return FALSE; 70 | strcpy(entry->path, path); 71 | *pNextPathEntry = entry; 72 | pNextPathEntry = &entry->next; 73 | entry->next = NULL; 74 | return TRUE; 75 | } 76 | 77 | int xbAddFilePath(const char *name) 78 | { 79 | PathEntry *entry; 80 | char *end; 81 | int len; 82 | if (!(end = strrchr(name, DIR_SEP))) 83 | return FALSE; 84 | len = (int)(end - name); 85 | if (!(entry = malloc(sizeof(PathEntry) + len))) 86 | return FALSE; 87 | strncpy(entry->path, name, len); 88 | entry->path[len] = '\0'; 89 | *pNextPathEntry = entry; 90 | pNextPathEntry = &entry->next; 91 | entry->next = NULL; 92 | return TRUE; 93 | } 94 | 95 | /* xbAddProgramPath - add the path relative the application directory */ 96 | int xbAddProgramPath(char *argv[]) 97 | { 98 | static char fullpath[1024]; 99 | char *p; 100 | #if defined(WIN32) 101 | 102 | #if defined(Q_OS_WIN32) || defined(MINGW) 103 | /* get the full path to the executable */ 104 | if (!GetModuleFileNameA(NULL, fullpath, sizeof(fullpath))) 105 | return FALSE; 106 | #else 107 | /* get the full path to the executable */ 108 | if (!GetModuleFileNameEx(GetCurrentProcess(), NULL, fullpath, sizeof(fullpath))) 109 | return FALSE; 110 | #endif /* Q_OS_WIN32 */ 111 | 112 | #elif defined(LINUX) 113 | int r; 114 | r = readlink("/proc/self/exe", fullpath, sizeof(fullpath)-1); 115 | if (r >= 0) 116 | fullpath[r] = 0; 117 | else 118 | return FALSE; 119 | #elif defined(MACOSX) 120 | uint32_t bufsize = sizeof(fullpath)-1; 121 | int r = _NSGetExecutablePath(fullpath, &bufsize); 122 | if (r < 0) 123 | return FALSE; 124 | #else 125 | /* fall back on argv[0]... probably not the best bet, since 126 | shells might not put the full path in, but it's the most portable 127 | */ 128 | strcpy(fullpath, argv[0]); 129 | #endif 130 | 131 | /* remove the executable filename */ 132 | if ((p = strrchr(fullpath, DIR_SEP)) != NULL) 133 | *p = '\0'; 134 | 135 | /* remove the immediate directory containing the executable (usually 'bin') */ 136 | if ((p = strrchr(fullpath, DIR_SEP)) != NULL) { 137 | *p = '\0'; 138 | 139 | /* check for the 'Release' or 'Debug' build directories used by Visual C++ */ 140 | if (strcmp(&p[1], "Release") == 0 || strcmp(&p[1], "Debug") == 0) { 141 | if ((p = strrchr(fullpath, DIR_SEP)) != NULL) 142 | *p = '\0'; 143 | } 144 | } 145 | 146 | /* add propeller-load for propeller-gcc */ 147 | strcat(fullpath, DIR_SEP_STR "propeller-load"); 148 | 149 | /* add the executable directory */ 150 | xbAddPath(fullpath); 151 | 152 | return TRUE; 153 | } 154 | 155 | int xbAddEnvironmentPath(const char *name) 156 | { 157 | char *p, *end; 158 | 159 | /* add path entries from the environment */ 160 | if ((p = getenv(name)) != NULL) { 161 | while ((end = strchr(p, PATH_SEP)) != NULL) { 162 | *end = '\0'; 163 | if (!xbAddPath(p)) 164 | return FALSE; 165 | p = end + 1; 166 | } 167 | if (!xbAddPath(p)) 168 | return FALSE; 169 | } 170 | 171 | return TRUE; 172 | } 173 | 174 | static const char *MakePath(PathEntry *entry, const char *name) 175 | { 176 | static char fullpath[PATH_MAX]; 177 | sprintf(fullpath, "%s%c%s", entry->path, DIR_SEP, name); 178 | return fullpath; 179 | } 180 | -------------------------------------------------------------------------------- /src/system.h: -------------------------------------------------------------------------------- 1 | /* system.h - an elf and spin binary loader for the Parallax Propeller microcontroller 2 | 3 | Copyright (c) 2011 David Michael Betz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 6 | and associated documentation files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 8 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or 12 | substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 15 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 16 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | 20 | */ 21 | 22 | #ifndef __SYSTEM_H__ 23 | #define __SYSTEM_H__ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include 30 | 31 | #ifndef TRUE 32 | #define TRUE 1 33 | #define FALSE 0 34 | #endif 35 | 36 | #if defined(WIN32) 37 | #define PATH_SEP ';' 38 | #define DIR_SEP '\\' 39 | #define DIR_SEP_STR "\\" 40 | #else 41 | #define PATH_SEP ':' 42 | #define DIR_SEP '/' 43 | #define DIR_SEP_STR "/" 44 | #endif 45 | 46 | int xbAddPath(const char *path); 47 | int xbAddFilePath(const char *name); 48 | int xbAddEnvironmentPath(const char *name); 49 | int xbAddProgramPath(char *argv[]); 50 | FILE *xbOpenFileInPath(const char *name, const char *mode); 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /src/wifipropconnection.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFIPROPCONNECTION_H 2 | #define WIFIPROPCONNECTION_H 3 | 4 | #include 5 | #include 6 | #include "propconnection.h" 7 | #include "sock.h" 8 | 9 | #define WIFI_REQUIRED_MAJOR_VERSION "v1." 10 | #define WIFI_REQUIRED_MAJOR_VERSION_LEGACY "02-" 11 | 12 | // timeout used when making an HTTP request or connecting a telnet session 13 | #define CONNECT_TIMEOUT 3000 14 | #define RESPONSE_TIMEOUT 3000 15 | #define DISCOVER_REPLY_TIMEOUT 250 16 | #define DISCOVER_ATTEMPTS 3 17 | 18 | class WiFiInfo { 19 | public: 20 | WiFiInfo() {} 21 | WiFiInfo(std::string name, std::string address) : m_name(name), m_address(address) {} 22 | const char *name() { return m_name.c_str(); } 23 | const char *address() { return m_address.c_str(); } 24 | private: 25 | std::string m_name; 26 | std::string m_address; 27 | }; 28 | 29 | typedef std::list WiFiInfoList; 30 | 31 | class WiFiPropConnection : public PropConnection 32 | { 33 | public: 34 | WiFiPropConnection(); 35 | ~WiFiPropConnection(); 36 | int setAddress(const char *ipaddr); 37 | int getVersion(); 38 | int checkVersion(); 39 | const char *version() { return m_version ? m_version : "(unknown)"; } 40 | bool isOpen(); 41 | int close(); 42 | int connect(); 43 | int disconnect(); 44 | int setName(const char *name); 45 | int setResetMethod(const char *method); 46 | int generateResetSignal(); 47 | int identify(int *pVersion); 48 | int loadImage(const uint8_t *image, int imageSize, uint8_t *response, int responseSize); 49 | int loadImage(const uint8_t *image, int imageSize, LoadType loadType = ltDownloadAndRun, int info = false); 50 | int sendData(const uint8_t *buf, int len); 51 | int receiveDataTimeout(uint8_t *buf, int len, int timeout); 52 | int receiveDataExactTimeout(uint8_t *buf, int len, int timeout); 53 | int setBaudRate(int baudRate); 54 | int maxDataSize() { return 1024; } 55 | int terminal(bool checkForExit, bool pstMode); 56 | static int findModules(bool show, WiFiInfoList &list, int count = -1); 57 | private: 58 | int sendRequest(uint8_t *req, int reqSize, uint8_t *res, int resMax, int *pResult); 59 | static uint8_t *getBody(uint8_t *msg, int msgSize, int *pBodySize); 60 | static void dumpHdr(const uint8_t *buf, int size); 61 | static void dumpResponse(const uint8_t *buf, int size); 62 | char *m_ipaddr; 63 | char *m_version; 64 | SOCKADDR_IN m_httpAddr; 65 | SOCKADDR_IN m_telnetAddr; 66 | SOCKET m_telnetSocket; 67 | int m_resetPin; 68 | }; 69 | 70 | #endif // WIFIPROPELLERCONNECTION_H 71 | -------------------------------------------------------------------------------- /toggle.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file toggle.c 3 | * This program demonstrates starting another COG running 4 | * LMM code. 5 | * The cog makes all IO except 30/31 toggle. 6 | * 7 | * Copyright (c) 2011 Parallax, Inc. 8 | * MIT Licensed (see at end of file for exact terms) 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #if defined(__PROPELLER_XMM__) 16 | #define STACK_SIZE (1024+128+32) /* need room for XMM cache */ 17 | #else 18 | #define STACK_SIZE 32 19 | #endif 20 | 21 | /* stack for cog 1 */ 22 | /* this must be in HUB memory */ 23 | HUBDATA static int cog1_stack[STACK_SIZE]; 24 | 25 | /* variables that we share between cogs */ 26 | /* these must go in HUB memory so as to avoid cache coherency issues */ 27 | HUBDATA volatile unsigned int wait_time; 28 | HUBDATA volatile unsigned int pins; 29 | 30 | /* per-thread library variables ("Thread Local Storage") */ 31 | static _thread_state_t thread_data; 32 | 33 | /* 34 | * here's the toggle code that runs in another cog 35 | */ 36 | 37 | //__attribute__((section(".hubtext"))) 38 | void 39 | do_toggle(void *arg __attribute__((unused)) ) 40 | { 41 | unsigned int nextcnt; 42 | unsigned tmppins = pins; 43 | 44 | /* figure out how long to wait the first time */ 45 | nextcnt = CNT + wait_time; 46 | 47 | /* loop forever, updating the wait time from the mailbox */ 48 | for(;;) { 49 | tmppins = pins; 50 | DIRA = tmppins; 51 | OUTA ^= tmppins; /* update the pins */ 52 | 53 | /* sleep until _CNT == nextcnt, and return the new _CNT + wait_time */ 54 | nextcnt = __builtin_propeller_waitcnt(nextcnt, wait_time); 55 | } 56 | } 57 | 58 | /* 59 | * main code 60 | * This is the code running in the LMM cog (cog 0). 61 | * It launches another cog to actually run the 62 | * toggling code 63 | */ 64 | #define MIN_GAP 800000 65 | 66 | void main (int argc, char* argv[]) 67 | { 68 | int n; 69 | int result; 70 | unsigned int startTime; 71 | unsigned int endTime; 72 | unsigned int executionTime; 73 | unsigned int rawTime; 74 | 75 | printf("hello, world!\n"); 76 | 77 | /* set up the parameters for the C cog */ 78 | #if defined(__PROPELLER_USE_XMM__) 79 | pins = 0x00008000; /* C3 led only */ 80 | #else 81 | pins = 0x3fffffff; 82 | #endif 83 | wait_time = _clkfreq; /* start by waiting for 1 second */ 84 | 85 | /* start the new cog */ 86 | n = _start_cog_thread(cog1_stack + STACK_SIZE, do_toggle, NULL, &thread_data); 87 | printf("toggle cog %d has started\n", n); 88 | 89 | /* every 2 seconds update the flashing frequency so the 90 | light blinks faster and faster */ 91 | while(1) { 92 | waitcnt(CNT + _clkfreq * 2); 93 | wait_time = wait_time >> 1; 94 | if (wait_time < MIN_GAP) 95 | wait_time = _clkfreq; 96 | printf("time = %d cycles\n", wait_time); 97 | } 98 | } 99 | 100 | /* +-------------------------------------------------------------------- 101 | * ¦ TERMS OF USE: MIT License 102 | * +-------------------------------------------------------------------- 103 | * Permission is hereby granted, free of charge, to any person obtaining 104 | * a copy of this software and associated documentation files 105 | * (the "Software"), to deal in the Software without restriction, 106 | * including without limitation the rights to use, copy, modify, merge, 107 | * publish, distribute, sublicense, and/or sell copies of the Software, 108 | * and to permit persons to whom the Software is furnished to do so, 109 | * subject to the following conditions: 110 | * 111 | * The above copyright notice and this permission notice shall be 112 | * included in all copies or substantial portions of the Software. 113 | * 114 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 115 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 116 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 117 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 118 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 119 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 120 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 121 | * +-------------------------------------------------------------------- 122 | */ 123 | -------------------------------------------------------------------------------- /tools/bin2c.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | char base[1024], opath[1024], *name, *p; 8 | FILE *ifp, *ofp; 9 | int byte, cnt; 10 | 11 | if (argc < 2 || argc > 3) { 12 | fprintf(stderr, "usage: bin2c [ ]\n"); 13 | exit(1); 14 | } 15 | 16 | strcpy(base, argv[1]); 17 | if ((p = strrchr(base, '.')) != NULL) 18 | *p = '\0'; 19 | 20 | if (argc >= 3) 21 | strcpy(opath, argv[2]); 22 | else { 23 | strcpy(opath, base); 24 | strcat(opath, ".c"); 25 | } 26 | 27 | if ((name = strrchr(base, '/')) != NULL) 28 | ++name; 29 | else 30 | name = base; 31 | 32 | if (!(ifp = fopen(argv[1], "rb"))) { 33 | fprintf(stderr, "error: can't open: %s\n", argv[1]); 34 | exit(1); 35 | } 36 | 37 | if (!(ofp = fopen(opath, "wb"))) { 38 | fprintf(stderr, "error: can't create: %s\n", argv[2]); 39 | exit(1); 40 | } 41 | 42 | fprintf(ofp, "\ 43 | #include \n\ 44 | \n\ 45 | uint8_t %s_array[] = {\n\ 46 | ", name); 47 | 48 | cnt = 0; 49 | while ((byte = getc(ifp)) != EOF) { 50 | fprintf(ofp, " 0x%02x,", byte); 51 | if (++cnt == 8) { 52 | putc('\n', ofp); 53 | cnt = 0; 54 | } 55 | } 56 | 57 | if (cnt > 0) 58 | putc('\n', ofp); 59 | 60 | fprintf(ofp, "\ 61 | };\n\ 62 | \n\ 63 | int %s_size = sizeof(%s_array);\n\ 64 | ", name, name); 65 | 66 | fclose(ifp); 67 | fclose(ofp); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /tools/split.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | //#define DEBUG 6 | 7 | /* target checksum for a binary file */ 8 | #define SPIN_TARGET_CHECKSUM 0x14 9 | 10 | /* spin object file header */ 11 | typedef struct { 12 | uint32_t clkfreq; 13 | uint8_t clkmode; 14 | uint8_t chksum; 15 | uint16_t pbase; 16 | uint16_t vbase; 17 | uint16_t dbase; 18 | uint16_t pcurr; 19 | uint16_t dcurr; 20 | uint16_t xxxxx; // jeff: what is this? 21 | uint16_t yyyyy; // jeff: what is this? 22 | uint16_t zzzzz; // jeff: what is this? 23 | } SpinHdr; 24 | 25 | #define PACKET_CODE 0x11111111 26 | 27 | static char *overlayNames[] = { 28 | "verifyRAM", 29 | "programVerifyEEPROM", 30 | "readyToLaunch", 31 | "launchNow" 32 | }; 33 | static int overlayNameCount = sizeof(overlayNames) / sizeof(char *); 34 | 35 | #ifdef DEBUG 36 | static void DumpSpinHdr(FILE *fp, const char *tag, SpinHdr *hdr); 37 | #endif 38 | static void DumpRange(FILE *fp, uint8_t *image, int imageOffset, int endOffset); 39 | 40 | int main(int argc, char *argv[]) 41 | { 42 | uint8_t *image; 43 | int imageSize, imageOffset, delta, oldSpinCodeOffset, firstOverlayMarker, overlayIndex, overlayOffset, chksum; 44 | FILE *ifp, *ofp; 45 | uint32_t *imagePtr, value; 46 | SpinHdr *hdr; 47 | 48 | if (argc != 3) { 49 | printf("usage: split \n"); 50 | return 1; 51 | } 52 | 53 | /* open the image file */ 54 | if (!(ifp = fopen(argv[1], "rb"))) { 55 | printf("error: can't open '%s'\n", argv[1]); 56 | return 1; 57 | } 58 | 59 | /* determine the file size */ 60 | fseek(ifp, 0, SEEK_END); 61 | imageSize = (int)ftell(ifp); 62 | fseek(ifp, 0, SEEK_SET); 63 | 64 | /* allocate space for the image */ 65 | if (!(image = (uint8_t *)malloc(imageSize))) { 66 | printf("error: insufficient memory\n"); 67 | return 1; 68 | } 69 | hdr = (SpinHdr *)image; 70 | 71 | /* read the entire image into memory */ 72 | if (fread(image, 1, imageSize, ifp) != imageSize) { 73 | printf("error: reading image\n"); 74 | return 1; 75 | } 76 | fclose(ifp); 77 | 78 | if (!(ofp = fopen(argv[2], "w"))) { 79 | printf("error: can't create '%s'\n", argv[2]); 80 | return 1; 81 | } 82 | 83 | /* dump the original image header */ 84 | #ifdef DEBUG 85 | DumpSpinHdr(ofp, "original", hdr); 86 | #endif 87 | 88 | oldSpinCodeOffset = hdr->pcurr; 89 | 90 | firstOverlayMarker = -1; 91 | imagePtr = (uint32_t *)image; 92 | imageOffset = 0; 93 | while (imageOffset < imageSize) { 94 | if (*imagePtr++ == PACKET_CODE) { 95 | firstOverlayMarker = imageOffset; 96 | break; 97 | } 98 | imageOffset += sizeof(uint32_t); 99 | } 100 | 101 | if (firstOverlayMarker == -1) { 102 | printf("error: no overlays found\n"); 103 | return 1; 104 | } 105 | #ifdef DEBUG 106 | fprintf(ofp, "/* firstOverlayMarker: %04x */\n\n", firstOverlayMarker); 107 | #endif 108 | 109 | /* fixup the spin header to remove the overlays */ 110 | delta = hdr->pcurr - firstOverlayMarker; 111 | hdr->vbase -= delta; 112 | hdr->dbase -= delta; 113 | hdr->pcurr -= delta; 114 | hdr->dcurr -= delta; 115 | hdr->xxxxx -= delta; 116 | hdr->zzzzz -= delta; 117 | 118 | /* recompute the image checksum */ 119 | hdr->chksum = chksum = 0; 120 | for (imageOffset = 0; imageOffset < firstOverlayMarker; ++imageOffset) 121 | chksum += image[imageOffset]; 122 | for (imageOffset = oldSpinCodeOffset; imageOffset < imageSize; ++imageOffset) 123 | chksum += image[imageOffset]; 124 | hdr->chksum = SPIN_TARGET_CHECKSUM - chksum; 125 | 126 | /* dump the patched spin header */ 127 | #ifdef DEBUG 128 | DumpSpinHdr(ofp, "patched", hdr); 129 | #endif 130 | 131 | fprintf(ofp, "static uint8_t rawLoaderImage[] = {"); 132 | DumpRange(ofp, image, 0, firstOverlayMarker); putc(',', ofp); 133 | DumpRange(ofp, image, oldSpinCodeOffset, imageSize); 134 | fprintf(ofp, "};\n\n"); 135 | 136 | imageOffset = firstOverlayMarker + sizeof(uint32_t); 137 | imagePtr = (uint32_t *)(image + imageOffset); 138 | overlayOffset = imageOffset; 139 | overlayIndex = 0; 140 | while (imageOffset < oldSpinCodeOffset) { 141 | value = *imagePtr++; 142 | if (value == PACKET_CODE) { 143 | char *overlayName = (overlayIndex < overlayNameCount ? overlayNames[overlayIndex++] : "unknown"); 144 | fprintf(ofp, "static uint8_t %s[] = {", overlayName); 145 | DumpRange(ofp, image, overlayOffset, imageOffset); 146 | fprintf(ofp, "};\n\n"); 147 | overlayOffset = imageOffset + sizeof(uint32_t); 148 | } 149 | imageOffset += sizeof(uint32_t); 150 | } 151 | fprintf(ofp, "static uint8_t %s[] = {", overlayIndex < overlayNameCount ? overlayNames[overlayIndex++] : "unknown"); 152 | DumpRange(ofp, image, overlayOffset, imageOffset); 153 | fprintf(ofp, "};\n"); 154 | 155 | fclose(ofp); 156 | 157 | return 0; 158 | } 159 | 160 | #ifdef DEBUG 161 | static void DumpSpinHdr(FILE *fp, const char *tag, SpinHdr *hdr) 162 | { 163 | fprintf(fp, "/* %s: \n", tag); 164 | fprintf(fp, " clkfreq: %d\n", hdr->clkfreq); 165 | fprintf(fp, " clkmode: %02x\n", hdr->clkmode); 166 | fprintf(fp, " chksum: %02x\n", hdr->chksum); 167 | fprintf(fp, " pbase: %04x\n", hdr->pbase); 168 | fprintf(fp, " vbase: %04x\n", hdr->vbase); 169 | fprintf(fp, " dbase: %04x\n", hdr->dbase); 170 | fprintf(fp, " pcurr: %04x\n", hdr->pcurr); 171 | fprintf(fp, " dcurr: %04x\n", hdr->dcurr); 172 | fprintf(fp, " xxxxx: %04x\n", hdr->xxxxx); 173 | fprintf(fp, " yyyyy: %04x\n", hdr->yyyyy); 174 | fprintf(fp, " zzzzz: %04x\n", hdr->zzzzz); 175 | fprintf(fp, "*/\n\n"); 176 | } 177 | #endif 178 | 179 | static void DumpRange(FILE *fp, uint8_t *image, int imageOffset, int endOffset) 180 | { 181 | uint32_t *imagePtr = (uint32_t *)(image + imageOffset); 182 | int needComma = 0; 183 | int cnt = 0; 184 | while (imageOffset < endOffset) { 185 | uint32_t value = *imagePtr++; 186 | if (needComma) { 187 | fprintf(fp, ","); 188 | needComma = 0; 189 | } 190 | if ((cnt % 16) == 0) 191 | fprintf(fp, "\n/* %04x */ ", imageOffset); 192 | fprintf(fp, "0x%02X,0x%02X,0x%02X,0x%02X", 193 | ( value & 0xff), 194 | ((value >> 8) & 0xff), 195 | ((value >> 16) & 0xff), 196 | ((value >> 24) & 0xff)); 197 | imageOffset += sizeof(uint32_t); 198 | needComma = 1; 199 | cnt += 4; 200 | } 201 | } 202 | --------------------------------------------------------------------------------