diff options
| author | Jan Sucan <sucanjan@fit.cvut.cz> | 2019-06-04 14:34:27 +0200 |
|---|---|---|
| committer | Jan Sucan <sucanjan@fit.cvut.cz> | 2019-06-04 14:34:27 +0200 |
| commit | dc8703206e3f0f69605c56d0e1127f7e17f3476a (patch) | |
| tree | 166823a741dc420c10d54250cb53d1e3a6b74faf /impl/DMBootloader | |
Initial commit
Diffstat (limited to 'impl/DMBootloader')
| -rw-r--r-- | impl/DMBootloader/DMBootloader.atsln | 19 | ||||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/DMBootloader.componentinfo.xml | 4 | ||||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/DMBootloader.cproj | 128 | ||||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/Release/DMBootloader.bin | bin | 0 -> 8868 bytes | |||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/main.c | 21 | ||||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/protocol.c | 298 | ||||
| -rw-r--r-- | impl/DMBootloader/DMBootloader/protocol.h | 8 |
7 files changed, 478 insertions, 0 deletions
diff --git a/impl/DMBootloader/DMBootloader.atsln b/impl/DMBootloader/DMBootloader.atsln new file mode 100644 index 0000000..9c2407b --- /dev/null +++ b/impl/DMBootloader/DMBootloader.atsln @@ -0,0 +1,19 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Atmel Studio Solution File, Format Version 11.00 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "DMBootloader", "DMBootloader\DMBootloader.cproj", "{DCE6C7E3-EE26-4D79-826B-08594B9AD897}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Release|AVR = Release|AVR + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.ActiveCfg = Release|AVR + {DCE6C7E3-EE26-4D79-826B-08594B9AD897}.Release|AVR.Build.0 = Release|AVR + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/impl/DMBootloader/DMBootloader/DMBootloader.componentinfo.xml b/impl/DMBootloader/DMBootloader/DMBootloader.componentinfo.xml new file mode 100644 index 0000000..e275755 --- /dev/null +++ b/impl/DMBootloader/DMBootloader/DMBootloader.componentinfo.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Store xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="AtmelPackComponentManagement"> + <ProjectComponents /> +</Store>
\ No newline at end of file diff --git a/impl/DMBootloader/DMBootloader/DMBootloader.cproj b/impl/DMBootloader/DMBootloader/DMBootloader.cproj new file mode 100644 index 0000000..9f3da61 --- /dev/null +++ b/impl/DMBootloader/DMBootloader/DMBootloader.cproj @@ -0,0 +1,128 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0"> + <PropertyGroup> + <SchemaVersion>2.0</SchemaVersion> + <ProjectVersion>7.0</ProjectVersion> + <ToolchainName>com.Atmel.AVRGCC32.C</ToolchainName> + <ProjectGuid>dce6c7e3-ee26-4d79-826b-08594b9ad897</ProjectGuid> + <avrdevice>AT32UC3C2512C</avrdevice> + <avrdeviceseries>none</avrdeviceseries> + <OutputType>Executable</OutputType> + <Language>C</Language> + <OutputFileName>$(MSBuildProjectName)</OutputFileName> + <OutputFileExtension>.elf</OutputFileExtension> + <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory> + <AssemblyName>DMBootloader</AssemblyName> + <Name>DMBootloader</Name> + <RootNamespace>DMBootloader</RootNamespace> + <ToolchainFlavour>Native</ToolchainFlavour> + <KeepTimersRunning>true</KeepTimersRunning> + <OverrideVtor>false</OverrideVtor> + <CacheFlash>true</CacheFlash> + <ProgFlashFromRam>true</ProgFlashFromRam> + <RamSnippetAddress>0x20000000</RamSnippetAddress> + <UncachedRange /> + <preserveEEPROM>true</preserveEEPROM> + <OverrideVtorValue>exception_table</OverrideVtorValue> + <BootSegment>2</BootSegment> + <eraseonlaunchrule>0</eraseonlaunchrule> + <AsfFrameworkConfig> + <framework-data xmlns=""> + <options /> + <configurations /> + <files /> + <documentation help="" /> + <offline-documentation help="" /> + <dependencies> + <content-extension eid="atmel.asf" uuidref="Atmel.ASF" version="3.34.1" /> + </dependencies> + </framework-data> + </AsfFrameworkConfig> + <ResetRule>0</ResetRule> + <EraseKey /> + <avrtool>com.atmel.avrdbg.tool.jtagice3plus</avrtool> + <avrtoolserialnumber>J30200039974</avrtoolserialnumber> + <avrdeviceexpectedsignature>0x0</avrdeviceexpectedsignature> + <com_atmel_avrdbg_tool_jtagice3plus> + <ToolOptions> + <InterfaceProperties> + <JtagDbgClock>7500000</JtagDbgClock> + </InterfaceProperties> + <InterfaceName>JTAG</InterfaceName> + </ToolOptions> + <ToolType>com.atmel.avrdbg.tool.jtagice3plus</ToolType> + <ToolNumber>J30200039974</ToolNumber> + <ToolName>JTAGICE3</ToolName> + </com_atmel_avrdbg_tool_jtagice3plus> + <avrtoolinterface>JTAG</avrtoolinterface> + <avrtoolinterfaceclock>7500000</avrtoolinterfaceclock> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> + <ToolchainSettings> + <Avr32Gcc> + <avr32gcc.common.outputfiles.hex>True</avr32gcc.common.outputfiles.hex> + <avr32gcc.common.outputfiles.lss>True</avr32gcc.common.outputfiles.lss> + <avr32gcc.common.outputfiles.eep>True</avr32gcc.common.outputfiles.eep> + <avr32gcc.common.outputfiles.srec>True</avr32gcc.common.outputfiles.srec> + <avr32gcc.compiler.symbols.DefSymbols> + <ListValues> + <Value>NDEBUG</Value> + </ListValues> + </avr32gcc.compiler.symbols.DefSymbols> + <avr32gcc.compiler.directories.IncludePaths> + <ListValues> + <Value>%24(PackRepoDir)\atmel\UC3C_DFP\1.0.49\include\AT32UC3C2512C</Value> + <Value>..</Value> + <Value>../../../DiagnosticModule/DiagnosticModule</Value> + </ListValues> + </avr32gcc.compiler.directories.IncludePaths> + <avr32gcc.compiler.optimization.level>Optimize (-O1)</avr32gcc.compiler.optimization.level> + <avr32gcc.compiler.optimization.PrepareFunctionsForGarbageCollection>True</avr32gcc.compiler.optimization.PrepareFunctionsForGarbageCollection> + <avr32gcc.compiler.optimization.UseAssemblerForPseudoInstructions>True</avr32gcc.compiler.optimization.UseAssemblerForPseudoInstructions> + <avr32gcc.compiler.warnings.AllWarnings>True</avr32gcc.compiler.warnings.AllWarnings> + <avr32gcc.compiler.warnings.ExtraWarnings>True</avr32gcc.compiler.warnings.ExtraWarnings> + <avr32gcc.compiler.warnings.Undefined>True</avr32gcc.compiler.warnings.Undefined> + <avr32gcc.compiler.warnings.WarningsAsErrors>True</avr32gcc.compiler.warnings.WarningsAsErrors> + <avr32gcc.linker.libraries.Libraries> + <ListValues> + <Value>libm</Value> + <Value>libDiagnosticModule.a</Value> + </ListValues> + </avr32gcc.linker.libraries.Libraries> + <avr32gcc.linker.libraries.LibrarySearchPaths> + <ListValues> + <Value>../../../DiagnosticModule/DiagnosticModule/Release</Value> + </ListValues> + </avr32gcc.linker.libraries.LibrarySearchPaths> + <avr32gcc.linker.optimization.GarbageCollectUnusedSections>True</avr32gcc.linker.optimization.GarbageCollectUnusedSections> + <avr32gcc.linker.optimization.PutReadOnlyDataInWritableDataSection>True</avr32gcc.linker.optimization.PutReadOnlyDataInWritableDataSection> + <avr32gcc.linker.optimization.AllowDirectReferencesToDataSection>True</avr32gcc.linker.optimization.AllowDirectReferencesToDataSection> + <avr32gcc.assembler.general.IncludePaths> + <ListValues> + <Value>%24(PackRepoDir)\atmel\UC3C_DFP\1.0.49\include\AT32UC3C2512C</Value> + </ListValues> + </avr32gcc.assembler.general.IncludePaths> + <avr32gcc.preprocessingassembler.general.IncludePaths> + <ListValues> + <Value>%24(PackRepoDir)\atmel\UC3C_DFP\1.0.49\include\AT32UC3C2512C</Value> + </ListValues> + </avr32gcc.preprocessingassembler.general.IncludePaths> + </Avr32Gcc> + </ToolchainSettings> + <PreBuildEvent> + </PreBuildEvent> + <PostBuildEvent>"$(ToolchainDir)\avr32-objcopy.exe" -O binary $(OutputDirectory)\$(OutputFileName)$(OutputFileExtension) $(OutputDirectory)\$(OutputFileName).bin</PostBuildEvent> + </PropertyGroup> + <ItemGroup> + <Compile Include="main.c"> + <SubType>compile</SubType> + </Compile> + <Compile Include="protocol.c"> + <SubType>compile</SubType> + </Compile> + <Compile Include="protocol.h"> + <SubType>compile</SubType> + </Compile> + </ItemGroup> + <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" /> +</Project>
\ No newline at end of file diff --git a/impl/DMBootloader/DMBootloader/Release/DMBootloader.bin b/impl/DMBootloader/DMBootloader/Release/DMBootloader.bin Binary files differnew file mode 100644 index 0000000..c455753 --- /dev/null +++ b/impl/DMBootloader/DMBootloader/Release/DMBootloader.bin diff --git a/impl/DMBootloader/DMBootloader/main.c b/impl/DMBootloader/DMBootloader/main.c new file mode 100644 index 0000000..8626f3a --- /dev/null +++ b/impl/DMBootloader/DMBootloader/main.c @@ -0,0 +1,21 @@ +/* Author: Jan Sucan */ + +#include <avr32/io.h> +#include <protocol.h> +#include <diagnostic_module/diagnostic_module.h> + +int +main(void) +{ + diagnostic_module_init(); + // Odteraz su hlavne hodiny na 64,512 MHz + + // Prijem a spracovavanie prikazov + protocol_loop(); + // Sem uz sa tok programu nikdy nedostane, pretoze sa bud opakovane spracovavaju + // prikazu pre zapis, mazanie a citanie stranok, alebo sa vykona prikaz pre + // spustenie aplikacneho firmware a ten prevezme riadenie namiesto Bootloaderu + while (1) { + ; + } +} diff --git a/impl/DMBootloader/DMBootloader/protocol.c b/impl/DMBootloader/DMBootloader/protocol.c new file mode 100644 index 0000000..115e0ce --- /dev/null +++ b/impl/DMBootloader/DMBootloader/protocol.c @@ -0,0 +1,298 @@ +/* Author: Jan Sucan */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> + +#include <diagnostic_module/diagnostic_module.h> + +/** + * @brief Ziskanie cisla prikazu z bufferu pre data prijate cez YUP. + */ +#define PROTOCOL_GET_CMD_NUMBER(b) (b[0]) + +/** + * @brief Ziskanie cisla stranky z bufferu pre data prijate cez YUP. + */ +#define PROTOCOL_GET_PAGE_NUMBER(b) (GET_UINT16_T_FROM_BYTES(b, 1)) + +/** + * @brief Minimum prijatych bajtov, aby sa uz jednalo o nejaky platny prikaz. + */ +#define PROTOCOL_MINIMAL_SIZE 3 + +/** + * @brief Maximum prijatych bajtov, aby sa este jednalo o nejaky platny prikaz. + */ +#define PROTOCOL_MAX_RECEIVED_BYTES 515 + +/** + * @brief Maximum odosielanych bajtov. + */ +#define PROTOCOL_MAX_SEND_BYTES 513 + +/** + * @brief Nazov aplikacie. + * + * Pouzity v identifikacnom retazci aplikacie. + */ +#define PROTOCOL_APPLICATION_NAME "DMBootloader" + +/** + * @brief Verzia aplikacie. + * + * Pouzita v identifikacnom retazci aplikacie. + */ +#define PROTOCOL_APPLICATION_VERSION "0.1.0" + +enum protocol_return_codes { + PROTOCOL_OK = 0x00, + // Chyby protokolu + PROTOCOL_ERROR_UNKNOWN_CMD = 0x10, + PROTOCOL_ERROR_MISSING_PAGENUM, + PROTOCOL_ERROR_PAGENUM_OUT_OF_BOUNDS, + PROTOCOL_ERROR_MISSING_DATA, + PROTOCOL_ERROR_EXTRA_DATA, + // Chyby FLASH subsystemu + PROTOCOL_ERROR_FLASH_WRITE = 0x30, + PROTOCOL_ERROR_FLASH_ERASE, + PROTOCOL_ERROR_FLASH_READ +}; + +enum protocol_command_codes { + PROTOCOL_CMD_WRITE_PAGE = 0, + PROTOCOL_CMD_ERASE_PAGE, + PROTOCOL_CMD_READ_PAGE, + PROTOCOL_CMD_EXEC_APPLICATION, + PROTOCOL_CMD_GET_APPLICATION_ID = UINT8_MAX +}; + +/** + * @brief Pocet aplikacnych prikazov podporovanych touto aplikaciou. + */ +#define PROTOCOL_CMD_COUNT 5 + +// Parametre dat pre kontrolu prikazov, minumum a maximum bajtov +typedef struct cmd_data_size_s { + size_t min_bytes; + size_t max_bytes; +} cmd_data_size_t; + +const cmd_data_size_t cmd_data_sizes[PROTOCOL_CMD_COUNT] = { + {PROTOCOL_MINIMAL_SIZE + 1, PROTOCOL_MAX_RECEIVED_BYTES}, // PROTOCOL_CMD_WRITE_PAGE + {PROTOCOL_MINIMAL_SIZE, PROTOCOL_MINIMAL_SIZE}, // PROTOCOL_CMD_ERASE_PAGE + {PROTOCOL_MINIMAL_SIZE, PROTOCOL_MINIMAL_SIZE}, // PROTOCOL_CMD_READ_PAGE + {PROTOCOL_MINIMAL_SIZE, PROTOCOL_MINIMAL_SIZE} // PROTOCOL_CMD_EXEC_APPLICATION +}; + +static bool protocol_is_cmd_code_valid(uint8_t cmd_code); +static void protocol_send_reply_with_data(uint8_t ret_code, uint8_t * const data, uint16_t data_size); +static void protocol_send_reply(uint8_t ret_code); +static bool protocol_cmd_has_minimal_size(uint8_t cmd_code, size_t cmd_bytes); +static bool protocol_cmd_has_extra_data(uint8_t cmd_code, size_t cmd_bytes); +static bool protocol_cmd_contains_page_number(size_t cmd_bytes); + +/** + * @brief Prikazova slucka aplikacneho protokolu bootloaderu. + * + * Podporovane prikazy: + * Zapis stranky: + * 1 B - kod prikazu + * 2 B - cislo stranky (od 0) + * 1 az 512 B - dat stranky + * Odpoved: + * 1 B - navratovy kod + * + * Mazanie stranky: + * 1 B - kod prikazu + * 2 B - cislo stranky (od 0) + * Odpoved: + * 1 B - navratovy kod + * + * Citanie stranky: + * 1 B - kod prikazu + * 2 B - cislo stranky (od 0) + * Odpoved: + * 1 B - navratovy kod + * 0 alebo 512 B - data stranky, 512B len pri spravnom prikaze + * + * Predanie riadenia na stranku: + * 1 B - kod prikazu + * 2 B - cislo stranky (od 0) + * Odpoved: + * 1 B - navratovy kod + * + * Ziskanie identifikacneho retazca aplikacie: + * 1 B - kod prikazu + * Odpoved: + * ASCII znaky ID retazca bez ukoncovacieho znaku '\0'. + */ +void +protocol_loop(void) +{ + while (1) { + // Najviac dat (1 + 2 + 512 = 515B) sa bude prijimat pre prikaz zapisu stranky + uint8_t buf[PROTOCOL_MAX_RECEIVED_BYTES]; + size_t bytes_received; + + // Prijmu sa data prikazu + while (data_receive(buf, PROTOCOL_MAX_RECEIVED_BYTES, &bytes_received) != DATA_OK); + + // Skontroluje sa cislo prikazu, urcite sa prijal aspon 1B + // Funkciam pre prikazy sa uz nemusi predavat cislo prikazu + // Kontrola ci je velkost dat prikazu v prislusnych medziach + const uint8_t cmd_number = PROTOCOL_GET_CMD_NUMBER(buf); + + if (!protocol_is_cmd_code_valid(cmd_number)) { + // Nezname cislo prikazu + protocol_send_reply(PROTOCOL_ERROR_UNKNOWN_CMD); + } + + // Cislo prikazu je OK + if (cmd_number == PROTOCOL_CMD_GET_APPLICATION_ID) { + // Prikaz pre ziskanie identifikacneho retazca aplikacie je OK + // Zostavi sa identifikacny retazec + char id_string[64]; + int id_string_length = firmware_identification_string(id_string, PROTOCOL_APPLICATION_NAME, PROTOCOL_APPLICATION_VERSION); + // Odosle sa + data_send((uint8_t *) id_string, id_string_length); + // Vsetky dalsie prikazy obsahuju viac bajtov pre kontrolu + } else if (!protocol_cmd_contains_page_number(bytes_received)) { + // Cislo stranky nie je pritomne + protocol_send_reply(PROTOCOL_ERROR_MISSING_PAGENUM); + } else if (!FLASH_IS_VALID_VIRT_PAGE_NUMBER(PROTOCOL_GET_PAGE_NUMBER(buf))) { + // Cislo stranky nie z platneho rozsahu + protocol_send_reply(PROTOCOL_ERROR_PAGENUM_OUT_OF_BOUNDS); + } else if (!protocol_cmd_has_minimal_size(cmd_number, bytes_received)) { + // Prikazu chybaju data + protocol_send_reply(PROTOCOL_ERROR_MISSING_DATA); + } else if (protocol_cmd_has_extra_data(cmd_number, bytes_received)) { + // Prikaz obsahuje data navyse + protocol_send_reply(PROTOCOL_ERROR_EXTRA_DATA); + } else { + // Prikaz OK, cislo stranky OK, velkost dat OK + int r = -1; + const uint16_t page_num = PROTOCOL_GET_PAGE_NUMBER(buf); + + switch (cmd_number) { + case PROTOCOL_CMD_WRITE_PAGE: + // Od velkosti dat sa odcita 1B prikazu a 2B cisla stranky + r = flash_write_virtual_page(page_num, buf + 3, bytes_received - 3); + protocol_send_reply((r == 0) ? PROTOCOL_OK : PROTOCOL_ERROR_FLASH_WRITE); + break; + + case PROTOCOL_CMD_ERASE_PAGE: + r = flash_erase_virtual_page(page_num); + protocol_send_reply((r == 0) ? PROTOCOL_OK : PROTOCOL_ERROR_FLASH_ERASE); + break; + + case PROTOCOL_CMD_READ_PAGE: + // Nacitavame az za 0. bajt, ten nechavame pre navratovy kod odpovede, aby sme recyklovali buffer + r = flash_read_virtual_page(page_num, buf + 1); + if (r == 0) { + // Uspech, mame data + protocol_send_reply_with_data(PROTOCOL_OK, buf, PROTOCOL_MAX_SEND_BYTES); + } else { + // Chyba + protocol_send_reply(PROTOCOL_ERROR_FLASH_READ); + } + break; + + case PROTOCOL_CMD_EXEC_APPLICATION: + // Odpoved sa musi poslat vopred, pretoze po spustenie uz strati Bootloader riadenie + protocol_send_reply(PROTOCOL_OK); + exec_application_firmware_at_virtual_page(page_num); + break; + } + } + + } +} + +/** + * @brief Kontrola, ci je cislo prikazu platne. + * + * @param cmd_code Cislo prikazu. + * + * @retval true Cislo prikazu je platne. + * @retval false Neplatne cislo prikazu. + */ +bool +protocol_is_cmd_code_valid(uint8_t cmd_code) +{ + return ((cmd_code == PROTOCOL_CMD_WRITE_PAGE) + || (cmd_code == PROTOCOL_CMD_ERASE_PAGE) + || (cmd_code == PROTOCOL_CMD_READ_PAGE) + || (cmd_code == PROTOCOL_CMD_EXEC_APPLICATION) + || (cmd_code == PROTOCOL_CMD_GET_APPLICATION_ID)); +} + +/** + * @brief Odoslanie odpovede aplikacneho protokolu, ktora nesie data. + * + * @param ret_code Navratovy kod v odpovedi. + * @param data Ukazovatel na buffer dat pre odoslanie. Musi mat vyhradeny prvy bajt pre umiestnenie navratoveho kodu. + * @param data_size Velkost bufferu v bajtoch. + */ +void +protocol_send_reply_with_data(uint8_t ret_code, uint8_t * const data, uint16_t data_size) +{ + data[0] = ret_code; + // Predpoklada sa, ze sa odosiela navratovy kod 1B + 512B dat stranky + data_send(data, data_size); +} + +/** + * @brief Odoslanie odpovede aplikacneho protokolu bez pripojenych dat. + * + * @param ret_code Navratovy kod v odpovedi. + */ +void +protocol_send_reply(uint8_t ret_code) +{ + // Nekontroluje sa uspesnost odoslania + data_send(&ret_code, 1); +} + +/** + * @brief Kontrola, ci prijaty prikaz obsahuje aj cislo stranky. + * + * @param cmd_bytes Velkost prijateho prikazu v bajtoch. + * + * @retval true Prikaz obsahuje cislo stranky. + * @retval false Prikaz neobsahuje cislo stranky. + */ +bool +protocol_cmd_contains_page_number(size_t cmd_bytes) { + return (cmd_bytes >= PROTOCOL_MINIMAL_SIZE); +} + +/** + * @brief Kontrola, ci konkretny prijaty prikaz obsahuje aspon minumum dat, aby bol platny. + * + * @param cmd_code Kod prijateho prikazu. + * @param cmd_bytes Velkost prijateho prikazu v bajtoch. + * + * @retval true Prikaz je platny. + * @retval false Prikaz nie je platny. + */ +bool +protocol_cmd_has_minimal_size(uint8_t cmd_code, size_t cmd_bytes) +{ + return (cmd_bytes >= cmd_data_sizes[cmd_code].min_bytes); +} + +/** + * @brief Kontrola, ci konkretny prijaty prikaz obsahuje zbytocne data navyse. + * + * @param cmd_code Kod prijateho prikazu. + * @param cmd_bytes Velkost prijateho prikazu v bajtoch. + * + * @retval true Prikaz je platny. + * @retval false Prikaz nie je platny pretoze obsahuje zbytocne data navyse. + */ +bool +protocol_cmd_has_extra_data(uint8_t cmd_code, size_t cmd_bytes) +{ + return (cmd_bytes > cmd_data_sizes[cmd_code].max_bytes); +} diff --git a/impl/DMBootloader/DMBootloader/protocol.h b/impl/DMBootloader/DMBootloader/protocol.h new file mode 100644 index 0000000..16959cf --- /dev/null +++ b/impl/DMBootloader/DMBootloader/protocol.h @@ -0,0 +1,8 @@ +/* Author: Jan Sucan */ + +#ifndef PROTOCOL_H_ +#define PROTOCOL_H_ + +void protocol_loop(void); + +#endif /* PROTOCOL_H_ */
\ No newline at end of file |
