diff --git a/.gitignore b/.gitignore index ea0c43c..778c321 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +Debug +Release +.vs teensy_loader_cli teensy_loader_cli.exe* *.swp diff --git a/Makefile b/Makefile index 2d1bda1..2f2a8b0 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,6 @@ CFLAGS ?= -O2 -Wall teensy_loader_cli: teensy_loader_cli.c $(CC) $(CFLAGS) $(CPPFLAGS) -s -DUSE_LIBUSB -o teensy_loader_cli teensy_loader_cli.c -lusb $(LDFLAGS) - else ifeq ($(OS), WINDOWS) CC ?= i586-mingw32msvc-gcc CFLAGS ?= -O2 -Wall diff --git a/README.md b/README.md index 5be3f18..8be33cf 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ Optional command line parameters: `-v` : Verbose output. Normally teensy_loader_cli prints only error messages if any operation fails. This enables verbose output, which can help with troubleshooting, or simply show you more status information. +## Building with Visual Studio for Windows + +Experimental Added support to build for windows using Visual Studio. + +I am currently using Visual Studio 2022, but belive it would work with 2019 as well. You will probably need to install the windows WDK to build +this. You will also need to x86 and not x64 as I don't believe all of the HID and setup APIS are available for x64. Or at least my few attempts +to build using them failed in the link phase. + ## System Specific Setup Linux requires UDEV rules for non-root users. diff --git a/Teensy_loader_cli.sln b/Teensy_loader_cli.sln new file mode 100644 index 0000000..9791d27 --- /dev/null +++ b/Teensy_loader_cli.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32228.430 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Teensy_loader_cli", "Teensy_loader_cli.vcxproj", "{F6BBBADD-8524-41F7-80B2-28DAB07CE994}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x64.ActiveCfg = Debug|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x64.Build.0 = Debug|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x86.ActiveCfg = Debug|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Debug|x86.Build.0 = Debug|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x64.ActiveCfg = Release|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x64.Build.0 = Release|x64 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x86.ActiveCfg = Release|Win32 + {F6BBBADD-8524-41F7-80B2-28DAB07CE994}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {01112176-E808-4C22-BCE1-02F372C38038} + EndGlobalSection +EndGlobal diff --git a/Teensy_loader_cli.vcxproj b/Teensy_loader_cli.vcxproj new file mode 100644 index 0000000..3609441 --- /dev/null +++ b/Teensy_loader_cli.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {f6bbbadd-8524-41f7-80b2-28dab07ce994} + Teensyloadercli + 10.0 + Teensy_loader_cli + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level2 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + hid.lib;setupapi.lib;winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;VSTUDIO;%(PreprocessorDefinitions) + true + + + Console + true + true + true + hid.lib;setupapi.lib;winmm.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/Teensy_loader_cli.vcxproj.filters b/Teensy_loader_cli.vcxproj.filters new file mode 100644 index 0000000..a7ba3e2 --- /dev/null +++ b/Teensy_loader_cli.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/Teensy_loader_cli.vcxproj.user b/Teensy_loader_cli.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/Teensy_loader_cli.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/teensy_loader_cli.c b/teensy_loader_cli.c index 84e5400..88f75eb 100644 --- a/teensy_loader_cli.c +++ b/teensy_loader_cli.c @@ -16,14 +16,14 @@ * along with this program. If not, see http://www.gnu.org/licenses/ */ -/* Want to incorporate this code into a proprietary application?? - * Just email paul@pjrc.com to ask. Usually it's not a problem, - * but you do need to ask to use this code in any way other than - * those permitted by the GNU General Public License, version 3 */ + /* Want to incorporate this code into a proprietary application?? + * Just email paul@pjrc.com to ask. Usually it's not a problem, + * but you do need to ask to use this code in any way other than + * those permitted by the GNU General Public License, version 3 */ -/* For non-root permissions on ubuntu or similar udev-based linux - * http://www.pjrc.com/teensy/49-teensy.rules - */ + /* For non-root permissions on ubuntu or similar udev-based linux + * http://www.pjrc.com/teensy/49-teensy.rules + */ #include @@ -31,19 +31,26 @@ #include #include #include -#include +//#include + +#if defined(WIN32) +#include +#endif +//#include "setup_hid.h" -void usage(const char *err) +void usage(const char* err) { - if(err != NULL) fprintf(stderr, "%s\n\n", err); + if (err != NULL) fprintf(stderr, "%s\n\n", err); fprintf(stderr, - "Usage: teensy_loader_cli --mcu= [-w] [-h] [-n] [-b] [-v] \n" + "Usage: teensy_loader_cli --mcu= [-w] [-h] [-n] [-b] [-v] [-f] [-d] \n" "\t-w : Wait for device to appear\n" "\t-r : Use hard reboot if device not online\n" "\t-s : Use soft reboot if device not online (Teensy 3.x & 4.x)\n" "\t-n : No reboot after programming\n" "\t-b : Boot only, do not program\n" "\t-v : Verbose output\n" + "\t-f : Fill memory region holes with 0s\n" + "\t-d : dump hex file memory ranges\n" "\nUse `teensy_loader_cli --list-mcus` to list supported MCUs.\n" "\nFor more information, please visit:\n" "http://www.pjrc.com/teensy/loader_cli.html\n"); @@ -58,17 +65,18 @@ int hard_reboot(void); int soft_reboot(void); // Intel Hex File Functions -int read_intel_hex(const char *filename); +int read_intel_hex(const char* filename); +void fill_the_holes(); int ihex_bytes_within_range(int begin, int end); -void ihex_get_data(int addr, int len, unsigned char *bytes); +void ihex_get_data(int addr, int len, unsigned char* bytes); int memory_is_blank(int addr, int block_size); // Misc stuff -int printf_verbose(const char *format, ...); +int printf_verbose(const char* format, ...); void delay(double seconds); -void die(const char *str, ...); -void parse_options(int argc, char **argv); -void boot(unsigned char *buf, int write_size); +void die(const char* str, ...); +void parse_options(int argc, char** argv); +void boot(unsigned char* buf, int write_size); // options (from user via command line args) int wait_for_device_to_appear = 0; @@ -78,7 +86,9 @@ int reboot_after_programming = 1; int verbose = 0; int boot_only = 0; int code_size = 0, block_size = 0; -const char *filename=NULL; +int fill_holes = 0; +int dump_memory_ranges = 0; +const char* filename = NULL; /****************************************************************/ @@ -87,12 +97,12 @@ const char *filename=NULL; /* */ /****************************************************************/ -int main(int argc, char **argv) +int main(int argc, char** argv) { unsigned char buf[2048]; int num, addr, r, write_size; - int first_block=1, waited=0; + int first_block = 1, waited = 0; // parse command line arguments parse_options(argc, argv); @@ -102,11 +112,12 @@ int main(int argc, char **argv) if (!code_size) { usage("MCU type must be specified"); } - printf_verbose("Teensy Loader, Command Line, Version 2.2\n"); + printf_verbose("Teensy Loader, Command Line, Version 2.S\n"); if (block_size == 512 || block_size == 1024) { write_size = block_size + 64; - } else { + } + else { write_size = block_size + 2; }; @@ -114,6 +125,7 @@ int main(int argc, char **argv) // read the intel hex file // this is done first so any error is reported before using USB num = read_intel_hex(filename); + if (fill_holes) fill_the_holes(); if (num < 0) die("error reading intel hex file \"%s\"", filename); printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n", filename, num, (double)num / (double)code_size * 100.0); @@ -157,7 +169,7 @@ int main(int argc, char **argv) num = read_intel_hex(filename); if (num < 0) die("error reading intel hex file \"%s\"", filename); printf_verbose("Read \"%s\": %d bytes, %.1f%% usage\n", - filename, num, (double)num / (double)code_size * 100.0); + filename, num, (double)num / (double)code_size * 100.0); } // program the data @@ -176,19 +188,22 @@ int main(int argc, char **argv) buf[1] = (addr >> 8) & 255; ihex_get_data(addr, block_size, buf + 2); write_size = block_size + 2; - } else if (block_size == 256) { + } + else if (block_size == 256) { buf[0] = (addr >> 8) & 255; buf[1] = (addr >> 16) & 255; ihex_get_data(addr, block_size, buf + 2); write_size = block_size + 2; - } else if (block_size == 512 || block_size == 1024) { + } + else if (block_size == 512 || block_size == 1024) { buf[0] = addr & 255; buf[1] = (addr >> 8) & 255; buf[2] = (addr >> 16) & 255; memset(buf + 3, 0, 61); ihex_get_data(addr, block_size, buf + 64); write_size = block_size + 64; - } else { + } + else { die("Unknown code/block size\n"); } r = teensy_write(buf, write_size, first_block ? 5.0 : 0.5); @@ -521,6 +536,177 @@ int soft_reboot(void) return 0; } +#elif defined(WIN32) +// Visual studio build +#include +#include +#include +#include +#include +#include +#include +static HANDLE rx_event = NULL; +static HANDLE tx_event = NULL; +static CRITICAL_SECTION rx_mutex; +static CRITICAL_SECTION tx_mutex; + +HANDLE open_usb_device(int vid, int pid) +{ + GUID guid; + HDEVINFO info; + DWORD index, required_size; + SP_DEVICE_INTERFACE_DATA iface; + SP_DEVICE_INTERFACE_DETAIL_DATA* details; + HIDD_ATTRIBUTES attrib; + HANDLE h; + BOOL ret; + + if (!rx_event) { + rx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + tx_event = CreateEvent(NULL, TRUE, TRUE, NULL); + InitializeCriticalSection(&rx_mutex); + InitializeCriticalSection(&tx_mutex); + } + + HidD_GetHidGuid(&guid); + info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + if (info == INVALID_HANDLE_VALUE) return NULL; + for (index = 0; 1; index++) { + iface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + ret = SetupDiEnumDeviceInterfaces(info, NULL, &guid, index, &iface); + if (!ret) { + SetupDiDestroyDeviceInfoList(info); + break; + } + SetupDiGetInterfaceDeviceDetail(info, &iface, NULL, 0, &required_size, NULL); + details = (SP_DEVICE_INTERFACE_DETAIL_DATA*)malloc(required_size); + if (details == NULL) continue; + memset(details, 0, required_size); + details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + ret = SetupDiGetDeviceInterfaceDetail(info, &iface, details, + required_size, NULL, NULL); + if (!ret) { + free(details); + continue; + } + h = CreateFile(details->DevicePath, GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL); + free(details); + if (h == INVALID_HANDLE_VALUE) continue; + attrib.Size = sizeof(HIDD_ATTRIBUTES); + ret = HidD_GetAttributes(h, &attrib); + if (!ret) { + CloseHandle(h); + continue; + } + if (attrib.VendorID != vid || attrib.ProductID != pid) { + CloseHandle(h); + continue; + } + SetupDiDestroyDeviceInfoList(info); + return h; + } + return NULL; +} + +int write_usb_device(HANDLE h, void* buf, int len, int timeout) +{ + static HANDLE event = NULL; + unsigned char tmpbuf[1089]; + OVERLAPPED ov; + DWORD n, r; + + if (len > sizeof(tmpbuf) - 1) return 0; + if (event == NULL) { + event = CreateEvent(NULL, TRUE, TRUE, NULL); + if (!event) return 0; + } + ResetEvent(&event); + memset(&ov, 0, sizeof(ov)); + ov.hEvent = event; + tmpbuf[0] = 0; + memcpy(tmpbuf + 1, buf, len); + if (!WriteFile(h, tmpbuf, len + 1, NULL, &ov)) { + if (GetLastError() != ERROR_IO_PENDING) return 0; + r = WaitForSingleObject(event, timeout); + if (r == WAIT_TIMEOUT) { + CancelIo(h); + return 0; + } + if (r != WAIT_OBJECT_0) return 0; + } + if (!GetOverlappedResult(h, &ov, &n, FALSE)) return 0; + if (n <= 0) return 0; + return 1; +} + +void print_win32_err(void) +{ + char buf[256]; + DWORD err; + + err = GetLastError(); + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, + 0, buf, sizeof(buf), NULL); + printf("err %ld: %s\n", err, buf); +} + +static HANDLE win32_teensy_handle = NULL; + +int teensy_open(void) +{ + teensy_close(); + win32_teensy_handle = open_usb_device(0x16C0, 0x0478); + if (win32_teensy_handle) return 1; + return 0; +} + +int teensy_write(void* buf, int len, double timeout) +{ + int r; + uint32_t begin, now, total; + + if (!win32_teensy_handle) return 0; + total = (uint32_t)(timeout * 1000.0); + begin = timeGetTime(); + now = begin; + do { + r = write_usb_device(win32_teensy_handle, buf, len, total - (now - begin)); + if (r > 0) return 1; + Sleep(10); + now = timeGetTime(); + } while (now - begin < total); + return 0; + +} + +void teensy_close(void) +{ + if (!win32_teensy_handle) return; + CloseHandle(win32_teensy_handle); + win32_teensy_handle = NULL; +} + +int hard_reboot(void) +{ + HANDLE rebootor; + int r; + + rebootor = open_usb_device(0x16C0, 0x0477); + if (!rebootor) return 0; + r = write_usb_device(rebootor, (void*)"reboot", 6, 100); + CloseHandle(rebootor); + return r; +} + +int soft_reboot(void) +{ + printf("Soft reboot is not implemented for Win32\n"); + return 0; +} + + #endif @@ -843,27 +1029,38 @@ int soft_reboot(void) static unsigned char firmware_image[MAX_MEMORY_SIZE]; static unsigned char firmware_mask[MAX_MEMORY_SIZE]; -static int end_record_seen=0; +static int end_record_seen = 0; static int byte_count; static unsigned int extended_addr = 0; -static int parse_hex_line(char *line); +static unsigned int actual_extended_addr = 0; +static unsigned int start_addr = 0; +static unsigned int end_addr = 0; +static int parse_hex_line(char* line); + +static unsigned int min_address = MAX_MEMORY_SIZE; +static unsigned int max_address = 0; -int read_intel_hex(const char *filename) +int read_intel_hex(const char* filename) { - FILE *fp; - int i, lineno=0; + FILE* fp; + int i, lineno = 0; char buf[1024]; byte_count = 0; end_record_seen = 0; - for (i=0; i= MAX_MEMORY_SIZE) return 0; ptr += 2; sum = (len & 255) + ((addr >> 8) & 255) + (addr & 255) + (code & 255); if (code != 0) { if (code == 1) { + if (dump_memory_ranges && (start_addr != end_addr)) { + printf("%8x - %8x\n$$\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + } + if (verbose && actual_extended_addr >= (0x60000000u)) { + printf("\nFlashconfig\n"); + uint32_t* flash_config = (uint32_t*)(&firmware_image[0]); + for (unsigned int i = 0; i < 128; i += 4) { + printf("\t%04x:%08x %08x %08x %08x\n", i * 4, flash_config[i], flash_config[i + 1], + flash_config[i + 2], flash_config[i + 3]); + } + + uint32_t* ivt = (uint32_t*)(&firmware_image[0x1000]); + printf("\nInterrupt Vector\n"); + for (unsigned int i = 0; i < 8; i += 4) { + printf("\t%04x:%08x %08x %08x %08x\n", i * 4, ivt[i], ivt[i + 1], + ivt[i + 2], ivt[i + 3]); + } + + uint32_t* boot_data = (uint32_t*)(&firmware_image[0x1020]); + printf("\nBoot data: %08x %08x(%u) %08x\n", boot_data[0], boot_data[1], boot_data[1], boot_data[2]); + } end_record_seen = 1; return 1; } if (code == 2 && len == 2) { - if (!sscanf(ptr, "%04x", &i)) return 1; + if (!SSCANF(ptr, "%04x", &i)) return 1; ptr += 4; sum += ((i >> 8) & 255) + (i & 255); - if (!sscanf(ptr, "%02x", &cksum)) return 1; + if (!SSCANF(ptr, "%02x", &cksum)) return 1; if (((sum & 255) + (cksum & 255)) & 255) return 1; - extended_addr = i << 4; + actual_extended_addr = extended_addr = i << 4; //printf("ext addr = %05X\n", extended_addr); } if (code == 4 && len == 2) { - if (!sscanf(ptr, "%04x", &i)) return 1; + if (!SSCANF(ptr, "%04x", &i)) return 1; ptr += 4; sum += ((i >> 8) & 255) + (i & 255); - if (!sscanf(ptr, "%02x", &cksum)) return 1; + if (!SSCANF(ptr, "%02x", &cksum)) return 1; if (((sum & 255) + (cksum & 255)) & 255) return 1; - extended_addr = i << 16; + if (dump_memory_ranges && (start_addr != end_addr)) printf("%8x - %8x\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + actual_extended_addr = extended_addr = i << 16; + start_addr = 0; + end_addr = 0; if (code_size > 1048576 && block_size >= 1024 && - extended_addr >= 0x60000000 && extended_addr < 0x60000000 + code_size) { + extended_addr >= 0x60000000u && extended_addr < 0x60000000u + code_size) { // Teensy 4.0 HEX files have 0x60000000 FlexSPI offset extended_addr -= 0x60000000; } @@ -945,8 +1170,13 @@ parse_hex_line(char *line) return 1; // non-data line } byte_count += len; + if (addr != (end_addr)) { + if (dump_memory_ranges && (start_addr != end_addr)) printf("%8x - %8x\n", actual_extended_addr + start_addr, actual_extended_addr + end_addr); + start_addr = addr; + } + end_addr = addr + len; while (num != len) { - if (sscanf(ptr, "%02x", &i) != 1) return 0; + if (SSCANF(ptr, "%02x", &i) != 1) return 0; i &= 255; firmware_image[addr + extended_addr + num] = i; firmware_mask[addr + extended_addr + num] = 1; @@ -955,39 +1185,56 @@ parse_hex_line(char *line) (num)++; if (num >= 256) return 0; } - if (!sscanf(ptr, "%02x", &cksum)) return 0; + // keep track of minimum address and max address + if ((addr + extended_addr) < min_address) min_address = addr + extended_addr; + if ((addr + extended_addr + len) > max_address) max_address = addr + extended_addr + len; + + if (!SSCANF(ptr, "%02x", &cksum)) return 0; if (((sum & 255) + (cksum & 255)) & 255) return 0; /* checksum error */ return 1; } +void fill_the_holes() +{ + unsigned int addr; + for (addr = min_address; addr < max_address; addr++) { + if (firmware_mask[addr] == 0) { + firmware_mask[addr] = 1; + firmware_image[addr] = 0; + } + + } +} + int ihex_bytes_within_range(int begin, int end) { int i; if (begin < 0 || begin >= MAX_MEMORY_SIZE || - end < 0 || end >= MAX_MEMORY_SIZE) { + end < 0 || end >= MAX_MEMORY_SIZE) { return 0; } - for (i=begin; i<=end; i++) { + for (i = begin; i <= end; i++) { if (firmware_mask[i]) return 1; } return 0; } -void ihex_get_data(int addr, int len, unsigned char *bytes) +void ihex_get_data(int addr, int len, unsigned char* bytes) { int i; if (addr < 0 || len < 0 || addr + len >= MAX_MEMORY_SIZE) { - for (i=0; i