From fa343db3347721dd01289a62cef9cb34a1f1025b Mon Sep 17 00:00:00 2001 From: pvvx Date: Sat, 22 Apr 2017 16:54:00 +0300 Subject: [PATCH] first commit --- .cproject | 735 ++++++ .gitignore | 2 + .project | 117 + ...ilg.gnuarmeclipse.managedbuild.cross.prefs | 3 + .settings/language.settings.xml | 36 + .settings/org.eclipse.cdt.codan.core.prefs | 67 + .settings/org.eclipse.cdt.core.prefs | 52 + .../org.eclipse.cdt.managedbuilder.core.prefs | 25 + .settings/org.eclipse.core.resources.prefs | 2 + .../org.eclipse.ltk.core.refactoring.prefs | 2 + DAPLink-RTL00Console.bat | 7 + DAPLink-RdFullFlash.bat | 7 + DAPLink-Reset.bat | 6 + DAPLink_WrFullFlash.bat | 6 + JLink-RTL00ConsoleROM.bat | 3 + JLink-RdFullFlash.bat | 3 + JLink-Reset.bat | 3 + JLink-RunRAM.bat | 3 + JLinkGDB-RdFullFlash.bat | 6 + JLinkGDB-RunRAM.bat | 15 + JLinkGDB-WrFlash.bat | 21 + JLinkGDBServer.bat | 6 + JLinkGDB_OTA.bat | 15 + JLinkOCD-RdFullFlash.bat | 7 + JLink_RdFullFlash.bat | 6 + JlinkOpenOCD.bat | 4 + LICENSE | 24 + Makefile | 57 + README.md | 12 + STLink-RdFullFlash.bat | 7 + STLink-Reset.bat | 6 + WEBFiles/$js.inc | 1 + WEBFiles/404.htm | 14 + WEBFiles/adc.htm | 10 + WEBFiles/disk_er1.htm | 5 + WEBFiles/disk_er2.htm | 5 + WEBFiles/disk_er3.htm | 5 + WEBFiles/disk_ok.htm | 5 + WEBFiles/favicon.ico | Bin 0 -> 2238 bytes WEBFiles/footer.inc | 4 + WEBFiles/grf.js | 613 +++++ WEBFiles/grfx1.inc | 13 + WEBFiles/grfx2.inc | 6 + WEBFiles/heap.htm | 9 + WEBFiles/heap.xml | 1 + WEBFiles/index.htm | 20 + WEBFiles/logo.gif | Bin 0 -> 393 bytes WEBFiles/menu.inc | 26 + WEBFiles/protect/chiprams.xml | 4 + WEBFiles/protect/cookie.js | 70 + WEBFiles/protect/debug.htm | 25 + WEBFiles/protect/dsleep.htm | 30 + WEBFiles/protect/fullflash.bin | 1 + WEBFiles/protect/hexdmpb.htm | 41 + WEBFiles/protect/hexdmpb.txt | 1 + WEBFiles/protect/hexdmpd.htm | 41 + WEBFiles/protect/hexdmpd.txt | 1 + WEBFiles/protect/ram.bin | 1 + WEBFiles/protect/scan.htm | 98 + WEBFiles/protect/scan.xml | 1 + WEBFiles/protect/setup.htm | 89 + WEBFiles/protect/tstfuncs.htm | 91 + WEBFiles/protect/upload.htm | 23 + WEBFiles/protect/wifi.htm | 192 ++ WEBFiles/rtl.gif | Bin 0 -> 750 bytes WEBFiles/rtl1.gif | Bin 0 -> 6763 bytes WEBFiles/scripts.js | 33 + WEBFiles/site.js | 95 + WEBFiles/slider.js | 97 + WEBFiles/smoothie.js | 808 +++++++ WEBFiles/style.css | 235 ++ WEBFiles/time.inc | 9 + WEBFiles/timeout.htm | 16 + WEBFiles/timer.inc | 12 + WEBFiles/tst.htm | 9 + WEBFiles/tst.xml | 1 + WEBFiles/websock.htm | 116 + WEBFiles/ws2.css | 12 + flasher.mk | 307 +++ flasher/RTL00ConsoleROM.JLinkScript | 6 + flasher/RTL00Console_ROM.bin | Bin 0 -> 600 bytes flasher/RTL8710.jflash | 119 + flasher/RTL8710AF.hex | 130 ++ flasher/RTL8710AF.jflash | 153 ++ flasher/RTL_FFlash.JLinkScript | 17 + flasher/RTL_Reset.JLinkScript | 9 + flasher/RTL_RunRAM.JLinkScript | 12 + flasher/ameba1.cfg | 124 + flasher/cortex.ocd | 99 + flasher/file_info.jlink | 7 + flasher/flash_file.jlink | 9 + flasher/gdb_flasher.jlink | 198 ++ flasher/gdb_init.jlink | 30 + flasher/gdb_ota.jlink | 374 +++ flasher/gdb_rdflash.jlink | 17 + flasher/gdb_run_ram.jlink | 11 + flasher/gdb_wrfile.jlink | 156 ++ flasher/gdb_wrflash.jlink | 164 ++ flasher/rtl8710.ocd | 340 +++ flasher/rtl8710_flasher.bin | Bin 0 -> 968 bytes paths.bat | 1 + paths.mk | 44 + project/inc/.gitignore | 1 + project/inc/FreeRTOSConfig.h | 210 ++ project/inc/feep_config.h | 30 + project/inc/lwipopts.h | 381 +++ project/inc/main.h | 119 + project/inc/platform_autoconf.h | 251 ++ project/inc/platform_opts.h | 172 ++ project/inc/rtl8195a/c_types.h | 96 + project/inc/rtl8195a/os.h | 593 +++++ project/inc/rtl8195a/queue.h | 204 ++ project/inc/rtl8195a/rom_wps_os.h | 24 + project/inc/rtl8195a/rtl_common.h | 15 + project/inc/rtl8195a/rtl_libc.h | 164 ++ project/inc/tcpsrv/tcp_srv_conn.h | 191 ++ project/inc/user/main.h | 68 + project/inc/user/sys_cfg.h | 64 + project/inc/user_config.h | 34 + project/inc/web/web_srv.h | 196 ++ project/inc/web/web_srv_int.h | 49 + project/inc/web/web_utils.h | 33 + project/inc/web/web_websocket.h | 17 + project/inc/web/websock.h | 108 + project/inc/webfs/webfs.h | 116 + project/inc/wifi_user_set.h | 67 + project/src/console/adc_tst.c | 258 ++ project/src/console/atcmd_user.c | 359 +++ project/src/console/flash_tst.c | 38 + project/src/console/gpio_irq_test.c | 99 + project/src/console/power_tst.c | 39 + project/src/console/pwm_tst.c | 59 + project/src/console/spi_tst.c | 83 + project/src/console/wifi_console.c | 338 +++ project/src/console/wlan_tst.c | 50 + project/src/tcpsrv/tcp_srv_conn.c | 1269 ++++++++++ project/src/user/main.c | 97 + project/src/user/user_start.c | 77 + project/src/web/web_int_callbacks.c | 688 ++++++ project/src/web/web_int_vars.c | 356 +++ project/src/web/web_srv.c | 2073 +++++++++++++++++ project/src/web/web_utils.c | 640 +++++ project/src/web/web_websocket.c | 347 +++ project/src/web/websock.c | 245 ++ project/src/webfs/webfs.c | 483 ++++ project_set.xml | 177 ++ sdkbuild.mk | 93 + sdkset.mk | 433 ++++ tools/webfs/WEBFS22.exe | Bin 0 -> 76800 bytes tools/webfs/py/README.md | 7 + tools/webfs/py/webfs_upload1.py | 157 ++ tools/webfs/py/webfs_upload2.py | 30 + tools/webfs/src/WEBFS22.zip | Bin 0 -> 321400 bytes webfs.mk | 7 + 154 files changed, 18186 insertions(+) create mode 100644 .cproject create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/ilg.gnuarmeclipse.managedbuild.cross.prefs create mode 100644 .settings/language.settings.xml create mode 100644 .settings/org.eclipse.cdt.codan.core.prefs create mode 100644 .settings/org.eclipse.cdt.core.prefs create mode 100644 .settings/org.eclipse.cdt.managedbuilder.core.prefs create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.ltk.core.refactoring.prefs create mode 100644 DAPLink-RTL00Console.bat create mode 100644 DAPLink-RdFullFlash.bat create mode 100644 DAPLink-Reset.bat create mode 100644 DAPLink_WrFullFlash.bat create mode 100644 JLink-RTL00ConsoleROM.bat create mode 100644 JLink-RdFullFlash.bat create mode 100644 JLink-Reset.bat create mode 100644 JLink-RunRAM.bat create mode 100644 JLinkGDB-RdFullFlash.bat create mode 100644 JLinkGDB-RunRAM.bat create mode 100644 JLinkGDB-WrFlash.bat create mode 100644 JLinkGDBServer.bat create mode 100644 JLinkGDB_OTA.bat create mode 100644 JLinkOCD-RdFullFlash.bat create mode 100644 JLink_RdFullFlash.bat create mode 100644 JlinkOpenOCD.bat create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 STLink-RdFullFlash.bat create mode 100644 STLink-Reset.bat create mode 100644 WEBFiles/$js.inc create mode 100644 WEBFiles/404.htm create mode 100644 WEBFiles/adc.htm create mode 100644 WEBFiles/disk_er1.htm create mode 100644 WEBFiles/disk_er2.htm create mode 100644 WEBFiles/disk_er3.htm create mode 100644 WEBFiles/disk_ok.htm create mode 100644 WEBFiles/favicon.ico create mode 100644 WEBFiles/footer.inc create mode 100644 WEBFiles/grf.js create mode 100644 WEBFiles/grfx1.inc create mode 100644 WEBFiles/grfx2.inc create mode 100644 WEBFiles/heap.htm create mode 100644 WEBFiles/heap.xml create mode 100644 WEBFiles/index.htm create mode 100644 WEBFiles/logo.gif create mode 100644 WEBFiles/menu.inc create mode 100644 WEBFiles/protect/chiprams.xml create mode 100644 WEBFiles/protect/cookie.js create mode 100644 WEBFiles/protect/debug.htm create mode 100644 WEBFiles/protect/dsleep.htm create mode 100644 WEBFiles/protect/fullflash.bin create mode 100644 WEBFiles/protect/hexdmpb.htm create mode 100644 WEBFiles/protect/hexdmpb.txt create mode 100644 WEBFiles/protect/hexdmpd.htm create mode 100644 WEBFiles/protect/hexdmpd.txt create mode 100644 WEBFiles/protect/ram.bin create mode 100644 WEBFiles/protect/scan.htm create mode 100644 WEBFiles/protect/scan.xml create mode 100644 WEBFiles/protect/setup.htm create mode 100644 WEBFiles/protect/tstfuncs.htm create mode 100644 WEBFiles/protect/upload.htm create mode 100644 WEBFiles/protect/wifi.htm create mode 100644 WEBFiles/rtl.gif create mode 100644 WEBFiles/rtl1.gif create mode 100644 WEBFiles/scripts.js create mode 100644 WEBFiles/site.js create mode 100644 WEBFiles/slider.js create mode 100644 WEBFiles/smoothie.js create mode 100644 WEBFiles/style.css create mode 100644 WEBFiles/time.inc create mode 100644 WEBFiles/timeout.htm create mode 100644 WEBFiles/timer.inc create mode 100644 WEBFiles/tst.htm create mode 100644 WEBFiles/tst.xml create mode 100644 WEBFiles/websock.htm create mode 100644 WEBFiles/ws2.css create mode 100644 flasher.mk create mode 100644 flasher/RTL00ConsoleROM.JLinkScript create mode 100644 flasher/RTL00Console_ROM.bin create mode 100644 flasher/RTL8710.jflash create mode 100644 flasher/RTL8710AF.hex create mode 100644 flasher/RTL8710AF.jflash create mode 100644 flasher/RTL_FFlash.JLinkScript create mode 100644 flasher/RTL_Reset.JLinkScript create mode 100644 flasher/RTL_RunRAM.JLinkScript create mode 100644 flasher/ameba1.cfg create mode 100644 flasher/cortex.ocd create mode 100644 flasher/file_info.jlink create mode 100644 flasher/flash_file.jlink create mode 100644 flasher/gdb_flasher.jlink create mode 100644 flasher/gdb_init.jlink create mode 100644 flasher/gdb_ota.jlink create mode 100644 flasher/gdb_rdflash.jlink create mode 100644 flasher/gdb_run_ram.jlink create mode 100644 flasher/gdb_wrfile.jlink create mode 100644 flasher/gdb_wrflash.jlink create mode 100644 flasher/rtl8710.ocd create mode 100644 flasher/rtl8710_flasher.bin create mode 100644 paths.bat create mode 100644 paths.mk create mode 100644 project/inc/.gitignore create mode 100644 project/inc/FreeRTOSConfig.h create mode 100644 project/inc/feep_config.h create mode 100644 project/inc/lwipopts.h create mode 100644 project/inc/main.h create mode 100644 project/inc/platform_autoconf.h create mode 100644 project/inc/platform_opts.h create mode 100644 project/inc/rtl8195a/c_types.h create mode 100644 project/inc/rtl8195a/os.h create mode 100644 project/inc/rtl8195a/queue.h create mode 100644 project/inc/rtl8195a/rom_wps_os.h create mode 100644 project/inc/rtl8195a/rtl_common.h create mode 100644 project/inc/rtl8195a/rtl_libc.h create mode 100644 project/inc/tcpsrv/tcp_srv_conn.h create mode 100644 project/inc/user/main.h create mode 100644 project/inc/user/sys_cfg.h create mode 100644 project/inc/user_config.h create mode 100644 project/inc/web/web_srv.h create mode 100644 project/inc/web/web_srv_int.h create mode 100644 project/inc/web/web_utils.h create mode 100644 project/inc/web/web_websocket.h create mode 100644 project/inc/web/websock.h create mode 100644 project/inc/webfs/webfs.h create mode 100644 project/inc/wifi_user_set.h create mode 100644 project/src/console/adc_tst.c create mode 100644 project/src/console/atcmd_user.c create mode 100644 project/src/console/flash_tst.c create mode 100644 project/src/console/gpio_irq_test.c create mode 100644 project/src/console/power_tst.c create mode 100644 project/src/console/pwm_tst.c create mode 100644 project/src/console/spi_tst.c create mode 100644 project/src/console/wifi_console.c create mode 100644 project/src/console/wlan_tst.c create mode 100644 project/src/tcpsrv/tcp_srv_conn.c create mode 100644 project/src/user/main.c create mode 100644 project/src/user/user_start.c create mode 100644 project/src/web/web_int_callbacks.c create mode 100644 project/src/web/web_int_vars.c create mode 100644 project/src/web/web_srv.c create mode 100644 project/src/web/web_utils.c create mode 100644 project/src/web/web_websocket.c create mode 100644 project/src/web/websock.c create mode 100644 project/src/webfs/webfs.c create mode 100644 project_set.xml create mode 100644 sdkbuild.mk create mode 100644 sdkset.mk create mode 100644 tools/webfs/WEBFS22.exe create mode 100644 tools/webfs/py/README.md create mode 100644 tools/webfs/py/webfs_upload1.py create mode 100644 tools/webfs/py/webfs_upload2.py create mode 100644 tools/webfs/src/WEBFS22.zip create mode 100644 webfs.mk diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..e90b2e5 --- /dev/null +++ b/.cproject @@ -0,0 +1,735 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mingw32-make.exe + -s -j 4 + all + true + false + false + + + make + -s + clean + true + true + false + + + make + -s + flashburn + true + true + false + + + make + -s + reset + true + true + false + + + make + -s + test + true + true + false + + + mingw32-make.exe + -s + readfullflash + true + true + false + + + mingw32-make.exe + -s + runram + true + true + false + + + mingw32-make.exe + -s + mp + true + true + false + + + mingw32-make.exe + -s + webfs + true + true + true + + + mingw32-make.exe + -s + flashwebfs + true + true + true + + + mingw32-make.exe + -s + flash_OTA + true + true + true + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0372c5a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/AutoMake/ +/build/ \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..4ba9658 --- /dev/null +++ b/.project @@ -0,0 +1,117 @@ + + + RTL00_WEB + + + RTL00_SDKV35a + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + + + RTL00_SDKV35a + 2 + PARENT-1-PROJECT_LOC/RTL00MP3/RTL00_SDKV35a + + + + + 0 + RTL00_SDKV35a + 5 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.h + + + + 0 + RTL00_SDKV35a + 5 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-*.c + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-AutoMake + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-build + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-flasher + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-LibAutoMake + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-project + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-.git + + + + 0 + RTL00_SDKV35a + 10 + + org.eclipse.ui.ide.multiFilter + 1.0-name-matches-false-false-.settings + + + + diff --git a/.settings/ilg.gnuarmeclipse.managedbuild.cross.prefs b/.settings/ilg.gnuarmeclipse.managedbuild.cross.prefs new file mode 100644 index 0000000..ba91ca5 --- /dev/null +++ b/.settings/ilg.gnuarmeclipse.managedbuild.cross.prefs @@ -0,0 +1,3 @@ +buildTools.path=D\:\\MCU\\GNU_Tools_ARM_Embedded\\5.4_2016q2\\bin +eclipse.preferences.version=1 +toolchain.path.1287942917=D\:\\MCU\\GNU_Tools_ARM_Embedded\\5.4_2016q2\\ diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml new file mode 100644 index 0000000..baf2884 --- /dev/null +++ b/.settings/language.settings.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.settings/org.eclipse.cdt.codan.core.prefs b/.settings/org.eclipse.cdt.codan.core.prefs new file mode 100644 index 0000000..77386c2 --- /dev/null +++ b/.settings/org.eclipse.cdt.codan.core.prefs @@ -0,0 +1,67 @@ +eclipse.preferences.version=1 +org.eclipse.cdt.codan.checkers.errnoreturn=Warning +org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} +org.eclipse.cdt.codan.checkers.errreturnvalue=Error +org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.checkers.noreturn=Error +org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error +org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error +org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error +org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning +org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} +org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning +org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error +org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning +org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error +org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error +org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error +org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info +org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning +org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error +org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error +org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning +org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning +org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning +org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning +org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error +org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs new file mode 100644 index 0000000..d24cd11 --- /dev/null +++ b/.settings/org.eclipse.cdt.core.prefs @@ -0,0 +1,52 @@ +eclipse.preferences.version=1 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MINGW_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MINGW_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MINGW_HOME/value=C\:\\MinGW +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MSYS_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MSYS_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/MSYS_HOME/value=C\:\\MinGW\\msys\\1.0 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/OCD_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/OCD_PATH/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/OCD_PATH/value=D\:\\MCU\\OpenOCD\\bin +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/PATH/value=${TL_PATH}\\bin;${MINGW_HOME}\\mingw64\\bin;${MINGW_HOME}\\bin;${MSYS_HOME}\\bin;${OCD_PATH};C\:/Program Files (x86)/Java/jre1.8.0_101/bin/client;C\:/Program Files (x86)/Java/jre1.8.0_101/bin;C\:/Program Files (x86)/Java/jre1.8.0_101/lib/i386;C\:\\MinGW\\mingw64\\bin;C\:\\MinGW\\msys\\1.0\\bin;C\:\\MinGW\\bin;D\:\\MCU\\STMicroelectronics\\st_toolset\\asm;C\:\\Python27;C\:\\Utils\\FarUtils;C\:\\Utils\\FarUtils\\HIEW810;C\:\\Windows;C\:\\Windows\\system32;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0;D\:\\MCU\\Microchip\\xc32\\v1.42\\bin;D\:\\MCU\\Microchip\\mplabc30\\v3.31\\bin;D\:\\MCU\\Microchip\\MPLAB C32 Suite\\bin;D\:\\MCU\\Microchip\\mplabc32\\v1.12\\bin;D\:\\MCU\\Microchip\\mcc18\\mpasm;D\:\\MCU\\Microchip\\mcc18\\bin;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\binC\:\\Program Files (x86)\\Git\\cmd;C\:\\Program Files (x86)\\Borland\\Delphi7\\Bin;C\:\\Program Files (x86)\\Borland\\Delphi7\\Projects\\Bpl\\;C\:\\Program Files (x86)\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files (x86)\\ATI Technologies\\ATI.ACE\\Core-Static;C\:\\Program Files (x86)\\Common Files\\Acronis\\SnapAPI;C\:\\Program Files (x86)\\Windows Live\\Shared;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit;C\:\\Program Files (x86)\\Microsoft SDKs\\TypeScript\\1.0;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn;C\:\\Program Files\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn;C\:\\Program Files\\Microsoft DNX\\Dnvm;C\:\\Program Files\\IVI Foundation\\VISA\\Win64\\Bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\X86\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\X86\\bin;C\:\\Users\\PVV\\.dnx\\bin;C\:\\ProgramData\\chocolatey\\bin;C\:\\ProgramData\\Oracle\\Java\\javapath;C\:\\Program Files (x86)\\QuickTime\\QTSystem;C\:\\Program Files\\nodejs;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\bin;C\:\\Program Files (x86)\\Git\\cmd;D\:\\MentorGraphics\\Sourcery_CodeBench_Lite_for_MIPS_ELF\\bin;C\:\\Eclipse +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/TL_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/TL_PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/TL_PATH/value=D\:\\MCU\\GNU_Tools_ARM_Embedded\\5.4_2016q2 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/append=true +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/appendContributed=true +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MINGW_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MINGW_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MINGW_HOME/value=C\:\\MinGW +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MSYS_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MSYS_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/MSYS_HOME/value=C\:\\MinGW\\msys\\1.0 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/OCD_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/OCD_PATH/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/OCD_PATH/value=D\:\\MCU\\OpenOCD\\bin +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/PATH/value=${TL_PATH}\\bin;${MINGW_HOME}\\mingw64\\bin;${MINGW_HOME}\\bin;${MSYS_HOME}\\bin;${OCD_PATH};C\:/Program Files (x86)/Java/jre1.8.0_101/bin/client;C\:/Program Files (x86)/Java/jre1.8.0_101/bin;C\:/Program Files (x86)/Java/jre1.8.0_101/lib/i386;C\:\\MinGW\\mingw64\\bin;C\:\\MinGW\\msys\\1.0\\bin;C\:\\MinGW\\bin;D\:\\MCU\\STMicroelectronics\\st_toolset\\asm;C\:\\Python27;C\:\\Utils\\FarUtils;C\:\\Utils\\FarUtils\\HIEW810;C\:\\Windows;C\:\\Windows\\system32;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0;D\:\\MCU\\Microchip\\xc32\\v1.42\\bin;D\:\\MCU\\Microchip\\mplabc30\\v3.31\\bin;D\:\\MCU\\Microchip\\MPLAB C32 Suite\\bin;D\:\\MCU\\Microchip\\mplabc32\\v1.12\\bin;D\:\\MCU\\Microchip\\mcc18\\mpasm;D\:\\MCU\\Microchip\\mcc18\\bin;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\binC\:\\Program Files (x86)\\Git\\cmd;C\:\\Program Files (x86)\\Borland\\Delphi7\\Bin;C\:\\Program Files (x86)\\Borland\\Delphi7\\Projects\\Bpl\\;C\:\\Program Files (x86)\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files (x86)\\ATI Technologies\\ATI.ACE\\Core-Static;C\:\\Program Files (x86)\\Common Files\\Acronis\\SnapAPI;C\:\\Program Files (x86)\\Windows Live\\Shared;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit;C\:\\Program Files (x86)\\Microsoft SDKs\\TypeScript\\1.0;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn;C\:\\Program Files\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn;C\:\\Program Files\\Microsoft DNX\\Dnvm;C\:\\Program Files\\IVI Foundation\\VISA\\Win64\\Bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\X86\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\X86\\bin;C\:\\Users\\PVV\\.dnx\\bin;C\:\\ProgramData\\chocolatey\\bin;C\:\\ProgramData\\Oracle\\Java\\javapath;C\:\\Program Files (x86)\\QuickTime\\QTSystem;C\:\\Program Files\\nodejs;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\bin;C\:\\Program Files (x86)\\Git\\cmd;D\:\\MentorGraphics\\Sourcery_CodeBench_Lite_for_MIPS_ELF\\bin;C\:\\Eclipse +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/TL_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/TL_PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/TL_PATH/value=D\:\\MCU\\GNU_Tools_ARM_Embedded\\5.4_2016q2 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/append=true +environment/project/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/appendContributed=true +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MINGW_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MINGW_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MINGW_HOME/value=C\:\\MinGW +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MSYS_HOME/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MSYS_HOME/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/MSYS_HOME/value=C\:\\MinGW\\msys\\1.0 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/OCD_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/OCD_PATH/operation=append +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/OCD_PATH/value=D\:\\MCU\\OpenOCD\\bin +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/PATH/value=${TL_PATH}\\bin;${MINGW_HOME}\\mingw64\\bin;${MINGW_HOME}\\bin;${MSYS_HOME}\\bin;${OCD_PATH};C\:/Program Files (x86)/Java/jre1.8.0_101/bin/client;C\:/Program Files (x86)/Java/jre1.8.0_101/bin;C\:/Program Files (x86)/Java/jre1.8.0_101/lib/i386;C\:\\MinGW\\mingw64\\bin;C\:\\MinGW\\msys\\1.0\\bin;C\:\\MinGW\\bin;D\:\\MCU\\STMicroelectronics\\st_toolset\\asm;C\:\\Python27;C\:\\Utils\\FarUtils;C\:\\Utils\\FarUtils\\HIEW810;C\:\\Windows;C\:\\Windows\\system32;C\:\\Windows\\System32\\Wbem;C\:\\Windows\\System32\\WindowsPowerShell\\v1.0;D\:\\MCU\\Microchip\\xc32\\v1.42\\bin;D\:\\MCU\\Microchip\\mplabc30\\v3.31\\bin;D\:\\MCU\\Microchip\\MPLAB C32 Suite\\bin;D\:\\MCU\\Microchip\\mplabc32\\v1.12\\bin;D\:\\MCU\\Microchip\\mcc18\\mpasm;D\:\\MCU\\Microchip\\mcc18\\bin;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\binC\:\\Program Files (x86)\\Git\\cmd;C\:\\Program Files (x86)\\Borland\\Delphi7\\Bin;C\:\\Program Files (x86)\\Borland\\Delphi7\\Projects\\Bpl\\;C\:\\Program Files (x86)\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files (x86)\\ATI Technologies\\ATI.ACE\\Core-Static;C\:\\Program Files (x86)\\Common Files\\Acronis\\SnapAPI;C\:\\Program Files (x86)\\Windows Live\\Shared;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files (x86)\\Windows Kits\\8.1\\Windows Performance Toolkit;C\:\\Program Files (x86)\\Microsoft SDKs\\TypeScript\\1.0;C\:\\Program Files (x86)\\IVI Foundation\\VISA\\WinNT\\Bin;C\:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn;C\:\\Program Files\\Common Files\\Microsoft Shared\\Windows Live;C\:\\Program Files\\Microsoft SQL Server\\120\\Tools\\Binn;C\:\\Program Files\\Microsoft DNX\\Dnvm;C\:\\Program Files\\IVI Foundation\\VISA\\Win64\\Bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SKWorkshop\\Marco\\X86\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\HMI\\bin;D\:\\Automation\\Samcoon\\SK035AE\\SKWorkshop\\Marco\\X86\\bin;C\:\\Users\\PVV\\.dnx\\bin;C\:\\ProgramData\\chocolatey\\bin;C\:\\ProgramData\\Oracle\\Java\\javapath;C\:\\Program Files (x86)\\QuickTime\\QTSystem;C\:\\Program Files\\nodejs;D\:\\WRK\\TortoiseGit\\bin;C\:\\Utils\\TortoiseSVN\\bin;C\:\\Program Files (x86)\\Git\\cmd;D\:\\MentorGraphics\\Sourcery_CodeBench_Lite_for_MIPS_ELF\\bin;C\:\\Eclipse +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/TL_PATH/delimiter=; +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/TL_PATH/operation=replace +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/TL_PATH/value=D\:\\MCU\\GNU_Tools_ARM_Embedded\\5.4_2016q2 +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/append=true +environment/project/cdt.managedbuild.config.gnu.mingw.exe.release.510381534/appendContributed=true diff --git a/.settings/org.eclipse.cdt.managedbuilder.core.prefs b/.settings/org.eclipse.cdt.managedbuilder.core.prefs new file mode 100644 index 0000000..4630071 --- /dev/null +++ b/.settings/org.eclipse.cdt.managedbuilder.core.prefs @@ -0,0 +1,25 @@ +eclipse.preferences.version=1 +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/CPATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/CPATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/CPLUS_INCLUDE_PATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/CPLUS_INCLUDE_PATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/C_INCLUDE_PATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/C_INCLUDE_PATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/append=true +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/appendContributed=true +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/CPATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/CPATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/CPLUS_INCLUDE_PATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/CPLUS_INCLUDE_PATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/C_INCLUDE_PATH/delimiter=; +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/C_INCLUDE_PATH/operation=remove +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/append=true +environment/buildEnvironmentInclude/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/appendContributed=true +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/LIBRARY_PATH/delimiter=; +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/LIBRARY_PATH/operation=remove +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/append=true +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404.1853483235/appendContributed=true +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/LIBRARY_PATH/delimiter=; +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/LIBRARY_PATH/operation=remove +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/append=true +environment/buildEnvironmentLibrary/cdt.managedbuild.config.gnu.mingw.exe.debug.1273936404/appendContributed=true diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.ltk.core.refactoring.prefs b/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 0000000..b196c64 --- /dev/null +++ b/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/DAPLink-RTL00Console.bat b/DAPLink-RTL00Console.bat new file mode 100644 index 0000000..bc0d13c --- /dev/null +++ b/DAPLink-RTL00Console.bat @@ -0,0 +1,7 @@ +@echo off +call paths.bat +cd flasher +openocd -f interface/cmsis-dap.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "load_ram_binary RTL00Console_ROM.bin 0x10000BA8" -c "exit" +rem -c "shutdown" + + diff --git a/DAPLink-RdFullFlash.bat b/DAPLink-RdFullFlash.bat new file mode 100644 index 0000000..5861662 --- /dev/null +++ b/DAPLink-RdFullFlash.bat @@ -0,0 +1,7 @@ +@echo off +call paths.bat +cd flasher +openocd -f interface/cmsis-dap.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "rtl8710_flash_read_id" -c "adapter_khz 5000" -c "rtl8710_flash_read ../fullflash.bin 0 1048576" -c "shutdown" +echo flash read fullflash.bin +pause + diff --git a/DAPLink-Reset.bat b/DAPLink-Reset.bat new file mode 100644 index 0000000..aceee70 --- /dev/null +++ b/DAPLink-Reset.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +@cd flasher +openocd -f interface/cmsis-dap.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "restart_from_falsh" -c "shutdown" +rem + diff --git a/DAPLink_WrFullFlash.bat b/DAPLink_WrFullFlash.bat new file mode 100644 index 0000000..bd1cf72 --- /dev/null +++ b/DAPLink_WrFullFlash.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +cd flasher +openocd -f interface/cmsis-dap.cfg -c "adapter_khz 3500" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "rtl8710_flash_read_id" -c "rtl8710_flash_auto_erase 1" -c "rtl8710_flash_auto_verify 1" -c "rtl8710_flash_write fullflash.bin 0" -c "shutdown" +pause + diff --git a/JLink-RTL00ConsoleROM.bat b/JLink-RTL00ConsoleROM.bat new file mode 100644 index 0000000..d8488c6 --- /dev/null +++ b/JLink-RTL00ConsoleROM.bat @@ -0,0 +1,3 @@ +@echo off +call paths.bat +start JLink.exe -Device CORTEX-M3 -If SWD -Speed 4000 flasher\RTL00ConsoleROM.JLinkScript diff --git a/JLink-RdFullFlash.bat b/JLink-RdFullFlash.bat new file mode 100644 index 0000000..de5d955 --- /dev/null +++ b/JLink-RdFullFlash.bat @@ -0,0 +1,3 @@ +@echo off +call paths.bat +JLink.exe -Device CORTEX-M3 -If SWD -Speed 10000 flasher/RTL_FFlash.JLinkScript diff --git a/JLink-Reset.bat b/JLink-Reset.bat new file mode 100644 index 0000000..51ea2a8 --- /dev/null +++ b/JLink-Reset.bat @@ -0,0 +1,3 @@ +@echo off +call paths.bat +JLink.exe -Device CORTEX-M3 -If SWD -Speed 1000 flasher\RTL_Reset.JLinkScript diff --git a/JLink-RunRAM.bat b/JLink-RunRAM.bat new file mode 100644 index 0000000..4282a8e --- /dev/null +++ b/JLink-RunRAM.bat @@ -0,0 +1,3 @@ +@echo off +call paths.bat +start JLink.exe -Device CORTEX-M3 -If SWD -Speed 4000 flasher\RTL_RunRAM.JLinkScript diff --git a/JLinkGDB-RdFullFlash.bat b/JLinkGDB-RdFullFlash.bat new file mode 100644 index 0000000..76865da --- /dev/null +++ b/JLinkGDB-RdFullFlash.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +start JLinkGDBServer.exe -device Cortex-M3 -if SWD -ir -endian little -speed 1000 +arm-none-eabi-gdb.exe -x flasher/gdb_rdflash.jlink +taskkill /F /IM JLinkGDBServer.exe + diff --git a/JLinkGDB-RunRAM.bat b/JLinkGDB-RunRAM.bat new file mode 100644 index 0000000..323aa3a --- /dev/null +++ b/JLinkGDB-RunRAM.bat @@ -0,0 +1,15 @@ +@echo off +call paths.bat +@if exist build\obj\build.axf goto run +echo File 'build\obj\build.axf' not found! +echo Build project... +mingw32-make.exe -f Makefile all +@if not exist build\obj\build.axf goto err +:run +start JLinkGDBServer.exe -device Cortex-M3 -if SWD -ir -endian little -speed 1000 +arm-none-eabi-gdb.exe -x flasher/gdb_run_ram.jlink +taskkill /F /IM JLinkGDBServer.exe +goto end +:err +echo Error! +:end \ No newline at end of file diff --git a/JLinkGDB-WrFlash.bat b/JLinkGDB-WrFlash.bat new file mode 100644 index 0000000..c70f651 --- /dev/null +++ b/JLinkGDB-WrFlash.bat @@ -0,0 +1,21 @@ +@echo off +call paths.bat +@if %1x==x goto xxx +set img_file=%1 +goto run +:xxx +set img_file=build/bin/ram_all.bin +:run +echo define call1>flasher/flash_file.jlink +echo SetFirwareSize %img_file%>>flasher/flash_file.jlink +echo end>>flasher/flash_file.jlink +echo define call2>>flasher/flash_file.jlink +echo FlasherWrite %img_file% 0 $Image1Size>>flasher/flash_file.jlink +echo end>>flasher/flash_file.jlink +echo define call3>>flasher/flash_file.jlink +echo FlasherWrite %img_file% $Image2Addr $Image2Size>>flasher/flash_file.jlink +echo end>>flasher/flash_file.jlink +start JLinkGDBServer.exe -device Cortex-M3 -if SWD -ir -endian little -speed 3500 +arm-none-eabi-gdb.exe -x flasher/gdb_wrflash.jlink +taskkill /F /IM JLinkGDBServer.exe + diff --git a/JLinkGDBServer.bat b/JLinkGDBServer.bat new file mode 100644 index 0000000..c356ac1 --- /dev/null +++ b/JLinkGDBServer.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +start JLinkGDBServer.exe -device Cortex-M3 -if SWD -ir -endian little -speed 1000 +arm-none-eabi-gdb.exe -x flasher/gdb_init.jlink +taskkill /F /IM JLinkGDBServer.exe + diff --git a/JLinkGDB_OTA.bat b/JLinkGDB_OTA.bat new file mode 100644 index 0000000..f64317e --- /dev/null +++ b/JLinkGDB_OTA.bat @@ -0,0 +1,15 @@ +@echo off +call paths.bat +@if exist build\bin\ota.bin goto run +echo File 'build\obj\ota.bin' not found! +echo Build project... +mingw32-make.exe -f Makefile all +@if not exist build\bin\ota.bin goto err +:run +start start JLinkGDBServer.exe -device Cortex-M3 -if SWD -ir -endian little -speed 1000 +arm-none-eabi-gdb.exe -x flasher/gdb_ota.jlink +taskkill /F /IM JLinkGDBServer.exe +goto end +:err +echo Error! +:end \ No newline at end of file diff --git a/JLinkOCD-RdFullFlash.bat b/JLinkOCD-RdFullFlash.bat new file mode 100644 index 0000000..4708bea --- /dev/null +++ b/JLinkOCD-RdFullFlash.bat @@ -0,0 +1,7 @@ +@echo off +call paths.bat +cd flasher +openocd -f interface/Jlink.cfg -c "adapter_khz 3500" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "rtl8710_flash_read_id" -c "adapter_khz 3900" -c "rtl8710_flash_read ../fullflash.bin 0 1048576" -c "shutdown" +echo flash read fullflash.bin +pause + diff --git a/JLink_RdFullFlash.bat b/JLink_RdFullFlash.bat new file mode 100644 index 0000000..450e141 --- /dev/null +++ b/JLink_RdFullFlash.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +openocd -f interface/Jlink.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "adapter_khz 3500" -c "rtl8710_flash_read_id" -c "rtl8710_flash_read ../fullflash.bin 0 1048576" -c "shutdown" +echo flash read fullflash.bin +pause + diff --git a/JlinkOpenOCD.bat b/JlinkOpenOCD.bat new file mode 100644 index 0000000..6e354f6 --- /dev/null +++ b/JlinkOpenOCD.bat @@ -0,0 +1,4 @@ +@echo off +call paths.bat +taskkill /F /IM openocd.exe +start openocd -f interface\Jlink.cfg -f flasher\ameba1.cfg diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..794f882 --- /dev/null +++ b/Makefile @@ -0,0 +1,57 @@ + +all: ram_all +mp: ram_all_mp + +.PHONY: ram_all +ram_all: + @$(MAKE) -f sdkbuild.mk + @$(MAKE) -f flasher.mk genbin1 genbin23 + +.PHONY: ram_all_mp +ram_all_mp: + @$(MAKE) -f sdkbuild.mk mp + @$(MAKE) -f flasher.mk mp + +.PHONY: clean clean_all +clean: + @$(MAKE) -f sdkbuild.mk clean + +clean_all: + @$(MAKE) -f sdkbuild.mk clean_all + +.PHONY: flashburn runram reset test readfullflash flashwebfs +flashburn: + #JLinkGDB-WrFlash.bat + @$(MAKE) -f flasher.mk flashburn + +flash_OTA: + @$(MAKE) -f flasher.mk flash_OTA + +webfs: + @$(MAKE) -f webfs.mk + +flashwebfs: + @$(MAKE) -f webfs.mk + @$(MAKE) -f flasher.mk flashwebfs + #JLinkGDB-WrWebFs.bat + +runram: + #JLink-RunRAM.bat + @$(MAKE) --f flasher.mk runram + +reset: + #JLink-Reset.bat + @$(MAKE) -f flasher.mk reset + +test: + JLink-RTL00ConsoleROM.bat + #@make -f flasher.mk test + +readfullflash: + #JLink-RdFullFlash.bat + @$(MAKE) -f flasher.mk readfullflash + +.PHONY: prerequirement +prerequirement: + @$(file >DEPENDENCY_LIST.txt,$(DEPENDENCY_LIST)) + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0ab692 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# RTL00 Web +--- +Проект на начальной Ñтадии, не завершен!
+
+[форум](https://esp8266.ru/forum/threads/web-svalka-na-rtl871x.2403/)
+--- +Used Modules:
+RTL00(RTL8710AF), [F11AMIM13](http://fn-link.en.made-in-china.com/product/sSinPtAKZBke/China-RTL8711AM-Iot-Module.html) (RTL8711AM), [F11AFIM13-B1](http://fn-link.en.made-in-china.com/product/PSHnuEtJVXWh/China-RTL8711AF-IoT-Module-IEEE-802-11-B-G-N-2-4GHz-1T1R-WiFi-NFC-Module.html) (RTL8711AF)
+[PADI](https://www.pine64.org/?page_id=946) (RTL8710AF), [F10AFIM13-B1](http://en.ofeixin.com/products_detail/productId=65.html) (RTL8710AF), [TinyCon2005-A-BE](http://www.ralinwi.com/product.aspx?info_lb=54&flag=1) (RTL8711AF),
+[WFM-400](http://www.rayson.com/rayson/en/?pros=product&pros=product&b_cat_id=A03&m_cat_id=A0304&s_cat_id=A030401&prod_id=P0113&level=3) (RTL8711AM), [WFM-410](http://www.rayson.com/rayson/en/?pros=product&pros=product&b_cat_id=A03&m_cat_id=A0304&s_cat_id=A030401&prod_id=P0114&level=3) (RTL8711AF), [WFM-250](http://www.rayson.com/rayson/en/?pros=product&pros=product&b_cat_id=A03&m_cat_id=A0304&s_cat_id=A030401&prod_id=P0112&level=3) (RTL8195AM),
+[AW-CU238, AW-CU239](https://www.buyiot.net/pd-1) (RTL8711AM), [AW-CU245, AW-CU245, AW-CU245](https://www.buyiot.net/home-1) (RTL8711AM/RTL8195AM/RTL8711AF),
+[WG6611](http://www.jorjin.com/product.php?id=98) (RTL8711AM), [RAK473](http://www.rakwireless.com/en/download/RAK473/Firmware%20Upgrade) (RTL8711AM), [RAK474, RAK476](http://www.rakwireless.com/en/download/RAK473/Firmware%20Upgrade) (RTL8711AF), ...
diff --git a/STLink-RdFullFlash.bat b/STLink-RdFullFlash.bat new file mode 100644 index 0000000..af82648 --- /dev/null +++ b/STLink-RdFullFlash.bat @@ -0,0 +1,7 @@ +@echo off +call paths.bat +cd flasher +openocd -f interface/stlink-v2.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "rtl8710_flash_read_id" -c "adapter_khz 5000" -c "rtl8710_flash_read ../fullflash.bin 0 1048576" -c "shutdown" +echo flash read fullflash.bin +pause + diff --git a/STLink-Reset.bat b/STLink-Reset.bat new file mode 100644 index 0000000..d4aeb47 --- /dev/null +++ b/STLink-Reset.bat @@ -0,0 +1,6 @@ +@echo off +call paths.bat +@cd flasher +openocd -f interface/stlink-v2.cfg -c "adapter_khz 1000" -f rtl8710.ocd -f cortex.ocd -c "init" -c "reset halt" -c "cortex_reboot" -c "shutdown" +rem + diff --git a/WEBFiles/$js.inc b/WEBFiles/$js.inc new file mode 100644 index 0000000..515a2a2 --- /dev/null +++ b/WEBFiles/$js.inc @@ -0,0 +1 @@ +var $ = function(id){return document.getElementById(id);} \ No newline at end of file diff --git a/WEBFiles/404.htm b/WEBFiles/404.htm new file mode 100644 index 0000000..8836bfc --- /dev/null +++ b/WEBFiles/404.htm @@ -0,0 +1,14 @@ + + + + 404 - Page not found + + + +

RTL871X Built-in Web server ©

+
+

404 - Page not found

+

Sorry, the page you are requesting was not found on this server.

+
+~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/adc.htm b/WEBFiles/adc.htm new file mode 100644 index 0000000..a4e492c --- /dev/null +++ b/WEBFiles/adc.htm @@ -0,0 +1,10 @@ +~inc:grfx1.inc~ +ADC: ? + +~inc:grfx2.inc~ \ No newline at end of file diff --git a/WEBFiles/disk_er1.htm b/WEBFiles/disk_er1.htm new file mode 100644 index 0000000..74a19e5 --- /dev/null +++ b/WEBFiles/disk_er1.htm @@ -0,0 +1,5 @@ +~inc:timer.inc~ +

Image Corrupt or Wrong Version!

+ +~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/disk_er2.htm b/WEBFiles/disk_er2.htm new file mode 100644 index 0000000..65e5a15 --- /dev/null +++ b/WEBFiles/disk_er2.htm @@ -0,0 +1,5 @@ +~inc:timer.inc~ +

File to big in flash!

+ +~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/disk_er3.htm b/WEBFiles/disk_er3.htm new file mode 100644 index 0000000..31552cb --- /dev/null +++ b/WEBFiles/disk_er3.htm @@ -0,0 +1,5 @@ +~inc:timer.inc~ +

Bad file!

+ +~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/disk_ok.htm b/WEBFiles/disk_ok.htm new file mode 100644 index 0000000..05e8f52 --- /dev/null +++ b/WEBFiles/disk_ok.htm @@ -0,0 +1,5 @@ +~inc:timer.inc~ +

Update Successful!

+ +~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/favicon.ico b/WEBFiles/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8c3e4381854c97705f077e246c168e5468f5137b GIT binary patch literal 2238 zcmeHHy^ho{5FR_RXY8BA9kfSBiFg8f9)OMqK%#-ixE5;KI|_OVBnprys1Onb9am6M zP*JRw<(tVxS?w+o5(Prq*~Cu#{mgh`Em6Sl^pr3PoeR-5BD#qiOLPaft=QkIcR}C3UDD_GFX+k3OZu@|(fa3xK7RR1*B^eM z+h0#G)Zu!YQnl?Wp6J@t%W?ha!w)&^)pBaD@y6M5df~ticAB8|N(5 z9+v|!8>vbCyE$`So@Ne#+1-PUSwp}9SwgVLsC=R*`|AQXLe;i8#yPLeQtj^12*J`f j^$c6$=~0&S`)Z?9$xZ>fzq`qUSaP + Version: ~sys_sysver~. WEB Connection: ~web_remote~
+ + \ No newline at end of file diff --git a/WEBFiles/grf.js b/WEBFiles/grf.js new file mode 100644 index 0000000..bf2669e --- /dev/null +++ b/WEBFiles/grf.js @@ -0,0 +1,613 @@ +; +(function(exports) { + var Util = { + extend : function() { + arguments[0] = arguments[0] || {}; + for (var i = 1; i < arguments.length; i++) { + for ( var key in arguments[i]) { + if (arguments[i].hasOwnProperty(key)) { + if (typeof (arguments[i][key]) === 'object') { + if (arguments[i][key] instanceof Array) { + arguments[0][key] = arguments[i][key] + } else { + arguments[0][key] = Util.extend( + arguments[0][key], arguments[i][key]) + } + } else { + arguments[0][key] = arguments[i][key] + } + } + } + } + return arguments[0] + } + }; + function TimeSeries(options) { + this.options = Util.extend({}, TimeSeries.defaultOptions, options); + this.clear() + } + TimeSeries.defaultOptions = { + resetBoundsInterval : 3000, + resetBounds : true + }; + TimeSeries.prototype.clear = function() { + this.data = []; + this.maxValue = Number.NaN; + this.minValue = Number.NaN + }; + TimeSeries.prototype.resetBounds = function() { + if (this.data.length) { + this.maxValue = this.data[0][1]; + this.minValue = this.data[0][1]; + for (var i = 1; i < this.data.length; i++) { + var value = this.data[i][1]; + if (value > this.maxValue) { + this.maxValue = value + } + if (value < this.minValue) { + this.minValue = value + } + } + } else { + this.maxValue = Number.NaN; + this.minValue = Number.NaN + } + }; + TimeSeries.prototype.append = function(timestamp, value, + sumRepeatedTimeStampValues) { + var i = this.data.length - 1; + while (i >= 0 && this.data[i][0] > timestamp) { + i-- + } + if (i === -1) { + this.data.splice(0, 0, [ timestamp, value ]) + } else if (this.data.length > 0 && this.data[i][0] === timestamp) { + if (sumRepeatedTimeStampValues) { + this.data[i][1] += value; + value = this.data[i][1] + } else { + this.data[i][1] = value + } + } else if (i < this.data.length - 1) { + this.data.splice(i + 1, 0, [ timestamp, value ]) + } else { + this.data.push([ timestamp, value ]) + } + this.maxValue = isNaN(this.maxValue) ? value : Math.max(this.maxValue, + value); + this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, + value) + }; + TimeSeries.prototype.dropOldData = function(oldestValidTime, + maxDataSetLength) { + var removeCount = 0; + while (this.data.length - removeCount >= maxDataSetLength + && this.data[removeCount + 1][0] < oldestValidTime) { + removeCount++ + } + if (removeCount !== 0) { + this.data.splice(0, removeCount) + } + }; + function SmoothieChart(options) { + this.options = Util.extend({}, SmoothieChart.defaultChartOptions, + options); + this.seriesSet = []; + this.currentValueRange = 1; + this.currentVisMinValue = 0; + this.lastRenderTimeMillis = 0 + } + SmoothieChart.defaultChartOptions = { + millisPerPixel : 20, + enableDpiScaling : true, + yMinFormatter : function(min, precision) { + return parseFloat(min).toFixed(precision) + }, + yMaxFormatter : function(max, precision) { + return parseFloat(max).toFixed(precision) + }, + maxValueScale : 1, + interpolation : 'bezier', + scaleSmoothing : 0.125, + maxDataSetLength : 2, + grid : { + fillStyle : '#000000', + strokeStyle : '#777777', + lineWidth : 1, + sharpLines : false, + millisPerLine : 1000, + verticalSections : 2, + borderVisible : true + }, + labels : { + fillStyle : '#ffffff', + disabled : false, + fontSize : 10, + fontFamily : 'monospace', + precision : 2 + }, + horizontalLines : [] + }; + SmoothieChart.AnimateCompatibility = (function() { + var requestAnimationFrame = function(callback, element) { + var requestAnimationFrame = window.requestAnimationFrame + || window.webkitRequestAnimationFrame + || window.mozRequestAnimationFrame + || window.oRequestAnimationFrame + || window.msRequestAnimationFrame || function(callback) { + return window.setTimeout(function() { + callback(new Date().getTime()) + }, 16) + }; + return requestAnimationFrame.call(window, callback, element) + }, cancelAnimationFrame = function(id) { + var cancelAnimationFrame = window.cancelAnimationFrame + || function(id) { + clearTimeout(id) + }; + return cancelAnimationFrame.call(window, id) + }; + return { + requestAnimationFrame : requestAnimationFrame, + cancelAnimationFrame : cancelAnimationFrame + } + })(); + SmoothieChart.defaultSeriesPresentationOptions = { + lineWidth : 1, + strokeStyle : '#ffffff' + }; + SmoothieChart.prototype.addTimeSeries = function(timeSeries, options) { + this.seriesSet.push({ + timeSeries : timeSeries, + options : Util.extend({}, + SmoothieChart.defaultSeriesPresentationOptions, options) + }); + if (timeSeries.options.resetBounds + && timeSeries.options.resetBoundsInterval > 0) { + timeSeries.resetBoundsTimerId = setInterval(function() { + timeSeries.resetBounds() + }, timeSeries.options.resetBoundsInterval) + } + }; + SmoothieChart.prototype.removeTimeSeries = function(timeSeries) { + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + this.seriesSet.splice(i, 1); + break + } + } + if (timeSeries.resetBoundsTimerId) { + clearInterval(timeSeries.resetBoundsTimerId) + } + }; + SmoothieChart.prototype.getTimeSeriesOptions = function(timeSeries) { + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + return this.seriesSet[i].options + } + } + }; + SmoothieChart.prototype.bringToFront = function(timeSeries) { + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + var set = this.seriesSet.splice(i, 1); + this.seriesSet.push(set[0]); + break + } + } + }; + SmoothieChart.prototype.streamTo = function(canvas, delayMillis) { + this.canvas = canvas; + this.delay = delayMillis; + this.start() + }; + SmoothieChart.prototype.start = function() { + if (this.frame) { + return + } + if (this.options.enableDpiScaling && window + && window.devicePixelRatio !== 1) { + var canvasWidth = this.canvas.getAttribute('width'); + var canvasHeight = this.canvas.getAttribute('height'); + this.canvas.setAttribute('width', canvasWidth + * window.devicePixelRatio); + this.canvas.setAttribute('height', canvasHeight + * window.devicePixelRatio); + this.canvas.style.width = canvasWidth + 'px'; + this.canvas.style.height = canvasHeight + 'px'; + this.canvas.getContext('2d').scale(window.devicePixelRatio, + window.devicePixelRatio) + } + var animate = function() { + this.frame = SmoothieChart.AnimateCompatibility + .requestAnimationFrame(function() { + this.render(); + animate() + }.bind(this)) + }.bind(this); + animate() + }; + SmoothieChart.prototype.stop = function() { + if (this.frame) { + SmoothieChart.AnimateCompatibility.cancelAnimationFrame(this.frame); + delete this.frame + } + }; + SmoothieChart.prototype.updateValueRange = function() { + var chartOptions = this.options, chartMaxValue = Number.NaN, chartMinValue = Number.NaN; + for (var d = 0; d < this.seriesSet.length; d++) { + var timeSeries = this.seriesSet[d].timeSeries; + if (!isNaN(timeSeries.maxValue)) { + chartMaxValue = !isNaN(chartMaxValue) ? Math.max(chartMaxValue, + timeSeries.maxValue) : timeSeries.maxValue + } + if (!isNaN(timeSeries.minValue)) { + chartMinValue = !isNaN(chartMinValue) ? Math.min(chartMinValue, + timeSeries.minValue) : timeSeries.minValue + } + } + if (chartOptions.maxValue != null) { + chartMaxValue = chartOptions.maxValue + } else { + chartMaxValue *= chartOptions.maxValueScale + } + if (chartOptions.minValue != null) { + chartMinValue = chartOptions.minValue + } + if (this.options.yRangeFunction) { + var range = this.options.yRangeFunction({ + min : chartMinValue, + max : chartMaxValue + }); + chartMinValue = range.min; + chartMaxValue = range.max + } + if (!isNaN(chartMaxValue) && !isNaN(chartMinValue)) { + var targetValueRange = chartMaxValue - chartMinValue; + var valueRangeDiff = (targetValueRange - this.currentValueRange); + var minValueDiff = (chartMinValue - this.currentVisMinValue); + this.isAnimatingScale = Math.abs(valueRangeDiff) > 0.1 + || Math.abs(minValueDiff) > 0.1; + this.currentValueRange += chartOptions.scaleSmoothing + * valueRangeDiff; + this.currentVisMinValue += chartOptions.scaleSmoothing + * minValueDiff + } + this.valueRange = { + min : chartMinValue, + max : chartMaxValue + } + }; + SmoothieChart.prototype.render = function(canvas, time) { + var nowMillis = new Date().getTime(); + if (!this.isAnimatingScale) { + var maxIdleMillis = Math.min(1000 / 6, this.options.millisPerPixel); + if (nowMillis - this.lastRenderTimeMillis < maxIdleMillis) { + return + } + } + this.lastRenderTimeMillis = nowMillis; + canvas = canvas || this.canvas; + time = time || nowMillis - (this.delay || 0); + time -= time % this.options.millisPerPixel; + var context = canvas.getContext('2d'), chartOptions = this.options, dimensions = { + top : 0, + left : 0, + width : canvas.clientWidth, + height : canvas.clientHeight + }, oldestValidTime = time + - (dimensions.width * chartOptions.millisPerPixel), valueToYPixel = function( + value) { + var offset = value - this.currentVisMinValue; + return this.currentValueRange === 0 ? dimensions.height + : dimensions.height + - (Math.round((offset / this.currentValueRange) + * dimensions.height)) + }.bind(this), timeToXPixel = function(t) { + return Math.round(dimensions.width + - ((time - t) / chartOptions.millisPerPixel)) + }; + this.updateValueRange(); + context.font = chartOptions.labels.fontSize + 'px ' + + chartOptions.labels.fontFamily; + context.save(); + context.translate(dimensions.left, dimensions.top); + context.beginPath(); + context.rect(0, 0, dimensions.width, dimensions.height); + context.clip(); + context.save(); + context.fillStyle = chartOptions.grid.fillStyle; + context.clearRect(0, 0, dimensions.width, dimensions.height); + context.fillRect(0, 0, dimensions.width, dimensions.height); + context.restore(); + context.save(); + context.lineWidth = chartOptions.grid.lineWidth; + context.strokeStyle = chartOptions.grid.strokeStyle; + if (chartOptions.grid.millisPerLine > 0) { + context.beginPath(); + for (var t = time - (time % chartOptions.grid.millisPerLine); t >= oldestValidTime; t -= chartOptions.grid.millisPerLine) { + var gx = timeToXPixel(t); + if (chartOptions.grid.sharpLines) { + gx -= 0.5 + } + context.moveTo(gx, 0); + context.lineTo(gx, dimensions.height) + } + context.stroke(); + context.closePath() + } + for (var v = 1; v < chartOptions.grid.verticalSections; v++) { + var gy = Math.round(v * dimensions.height + / chartOptions.grid.verticalSections); + if (chartOptions.grid.sharpLines) { + gy -= 0.5 + } + context.beginPath(); + context.moveTo(0, gy); + context.lineTo(dimensions.width, gy); + context.stroke(); + context.closePath() + } + if (chartOptions.grid.borderVisible) { + context.beginPath(); + context.strokeRect(0, 0, dimensions.width, dimensions.height); + context.closePath() + } + context.restore(); + if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) { + for (var hl = 0; hl < chartOptions.horizontalLines.length; hl++) { + var line = chartOptions.horizontalLines[hl], hly = Math + .round(valueToYPixel(line.value)) - 0.5; + context.strokeStyle = line.color || '#ffffff'; + context.lineWidth = line.lineWidth || 1; + context.beginPath(); + context.moveTo(0, hly); + context.lineTo(dimensions.width, hly); + context.stroke(); + context.closePath() + } + } + for (var d = 0; d < this.seriesSet.length; d++) { + context.save(); + var timeSeries = this.seriesSet[d].timeSeries, dataSet = timeSeries.data, seriesOptions = this.seriesSet[d].options; + timeSeries.dropOldData(oldestValidTime, + chartOptions.maxDataSetLength); + context.lineWidth = seriesOptions.lineWidth; + context.strokeStyle = seriesOptions.strokeStyle; + context.beginPath(); + var firstX = 0, lastX = 0, lastY = 0; + for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) { + var x = timeToXPixel(dataSet[i][0]), y = valueToYPixel(dataSet[i][1]); + if (i === 0) { + firstX = x; + context.moveTo(x, y) + } else { + switch (chartOptions.interpolation) { + case "linear": + case "line": { + context.lineTo(x, y); + break + } + case "bezier": + default: { + context.bezierCurveTo(Math.round((lastX + x) / 2), + lastY, Math.round((lastX + x)) / 2, y, x, y); + break + } + case "step": { + context.lineTo(x, lastY); + context.lineTo(x, y); + break + } + } + } + lastX = x; + lastY = y + } + if (dataSet.length > 1) { + if (seriesOptions.fillStyle) { + context.lineTo(dimensions.width + seriesOptions.lineWidth + + 1, lastY); + context.lineTo(dimensions.width + seriesOptions.lineWidth + + 1, dimensions.height + seriesOptions.lineWidth + + 1); + context.lineTo(firstX, dimensions.height + + seriesOptions.lineWidth); + context.fillStyle = seriesOptions.fillStyle; + context.fill() + } + if (seriesOptions.strokeStyle + && seriesOptions.strokeStyle !== 'none') { + context.stroke() + } + context.closePath() + } + context.restore() + } + if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) + && !isNaN(this.valueRange.max)) { + var maxValueString = chartOptions.yMaxFormatter( + this.valueRange.max, chartOptions.labels.precision), minValueString = chartOptions + .yMinFormatter(this.valueRange.min, + chartOptions.labels.precision); + context.fillStyle = chartOptions.labels.fillStyle; + context.fillText(maxValueString, dimensions.width + - context.measureText(maxValueString).width - 2, + chartOptions.labels.fontSize); + context.fillText(minValueString, dimensions.width + - context.measureText(minValueString).width - 2, + dimensions.height - 2) + } + if (chartOptions.timestampFormatter + && chartOptions.grid.millisPerLine > 0) { + var textUntilX = dimensions.width + - context.measureText(minValueString).width + 4; + for (var t = time - (time % chartOptions.grid.millisPerLine); t >= oldestValidTime; t -= chartOptions.grid.millisPerLine) { + var gx = timeToXPixel(t); + if (gx < textUntilX) { + var tx = new Date(t), ts = chartOptions + .timestampFormatter(tx), tsWidth = context + .measureText(ts).width; + textUntilX = gx - tsWidth - 2; + context.fillStyle = chartOptions.labels.fillStyle; + context.fillText(ts, gx - tsWidth, dimensions.height - 2) + } + } + } + context.restore() + }; + SmoothieChart.timeFormatter = function(date) { + function pad2(number) { + return (number < 10 ? '0' : '') + number + } + return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + + pad2(date.getSeconds()) + }; + exports.TimeSeries = TimeSeries; + exports.SmoothieChart = SmoothieChart +})(typeof exports === 'undefined' ? this : exports); +var line1 = new TimeSeries(); +var newval = 0; +function addpoint(xmlData) { + if (xmlData) { + newval = eval(getXMLValue(xmlData, 'value')); + line1.append(new Date().getTime(), newval); + document.getElementById('xdata').innerHTML = newval; + if (newval > xmax) + document.getElementById('xdata').style.color = '#0000A0'; + else if (newval < xmin) + document.getElementById('xdata').style.color = '#A00000'; + else + document.getElementById('xdata').style.color = '#00A000' + } else + line1.append(new Date().getTime(), newval) +} +var smoothie = new SmoothieChart({ + interpolation : 'linear', + minValue : 0, + millisPerPixel : millisPerPixel, + grid : { + strokeStyle : 'rgb(100, 110, 150)', + fillStyle : 'rgb(50, 55, 75)', + lineWidth : 1, + millisPerLine : millisPerLine, + verticalSections : 6 + }, + labels : { + precision : 0 + } +}); +smoothie.addTimeSeries(line1, { + strokeStyle : 'rgb(255, 0, 200)', + fillStyle : 'rgba(255, 0, 200, 0.3)', + lineWidth : 3 +}); +smoothie.streamTo(document.getElementById("mycanvas"), nextimeout); +setTimeout("newAJAXCommand(xmlfile, addpoint, true)", 100); +function slider(elemId, sliderWidth, range1, range2, step) { + var knobWidth = 17; + var knobHeight = 21; + var sliderHeight = 21; + var offsX, tmp; + var d = document; + var isIE = d.all || window.opera; + var point = (sliderWidth - knobWidth - 3) / (range2 - range1); + var slider = d.createElement('DIV'); + slider.id = elemId + '_slider'; + slider.className = 'slider'; + d.getElementById(elemId).appendChild(slider); + var knob = d.createElement('DIV'); + knob.id = elemId + '_knob'; + knob.className = 'knob'; + slider.appendChild(knob); + knob.style.left = 0; + knob.style.width = knobWidth + 'px'; + knob.style.height = knobHeight + 'px'; + slider.style.width = sliderWidth + 'px'; + slider.style.height = sliderHeight + 'px'; + var sliderOffset = slider.offsetLeft; + tmp = slider.offsetParent; + while (tmp.tagName != 'BODY') { + sliderOffset += tmp.offsetLeft; + tmp = tmp.offsetParent + } + if (isIE) { + knob.onmousedown = startCoord; + slider.onclick = sliderClick; + knob.onmouseup = endCoord; + slider.onmouseup = endCoord + } else { + knob.addEventListener("mousedown", startCoord, true); + slider.addEventListener("click", sliderClick, true); + knob.addEventListener("mouseup", endCoord, true); + slider.addEventListener("mouseup", endCoord, true) + } + function setValue(x) { + if (x < 0) + knob.style.left = 0; + else if (x > sliderWidth - knobWidth - 3) + knob.style.left = (sliderWidth - 3 - knobWidth) + 'px'; + else { + if (step == 0) + knob.style.left = x + 'px'; + else + knob.style.left = Math.round(x / (step * point)) * step * point + + 'px' + } + nextimeout = getValue(); + d.getElementById('toutid').value = nextimeout; + document.getElementById('toutid').innerHTML = nextimeout + } + function setValue2(x) { + if (x < range1 || x > range2) + alert('Value is not included into a slider range!'); + else + setValue((x - range1) * point); + nextimeout = getValue(); + d.getElementById('toutid').value = nextimeout; + document.getElementById('toutid').innerHTML = nextimeout + } + function getValue() { + return Math.round(parseInt(knob.style.left) / point) + range1 + } + function sliderClick(e) { + var x; + if (isIE) { + if (event.srcElement != slider) + return; + x = event.offsetX - Math.round(knobWidth / 2) + } else + x = e.pageX - sliderOffset - knobWidth / 2; + setValue(x) + } + function startCoord(e) { + if (isIE) { + offsX = event.clientX - parseInt(knob.style.left); + slider.onmousemove = mov + } else { + slider.addEventListener("mousemove", mov, true) + } + } + function mov(e) { + var x; + if (isIE) + x = event.clientX - offsX; + else + x = e.pageX - sliderOffset - knobWidth / 2; + setValue(x) + } + function endCoord() { + if (isIE) + slider.onmousemove = null; + else + slider.removeEventListener("mousemove", mov, true) + } + this.setValue = setValue2; + this.getValue = getValue +} +var mysl1 = new slider('sl', 333, 20, 10020, 0); +mysl1.setValue(500); +document.getElementById('toutid').innerHTML = mysl1.getValue(); \ No newline at end of file diff --git a/WEBFiles/grfx1.inc b/WEBFiles/grfx1.inc new file mode 100644 index 0000000..d3943eb --- /dev/null +++ b/WEBFiles/grfx1.inc @@ -0,0 +1,13 @@ + + + + RTL871X + + + +~inc:menu.inc~ + +
\ No newline at end of file diff --git a/WEBFiles/grfx2.inc b/WEBFiles/grfx2.inc new file mode 100644 index 0000000..30b1323 --- /dev/null +++ b/WEBFiles/grfx2.inc @@ -0,0 +1,6 @@ +
+
GET TimeOuts ? ms'
+
+ + +~inc:footer.inc~ diff --git a/WEBFiles/heap.htm b/WEBFiles/heap.htm new file mode 100644 index 0000000..1cf6850 --- /dev/null +++ b/WEBFiles/heap.htm @@ -0,0 +1,9 @@ +~inc:grfx1.inc~ +Heap Size: ? bytes +~inc:grfx2.inc~ diff --git a/WEBFiles/heap.xml b/WEBFiles/heap.xml new file mode 100644 index 0000000..edc3199 --- /dev/null +++ b/WEBFiles/heap.xml @@ -0,0 +1 @@ +Heap Size~sys_heap~ \ No newline at end of file diff --git a/WEBFiles/index.htm b/WEBFiles/index.htm new file mode 100644 index 0000000..881ec16 --- /dev/null +++ b/WEBFiles/index.htm @@ -0,0 +1,20 @@ + + + + RTL871X + + +~inc:menu.inc~ +
+

Info

+

+Name: ~wifi_ap_ssid~ Password: ~wifi_ap_psw~
+Web connect: ~web_remote~,
+WEB ver: ~sys_webver~, SDK ver: ~sys_sdkver~.
+ChipID: ~sys_cid~.

+DevTime: SNTP disable
+PowerStartTime: ?

+

+~inc:footer.inc~ +~inc:time.inc~ + \ No newline at end of file diff --git a/WEBFiles/logo.gif b/WEBFiles/logo.gif new file mode 100644 index 0000000000000000000000000000000000000000..b930b2a31cabf1bedef26c299ee610de79a32a22 GIT binary patch literal 393 zcmV;40e1dJNk%w1VITk?0OJ4v|NsBv?eVF|s^hto=i=1TZ5PFAA=isba+OzJpTMrn z+c0yz`}_9{i!uNJ00000EC2ui03ZM$000F35XecZy*SUHgdmJ20!)WAXetEbrJN}O zt1TtO3PsF94cw?6AV4UF2(*zR#S+L;4Fn;;90g>7EA$!MAwo4|DkpgbeG^zO;8BEN zEcC87JA2R-m=q|)PZ(zsPJMn796JUBg&7A11qB2E1PTfXmX!-+6%3IR0|x{Hjs}qn zrl$#&0Sp6*5)20g2O$BDv!99#rm0x~lmG*doxzTZ1jU=X8>U2$uEesy#S8|ZL%XU0 z%+%2Uu?E$^!p8-Q+NTSt%-)X-43FTDu?LdK=cx)=>%ou&&at1OkuzsXlY({r%JC!c z4cWkJnI;@e0A}7iagWN?E61VWDqlb8JY^K<$XTdj9d>~*6i3oXbFpN3XvC3#fCMcs n@GDm&W}=?fYV2%v0D+@UenP<;P^3ssJZxGaT|~92)d&DPJI|kC literal 0 HcmV?d00001 diff --git a/WEBFiles/menu.inc b/WEBFiles/menu.inc new file mode 100644 index 0000000..a409e6c --- /dev/null +++ b/WEBFiles/menu.inc @@ -0,0 +1,26 @@ +

RTL871x Built-in Web server ©

+ + \ No newline at end of file diff --git a/WEBFiles/protect/chiprams.xml b/WEBFiles/protect/chiprams.xml new file mode 100644 index 0000000..5cad0a9 --- /dev/null +++ b/WEBFiles/protect/chiprams.xml @@ -0,0 +1,4 @@ + +~start~ +~xml_ram~ + diff --git a/WEBFiles/protect/cookie.js b/WEBFiles/protect/cookie.js new file mode 100644 index 0000000..ea8a307 --- /dev/null +++ b/WEBFiles/protect/cookie.js @@ -0,0 +1,70 @@ +function getCookie(name) { + var prefix = name + "="; + var cookieStartIndex = document.cookie.indexOf(prefix); + if (cookieStartIndex == -1) + return null; + var cookieEndIndex = document.cookie.indexOf(";", cookieStartIndex + + prefix.length); + if (cookieEndIndex == -1) + cookieEndIndex = document.cookie.length; + return unescape(document.cookie.substring(cookieStartIndex + prefix.length, + cookieEndIndex)); +} +function setCookie(name, value) { + document.cookie = name + "=" + escape(value) + "; path=/"; +} +function setCookieElem(name, defv) { + var val = getCookie(name); + if (val == null || val.charAt(0) != '0' || val.charAt(1) != 'x') { + val = defv; + setCookie(name, val); + } + document.getElementById(name).value = val; +} +function NewCookie(add) { + var val = parseInt(document.getElementById('start').value, 16) & 0xFFFFFFF0; + if (val == NaN) + setCookieElem('start', '0x40000000'); + else { + val += add; + setCookie('start', '0x' + val.toString(16)); + var nval = val + 256; + setCookie('stop', '0x' + nval.toString(16)); + document.getElementById('start').value = '0x' + val.toString(16); + document.getElementById('pmem').contentWindow.location.reload(); + } +} +setCookieElem('start', '0x40000000'); +setCookieElem('set_ramaddr', '0x3FFF0000'); +setCookieElem('set_ramdata', '0x12345678'); +function UpTxt(xD, n, v) { + var x = getXMLValue(xD, n, v); + if (x == '?') + document.getElementById("id_" + n).style.color = "#833"; + else + document.getElementById("id_" + n).style.color = "#333"; + document.getElementById("id_" + n).innerHTML = x + v; +} +function UpdateValuesRam(xD) { + if (xD) { + UpTxt(xD, "ramaddr", ""); + UpTxt(xD, "ramdata", ""); + } +} +function SendRamVal(x) { + var addr = parseInt(document.getElementById('set_ramaddr').value, 16); + var val = parseInt(document.getElementById('set_ramdata').value, 16); + if (addr != NaN && val != NaN) { + document.getElementById('set_ramaddr').value = '0x' + addr.toString(16); + setCookie('set_ramaddr','0x' + addr.toString(16)); + document.getElementById('set_ramdata').value = '0x' + val.toString(16); + setCookie('set_ramdata','0x' + val.toString(16)); + if (x != 0) + newAJAXCommand('chiprams.xml?start=0x' + addr.toString(16), + UpdateValuesRam, 0); + else + newAJAXCommand('chiprams.xml?sys_ram0x' + addr.toString(16) + '=0x' + + val.toString(16) + '&start=0x' + addr.toString(16), + UpdateValuesRam, 0); + } +} \ No newline at end of file diff --git a/WEBFiles/protect/debug.htm b/WEBFiles/protect/debug.htm new file mode 100644 index 0000000..21c1e35 --- /dev/null +++ b/WEBFiles/protect/debug.htm @@ -0,0 +1,25 @@ + + + + RTL871X Debug and Tests + + +~inc:menu.inc~ +
+

Debug and Tests

+

+Chart 'heap', ST-AP RSSI

+System Restart

+Counter erase the last flash sector config: ~sys_rdec0x980FE000~

+

+
+
+

System constants?

+ + + +
+
+~inc:footer.inc~ + + \ No newline at end of file diff --git a/WEBFiles/protect/dsleep.htm b/WEBFiles/protect/dsleep.htm new file mode 100644 index 0000000..cbee35f --- /dev/null +++ b/WEBFiles/protect/dsleep.htm @@ -0,0 +1,30 @@ + + + + RTL871X + + + + + ~inc:menu.inc~ +
+

Test DeepSleep

+ + + + + + + + + + +
DeepSleep Time(ms):
DeepSleep Mode:
+

+ Reset event = ~sys_res_event~ (1-power, 2-reset, 3-software, 4-wdt)
+

+
+~inc:footer.inc~ + + + diff --git a/WEBFiles/protect/fullflash.bin b/WEBFiles/protect/fullflash.bin new file mode 100644 index 0000000..7bf721b --- /dev/null +++ b/WEBFiles/protect/fullflash.bin @@ -0,0 +1 @@ +~bin_flash_all~ \ No newline at end of file diff --git a/WEBFiles/protect/hexdmpb.htm b/WEBFiles/protect/hexdmpb.htm new file mode 100644 index 0000000..c7f4707 --- /dev/null +++ b/WEBFiles/protect/hexdmpb.htm @@ -0,0 +1,41 @@ + + + + RTL871X HexDump Byte + + + + ~inc:menu.inc~ +
+

HexDump Bytes

+ + + + + + + + +
RAM Start addr
+ + + + + + + + + + + + + + +
Write addr, value:
Read addr, value:??
+
+ + + \ No newline at end of file diff --git a/WEBFiles/protect/hexdmpb.txt b/WEBFiles/protect/hexdmpb.txt new file mode 100644 index 0000000..0331abd --- /dev/null +++ b/WEBFiles/protect/hexdmpb.txt @@ -0,0 +1 @@ +~hexdmpb~ \ No newline at end of file diff --git a/WEBFiles/protect/hexdmpd.htm b/WEBFiles/protect/hexdmpd.htm new file mode 100644 index 0000000..f754d8f --- /dev/null +++ b/WEBFiles/protect/hexdmpd.htm @@ -0,0 +1,41 @@ + + + + RTL871X HexDump DWord + + + + ~inc:menu.inc~ +
+

HexDump DWord

+ + + + + + + + +
RAM Start addr   
+ + + + + + + + + + + + + + +
Write addr, value:
Read addr, value:??
+
+ + + \ No newline at end of file diff --git a/WEBFiles/protect/hexdmpd.txt b/WEBFiles/protect/hexdmpd.txt new file mode 100644 index 0000000..8d03bdc --- /dev/null +++ b/WEBFiles/protect/hexdmpd.txt @@ -0,0 +1 @@ +~hexdmpd~ \ No newline at end of file diff --git a/WEBFiles/protect/ram.bin b/WEBFiles/protect/ram.bin new file mode 100644 index 0000000..cca0930 --- /dev/null +++ b/WEBFiles/protect/ram.bin @@ -0,0 +1 @@ +~bin_ram~ \ No newline at end of file diff --git a/WEBFiles/protect/scan.htm b/WEBFiles/protect/scan.htm new file mode 100644 index 0000000..4d30082 --- /dev/null +++ b/WEBFiles/protect/scan.htm @@ -0,0 +1,98 @@ + + + + +WiFi Scan + + + + ~inc:menu.inc~ +
+

+
Stations scanning...
+

+ + +
SSIDBSSIDAuthChRSSIHd
+ +
+
+ ~inc:footer.inc~ + + + + + diff --git a/WEBFiles/protect/scan.xml b/WEBFiles/protect/scan.xml new file mode 100644 index 0000000..fc7188e --- /dev/null +++ b/WEBFiles/protect/scan.xml @@ -0,0 +1 @@ +~wifi_scan~ diff --git a/WEBFiles/protect/setup.htm b/WEBFiles/protect/setup.htm new file mode 100644 index 0000000..3be19ef --- /dev/null +++ b/WEBFiles/protect/setup.htm @@ -0,0 +1,89 @@ + + + + RTL871X Setup + + + + +~inc:menu.inc~ +
+

System Setup

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
WEB (HTTP) port:
WEB recved timeout:
WEB close timeout:
LogUART Printf() enable: + + +
Web pcb close enable: + + +
Checking pin WiFi cfg reset: + + +
NetBIOS enable: + + +
SNTP enable: + + +
Captive Portal AP: + + +
+

+ + + +

+
+
+~inc:footer.inc~ + + \ No newline at end of file diff --git a/WEBFiles/protect/tstfuncs.htm b/WEBFiles/protect/tstfuncs.htm new file mode 100644 index 0000000..417e3ed --- /dev/null +++ b/WEBFiles/protect/tstfuncs.htm @@ -0,0 +1,91 @@ + + + + RTL871X Download bin + + + +~inc:menu.inc~ +
+

Download bin

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ROM-BIOS:0x00000000..0x00080000512 kbytes
SRAM:0x10000000..0x10070000448 kbytes
TCM-RAM:0x1FFF0000..0x2000000064 kbytes
SDRAM:0x30000000..0x302000002048 kbytes
FLASH Bus:0x98000000..0x981000001024 kbytes
I/O SoC:0x40000000..0x40080000.. kbytes
ARM:0xEF000000..0xEFFFFFFF.. kbytes
+ + + + + + + + + + +
Start addr
End addr
+

+
+~inc:footer.inc~ + + + diff --git a/WEBFiles/protect/upload.htm b/WEBFiles/protect/upload.htm new file mode 100644 index 0000000..d8e355d --- /dev/null +++ b/WEBFiles/protect/upload.htm @@ -0,0 +1,23 @@ + + + + RTL871X WebFS Image Upload + + +~inc:menu.inc~ + +

WebFS Image Upload

+

+ Select WEBFiles.bin file...

+

+ +

+ +

+Curent Disk has ~wfs_files~ files, Disk Size: ~wfs_size~ bytes.
+Disk Addres: ~wfs_addr~, Max Disk Size: ~wfs_max_size~ bytes, Max 250 files.

+Flash ID: ~sys_fid~, Size: ~sys_fsize~ bytes.
+Download fullflash.bin

+

+~inc:footer.inc~ + \ No newline at end of file diff --git a/WEBFiles/protect/wifi.htm b/WEBFiles/protect/wifi.htm new file mode 100644 index 0000000..45af0a4 --- /dev/null +++ b/WEBFiles/protect/wifi.htm @@ -0,0 +1,192 @@ + + + + RTL871X WiFi + + + + ~inc:menu.inc~ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

WiFi SoftAP

WiFi Station

WiFi Mode:SSID:
AP SSID:Password:
Hidden SSID: + + + BSSID:
AP Password:Use BSSID: + + +
Channel:Auth Mode:
IEEE PHY:IP:
Auth Mode:Subnet Mask:
IP:Gateway:
Subnet Mask:AutoReConnect:
Gateway:ReConnectPause:
MAC:MAC:
DHCP: + + + DHCP: +
Country Code:RSSI:~wifi_st_rssi~ dB
RF Tx Power:Sleep Mode:
Max connections:Beacon (ms):
AP Host Name:ST Host Name:
+

+ + +

+
+
+~inc:footer.inc~ + + + \ No newline at end of file diff --git a/WEBFiles/rtl.gif b/WEBFiles/rtl.gif new file mode 100644 index 0000000000000000000000000000000000000000..71b3e8d5938af152e5b9df4a79c52fbdddb8b37c GIT binary patch literal 750 zcmVCsYr-wC2_`;Ty_h*)v z$mDn^5h>Y18CZB(My0t~(dh{Ty3{zT$Y~0fO1J7a3fY=LsdL~M5=mS82!flth{@5L zYs*VKy!#KO^RLqH>aEKl2lqn%_bt+j)1L0w3r%oHp^Uvj_A3@J!NC;gI<|XxGCFWoaBYVCIjyvTt4*gy zO?hPL)1JD)uKAc*mfEau_p_{q;M1ateG_jyoT0?yG6{Q5jr_@HQrY74#Y}xO=g3i} zquKNZys$n$K`$@ky^VP5$*IkTE}Q#zl-cDo;%&}xdGqJT)hnNm*h}~wLZ5c&%%@0t z3Hk@!fWH+dOMMF#$RKC|`qkWn86I|_T^{~Y5KkbQhhcH>K`7cO2!7a{h!Xlh8*}mT z(cgy(x`-fy0%{l_JL|Ei*@Qx>_!^J=jVGOse%KgXkusX7Op-z_IbwS|KDl8$HIA4G zlN(WH g9*St9iZ04%qmDibX{3@)N@=B*UW#d^Z599kJL|V`UH||9 literal 0 HcmV?d00001 diff --git a/WEBFiles/rtl1.gif b/WEBFiles/rtl1.gif new file mode 100644 index 0000000000000000000000000000000000000000..ce3c45bf60ad1c0532516ec50ee2366957ce9951 GIT binary patch literal 6763 zcmWkwcUTnX_MW~ov%9$TBH{uHE6t%SEP}erf)oJwuDgqOv)%;z)Q`OhLWo#W9V_l$f|bZ)(-Taq&Cu zcwzU4G4Z8paL1hRnmNx7hA*#62yIvz-kkI1NuRkTrLIJCzKOcisdhERdsip#-xry< zCs(#h5AIO=?o`(ltKPi%l4(eBD~-L^6B)5@S!cE1op%3{j!mO4Ki{@7P+A|ITo>U} z6Zxbgu(>RD(fVL%TkO@2h)X^5oJ;0p7W(xR&DwCPVAHXk8}=6!ohX-9MmJw*j9Q^j z%1hKFs$U(Olesjis5H0kU`0e-W@>YC$FY*8t%2D&Gn}fUj$b)Yd$Pu*Jt3oWL&T26 zjw_vU9a$x(s+S%AAnSNR>HgBB@{EqX#Y@tX(rdGFy0%B`Tc5u#@8t0Dey(Z5hRlTJ zu`_+V!y6M9n3g+lj7Z$1>MT`xRRok=s;xZNSX+}HHYX^vD04+^?y}|ae*QD_i?ixK z?)c@+{rvSw%k$Isc2z~kgaroqWoM_B?#S#oSyxxLy|AmIqh`h0+$E)^?N>TT-e^Go7~ux^7+|Tk3zZGw=9;66g3? z#btgGn-e1=XQeFGFG$qIB}E0#_CLROQ`g@0ou^ylE%bSaAxqUO%=_MaIR9x_iC9T*ajv{1kASRS}xmaBROvPm5o72e%LhcDGY z+jYwiZ0{X7MplGJYQkN8+`oIcQ#N~QUgNgZ?F$c|Xa-y6x&(M_shT#)HhEB!IZQH4 zdM1&Ru*pI^|ATX^B^$ku>Ngj4+6K4Vsj@S_@m+ZKwFIW=@RG$ zu~lCy6UEh!d@9eB+)kPZUXh#H9K39Q#=6g9s!Rve8PB=WmYh8bmniAV+fBj1KHHg9 zIy|VP(Qc9Y2Hz?T{>8;R#b>^YeX`Gw=eVKidegW|g}Ba^|&I?`oj*eQV0h>&%rw#kS0Z5zJtwhU`uP zeQS=Opm7A7$f&Zdk2zac+2N*mHorc zsKwc%)7LVOV(sR8Gg#x}GZ3uHJB%tT-F7nquBlpI?tk^4&m+aFQaz6ZoL=OJ^(fNzgVXyRo|xTr5^RN#g!!GufQbPDud2FtMD=GyRvY6 z2rSEwfox`X*|jaxCW}0raIs%=e&MS>9-K?reY5A2ql)t>a|-j}`+ldaX(_5x?2s(* zscdcO8)`nw`npm*CUgqEUGAOH&KU`BW^h=MG@kgxk!rNnNh(eHyFWVr{^0U0OXj;& z&118doZ?lX3qz_ht9+BG%82q9*E2$OSeYU6LaA24l%RndN68gqo)SM1alWI)+&hQd z>C7sh9wp>5HfS%W)+CWa!&DUyU*xN2cD|7vHx5lpx#>9j8(jc>(aUAx)92Ox8$H<= zWXOJAC#xzOCzFnO+2kc?zf}QHeq)dOTSc65bxUo`fJM5>Y@GGfP(QP<8~KdBNov9> zbvW;<8im(rv=Z7pl(Fn?_AS$;`W+*%^JwPCw0V8fbRpa6nhA+|Pt`}yh_P#7^}Zg0 zF;u0kw=oaTh_Tj3(t6va1A6sp4qUQQPuAnB>;)z2$JglJn4u^^)#|^}8@t{IC^rW0 zDNx=`I(ui;6Q++`_(PEMQFBem%Ig`<{tRRTuU8{Xj(T@jEF zT(`Pvt@AO>BA>$1YGR;=SoM6^`*S@YHd#Jr|zh zcINR<1GY^CuY26Ln`>1bwnSQNj^ca6ZQohc0~SF+<6#e>F5YX7cDN8a6!RQC|kM*M5Ho-A6%>=xpeub|jeUu2P11 zd+gPq(x$Yg3=7n=a@xcjzwhWh$z!@H1(}GW$6!<3z>xE&ygp1TG-NamIlBNlswBH1 zdSKM{kp-Jp)_!aikaK@079k3_ z{jY{;twN<28H~?Z=_Ogt<=N6S+-IwkEqHExu3UnInt2FF|{&r;RNTi~FoI zEphR7-hX9*bc-=Ao&SIbwycQY(`u8_*Sdt>(UHd(llyQCFmeUH5TldE|2eutWg40A z90e*8g+XbPdFXnxaG?oEfgLJS*8rWY&XUESlgRWhhn}{*q>qIt$0}y=qPOMX_ z|DKHk=t%oe#!vg|r(T2IUv^f0k!-GWJ*KrQ?1bm`jN>{L3mkSqpx3-9ojdCM6mJf! zFjRZ?>Zb8NF*a6fmD@2k-bq{O_{_FdI;_Mtyp`VfVk_K}L8!unP-7N0zH7*N2PN@- zqZWWEC64=@D9x2eZU>6vl3a z-~DUPqE|h(pHoJ+EmmUQuMl#m^LE(j7TJtKFWHna1b<_Ts|S1wCBC|`-^@_-l5EK; z6BszZAE6uup_q59XDU@~&+yn6d?f>po8q+H@MbnngAcp|ZEt%n1(Sne6pz2(YP+;v_*P~p5>~YQqQ4;692j#2r*-wBHW(mS(X;+6oZ6lv0!t$ zXRyr_g~ZT-n?wM%V(xU2AD5G5N%Y=<*I3AF9UFptE5~Z2_gU%nFXUGM@*7cq#40@u z;5|yRnpmL+5jluVvD<)uZqr zBe~%y8|4ai-QGFzh3pC^AH!@wjD290J)wz0%EpbBWeWIKi;b@c+p?P{7I4Tc0h`Zz zFLtR8rB@^3uG!kKUE_cwi&roejtbCMA(=12j>;K!K4cpwee?0o(9GJuYAcH`lXuP1 zds^ZtfN!OUhqTPi0(uE^%Q%Q&kRk?AD~V)E>(Sk-152wL!7H=`g-n=RB|JUxKh}`p?8!TMqJ(e6)W(Lx-5k@*lWBp3#Q>`pr z3vFN#HNY4t_~|k*QkZSNgzq1*1+`N%M9JsRv1SGy07D6O^JWWawMehAx;Bg4=wJ|- z%3?|ReWlHRFU#+9O>>_{3Fe873l)$RkoPNOJWGxMn9z-;iLkji!^z<3B*3kJWFw2M zWL7K3eS=xqbO3b}5ff9m^aTN1026Q1Lz8dKGTI8aQDks$91J8?a~w2@~KxflwR3o;tQ3yMOZ@kY*ed?rs`-|xXK}R{61qk^Fn9^_VC|iyV zD5hjs&`y9j%-}{Yr_6|KHRE9%ypF@S-?X)(h|%k`+ILkKY)G9$buQ7OvDcv$U_{|(*lFP<2eT8j*{?913uBY zI=uz6DBuu{85qeZMIK|x4;4r|Kt3_Zpm2jmlk5Lo&!3lj9*76_*Ql;=vQx?_ixrU0 z(A=+-zE?rTfJZ0|E>`BH3&6yJYtYH{Kmw2x&+Twe1Ckgd@0B;T9cBbHSCBjw<>vue zs>71EX8D`M0Qh71#toG(t+Mqm4sY+=-Sshfm6kLxjpAfSZEqW7q%m4DXEE3*;sr`c zotPPCfe-OT%eB>&3Omq3WScvGG=!53c36IWiQXc`+S&>vF6!DgPC`kU^kfk<|k9eDTq9mF{%~oIyAX#of z?+Ck;X+V(H_3^W*xLM+=KnAtsd8_OwBY7*}^%U4j2c1%#&m(O7SDX)j-gNNe-8EGc zWQ!U8j!X44W1t^$*Br9M3Wp0=trBo#kbDFDy;AZ6g~#zQ#rIlWk3W6}2Djnm7DWGy z3^2>yo5%~ayqN_{cv-T(b^RYZf4nOHL}|ml*zgV_Pif^o7Rasy04%tkL)J60VG8M> z>d(cNrYX?r2GWI=F6Q7=0rxb5>nTj})l}*Ny2+WyR1)@9>6eVOQ7PS_Af{^Z9#+!M z%LO6KBGkM(efsUkWR!{gQcILtplLKnYhil}xmCb?6krYykC~xrrKI&Hn93k#C9zC_ zFSoQGR@f!cP{$oGl8GE>fgP>HAwW_vA!45ivT8(@wIXZ3S+<(TIqq6_!-1lR+R&0M zXJ}%im8_!BJQ18PVD%z-@Exw;Bnwz{7MBar@LDUX7Kj9T>(&24|9EaJA9s724~J23 zdCN8r0NqX-Dk1W*;^b2$`kF;rcu6^Mx}B9S_BkC~Tqzb@sCWqC-8)jTx~xWy2Ps6CDVewvXxC~-mnO%}ZOEBwB~ zMxe+NI^n_5$>AHFYsIedl3ReZotLGCCg=Z0{y<^VpP6M8i4s8K&R{Altkwb$Up}$H zjF|yqF(9eqpnT=P7A+jfLFpy9=jfb=MRn!D_!kDuqi(-RffsT;Ut8th0OS#R?gKOV zf+iUjpLhv68lX}C8|NtLS7!Z^H+oG4Q7SflWX7LSfQo{*DRC)76tSpU3-8VbI|QTy zAXo}KK_lcretNc?q^7oy&tVw(x@uyP2o#uSUAEjh!^$7iSh98gCloooO>#)Y!VU5h zBEFbKR*RFmlee?i4%JA%q~!ZWjHSs_JkGPA(TqEYuiNPw#5TUqN&O{zyckUpfImArEbErpi6$54pb>ck8?~f@T0lCcd z{Q&$SEAs&QX6Jy_A~|f9X~gap3T!jTE?Z>xcqHE{xy#EUUdYZ1$cG%+G*LkwHOs;P zNw^l-LJ@ndBrU>zW@IykV@k<&v*d^%t5U)@abi0Cr7eS`YT=+HA6du%9#1WjT-Itl1@e@D->|69XeYld>p3xN{(Gzv z_#{sOo^X|wi}&Z6u{13_VFv!`VL^was6!{VlIK+0&?LjD7z)~NaL_2-oW#j{z%%~9 z`v4rBDLq3;YeRMA2C30Xm}{h04bp3zbQqE9cxlX2>1H8Bd2;fVLaSI{k1rZ-hQvTD zRlC*C;Cb~E_>awY&J0=3!nriG-?G5|IyRF=7jfujv!s_pdd=ikhFC3NkG~$>ByV9Q?|=^*eYoeO;lQdjyyy#19Erk9IONfvW_dfIohml{C^| z9*07wpyLm~HF83G1KMCkYHm2#F<>D3I#~fvHDIf6gC6X7jRjCx0Q(2OE}V|l{|jIh zM1uuy(xP*7S0Mh$eA}hDSaS z3ft1q3?&>VT-{0o| z&r|oH3DM4LM6_Ilb^+K%tJCin;gsp`D?S2tD^XT~FBakD;joHhvnaeki&q=)xr!Gy zXW`?Aupj|GIKMg{`0VUN0f;G}c+vAAhK2C2_Ibab{RCV>!>(LfDWj`>o-*BxM)DuV z2}q_ER#Rwu2e*>~W-)n@7L@sCAPgdy_=^U>g1Ps=pl6fRE~Km~O)*&MK70Gv(_q`t zWC=RW?^m`b#wd%5&$o8=uN|=so31~vxCu*PAaK=_q9n&{mh=MRrBnZs64_*XZbHy}>ynoj&UtH5>Zj8v6j}kiW5}-^d!L%(A9CEk{lE zzG>^C>#ShXzUgF9{)-Ca`_J5iS2J(d4ktLz9(-`*WGQ&_AI>w9h7FI0>uP*e7JQy( zkIHVAI<-JiSMO&~4w9f%PwBd3fTzi&irI=wO`X{_lY_}i(-br3NxaAa6tOKCot^-? zD4B-f%rTk7*>EQtn^q)rOB^?egEi9^IHb*x=4*o!P}tCoI)7Q|iMghC8f)A(C4j?t z*~-xxxN10L5YXbfD%-C^M-hj`v=Kf@Q%~aI{&n`ce}`BNuv9a)#sc?7YwBU?Je4J7 z{-ZVnMEaFzLH+oPN7Tz!6@Pna*=iU9qW&LG8T_08 literal 0 HcmV?d00001 diff --git a/WEBFiles/scripts.js b/WEBFiles/scripts.js new file mode 100644 index 0000000..a9cadab --- /dev/null +++ b/WEBFiles/scripts.js @@ -0,0 +1,33 @@ +var setFormValues = function(form, cfg) { + var name, field; + for (name in cfg){ + if (form[name]) { + field = form[name]; + if (field[1] && field[1].type === 'checkbox') { + field = field[1]; + } + if (field.type === 'checkbox'){ + field.checked = cfg[name] === '1' ? true : false; + } else { + field.value = cfg[name]; + } + } + } +} +var $ = function(id) { + return document.getElementById(id); +} +var reloadTimer={ + s:10, + reload:function(start) { + if(start) { + this.s = start; + } + $('timer').innerHTML = this.s < 10 ? '0' + this.s : this.s; + if (this.s == 0){ + document.location.href = document.referrer != '' ? document.referrer : '/'; + } + this.s--; + setTimeout('reloadTimer.reload()', 1000); + } +} diff --git a/WEBFiles/site.js b/WEBFiles/site.js new file mode 100644 index 0000000..c083aaa --- /dev/null +++ b/WEBFiles/site.js @@ -0,0 +1,95 @@ +/* Java for WEB device */ +var ajaxList = new Array(); // Stores a queue of AJAX events to process +var nextimeout = 500; +function newAJAXCommand(url, container, repeat, data) { + // Set up our object + var newAjax = new Object(); + var theTimer = new Date(); + newAjax.url = url; + newAjax.container = container; + newAjax.repeat = repeat; + newAjax.ajaxReq = null; + // Create and send the request + if (window.XMLHttpRequest) { + newAjax.ajaxReq = new XMLHttpRequest(); + newAjax.ajaxReq.open((data == null) ? "GET" : "POST", newAjax.url, true); + newAjax.ajaxReq.send(data); + // If we're using IE6 style (maybe 5.5 compatible too) + } else if (window.ActiveXObject) { + newAjax.ajaxReq = new ActiveXObject("Microsoft.XMLHTTP"); + if (newAjax.ajaxReq) { + newAjax.ajaxReq.open((data == null) ? "GET" : "POST", newAjax.url, true); + newAjax.ajaxReq.send(data); + } + } + newAjax.lastCalled = theTimer.getTime(); + // Store in our array + ajaxList.push(newAjax); +} +function pollAJAX() { + var curAjax = new Object(); + var theTimer = new Date(); + var elapsed; + // Read off the ajaxList objects one by one + for (i = ajaxList.length; i > 0; i--) { + curAjax = ajaxList.shift(); + if (!curAjax) + continue; + elapsed = theTimer.getTime() - curAjax.lastCalled; + // If we suceeded + if (curAjax.ajaxReq.readyState == 4 && curAjax.ajaxReq.status == 200) { + // If it has a container, write the result + if (typeof (curAjax.container) == 'function') + curAjax.container(curAjax.ajaxReq.responseXML.documentElement); + else if (typeof (curAjax.container) == 'string') + document.getElementById(curAjax.container).innerHTML = curAjax.ajaxReq.responseText; + // (otherwise do nothing for null values) + curAjax.ajaxReq.abort(); + curAjax.ajaxReq = null; + // If it's a repeatable request, then do so + if (curAjax.repeat) { + if (elapsed >= curAjax.repeat) + elapsed = 100; + else + elapsed = curAjax.repeat - elapsed; + setTimeout("newAJAXCommand('" + curAjax.url + "'," + + curAjax.container + "," + curAjax.repeat + ")", + elapsed); + } + continue; + } + // If we've waited over 4 second, then we timed out + if ((curAjax.ajaxReq.readyState == 4 && curAjax.ajaxReq.status == 404) + || (elapsed > 4000)) { + // Invoke the user function with null input + if (typeof (curAjax.container) == 'function') + curAjax.container(null); + else + // Alert the user + alert("Command failed.\nConnection to device was lost."); + curAjax.ajaxReq.abort(); + curAjax.ajaxReq = null; + // If it's a repeatable request, then do so + if (curAjax.repeat) + setTimeout("newAJAXCommand('" + curAjax.url + "'," + + curAjax.container + "," + curAjax.repeat + ")", 200); + continue; + } + // Otherwise, just keep waiting + ajaxList.push(curAjax); + } + // Call ourselves again in 10ms? + setTimeout("pollAJAX()", nextimeout); +}// End pollAjax +function getXMLValue(xmlData, field) { + try { + if (xmlData.getElementsByTagName(field)[0].firstChild.nodeValue) + return xmlData.getElementsByTagName(field)[0].firstChild.nodeValue; + else + return null; + } catch (err) { + return null; + } +} +//kick off the AJAX Updater +setTimeout("pollAJAX()", nextimeout); diff --git a/WEBFiles/slider.js b/WEBFiles/slider.js new file mode 100644 index 0000000..e6db7f7 --- /dev/null +++ b/WEBFiles/slider.js @@ -0,0 +1,97 @@ +function slider(elemId, sliderWidth, range1, range2, step) { + var knobWidth = 17; + var knobHeight = 21; + var sliderHeight = 21; + var offsX, tmp; + var d = document; + var isIE = d.all || window.opera; + var point = (sliderWidth - knobWidth - 3) / (range2 - range1); + var slider = d.createElement('DIV'); + slider.id = elemId + '_slider'; + slider.className = 'slider'; + d.getElementById(elemId).appendChild(slider); + var knob = d.createElement('DIV'); + knob.id = elemId + '_knob'; + knob.className = 'knob'; + slider.appendChild(knob); + knob.style.left = 0; + knob.style.width = knobWidth + 'px'; + knob.style.height = knobHeight + 'px'; + slider.style.width = sliderWidth + 'px'; + slider.style.height = sliderHeight + 'px'; + var sliderOffset = slider.offsetLeft; + tmp = slider.offsetParent; + while (tmp.tagName != 'BODY') { + sliderOffset += tmp.offsetLeft; + tmp = tmp.offsetParent + } + if (isIE) { + knob.onmousedown = startCoord; + slider.onclick = sliderClick; + knob.onmouseup = endCoord; + slider.onmouseup = endCoord + } else { + knob.addEventListener("mousedown", startCoord, true); + slider.addEventListener("click", sliderClick, true); + knob.addEventListener("mouseup", endCoord, true); + slider.addEventListener("mouseup", endCoord, true) + } + function setValue(x) { + if (x < 0) + knob.style.left = 0; + else if (x > sliderWidth - knobWidth - 3) + knob.style.left = (sliderWidth - 3 - knobWidth) + 'px'; + else { + if (step == 0) + knob.style.left = x + 'px'; + else + knob.style.left = Math.round(x / (step * point)) * step * point + + 'px' + } + NewTimeScale(getValue()) + } + function setValue2(x) { + if (x < range1 || x > range2) + alert('Value is not included into a slider range!'); + else + setValue((x - range1) * point); + NewTimeScale(getValue()) + } + function getValue() { + return Math.round(parseInt(knob.style.left) / point) + range1 + } + function sliderClick(e) { + var x; + if (isIE) { + if (event.srcElement != slider) + return; + x = event.offsetX - Math.round(knobWidth / 2) + } else + x = e.pageX - sliderOffset - knobWidth / 2; + setValue(x) + } + function startCoord(e) { + if (isIE) { + offsX = event.clientX - parseInt(knob.style.left); + slider.onmousemove = mov + } else { + slider.addEventListener("mousemove", mov, true) + } + } + function mov(e) { + var x; + if (isIE) + x = event.clientX - offsX; + else + x = e.pageX - sliderOffset - knobWidth / 2; + setValue(x) + } + function endCoord() { + if (isIE) + slider.onmousemove = null; + else + slider.removeEventListener("mousemove", mov, true) + } + this.setValue = setValue2; + this.getValue = getValue +} diff --git a/WEBFiles/smoothie.js b/WEBFiles/smoothie.js new file mode 100644 index 0000000..77bd785 --- /dev/null +++ b/WEBFiles/smoothie.js @@ -0,0 +1,808 @@ +// MIT License: +// +// Copyright (c) 2010-2013, Joe Walnes +// 2013-2014, Drew Noakes +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +/** + * Smoothie Charts - http://smoothiecharts.org/ + * (c) 2010-2013, Joe Walnes + * 2013-2014, Drew Noakes + * + * v1.0: Main charting library, by Joe Walnes + * v1.1: Auto scaling of axis, by Neil Dunn + * v1.2: fps (frames per second) option, by Mathias Petterson + * v1.3: Fix for divide by zero, by Paul Nikitochkin + * v1.4: Set minimum, top-scale padding, remove timeseries, add optional timer to reset bounds, by Kelley Reynolds + * v1.5: Set default frames per second to 50... smoother. + * .start(), .stop() methods for conserving CPU, by Dmitry Vyal + * options.interpolation = 'bezier' or 'line', by Dmitry Vyal + * options.maxValue to fix scale, by Dmitry Vyal + * v1.6: minValue/maxValue will always get converted to floats, by Przemek Matylla + * v1.7: options.grid.fillStyle may be a transparent color, by Dmitry A. Shashkin + * Smooth rescaling, by Kostas Michalopoulos + * v1.8: Set max length to customize number of live points in the dataset with options.maxDataSetLength, by Krishna Narni + * v1.9: Display timestamps along the bottom, by Nick and Stev-io + * (https://groups.google.com/forum/?fromgroups#!topic/smoothie-charts/-Ywse8FCpKI%5B1-25%5D) + * Refactored by Krishna Narni, to support timestamp formatting function + * v1.10: Switch to requestAnimationFrame, removed the now obsoleted options.fps, by Gergely Imreh + * v1.11: options.grid.sharpLines option added, by @drewnoakes + * Addressed warning seen in Firefox when seriesOption.fillStyle undefined, by @drewnoakes + * v1.12: Support for horizontalLines added, by @drewnoakes + * Support for yRangeFunction callback added, by @drewnoakes + * v1.13: Fixed typo (#32), by @alnikitich + * v1.14: Timer cleared when last TimeSeries removed (#23), by @davidgaleano + * Fixed diagonal line on chart at start/end of data stream, by @drewnoakes + * v1.15: Support for npm package (#18), by @dominictarr + * Fixed broken removeTimeSeries function (#24) by @davidgaleano + * Minor performance and tidying, by @drewnoakes + * v1.16: Bug fix introduced in v1.14 relating to timer creation/clearance (#23), by @drewnoakes + * TimeSeries.append now deals with out-of-order timestamps, and can merge duplicates, by @zacwitte (#12) + * Documentation and some local variable renaming for clarity, by @drewnoakes + * v1.17: Allow control over font size (#10), by @drewnoakes + * Timestamp text won't overlap, by @drewnoakes + * v1.18: Allow control of max/min label precision, by @drewnoakes + * Added 'borderVisible' chart option, by @drewnoakes + * Allow drawing series with fill but no stroke (line), by @drewnoakes + * v1.19: Avoid unnecessary repaints, and fixed flicker in old browsers having multiple charts in document (#40), by @asbai + * v1.20: Add SmoothieChart.getTimeSeriesOptions and SmoothieChart.bringToFront functions, by @drewnoakes + * v1.21: Add 'step' interpolation mode, by @drewnoakes + * v1.22: Add support for different pixel ratios. Also add optional y limit formatters, by @copacetic + * v1.23: Fix bug introduced in v1.22 (#44), by @drewnoakes + * v1.24: Fix bug introduced in v1.23, re-adding parseFloat to y-axis formatter defaults, by @siggy_sf + * v1.25: Fix bug seen when adding a data point to TimeSeries which is older than the current data, by @Nking92 + * Draw time labels on top of series, by @comolosabia + * Add TimeSeries.clear function, by @drewnoakes + * v1.26: Add support for resizing on high device pixel ratio screens, by @copacetic + * v1.27: Fix bug introduced in v1.26 for non whole number devicePixelRatio values, by @zmbush + * v1.28: Add 'minValueScale' option, by @megawac + */ + +;(function(exports) { + + var Util = { + extend: function() { + arguments[0] = arguments[0] || {}; + for (var i = 1; i < arguments.length; i++) + { + for (var key in arguments[i]) + { + if (arguments[i].hasOwnProperty(key)) + { + if (typeof(arguments[i][key]) === 'object') { + if (arguments[i][key] instanceof Array) { + arguments[0][key] = arguments[i][key]; + } else { + arguments[0][key] = Util.extend(arguments[0][key], arguments[i][key]); + } + } else { + arguments[0][key] = arguments[i][key]; + } + } + } + } + return arguments[0]; + } + }; + + /** + * Initialises a new TimeSeries with optional data options. + * + * Options are of the form (defaults shown): + * + *
+   * {
+   *   resetBounds: true,        // enables/disables automatic scaling of the y-axis
+   *   resetBoundsInterval: 3000 // the period between scaling calculations, in millis
+   * }
+   * 
+ * + * Presentation options for TimeSeries are specified as an argument to SmoothieChart.addTimeSeries. + * + * @constructor + */ + function TimeSeries(options) { + this.options = Util.extend({}, TimeSeries.defaultOptions, options); + this.clear(); + } + + TimeSeries.defaultOptions = { + resetBoundsInterval: 3000, + resetBounds: false // true + }; + + /** + * Clears all data and state from this TimeSeries object. + */ + TimeSeries.prototype.clear = function() { + this.data = []; + this.maxValue = Number.NaN; // The maximum value ever seen in this TimeSeries. + this.minValue = Number.NaN; // The minimum value ever seen in this TimeSeries. + }; + + /** + * Recalculate the min/max values for this TimeSeries object. + * + * This causes the graph to scale itself in the y-axis. + */ + TimeSeries.prototype.resetBounds = function() { + if (this.data.length) { + // Walk through all data points, finding the min/max value + this.maxValue = this.data[0][1]; + this.minValue = this.data[0][1]; + for (var i = 1; i < this.data.length; i++) { + var value = this.data[i][1]; + if (value > this.maxValue) { + this.maxValue = value; + } + if (value < this.minValue) { + this.minValue = value; + } + } + } else { + // No data exists, so set min/max to NaN + this.maxValue = Number.NaN; + this.minValue = Number.NaN; + } + }; + + /** + * Adds a new data point to the TimeSeries, preserving chronological order. + * + * @param timestamp the position, in time, of this data point + * @param value the value of this data point + * @param sumRepeatedTimeStampValues if timestamp has an exact match in the series, this flag controls + * whether it is replaced, or the values summed (defaults to false.) + */ + TimeSeries.prototype.append = function(timestamp, value, sumRepeatedTimeStampValues) { + // Rewind until we hit an older timestamp + var i = this.data.length - 1; + while (i >= 0 && this.data[i][0] > timestamp) { + i--; + } + + if (i === -1) { + // This new item is the oldest data + this.data.splice(0, 0, [timestamp, value]); + } else if (this.data.length > 0 && this.data[i][0] === timestamp) { + // Update existing values in the array + if (sumRepeatedTimeStampValues) { + // Sum this value into the existing 'bucket' +// this.data[i][1] += value; +// value = this.data[i][1]; + // + this.data[i][1] = (value + this.data[i][1])/2.0; + value = this.data[i]; + } else { + // Replace the previous value + this.data[i][1] = value; + } + } else if (i < this.data.length - 1) { + // Splice into the correct position to keep timestamps in order + this.data.splice(i + 1, 0, [timestamp, value]); + } else { + // Add to the end of the array + this.data.push([timestamp, value]); + } + + this.maxValue = isNaN(this.maxValue) ? value : Math.max(this.maxValue, value); + this.minValue = isNaN(this.minValue) ? value : Math.min(this.minValue, value); + }; + + TimeSeries.prototype.dropOldData = function(oldestValidTime, maxDataSetLength) { + // We must always keep one expired data point as we need this to draw the + // line that comes into the chart from the left, but any points prior to that can be removed. + var removeCount = 0; + while (this.data.length - removeCount >= maxDataSetLength && this.data[removeCount + 1][0] < oldestValidTime) { + removeCount++; + } + if (removeCount !== 0) { + this.data.splice(0, removeCount); + } + }; + + /** + * Initialises a new SmoothieChart. + * + * Options are optional, and should be of the form below. Just specify the values you + * need and the rest will be given sensible defaults as shown: + * + *
+   * {
+   *   minValue: undefined,                      // specify to clamp the lower y-axis to a given value
+   *   maxValue: undefined,                      // specify to clamp the upper y-axis to a given value
+   *   maxValueScale: 1,                         // allows proportional padding to be added above the chart. for 10% padding, specify 1.1.
+   *   minValueScale: 1,                         // allows proportional padding to be added below the chart. for 10% padding, specify 1.1.
+   *   yRangeFunction: undefined,                // function({min: , max: }) { return {min: , max: }; }
+   *   scaleSmoothing: 0.125,                    // controls the rate at which y-value zoom animation occurs
+   *   millisPerPixel: 20,                       // sets the speed at which the chart pans by
+   *   enableDpiScaling: true,                   // support rendering at different DPI depending on the device
+   *   yMinFormatter: function(min, precision) { // callback function that formats the min y value label
+   *     return parseFloat(min).toFixed(precision);
+   *   },
+   *   yMaxFormatter: function(max, precision) { // callback function that formats the max y value label
+   *     return parseFloat(max).toFixed(precision);
+   *   },
+   *   maxDataSetLength: 2,
+   *   interpolation: 'bezier'                   // one of 'bezier', 'linear', or 'step'
+   *   timestampFormatter: null,                 // optional function to format time stamps for bottom of chart
+   *                                             // you may use SmoothieChart.timeFormatter, or your own: function(date) { return ''; }
+   *   scrollBackwards: false,                   // reverse the scroll direction of the chart
+   *   horizontalLines: [],                      // [ { value: 0, color: '#ffffff', lineWidth: 1 } ]
+   *   grid:
+   *   {
+   *     fillStyle: '#000000',                   // the background colour of the chart
+   *     lineWidth: 1,                           // the pixel width of grid lines
+   *     strokeStyle: '#777777',                 // colour of grid lines
+   *     millisPerLine: 1000,                    // distance between vertical grid lines
+   *     sharpLines: false,                      // controls whether grid lines are 1px sharp, or softened
+   *     verticalSections: 2,                    // number of vertical sections marked out by horizontal grid lines
+   *     borderVisible: true                     // whether the grid lines trace the border of the chart or not
+   *   },
+   *   labels
+   *   {
+   *     disabled: false,                        // enables/disables labels showing the min/max values
+   *     fillStyle: '#ffffff',                   // colour for text of labels,
+   *     fontSize: 15,
+   *     fontFamily: 'sans-serif',
+   *     precision: 2
+   *   }
+   * }
+   * 
+ * + * @constructor + */ + function SmoothieChart(options) { + this.options = Util.extend({}, SmoothieChart.defaultChartOptions, options); + this.seriesSet = []; + this.currentValueRange = 1; + this.currentVisMinValue = 0; + this.lastRenderTimeMillis = 0; + } + + SmoothieChart.defaultChartOptions = { + millisPerPixel: 20, + enableDpiScaling: true, + yMinFormatter: function(min, precision) { + return parseFloat(min).toFixed(precision); + }, + yMaxFormatter: function(max, precision) { + return parseFloat(max).toFixed(precision); + }, + maxValueScale: 1, + minValueScale: 1, + interpolation: 'bezier', + scaleSmoothing: 0.125, + maxDataSetLength: 2, + scrollBackwards: false, + grid: { + fillStyle: '#000000', + strokeStyle: '#777777', + lineWidth: 1, + sharpLines: false, + millisPerLine: 1000, + verticalSections: 2, + borderVisible: true + }, + labels: { + fillStyle: '#ffffff', + disabled: false, + fontSize: 10, + fontFamily: 'monospace', + precision: 2 + }, + horizontalLines: [] + }; + + // Based on http://inspirit.github.com/jsfeat/js/compatibility.js + SmoothieChart.AnimateCompatibility = (function() { + var requestAnimationFrame = function(callback, element) { + var requestAnimationFrame = + window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame || + window.oRequestAnimationFrame || + window.msRequestAnimationFrame || + function(callback) { + return window.setTimeout(function() { + callback(new Date().getTime()); + }, 16); + }; + return requestAnimationFrame.call(window, callback, element); + }, + cancelAnimationFrame = function(id) { + var cancelAnimationFrame = + window.cancelAnimationFrame || + function(id) { + clearTimeout(id); + }; + return cancelAnimationFrame.call(window, id); + }; + + return { + requestAnimationFrame: requestAnimationFrame, + cancelAnimationFrame: cancelAnimationFrame + }; + })(); + + SmoothieChart.defaultSeriesPresentationOptions = { + lineWidth: 1, + strokeStyle: '#ffffff' + }; + + /** + * Adds a TimeSeries to this chart, with optional presentation options. + * + * Presentation options should be of the form (defaults shown): + * + *
+   * {
+   *   lineWidth: 1,
+   *   strokeStyle: '#ffffff',
+   *   fillStyle: undefined
+   * }
+   * 
+ */ + SmoothieChart.prototype.addTimeSeries = function(timeSeries, options) { + this.seriesSet.push({timeSeries: timeSeries, options: Util.extend({}, SmoothieChart.defaultSeriesPresentationOptions, options)}); + if (timeSeries.options.resetBounds && timeSeries.options.resetBoundsInterval > 0) { + timeSeries.resetBoundsTimerId = setInterval( + function() { + timeSeries.resetBounds(); + }, + timeSeries.options.resetBoundsInterval + ); + } + }; + + /** + * Removes the specified TimeSeries from the chart. + */ + SmoothieChart.prototype.removeTimeSeries = function(timeSeries) { + // Find the correct timeseries to remove, and remove it + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + this.seriesSet.splice(i, 1); + break; + } + } + // If a timer was operating for that timeseries, remove it + if (timeSeries.resetBoundsTimerId) { + // Stop resetting the bounds, if we were + clearInterval(timeSeries.resetBoundsTimerId); + } + }; + + /** + * Gets render options for the specified TimeSeries. + * + * As you may use a single TimeSeries in multiple charts with different formatting in each usage, + * these settings are stored in the chart. + */ + SmoothieChart.prototype.getTimeSeriesOptions = function(timeSeries) { + // Find the correct timeseries to remove, and remove it + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + return this.seriesSet[i].options; + } + } + }; + + /** + * Brings the specified TimeSeries to the top of the chart. It will be rendered last. + */ + SmoothieChart.prototype.bringToFront = function(timeSeries) { + // Find the correct timeseries to remove, and remove it + var numSeries = this.seriesSet.length; + for (var i = 0; i < numSeries; i++) { + if (this.seriesSet[i].timeSeries === timeSeries) { + var set = this.seriesSet.splice(i, 1); + this.seriesSet.push(set[0]); + break; + } + } + }; + + /** + * Instructs the SmoothieChart to start rendering to the provided canvas, with specified delay. + * + * @param canvas the target canvas element + * @param delayMillis an amount of time to wait before a data point is shown. This can prevent the end of the series + * from appearing on screen, with new values flashing into view, at the expense of some latency. + */ + SmoothieChart.prototype.streamTo = function(canvas, delayMillis) { + this.canvas = canvas; + this.delay = delayMillis; + this.start(); + }; + + /** + * Make sure the canvas has the optimal resolution for the device's pixel ratio. + */ + SmoothieChart.prototype.resize = function() { + // TODO this function doesn't handle the value of enableDpiScaling changing during execution + if (!this.options.enableDpiScaling || !window || window.devicePixelRatio === 1) + return; + + var dpr = window.devicePixelRatio; + var width = parseInt(this.canvas.getAttribute('width')); + var height = parseInt(this.canvas.getAttribute('height')); + + if (!this.originalWidth || (Math.floor(this.originalWidth * dpr) !== width)) { + this.originalWidth = width; + this.canvas.setAttribute('width', (Math.floor(width * dpr)).toString()); + this.canvas.style.width = width + 'px'; + this.canvas.getContext('2d').scale(dpr, dpr); + } + + if (!this.originalHeight || (Math.floor(this.originalHeight * dpr) !== height)) { + this.originalHeight = height; + this.canvas.setAttribute('height', (Math.floor(height * dpr)).toString()); + this.canvas.style.height = height + 'px'; + this.canvas.getContext('2d').scale(dpr, dpr); + } + }; + + /** + * Starts the animation of this chart. + */ + SmoothieChart.prototype.start = function() { + if (this.frame) { + // We're already running, so just return + return; + } + + // Renders a frame, and queues the next frame for later rendering + var animate = function() { + this.frame = SmoothieChart.AnimateCompatibility.requestAnimationFrame(function() { + this.render(); + animate(); + }.bind(this)); + }.bind(this); + + animate(); + }; + + /** + * Stops the animation of this chart. + */ + SmoothieChart.prototype.stop = function() { + if (this.frame) { + SmoothieChart.AnimateCompatibility.cancelAnimationFrame(this.frame); + delete this.frame; + } + }; + + SmoothieChart.prototype.updateValueRange = function() { + // Calculate the current scale of the chart, from all time series. + var chartOptions = this.options, + chartMaxValue = Number.NaN, + chartMinValue = Number.NaN; + + for (var d = 0; d < this.seriesSet.length; d++) { + // TODO(ndunn): We could calculate / track these values as they stream in. + var timeSeries = this.seriesSet[d].timeSeries; + if (!isNaN(timeSeries.maxValue)) { + chartMaxValue = !isNaN(chartMaxValue) ? Math.max(chartMaxValue, timeSeries.maxValue) : timeSeries.maxValue; + } + + if (!isNaN(timeSeries.minValue)) { + chartMinValue = !isNaN(chartMinValue) ? Math.min(chartMinValue, timeSeries.minValue) : timeSeries.minValue; + } + } + + // Scale the chartMaxValue to add padding at the top if required + if (chartOptions.maxValue != null) { + chartMaxValue = chartOptions.maxValue; + } else { + chartMaxValue *= chartOptions.maxValueScale; + } + + // Set the minimum if we've specified one + if (chartOptions.minValue != null) { + chartMinValue = chartOptions.minValue; + } else { + chartMinValue -= Math.abs(chartMinValue * chartOptions.minValueScale - chartMinValue); + } + + // If a custom range function is set, call it + if (this.options.yRangeFunction) { + var range = this.options.yRangeFunction({min: chartMinValue, max: chartMaxValue}); + chartMinValue = range.min; + chartMaxValue = range.max; + } + + if (!isNaN(chartMaxValue) && !isNaN(chartMinValue)) { + var targetValueRange = chartMaxValue - chartMinValue; + var valueRangeDiff = (targetValueRange - this.currentValueRange); + var minValueDiff = (chartMinValue - this.currentVisMinValue); + this.isAnimatingScale = Math.abs(valueRangeDiff) > 0.01 || Math.abs(minValueDiff) > 0.01; + this.currentValueRange += chartOptions.scaleSmoothing * valueRangeDiff; + this.currentVisMinValue += chartOptions.scaleSmoothing * minValueDiff; + } + + this.valueRange = { min: chartMinValue, max: chartMaxValue }; + }; + + SmoothieChart.prototype.render = function(canvas, time) { + var nowMillis = new Date().getTime(); + + if (!this.isAnimatingScale) { + // We're not animating. We can use the last render time and the scroll speed to work out whether + // we actually need to paint anything yet. If not, we can return immediately. + + // Render at least every 1/6th of a second. The canvas may be resized, which there is + // no reliable way to detect. + var maxIdleMillis = Math.min(1000/6, this.options.millisPerPixel); + + if (nowMillis - this.lastRenderTimeMillis < maxIdleMillis) { + return; + } + } + + this.resize(); + + this.lastRenderTimeMillis = nowMillis; + + canvas = canvas || this.canvas; + time = time || nowMillis - (this.delay || 0); + + // Round time down to pixel granularity, so motion appears smoother. + time -= time % this.options.millisPerPixel; + + var context = canvas.getContext('2d'), + chartOptions = this.options, + dimensions = { top: 0, left: 0, width: canvas.clientWidth, height: canvas.clientHeight }, + // Calculate the threshold time for the oldest data points. + oldestValidTime = time - (dimensions.width * chartOptions.millisPerPixel), + valueToYPixel = function(value) { + var offset = value - this.currentVisMinValue; + return this.currentValueRange === 0 + ? dimensions.height + : dimensions.height - (Math.round((offset / this.currentValueRange) * dimensions.height)); + }.bind(this), + timeToXPixel = function(t) { + if(chartOptions.scrollBackwards) { + return Math.round((time - t) / chartOptions.millisPerPixel); + } + return Math.round(dimensions.width - ((time - t) / chartOptions.millisPerPixel)); + }; + + this.updateValueRange(); + + context.font = chartOptions.labels.fontSize + 'px ' + chartOptions.labels.fontFamily; + + // Save the state of the canvas context, any transformations applied in this method + // will get removed from the stack at the end of this method when .restore() is called. + context.save(); + + // Move the origin. + context.translate(dimensions.left, dimensions.top); + + // Create a clipped rectangle - anything we draw will be constrained to this rectangle. + // This prevents the occasional pixels from curves near the edges overrunning and creating + // screen cheese (that phrase should need no explanation). + context.beginPath(); + context.rect(0, 0, dimensions.width, dimensions.height); + context.clip(); + + // Clear the working area. + context.save(); + context.fillStyle = chartOptions.grid.fillStyle; + context.clearRect(0, 0, dimensions.width, dimensions.height); + context.fillRect(0, 0, dimensions.width, dimensions.height); + context.restore(); + + // Grid lines... + context.save(); + context.lineWidth = chartOptions.grid.lineWidth; + context.strokeStyle = chartOptions.grid.strokeStyle; + // Vertical (time) dividers. + if (chartOptions.grid.millisPerLine > 0) { + context.beginPath(); + for (var t = time - (time % chartOptions.grid.millisPerLine); + t >= oldestValidTime; + t -= chartOptions.grid.millisPerLine) { + var gx = timeToXPixel(t); + if (chartOptions.grid.sharpLines) { + gx -= 0.5; + } + context.moveTo(gx, 0); + context.lineTo(gx, dimensions.height); + } + context.stroke(); + context.closePath(); + } + + // Horizontal (value) dividers. + for (var v = 1; v < chartOptions.grid.verticalSections; v++) { + var gy = Math.round(v * dimensions.height / chartOptions.grid.verticalSections); + if (chartOptions.grid.sharpLines) { + gy -= 0.5; + } + context.beginPath(); + context.moveTo(0, gy); + context.lineTo(dimensions.width, gy); + context.stroke(); + context.closePath(); + } + // Bounding rectangle. + if (chartOptions.grid.borderVisible) { + context.beginPath(); + context.strokeRect(0, 0, dimensions.width, dimensions.height); + context.closePath(); + } + context.restore(); + + // Draw any horizontal lines... + if (chartOptions.horizontalLines && chartOptions.horizontalLines.length) { + for (var hl = 0; hl < chartOptions.horizontalLines.length; hl++) { + var line = chartOptions.horizontalLines[hl], + hly = Math.round(valueToYPixel(line.value)) - 0.5; + context.strokeStyle = line.color || '#ffffff'; + context.lineWidth = line.lineWidth || 1; + context.beginPath(); + context.moveTo(0, hly); + context.lineTo(dimensions.width, hly); + context.stroke(); + context.closePath(); + } + } + + // For each data set... + for (var d = 0; d < this.seriesSet.length; d++) { + context.save(); + var timeSeries = this.seriesSet[d].timeSeries, + dataSet = timeSeries.data, + seriesOptions = this.seriesSet[d].options; + + // Delete old data that's moved off the left of the chart. + timeSeries.dropOldData(oldestValidTime, chartOptions.maxDataSetLength); + + // Set style for this dataSet. + context.lineWidth = seriesOptions.lineWidth; + context.strokeStyle = seriesOptions.strokeStyle; + // Draw the line... + context.beginPath(); + // Retain lastX, lastY for calculating the control points of bezier curves. + var firstX = 0, lastX = 0, lastY = 0; + for (var i = 0; i < dataSet.length && dataSet.length !== 1; i++) { + var x = timeToXPixel(dataSet[i][0]), + y = valueToYPixel(dataSet[i][1]); + + if (i === 0) { + firstX = x; + context.moveTo(x, y); + } else { + switch (chartOptions.interpolation) { + case "linear": + case "line": { + context.lineTo(x,y); + break; + } + case "bezier": + default: { + // Great explanation of Bezier curves: http://en.wikipedia.org/wiki/Bezier_curve#Quadratic_curves + // + // Assuming A was the last point in the line plotted and B is the new point, + // we draw a curve with control points P and Q as below. + // + // A---P + // | + // | + // | + // Q---B + // + // Importantly, A and P are at the same y coordinate, as are B and Q. This is + // so adjacent curves appear to flow as one. + // + context.bezierCurveTo( // startPoint (A) is implicit from last iteration of loop + Math.round((lastX + x) / 2), lastY, // controlPoint1 (P) + Math.round((lastX + x)) / 2, y, // controlPoint2 (Q) + x, y); // endPoint (B) + break; + } + case "step": { + context.lineTo(x,lastY); + context.lineTo(x,y); + break; + } + } + } + + lastX = x; lastY = y; + } + + if (dataSet.length > 1) { + if (seriesOptions.fillStyle) { + // Close up the fill region. + context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, lastY); + context.lineTo(dimensions.width + seriesOptions.lineWidth + 1, dimensions.height + seriesOptions.lineWidth + 1); + context.lineTo(firstX, dimensions.height + seriesOptions.lineWidth); + context.fillStyle = seriesOptions.fillStyle; + context.fill(); + } + + if (seriesOptions.strokeStyle && seriesOptions.strokeStyle !== 'none') { + context.stroke(); + } + context.closePath(); + } + context.restore(); + } + + // Draw the axis values on the chart. + if (!chartOptions.labels.disabled && !isNaN(this.valueRange.min) && !isNaN(this.valueRange.max)) { + var maxValueString = chartOptions.yMaxFormatter(this.valueRange.max, chartOptions.labels.precision), + minValueString = chartOptions.yMinFormatter(this.valueRange.min, chartOptions.labels.precision), + labelPos = chartOptions.scrollBackwards ? 0 : dimensions.width - context.measureText(maxValueString).width - 2; + context.fillStyle = chartOptions.labels.fillStyle; + context.fillText(maxValueString, labelPos, chartOptions.labels.fontSize); + context.fillText(minValueString, labelPos, dimensions.height - 2); + } + + // Display timestamps along x-axis at the bottom of the chart. + if (chartOptions.timestampFormatter && chartOptions.grid.millisPerLine > 0) { + var textUntilX = chartOptions.scrollBackwards + ? context.measureText(minValueString).width + : dimensions.width - context.measureText(minValueString).width + 4; + for (var t = time - (time % chartOptions.grid.millisPerLine); + t >= oldestValidTime; + t -= chartOptions.grid.millisPerLine) { + var gx = timeToXPixel(t); + // Only draw the timestamp if it won't overlap with the previously drawn one. + if ((!chartOptions.scrollBackwards && gx < textUntilX) || (chartOptions.scrollBackwards && gx > textUntilX)) { + // Formats the timestamp based on user specified formatting function + // SmoothieChart.timeFormatter function above is one such formatting option + var tx = new Date(t), + ts = chartOptions.timestampFormatter(tx), + tsWidth = context.measureText(ts).width; + + textUntilX = chartOptions.scrollBackwards + ? gx + tsWidth + 2 + : gx - tsWidth - 2; + + context.fillStyle = chartOptions.labels.fillStyle; + if(chartOptions.scrollBackwards) { + context.fillText(ts, gx, dimensions.height - 2); + } else { + context.fillText(ts, gx - tsWidth, dimensions.height - 2); + } + } + } + } + + context.restore(); // See .save() above. + }; +/* + // Sample timestamp formatting function + SmoothieChart.timeFormatter = function(date) { + function pad2(number) { return (number < 10 ? '0' : '') + number } + return pad2(date.getHours()) + ':' + pad2(date.getMinutes()) + ':' + pad2(date.getSeconds()); + }; +*/ + exports.TimeSeries = TimeSeries; + exports.SmoothieChart = SmoothieChart; + +})(typeof exports === 'undefined' ? this : exports); + + diff --git a/WEBFiles/style.css b/WEBFiles/style.css new file mode 100644 index 0000000..f4874c0 --- /dev/null +++ b/WEBFiles/style.css @@ -0,0 +1,235 @@ +body{ +margin:4px auto; +width:640px; +color:black; +background:#fff; +font-size:14px; +font-family:"Lucida Grande", Tahoma, sans-serif; +} +img,table{ +border:0; +} +.top{ +color:#888; +background:url(logo.gif) no-repeat 0 0; +padding:3px 0 0 45px; +margin:0 0 10px 0; +} +.top img{ +vertical-align:middle; +} +h2.error{ +color:red; +} +h2.ok{ +color:green; +} +h2.title{ +text-align:center; +} +.menu{ +border-top:#fab548 5px solid; +background:#f1f1ed url(rtl.gif) no-repeat 10px 10px; +padding:5px 5px 5px 120px; +margin:0 0 10px 0; +min-height:120px; +border-radius:5px; +} +.menu div{ +width:220px; +float:left; +margin-right:10px; +} +.menu a, +.menu a:link{ +display:block; +font-weight:bold; +text-decoration:none; +color:#1486ba; +padding:0 5px 1px 10px; +font-size:13px; +border-radius:5px; +margin:0 0 1px 0; +border-bottom:#d6d9da 1px solid; +} +.menu a:visited{ +color:#777; +} +.menu a.active{ +color:#777; +background:#d6d9da; +} +.menu a:hover{ +color:#842; +background:#d6d9da; +} +table.form select{ +width:143px; +} +table.form{ +width:100%; +} +table.form td{ +font-size:13px; +padding:0; +} +table.form .label{ +width:45%; +font-weight:bold; +text-align:right; +padding-right:10px; +} +table.form .left{ +width:45%; +font-weight:bold; +text-align:left; +padding-left:10px; +} +table.scan{ +border-collapse:separate; +border-spacing: 0; +border:#aaa 2px solid; +text-align:center; +font-size:14px; +width:100%; +margin:0 0 10px 0; +background:#fff; +border-radius:5px; +} +table.scan th{ +color:#247; +border:#aaa 1px solid; +font-size:15px; +padding:2px 2px; +background:#fec; +} +table.scan td{ +border:#aaa 1px solid; +padding:2px 2px; +} +table.scan td.s:hover{ +background:#eef; +} +table.scan a:link{ +display:block; +font-weight:bold; +text-decoration:none; +color:#444; +border-radius:5px; +} +table.scan a:visited{ +color:#444; +} +table.scan a.active, +table.scan a:hover{ +color:#21e; +background:#d6d9da; +} +table.mdbtab{ +border-collapse:separate; +border-spacing: 0; +border:#aaa 2px solid; +text-align:center; +font-size:12px; +width:100%; +margin:0 0 10px 0; +background:#fff; +border-radius:5px; +} +table.mdbtab th{ +color:#247; +border:#aaa 1px solid; +font-size:12px; +padding:2px 2px; +background:#fec; +} +table.mdbtab td{ +border:#aaa 1px solid; +padding:2px 2px; +} +table.mdbtab td.s:hover{ +background:#eef; +} +table.mdbtab a:link{ +display:block; +font-weight:bold; +text-decoration:none; +color:#444; +border-radius:5px; +} +table.mdbtab a:visited{ +color:#444; +} +table.mdbtab a.active, +table.mdbtab a:hover{ +color:#21e; +background:#d6d9da; +} +.content{ +background:#f1f1ed; +border-radius:5px; +border-top:#1486ba 5px solid; +padding:10px; +margin:0 0 10px 0; +} +.footer{ +border-top:#75be45 5px solid;; +background:#d6d9da; +border-radius:5px; +padding:10px; +font-size:12px; +margin:0 0 5px 0; +} +.copyright{ +font-size:11px; +padding:0 0 0 10px; +text-align:right; +} +.button:visited, +.button{ +cursor:pointer; +display:inline-block; +font-weight:bold; +text-align:center; +text-decoration:none; +white-space:nowrap; +border-radius:5px; +background-image: linear-gradient(rgba(255,255,255,.1), rgba(255,255,255,.05) 49%, rgba(0,0,0,.05) 51%, rgba(0,0,0,.1)); +background-color:#f0f0eb; +color:#312c2a; +border:1px solid #aaa; +border-color: rgba(0,0,0,0.3); +border-bottom-color: #777; +border-bottom-color: rgba(0,0,0,0.5); +box-shadow: inset 0 0.08em 0 rgba(255,255,255,0.7), inset 0 0 0.08em rgba(255,255,255,0.5); +text-shadow: 0 1px 0 rgba(255,255,255,0.8); +padding:3px 15px; +} +.button:focus, +.button:hover{ +background-color:#ffffff; +background-image: linear-gradient(rgba(255,255,255,0.5), rgba(255,255,255,0.2) 49%, rgba(0,0,0,0.05) 51%, rgba(0,0,0,0.15)); +} +#timer{ +color:red; +} +.center{ +text-align:center; +} +hr{ +border:0; +height:1px; +background:#333; +background-image:-webkit-linear-gradient(left, #ccc, #333, #ccc); +background-image:-moz-linear-gradient(left, #ccc, #333, #ccc); +background-image:-ms-linear-gradient(left, #ccc, #333, #ccc); +background-image: -o-linear-gradient(left, #ccc, #333, #ccc); +} +select, +input, +iframe{ +border-collapse:separate; +border-spacing: 0; +background:#fff; +border-radius:5px; +} \ No newline at end of file diff --git a/WEBFiles/time.inc b/WEBFiles/time.inc new file mode 100644 index 0000000..dfef378 --- /dev/null +++ b/WEBFiles/time.inc @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/WEBFiles/timeout.htm b/WEBFiles/timeout.htm new file mode 100644 index 0000000..e0e8588 --- /dev/null +++ b/WEBFiles/timeout.htm @@ -0,0 +1,16 @@ + + + + RTL871X WIFI + + + +

RTL871X Built-in Web server ©

+
+

Timeout ? sec...

+ * Redirect: http://~sys_url~/ *

+ Main +
+~inc:footer.inc~ + + diff --git a/WEBFiles/timer.inc b/WEBFiles/timer.inc new file mode 100644 index 0000000..5a4fa14 --- /dev/null +++ b/WEBFiles/timer.inc @@ -0,0 +1,12 @@ + + + + RTL871X WIFI + + + +

RTL871X Built-in Web server ©

+
+

Timeout ? sec...

+ Redirect: Main + diff --git a/WEBFiles/tst.htm b/WEBFiles/tst.htm new file mode 100644 index 0000000..bc74021 --- /dev/null +++ b/WEBFiles/tst.htm @@ -0,0 +1,9 @@ +~inc:grfx1.inc~ +Test: ? +~inc:grfx2.inc~ \ No newline at end of file diff --git a/WEBFiles/tst.xml b/WEBFiles/tst.xml new file mode 100644 index 0000000..f817f5a --- /dev/null +++ b/WEBFiles/tst.xml @@ -0,0 +1 @@ +Test~wifi_st_rssi~? diff --git a/WEBFiles/websock.htm b/WEBFiles/websock.htm new file mode 100644 index 0000000..85a2d39 --- /dev/null +++ b/WEBFiles/websock.htm @@ -0,0 +1,116 @@ + + + + +WebSocket Test +

WebSocket Test

+
+ + +
+
+
+ Log messages +
+ +
+ + diff --git a/WEBFiles/ws2.css b/WEBFiles/ws2.css new file mode 100644 index 0000000..688bba6 --- /dev/null +++ b/WEBFiles/ws2.css @@ -0,0 +1,12 @@ +#consoleLog { + overflow-y: scroll; + width: 480px; + height: 800px; + border: solid 1px #aaaaaa; + background-color: #ffffff; + padding-left: 5px; } +#consoleLog > pre { + margin-top: 0; + margin-bottom: 0; } +#consoleLog > pre:nth-child(even) { + background-color: #fafafa; } diff --git a/flasher.mk b/flasher.mk new file mode 100644 index 0000000..27141d5 --- /dev/null +++ b/flasher.mk @@ -0,0 +1,307 @@ +# RTL8710 Flasher v0.0.alfa +# pvvx 21.09.2016 +-include paths.mk +#--------------------------- +#FLASHER = stlink-v2-1 +#FLASHER = stlink-v2 +FLASHER ?= Jlink +JLINK_PATH ?= D:/MCU/SEGGER/JLink_V612i/ +#--------------------------- +# Default +#--------------------------- +# TARGET dirs +TARGET ?= build +OBJ_DIR ?= build/obj +BIN_DIR ?= build/bin +ELFFILE ?= $(OBJ_DIR)/$(TARGET).axf +#--------------------------- +# Compilation tools +CROSS_COMPILE ?= $(GCC_PATH)arm-none-eabi- +AR ?= $(CROSS_COMPILE)ar +CC ?= $(CROSS_COMPILE)gcc +AS ?= $(CROSS_COMPILE)as +NM ?= $(CROSS_COMPILE)nm +LD ?= $(CROSS_COMPILE)gcc +GDB ?= $(CROSS_COMPILE)gdb +SIZE ?= $(CROSS_COMPILE)size +OBJCOPY ?= $(CROSS_COMPILE)objcopy +OBJDUMP ?= $(CROSS_COMPILE)objdump + +# Make bunary tools +TOOLS_PATH ?= component/soc/realtek/8195a/misc/iar_utility/common/tools/ +ifneq ($(shell uname), Linux) +EXE = .exe +endif +PICK = $(TOOLS_PATH)pick$(EXE) +PADDING = $(TOOLS_PATH)padding$(EXE) +CHCKSUM = $(TOOLS_PATH)checksum$(EXE) + +# openocd tools +OPENOCD = $(OPENOCD_PATH)openocd + +JLINK_GDB ?= JLinkGDBServer.exe +JLINK_EXE ?= JLink.exe + +ifeq ($(FLASHER), Jlink) +# Jlink FLASHER_SPEED ..4000 kHz +FLASHER_SPEED = 3500 +else +ifeq ($(FLASHER),stlink-v2) +# stlink-v2 FLASHER_SPEED ..1800 kHz +FLASHER_SPEED = 1800 +else +# over FLASHER_SPEED ..500 kHz ? +FLASHER_SPEED = 500 +endif +endif + +# COMPILED_BOOT if defined -> extract image1, boot head in elf +COMPILED_BOOT=1 +# COMPILED_BOOT_BIN if !defined -> use source startup boot +#COMPILED_BOOT_BIN=1 +# PADDINGSIZE defined -> image2 OTA +PADDINGSIZE =44k + +NMAPFILE = $(OBJ_DIR)/$(TARGET).nmap + +#FLASHER_PATH ?= flasher/ + +#RAM_IMAGE?= $(BIN_DIR)/ram.bin + +RAM1_IMAGE ?= $(BIN_DIR)/ram_1.bin +RAM1P_IMAGE ?= $(BIN_DIR)/ram_1.p.bin +RAM1R_IMAGE ?= $(BIN_DIR)/ram_1.r.bin + +RAM2_IMAGE = $(BIN_DIR)/ram_2.bin +RAM2P_IMAGE = $(BIN_DIR)/ram_2.p.bin +RAM2NS_IMAGE = $(BIN_DIR)/ram_2.ns.bin + +RAM3_IMAGE = $(BIN_DIR)/sdram.bin +RAM3P_IMAGE = $(BIN_DIR)/sdram.p.bin + +FLASH_IMAGE = $(BIN_DIR)/ram_all.bin +OTA_IMAGE = $(BIN_DIR)/ota.bin + +#all: FLASH_IMAGE = $(BIN_DIR)/ram_all.bin +#all: OTA_IMAGE = $(BIN_DIR)/ota.bin +mp: FLASH_IMAGE = $(BIN_DIR)/ram_all_mp.bin +mp: OTA_IMAGE = $(BIN_DIR)/ota_mp.bin + +TST_IMAGE = $(BIN_DIR)/ram_2.bin + +.PHONY: genbin1 genbin23 flashburn reset test readfullflash flashwebfs flash_OTA +.NOTPARALLEL: all mp genbin1 genbin23 flashburn reset test readfullflash _endgenbin flashwebfs flash_OTA + +all: $(ELFFILE) $(OTA_IMAGE) $(FLASH_IMAGE) _endgenbin +mp: $(ELFFILE) $(OTA_IMAGE) $(FLASH_IMAGE) _endgenbin + +genbin1: $(ELFFILE) $(RAM1P_IMAGE) + +genbin23: $(ELFFILE) $(OTA_IMAGE) $(FLASH_IMAGE) _endgenbin + + +_endgenbin: + @echo "-----------------------------------------------------------" + @echo "Image ($(OTA_IMAGE)) size $(shell printf '%d\n' $$(( $$(stat --printf="%s" $(OTA_IMAGE)) )) ) bytes" + @echo "Image ($(FLASH_IMAGE)) size $(shell printf '%d\n' $$(( $$(stat --printf="%s" $(FLASH_IMAGE)) )) ) bytes" + @echo "===========================================================" + +ifeq ($(FLASHER_TYPE), Jlink) + +reset: + @$(JLINK_PATH)$(JLINK_EXE) -Device CORTEX-M3 -If SWD -Speed 1000 $(FLASHER_PATH)RTL_Reset.JLinkScript + +runram: + @$(JLINK_PATH)$(JLINK_EXE) -Device CORTEX-M3 -If SWD -Speed 1000 $(FLASHER_PATH)RTL_RunRAM.JLinkScript + +readfullflash: + @$(JLINK_PATH)$(JLINK_EXE) -Device CORTEX-M3 -If SWD -Speed 1000 $(FLASHER_PATH)RTL_FFlash.JLinkScript + + +flashburn: + @echo define call1>$(FLASHER_PATH)flash_file.jlink + @echo SetFirwareSize build/bin/ram_all.bin>>$(FLASHER_PATH)flash_file.jlink + @echo end>>$(FLASHER_PATH)flash_file.jlink + @echo define call2>>$(FLASHER_PATH)flash_file.jlink + @echo FlasherWrite build/bin/ram_all.bin 0 '$$'Image1Size>>$(FLASHER_PATH)flash_file.jlink + @echo end>>$(FLASHER_PATH)flash_file.jlink + @echo define call3>>$(FLASHER_PATH)flash_file.jlink + @echo FlasherWrite build/bin/ram_all.bin '$$'Image2Addr '$$'Image2Size>>$(FLASHER_PATH)flash_file.jlink + @echo end>>$(FLASHER_PATH)flash_file.jlink + @cmd /K start $(JLINK_PATH)$(JLINK_GDBSRV) -device Cortex-M3 -if SWD -ir -endian little -speed 1000 + @$(GDB) -x $(FLASHER_PATH)gdb_wrflash.jlink + #@taskkill /F /IM $(JLINK_GDBSRV) + +flashwebfs: + @echo define call1>$(FLASHER_PATH)file_info.jlink + @echo set '$$'ImageSize = $(shell printf '0x%X\n' $$(stat --printf="%s" $(BIN_DIR)/WEBFiles.bin))>>$(FLASHER_PATH)file_info.jlink + @echo set '$$'ImageAddr = 0x0D0000>>$(FLASHER_PATH)file_info.jlink + @echo end>>$(FLASHER_PATH)file_info.jlink + @echo define call2>>$(FLASHER_PATH)file_info.jlink + @echo FlasherWrite $(BIN_DIR)/WEBFiles.bin '$$'ImageAddr '$$'ImageSize>>$(FLASHER_PATH)file_info.jlink + @echo end>>$(FLASHER_PATH)file_info.jlink + @cmd /K start $(JLINK_PATH)$(JLINK_GDBSRV) -device Cortex-M3 -if SWD -ir -endian little -speed 1000 + @$(GDB) -x $(FLASHER_PATH)gdb_wrfile.jlink + #@taskkill /F /IM $(JLINK_GDBSRV) + +flash_OTA: + @cmd /K start $(JLINK_PATH)$(JLINK_GDBSRV) -device Cortex-M3 -if SWD -ir -endian little -speed 1000 + @$(GDB) -x $(FLASHER_PATH)gdb_ota.jlink + #@taskkill /F /IM $(JLINK_GDBSRV) + +else + +flashburn: + @$(OPENOCD) -f interface/$(FLASHER).cfg -c "transport select swd" -f $(FLASHER_PATH)rtl8710.ocd -c "init" -c "adapter_khz $(FLASHER_SPEED)" -c "reset halt" \ + -c "rtl8710_flash_auto_erase 1" -c "rtl8710_flash_auto_verify 1" \ + -c "rtl8710_flash_write $(RAM1P_IMAGE) 0" \ + -c "rtl8710_flash_write $(RAM2P_IMAGE) 0xb000" \ + -c "rtl8710_reboot" -c "reset run" -c shutdown + +flashimage2p: + @$(OPENOCD) -f interface/$(FLASHER).cfg -c "transport select swd" -f $(FLASHER_PATH)rtl8710.ocd -c "init" -c "adapter_khz $(FLASHER_SPEED)" -c "reset halt" \ + -c "rtl8710_flash_auto_erase 1" -c "rtl8710_flash_auto_verify 1" \ + -c "rtl8710_flash_write $(RAM2P_IMAGE) 0xb000" \ + -c "rtl8710_reboot" -c "reset run" -c shutdown + +flashwebfs: + @$(OPENOCD) -f interface/$(FLASHER).cfg -c "transport select swd" -f $(FLASHER_PATH)rtl8710.ocd -c "init" -c "adapter_khz $(FLASHER_SPEED)" -c "reset halt" \ + -c "rtl8710_flash_auto_erase 1" -c "rtl8710_flash_auto_verify 1" \ + -c "rtl8710_flash_write $(BIN_DIR)/webpages.espfs 0xd0000" \ + -c "rtl8710_reboot" -c "reset run" -c shutdown + + +reset: +# @$(JLINK_PATH)$(JLINK_EXE) -Device CORTEX-M3 -If SWD -Speed $(FLASHER_SPEED) flasher/RTLreset.JLinkScript + @$(OPENOCD) -f interface/$(FLASHER).cfg -c "transport select swd" -f $(FLASHER_PATH)rtl8710.ocd -c "init" -c "adapter_khz $(FLASHER_SPEED)" -c "reset halt" \ + -c "rtl8710_reboot" -c shutdown + +runram: +# @$(JLINK_PATH)$(JLINK_GDB) -device Cortex-M3 -if SWD -ir -endian little -speed $(FLASHER_SPEED) +# @$(GDB) -x flasher/gdb_run_ram.jlink +# @taskkill.exe -F -IM $(JLINK_GDB) + @$(OPENOCD) -f interface/$(FLASHER).cfg -c "transport select swd" -f $(FLASHER_PATH)rtl8710.ocd -c "init" -c "adapter_khz $(FLASHER_SPEED)" -c "reset halt" \ + -c "load_image $(RAM1R_IMAGE) 0x10000bc8 bin" \ + -c "load_image $(RAM2_IMAGE) 0x10006000 bin" \ + -c "mww 0x40000210 0x20200113" \ + -c "reset run" -c shutdown + +endif + +$(NMAPFILE): $(ELFFILE) + @echo "===========================================================" + @echo "Build names map file" + @echo $@ + @$(NM) $< | sort > $@ +# @echo "===========================================================" + +$(FLASH_IMAGE): $(RAM1P_IMAGE) $(RAM2P_IMAGE) $(RAM3P_IMAGE) + @echo "===========================================================" + @echo "Make Flash image ($(FLASH_IMAGE))" +# @echo "===========================================================" + @mkdir -p $(BIN_DIR) + @rm -f $(FLASH_IMAGE) + @cat $(RAM1P_IMAGE) > $(FLASH_IMAGE) +# @chmod 777 $(FLASH_IMAGE) +ifdef PADDINGSIZE + @$(PADDING) $(PADDINGSIZE) 0xFF $(FLASH_IMAGE) +endif + @cat $(RAM2P_IMAGE) >> $(FLASH_IMAGE) + @cat $(RAM3P_IMAGE) >> $(FLASH_IMAGE) +# @echo "Image ($(FLASH_IMAGE)) size $(shell printf '%d\n' $$(( $$(stat --printf="%s" $(FLASH_IMAGE)) )) ) bytes" +# @echo "===========================================================" +# @rm $(BIN_DIR)/ram_*.p.bin + +$(OTA_IMAGE): $(RAM2NS_IMAGE) $(RAM3_IMAGE) + @echo "===========================================================" + @echo "Make OTA image ($(OTA_IMAGE))" + @rm -f $(OTA_IMAGE) + @cat $(RAM2NS_IMAGE) > $(OTA_IMAGE) + @cat $(RAM3P_IMAGE) >> $(OTA_IMAGE) +# @chmod 777 $(OTA_IMAGE) + @$(CHCKSUM) $(OTA_IMAGE) || true +# @echo "===========================================================" + +$(RAM1P_IMAGE): $(ELFFILE) $(NMAPFILE) + @echo "===========================================================" + @echo "Create image1r ($(RAM1R_IMAGE))" +# @echo "===========================================================" .bootloader +ifdef COMPILED_BOOT + @mkdir -p $(BIN_DIR) + @rm -f $(RAM1_IMAGE) $(RAM1R_IMAGE) +ifdef COMPILED_BOOT_BIN + @$(eval RAM1_START_ADDR := $(shell grep _binary_build_bin_ram_1_r_bin_start $(NMAPFILE) | awk '{print $$1}')) + @$(eval RAM1_END_ADDR := $(shell grep _binary_build_bin_ram_1_r_bin_end $(NMAPFILE) | awk '{print $$1}')) +else + @$(eval RAM1_START_ADDR := $(shell grep __ram_image1_text_start__ $(NMAPFILE) | awk '{print $$1}')) + @$(eval RAM1_END_ADDR := $(shell grep __ram_image1_text_end__ $(NMAPFILE) | awk '{print $$1}')) +endif + $(if $(RAM1_START_ADDR),,$(error "Not found __ram_image1_text_start__!")) + $(if $(RAM1_END_ADDR),,$(error "Not found __ram_image1_text_end__!")) +ifeq ($(RAM1_START_ADDR),$(RAM1_END_ADDR)) +ifdef COMPILED_BOOT_BIN + $(OBJCOPY) --change-section-address .boot.head=0x10000ba8 -j .boot.head -j .bootloader -Obinary $(ELFFILE) $(RAM1P_IMAGE) +else +# $(OBJCOPY) -j .rom_ram -Obinary $(ELFFILE) $(RAM_IMAGE) + $(OBJCOPY) -j .ram.start.table -j .ram_image1.text -Obinary $(ELFFILE) $(RAM1R_IMAGE) + $(PICK) 0x$(RAM1_START_ADDR) 0x$(RAM1_END_ADDR) $(RAM1R_IMAGE) $(RAM1P_IMAGE) head+reset_offset 0x0B000 +endif +else + $(error "BOOT-image size = 0") +# $(error Flasher: COMPILE_BOOT = No) +endif +else + @if [ -s $(RAM1R_IMAGE) ]; then echo "Use external $(RAM1R_IMAGE)!"; fi +endif + +$(RAM2P_IMAGE): $(ELFFILE) $(NMAPFILE) + @echo "===========================================================" + @echo "Create image2p ($(RAM2P_IMAGE))" +# @echo "===========================================================" + @mkdir -p $(BIN_DIR) + @rm -f $(RAM2_IMAGE) $(RAM2P_IMAGE) + @$(eval RAM2_START_ADDR = $(shell grep __ram_image2_text $(NMAPFILE) | grep _start__ | awk '{print $$1}')) + @$(eval RAM2_END_ADDR = $(shell grep __ram_image2_text $(NMAPFILE) | grep _end__ | awk '{print $$1}')) + $(if $(RAM2_START_ADDR),,$(error "Not found __ram_image2_text_start__!")) + $(if $(RAM2_END_ADDR),,$(error "Not found __ram_image2_text_end__!")) + @$(OBJCOPY) -j .image2.start.table -j .ram_image2.text -j .ram_image2.rodata -j .ram.data -Obinary $(ELFFILE) $(RAM2_IMAGE) + @$(PICK) 0x$(RAM2_START_ADDR) 0x$(RAM2_END_ADDR) $(RAM2_IMAGE) $(RAM2P_IMAGE) body+reset_offset+sig + +$(RAM2NS_IMAGE):$(ELFFILE) $(NMAPFILE) + @echo "===========================================================" + @echo "Create image2ns ($(RAM2NS_IMAGE))" +# @echo "===========================================================" + mkdir -p $(BIN_DIR) + rm -f $(RAM2_IMAGE) $(RAM2NS_IMAGE) + $(eval RAM2_START_ADDR = $(shell grep __ram_image2_text $(NMAPFILE) | grep _start__ | awk '{print $$1}')) + $(eval RAM2_END_ADDR = $(shell grep __ram_image2_text $(NMAPFILE) | grep _end__ | awk '{print $$1}')) + $(if $(RAM2_START_ADDR),,$(error "Not found __ram_image2_text_start__!")) + $(if $(RAM2_END_ADDR),,$(error "Not found __ram_image2_text_end__!")) + $(OBJCOPY) -j .image2.start.table -j .ram_image2.text -j .ram_image2.rodata -j .ram.data -Obinary $(ELFFILE) $(RAM2_IMAGE) + $(PICK) 0x$(RAM2_START_ADDR) 0x$(RAM2_END_ADDR) $(RAM2_IMAGE) $(RAM2NS_IMAGE) body+reset_offset + +$(RAM3_IMAGE): $(ELFFILE) $(NMAPFILE) + @echo "===========================================================" + @echo "Create image3 (SDRAM, $(RAM3P_IMAGE))" +# @echo "===========================================================" + @mkdir -p $(BIN_DIR) + @rm -f $(RAM3_IMAGE) $(RAM3P_IMAGE) + @$(eval RAM3_START_ADDR = $(shell grep __sdram_data_ $(NMAPFILE) | grep _start__ | awk '{print $$1}')) + @$(eval RAM3_END_ADDR = $(shell grep __sdram_data_ $(NMAPFILE) | grep _end__ | awk '{print $$1}')) + $(if $(RAM3_START_ADDR),,$(error "Not found __sdram_data_start__!")) + $(if $(RAM3_END_ADDR),,$(error "Not found __sdram_data_end__!")) +#ifneq ($(RAM3_START_ADDR),$(RAM3_END_ADDR)) + @echo $(RAM3_START_ADDR) $(RAM3_END_ADDR) + @$(OBJCOPY) -j .image3 -j .sdr_text -j .sdr_rodata -j .sdr_data -Obinary $(ELFFILE) $(RAM3_IMAGE) + $(PICK) 0x$(RAM3_START_ADDR) 0x$(RAM3_END_ADDR) $(RAM3_IMAGE) $(RAM3P_IMAGE) body+reset_offset +#else +# @rm -f $(RAM3_IMAGE) $(RAM3P_IMAGE) +# @echo "SDRAM not used (size = 0)" +#endif + +$(ELFFILE): + $(error Falsher: file $@ not found) + +clean: + @rm -f $(BIN_DIR)/*.bin + \ No newline at end of file diff --git a/flasher/RTL00ConsoleROM.JLinkScript b/flasher/RTL00ConsoleROM.JLinkScript new file mode 100644 index 0000000..5b85200 --- /dev/null +++ b/flasher/RTL00ConsoleROM.JLinkScript @@ -0,0 +1,6 @@ +h +loadbin flasher/RTL00Console_ROM.bin 0x10000ba8 +r +w4 0x40000210,0x4011117 +g +q \ No newline at end of file diff --git a/flasher/RTL00Console_ROM.bin b/flasher/RTL00Console_ROM.bin new file mode 100644 index 0000000000000000000000000000000000000000..e83cb165765b0706201fe3e2eb5bfcbadaf693d5 GIT binary patch literal 600 zcmbO^bJ{ffGiiSg7@uK&ddD!yf{B6Q1UG|#4#R&icnM^G1!6`X1_53mmIPwuO0f=* zBuFjCRt5zI?-}l@Kyi+(j7V%IBsMb=n+48p-pb;pu#?e^QR#+)8qkbnIVNrX8-?4L z6jXpb6>t9!|Ba*)ls%MzJQz;grtGWertIPWp}}Q^yJA*S*5a%OSqu~8J%GIIr0m7n z53(61$O3s+etZy=@woEii{h0ZKMYs6OLjhL1^~0Mt#SYW literal 0 HcmV?d00001 diff --git a/flasher/RTL8710.jflash b/flasher/RTL8710.jflash new file mode 100644 index 0000000..a5d0547 --- /dev/null +++ b/flasher/RTL8710.jflash @@ -0,0 +1,119 @@ + AppVersion = 47812 +[GENERAL] + ConnectMode = 0 + CurrentFile = "fullflash.bin" + DataFileSAddr = 0x98000000 + GUIMode = 0 + HostName = "" + TargetIF = 1 + USBPort = 0 + USBSerialNo = 0x00000000 +[JTAG] + IRLen = 0 + MultipleTargets = 0 + NumDevices = 0 + Speed0 = 400 + Speed1 = 12000 + TAP_Number = 0 + UseAdaptive0 = 0 + UseAdaptive1 = 0 + UseMaxSpeed0 = 0 + UseMaxSpeed1 = 0 +[CPU] + CheckCoreID = 0 + ChipName = "RTL8710AF" + ClockSpeed = 0x00000000 + Core = 0x030000FF + CoreID = 0x00000000 + CoreIDMask = 0x0F000FFF + DeviceFamily = 0x00000003 + EndianMode = 0 + HasInternalFlash = 0 + InitStep0_Action = "Reset" + InitStep0_Comment = "Reset and Halt" + InitStep0_Value0 = 0x00000000 + InitStep0_Value1 = 0x00000005 + InitStep1_Action = "Go" + InitStep1_Comment = "" + InitStep1_Value0 = 0x00000000 + InitStep1_Value1 = 0x00000000 + InitStep2_Action = "Reset" + InitStep2_Comment = "Reset and halt target" + InitStep2_Value0 = 0x00000000 + InitStep2_Value1 = 0x00000005 + InitStep3_Action = "Write Register" + InitStep3_Comment = "Only T=1" + InitStep3_Value0 = 0x00000010 + InitStep3_Value1 = 0x01000000 + InitStep4_Action = "Write 32bit" + InitStep4_Comment = "Setup SystemCoreClock" + InitStep4_Value0 = 0x40000014 + InitStep4_Value1 = 0x00000001 + InitStep5_Action = "Delay" + InitStep5_Comment = "" + InitStep5_Value0 = 0x00000000 + InitStep5_Value1 = 0x00000005 + InitStep6_Action = "Write 32bit" + InitStep6_Comment = "Write Page Size" + InitStep6_Value0 = 0x1FFFFFF0 + InitStep6_Value1 = 0x00000100 + InitStep7_Action = "Write 32bit" + InitStep7_Comment = "Write Sector Size" + InitStep7_Value0 = 0x1FFFFFF4 + InitStep7_Value1 = 0x00001000 + InitStep8_Action = "Write 32bit" + InitStep8_Comment = "Write Block Size" + InitStep8_Value0 = 0x1FFFFFF8 + InitStep8_Value1 = 0x00010000 + InitStep9_Action = "Write 32bit" + InitStep9_Comment = "Write Block Count" + InitStep9_Value0 = 0x1FFFFFFC + InitStep9_Value1 = 0x00000010 + NumExitSteps = 0 + NumInitSteps = 10 + RAMAddr = 0x10000000 + RAMSize = 0x00010000 + ScriptFile = "" + UseAutoSpeed = 0x00000001 + UseRAM = 1 + UseScriptFile = 0 +[FLASH] + aSectorSel[0] = + AutoDetect = 1 + BankName = "" + BankSelMode = 1 + BaseAddr = 0x98000000 + CheckId = 3 + CustomRAMCode = "RTL8710AF.hex" + DeviceName = "Auto detected flash memory" + EndBank = 8191 + NumBanks = 1 + OrgNumBits = 16 + OrgNumChips = 1 + StartBank = 0 + UseCustomRAMCode = 1 +[PRODUCTION] + AutoPerformsErase = 1 + AutoPerformsHardLock = 0 + AutoPerformsHardUnlock = 0 + AutoPerformsProgram = 1 + AutoPerformsSecure = 0 + AutoPerformsSoftLock = 0 + AutoPerformsSoftUnlock = 1 + AutoPerformsStartApp = 0 + AutoPerformsUnsecure = 0 + AutoPerformsVerify = 1 + EnableProductionMode = 0 + EnableTargetPower = 0 + EraseType = 2 + ProductionDelay = 0x000001F4 + ProductionThreshold = 0x00000BB8 + ProgramSN = 0 + SerialFile = "" + SNAddr = 0x00000000 + SNInc = 0x00000001 + SNLen = 0x00000004 + SNListFile = "" + SNValue = 0x00000001 + TargetPowerDelay = 0x00000014 + VerifyType = 1 diff --git a/flasher/RTL8710AF.hex b/flasher/RTL8710AF.hex new file mode 100644 index 0000000..fd2dc4c --- /dev/null +++ b/flasher/RTL8710AF.hex @@ -0,0 +1,130 @@ +:100000007D0439057F05FF056506D506D706000086 +:10001000D44908707047D44800680005FBD0D14827 +:100020000078C0B2704710B5D048006850F44070F6 +:10003000CE490860CE48006850F01000CC490860F6 +:10004000CC480468062000F0B7F934F00600C9492E +:100050000860C848006850F00100C64908600020E8 +:10006000C54908600020C54908600120C4490860EE +:100070000220C44908600020C34908600020C34929 +:1000800008600020C249086010BD2DE9F04104005D +:100090000D001600B6B2002E01D1002048E0B6B225 +:1000A000112E01DB102006004FF4407000F084F99F +:1000B00080465FF4401000F07FF95FF4403000F0BC +:1000C0007BF95FF4402000F077F9032010FA08F084 +:1000D00010F44070AF490860B6B2AF480660032024 +:1000E000FFF796FF200CC0B2FFF792FF200AC0B2C4 +:1000F000FFF78EFF2000C0B2FFF78AFF01209E4964 +:100100000860002007003800310080B289B28842C0 +:1001100005D2FFF780FFBFB2E8557F1CF3E79F4889 +:100120000068C007FBD4002093490860300080B20B +:10013000BDE8F08138B54FF4407000F03DF905009E +:100140005FF4401000F038F95FF4403000F034F90B +:100150005FF4402000F030F90320A84010F4407014 +:100160008C49086003208C4908600120824908609E +:100170009F20FFF74DFFFFF74EFF0400FFF74BFFF7 +:1001800054EA00200400FFF746FF54EA0040040050 +:1001900082480068C007FBD400207749086020002F +:1001A00032BD38B54FF4407000F006F905005FF439 +:1001B000401000F001F95FF4403000F0FDF85FF40A +:1001C000402000F0F9F80320A84010F44070714975 +:1001D0000860012070490860012067490860052017 +:1001E000FFF716FFFFF717FF04006C480068C00711 +:1001F000FBD40020604908602000C0B232BD38B591 +:1002000004004FF4407000F0D7F805005FF4401090 +:1002100000F0D2F85FF4403000F0CEF85FF44020F8 +:1002200000F0CAF80120A84010F440705949086055 +:100230000120514908602000C0B2FFF7E9FE57488D +:100240000068C007FBD400204B49086031BD38B5B9 +:1002500004004FF4407000F0AFF805005FF4401068 +:1002600000F0AAF85FF4403000F0A6F85FF44020F8 +:1002700000F0A2F80120A84010F440704549086041 +:1002800001203D4908602020FFF7C2FE200CC0B2CB +:10029000FFF7BEFE200AC0B2FFF7BAFE2000C0B2D0 +:1002A000FFF7B6FE3D480068C007FBD40020324986 +:1002B000086031BD2DE9F04104000D001600B6B212 +:1002C000002E01D100204BE0B6B2B6F5807F02DDF2 +:1002D0004FF4807006004FF4407000F06DF88046D7 +:1002E0005FF4401000F068F85FF4403000F064F80C +:1002F0005FF4402000F060F8012010FA08F010F4DC +:1003000040702449086001201B4908600220FFF763 +:100310007FFE200CC0B2FFF77BFE200AC0B2FFF7C1 +:1003200077FE2000C0B2FFF773FE00200700380000 +:10033000310080B289B2884205D2BFB2E85DFFF7D2 +:1003400067FE7F1CF3E7154800684007FBD513489C +:100350000068C007FBD4002007490860300080B265 +:10036000BDE8F08160600040246000403002004041 +:1003700010020040C0020040086000402C600040B5 +:100380001060004014600040186000401C60004095 +:100390004C60004000600040046000402860004065 +:1003A00080B5FFF7FEFEC007FBD401BD80B5FFF7A7 +:1003B000F8FE8007FBD501BD90FAA0F0B0FA80F0FE +:1003C00070470000010051EA510151EA910151EAE0 +:1003D000111151EA112151EA1141064A7A441432AD +:1003E000DFF8B03203FB01F3DB0E52F823201000DC +:1003F000704700BF80030000DFF89C02006850F0E7 +:100400004070DFF894120860DFF89002006850F442 +:100410008070DFF888120860FF20DFF8841208601F +:10042000FF20DFF880120860704780B500F054F9B3 +:1004300001BD0120DFF870120860704780B571B609 +:1004400072B6FFF7F6FFFFF7D7FFFFF7EEFF01BD2C +:1004500080B5FFF7E8FDFFF7A3FFFFF76BFEFFF79F +:100460009FFF92480068FFF7ADFF914908609148EF +:100470000068FFF7A7FF9049086001BDF8B50400C8 +:100480000D0016008D4800688D49884204D18D48C2 +:1004900000688D49884209D08948884908608A489F +:1004A00088490860FFF7CAFFFFF7D2FF814800685C +:1004B00086490968864A12685143B1FBF0F27368B5 +:1004C00040271F807A4F3F689F700027DF70012709 +:1004D0001F7101275F7100279F710127DF710127BD +:1004E0001F7200275F7201279F7201271F6100277B +:1004F000DFF8DCC1DCF800C067451CD2DFF8CCC1F6 +:10050000DCF800C00CFB07FC5FF00C0E0EFB07FED6 +:100510009E44CEF814C05FF00C0C0CFB07FC9C440E +:10052000CCF818205FF00C0C0CFB07FC9C44CCF8BA +:100530001C007F1CDCE7F1BD2DE9F04704000D0035 +:1005400016007769D6F81080D6F80490002F14D0E2 +:10055000200000F0C0F8112F02D35FF0100A00E075 +:10056000BA46524692B249464046FFF78EFD1AEB14 +:100570000808D144B7EB0A07E8E7BDE8F0872DE9A2 +:10058000F84F04000D0016000020286077694848E5 +:100590000068B84638FA00F8D6F810904448006869 +:1005A00039FA00F9D6F804A0B8F1000F25D0B7F554 +:1005B000807F02D94FF4807B00E0BB46FFF7F0FE5E +:1005C0000620FFF71CFEFFF7F1FE5A4692B2514695 +:1005D0003648006800FB09F0FFF76CFEFFF7E0FE0D +:1005E0000420FFF70CFEFFF7DBFEDA44B7EB0B0746 +:1005F000B8F1010819F10109D6E7BDE8F18F2DE93D +:10060000F84304000D00160030692B490968C84002 +:10061000070070698046002028602648006800FBBB +:1006200007F08146B8F1000F1AD0200000F053F80F +:10063000FFF7B6FE0620FFF7E2FDFFF7B7FE4846DC +:10064000FFF705FEFFF7ACFE0420FFF7D8FDFFF72C +:10065000A7FE1848006810EB0909B8F10108E1E7A6 +:10066000BDE8F18310B504001448006814498842BD +:1006700004D1144800681449884209D010480F4931 +:10068000086011480F490860FFF7D8FEFFF7E0FE49 +:1006900010BD0000DDACC407300200401C02004069 +:1006A000200300402403004014000040F0FFFF1F1F +:1006B000E0FFFF1FF4FFFF1FE4FFFF1FE8FFFF1F26 +:1006C000AA55AA55ECFFFF1F55AA55AAF8FFFF1F10 +:1006D000FCFFFF1F7047704710B51E48006850F4BC +:1006E00040501C4908601C48006850F480501A496A +:1006F00008601A4800684006FBD5194804685FF492 +:100700004000FFF759FE34F44000154908601448D2 +:10071000006850F4801012490860002011490860F8 +:100720001148006850F080000F49086044200F49CC +:10073000086000200B49086003240320FFF73CFEFB +:1007400014FA00F010F0030007490860002008497F +:10075000086010BD3002004010020040143000401C +:10076000C0020040043000400C3000400030004027 +:1007700008300040000000000900000001000000F7 +:100780000A0000000D00000015000000020000003B +:100790001D0000000B0000000E0000001000000013 +:1007A0001200000016000000190000000300000005 +:1007B0001E000000080000000C00000014000000F3 +:1007C0001C0000000F0000001100000018000000D5 +:1007D00007000000130000001B00000017000000CD +:1007E000060000001A0000000500000004000000E0 +:0407F0001F000000E6 +:0400000500000000F7 +:00000001FF diff --git a/flasher/RTL8710AF.jflash b/flasher/RTL8710AF.jflash new file mode 100644 index 0000000..ff1f6c4 --- /dev/null +++ b/flasher/RTL8710AF.jflash @@ -0,0 +1,153 @@ + AppVersion = 61001 + FileVersion = 2 +[GENERAL] + ConnectMode = 0 + CurrentFile = "" + DataFileSAddr = 0x00000000 + GUIMode = 0 + HostName = "" + TargetIF = 1 + USBPort = 0 + USBSerialNo = 0x00000000 +[JTAG] + IRLen = 0 + MultipleTargets = 0 + NumDevices = 0 + Speed0 = 6000 + Speed1 = 6000 + TAP_Number = 0 + UseAdaptive0 = 0 + UseAdaptive1 = 0 + UseMaxSpeed0 = 0 + UseMaxSpeed1 = 0 +[CPU] + NumInitSteps = 18 + InitStep0_Action = "Reset" + InitStep0_Value0 = 0x00000000 + InitStep0_Value1 = 0x00000000 + InitStep0_Comment = "Reset and halt target" + InitStep1_Action = "Read 32bit" + InitStep1_Value0 = 0x40000230 + InitStep1_Value1 = 0x00000000 + InitStep1_Comment = "enable spi flash peripheral clock" + InitStep2_Action = "Var OR" + InitStep2_Value0 = 0x00000000 + InitStep2_Value1 = 0x00000300 + InitStep2_Comment = "" + InitStep3_Action = "Var Write 32bit" + InitStep3_Value0 = 0x40000230 + InitStep3_Value1 = 0x00000000 + InitStep3_Comment = "" + InitStep4_Action = "Read 32bit" + InitStep4_Value0 = 0x40000210 + InitStep4_Value1 = 0x00000000 + InitStep4_Comment = "enable spi flash peripheral" + InitStep5_Action = "Var OR" + InitStep5_Value0 = 0x00000000 + InitStep5_Value1 = 0x00000010 + InitStep5_Comment = "" + InitStep6_Action = "Var Write 32bit" + InitStep6_Value0 = 0x40000210 + InitStep6_Value1 = 0x00000000 + InitStep6_Comment = "" + InitStep7_Action = "Read 32bit" + InitStep7_Value0 = 0x400002C0 + InitStep7_Value1 = 0x00000000 + InitStep7_Comment = "select spi flash pinout (0 - internal), enable spi flash pins" + InitStep8_Action = "Var AND" + InitStep8_Value0 = 0x00000000 + InitStep8_Value1 = 0xFFFFFFF8 + InitStep8_Comment = "" + InitStep9_Action = "Var OR" + InitStep9_Value0 = 0x00000000 + InitStep9_Value1 = 0x00000001 + InitStep9_Comment = "" + InitStep10_Action = "Var Write 32bit" + InitStep10_Value0 = 0x400002C0 + InitStep10_Value1 = 0x00000000 + InitStep10_Comment = "" + InitStep11_Action = "Write 32bit" + InitStep11_Value0 = 0x40006008 + InitStep11_Value1 = 0x00000000 + InitStep11_Comment = "disable SPI FLASH operation" + InitStep12_Action = "Write 32bit" + InitStep12_Value0 = 0x4000602C + InitStep12_Value1 = 0x00000000 + InitStep12_Comment = "disable all interrupts" + InitStep13_Action = "Write 32bit" + InitStep13_Value0 = 0x40006010 + InitStep13_Value1 = 0x00000001 + InitStep13_Comment = "use first 'slave select' pin" + InitStep14_Action = "Write 32bit" + InitStep14_Value0 = 0x40006014 + InitStep14_Value1 = 0x00000002 + InitStep14_Comment = "baud rate, default value" + InitStep15_Action = "Write 32bit" + InitStep15_Value0 = 0x40006018 + InitStep15_Value1 = 0x00000000 + InitStep15_Comment = "tx fifo threshold" + InitStep16_Action = "Write 32bit" + InitStep16_Value0 = 0x4000601C + InitStep16_Value1 = 0x00000000 + InitStep16_Comment = "rx fifo threshold" + InitStep17_Action = "Write 32bit" + InitStep17_Value0 = 0x4000604C + InitStep17_Value1 = 0x00000000 + InitStep17_Comment = "disable DMA" + NumExitSteps = 1 + ExitStep0_Action = "Write 32bit" + ExitStep0_Value0 = 0x40000210 + ExitStep0_Value1 = 0x00211157 + ExitStep0_Comment = "Boot from Flash" + UseScriptFile = 0 + ScriptFile = "" + UseRAM = 1 + RAMAddr = 0x10000000 + RAMSize = 0x00060000 + CheckCoreID = 1 + CoreID = 0x2BA01477 + CoreIDMask = 0xFFFFFFFF + UseAutoSpeed = 0x00000001 + ClockSpeed = 0x00000000 + EndianMode = 0 + ChipName = "Cortex-M3" +[FLASH] + aRangeSel[1] = 0-18 + BankSelMode = 1 + BaseAddr = 0x98000000 + CheckId = 0 + CustomRAMCode = "D:\MCU\SEGGER\JLink_V610a\Samples\JFlash\ProjectFiles\Atmel\AT91SAM9261_DataFlash_SPI0\RAMCodeV2_AT91SAM9261_DataFlash_SPI0_LE.mot" + DeviceName = "Am29F800BB" + NumBanks = 1 + OrgNumBits = 16 + OrgNumChips = 1 +[PRODUCTION] + AutoBlankCheck = 1 + AutoDisconnect = 0 + AutoMode = 0 + AutoPerformsErase = 0 + AutoPerformsProgram = 0 + AutoPerformsSecure = 0 + AutoPerformsStartApp = 0 + AutoPerformsUnsecure = 0 + AutoPerformsVerify = 1 + EnableTargetPower = 0 + EraseType = 2 + MonitorVTref = 0 + MonitorVTrefMax = 0x0000157C + MonitorVTrefMin = 0x000003E8 + OverrideTimeouts = 0 + ProgramSN = 0 + SerialFile = "" + SkipBlankOnRead = 0 + SNAddr = 0x00000000 + SNInc = 0x00000001 + SNLen = 0x00000008 + SNListFile = "" + SNValue = 0x00000001 + StartAppType = 0 + TargetPowerDelay = 0x00000014 + TimeoutErase = 0x00003A98 + TimeoutProgram = 0x00002710 + TimeoutVerify = 0x00002710 + VerifyType = 1 diff --git a/flasher/RTL_FFlash.JLinkScript b/flasher/RTL_FFlash.JLinkScript new file mode 100644 index 0000000..a9ed900 --- /dev/null +++ b/flasher/RTL_FFlash.JLinkScript @@ -0,0 +1,17 @@ +h +r +w4 0x40000230,0x0000D3C4 +w4 0x40000210,0x00200113 +w4 0x400002C0,0x00110001 +w4 0x40006008,0 +w4 0x4000602C,0 +w4 0x40006010,1 +w4 0x40006014,2 +w4 0x40006018,0 +w4 0x4000601C,0 +w4 0x4000604C,0 +savebin fullflash.bin 0x98000000 0x100000 +w4 0x40000210,0x211157 +r +g +q \ No newline at end of file diff --git a/flasher/RTL_Reset.JLinkScript b/flasher/RTL_Reset.JLinkScript new file mode 100644 index 0000000..196d12a --- /dev/null +++ b/flasher/RTL_Reset.JLinkScript @@ -0,0 +1,9 @@ +r0 +trst0 +r1 +trst1 +h +r +w4 0x40000210,0x111157 +g +q \ No newline at end of file diff --git a/flasher/RTL_RunRAM.JLinkScript b/flasher/RTL_RunRAM.JLinkScript new file mode 100644 index 0000000..5991456 --- /dev/null +++ b/flasher/RTL_RunRAM.JLinkScript @@ -0,0 +1,12 @@ +r0 +trst0 +r1 +trst1 +h +r +loadbin build/bin/ram_1.r.bin 0x10000bc8 +loadbin build/bin/ram_2.bin 0x10006000 +r +w4 0x40000210,0x20111157 +g +q \ No newline at end of file diff --git a/flasher/ameba1.cfg b/flasher/ameba1.cfg new file mode 100644 index 0000000..f3ee2db --- /dev/null +++ b/flasher/ameba1.cfg @@ -0,0 +1,124 @@ +# Main file for Ameba1 series Cortex-M3 parts +# +# !!!!!! +# + +set CHIPNAME rtl8195a +set CHIPSERIES ameba1 + +# Adapt based on what transport is active. +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + error "CHIPNAME not set. Please do not include ameba1.cfg directly." +} + +if { [info exists CHIPSERIES] } { + # Validate chip series is supported + if { $CHIPSERIES != "ameba1" } { + error "Unsupported chip series specified." + } + set _CHIPSERIES $CHIPSERIES +} else { + error "CHIPSERIES not set. Please do not include ameba1.cfg directly." +} + +if { [info exists CPUTAPID] } { + # Allow user override + set _CPUTAPID $CPUTAPID +} else { + # Ameba1 use a Cortex M3 core. + if { $_CHIPSERIES == "ameba1" } { + if { [using_jtag] } { + set _CPUTAPID 0x4ba00477 + } { + set _CPUTAPID 0x2ba01477 + } + } +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -chain-position $_TARGETNAME + + +# Run with *real slow* clock by default since the +# boot rom could have been playing with the PLL, so +# we have no idea what clock the target is running at. +adapter_khz 1000 + +# delays on reset lines +adapter_nsrst_delay 200 +if {[using_jtag]} { + jtag_ntrst_delay 200 +} + + +# Ameba1 (Cortex M3 core) support SYSRESETREQ +if {![using_hla]} { + # if srst is not fitted use SYSRESETREQ to + # perform a soft reset + cortex_m reset_config sysresetreq +} + +$_TARGETNAME configure -event reset-init {ameba1_init} + +# Ameba1 SDRAM enable +proc ameba1_init { } { + # init System + mww 0x40000014 0x00000021 + sleep 10 + mww 0x40000304 0x1fc00002 + sleep 10 + mww 0x40000250 0x00000400 + sleep 10 + mww 0x40000340 0x00000000 + sleep 10 + mww 0x40000230 0x0000dcc4 + sleep 10 + mww 0x40000210 0x00011117 + sleep 10 + mww 0x40000210 0x00011157 + sleep 10 + mww 0x400002c0 0x00110011 + sleep 10 + mww 0x40000320 0xffffffff + sleep 10 + # init SDRAM + mww 0x40000040 0x00fcc702 + sleep 10 + mdw 0x40000040 + mww 0x40005224 0x00000001 + sleep 10 + mww 0x40005004 0x00000208 + sleep 10 + mww 0x40005008 0xffffd000 + sleep 13 + mww 0x40005020 0x00000022 + sleep 13 + mww 0x40005010 0x09006201 + sleep 13 + mww 0x40005014 0x00002611 + sleep 13 + mww 0x40005018 0x00068413 + sleep 13 + mww 0x4000501c 0x00000042 + sleep 13 + mww 0x4000500c 0x700 ;# set Idle + sleep 20 + mww 0x40005000 0x1 ;# start init + sleep 100 + mdw 0x40005000 + mww 0x4000500c 0x600 ;# enter memory mode + sleep 30 + + mww 0x40005008 0x00000000 ;# 0xf00 + ;# mww 0x40005008 0x00000f00 + sleep 3 + mww 0x40000300 0x0006005e ;# 0x5e + ;# mww 0x40000300 0x0000005e + sleep 3 +} diff --git a/flasher/cortex.ocd b/flasher/cortex.ocd new file mode 100644 index 0000000..b0cdf18 --- /dev/null +++ b/flasher/cortex.ocd @@ -0,0 +1,99 @@ +proc cortex_bootstrap {start} { + # disable interrupts + reg faultmask 0x01 + set vectors "" + mem2array vectors 32 $start 2 + reg sp $vectors(0) + reg pc $vectors(1) + resume +} + +proc cortex_reboot {} { + set ddd [ format "0x%08x" [ rtl8710_flasher_mrw [ expr 0x40000210 ] ] ] +# set aaa [ format "0x%08x" [ expr 0x40000210 ] ] + echo "CLK_CTRL1 = $ddd" +# echo "# Set processor clock to default before system reset" + set ddd [ format "0x%08x" [ rtl8710_flasher_mrw [ expr 0x40000014 ] ] ] +# set aaa [ format "0x%08x" [ expr 0x40000014 ] ] + echo "SOC_FUNC_EN = $ddd" +# mww 0x40000014 0x00000021 + sleep 10 + echo "# Reboot (system reset)" + mww 0xE000ED0C 0x05FA0007 +} + +proc init_system {} { +# Set processor clock to default before system reset +# CLK_CTRL1 +# mww 0x40000014 0x00000011 + mww 0x40000014 0x00000021 + sleep 10 +# PESOC_SOC_CTRL +# mww 0x40000304 0x1fc00001 + mww 0x40000304 0x1fc00002 + sleep 10 +# PESOC_CLK_SEL + mww 0x40000250 0x00000400 + sleep 10 +# GPIO_PULL_CTRL4 + mww 0x40000340 0x00000000 + sleep 10 +# PESOC_CLK_CTRL +# mww 0x40000230 0x0000d3c4 + mww 0x40000230 0x0000dcc4 + sleep 10 +# SOC_FUNC_EN: FUN|OCP|LXBUS|FLASH|CPU|LOG_UART|GTIMER|SECURITY_ENGINE +# mww 0x40000210 0x00211117 + mww 0x40000210 0x00011117 + sleep 10 +# SOC_FUNC_EN: FUN|OCP|LXBUS|FLASH|CPU|LOG_UART|GTIMER|SECURITY_ENGINE + MEM_CTRL + mww 0x40000210 0x00011157 + sleep 10 +# CPU_PERIPHERAL_CTRL SPI_FLASH_PIN_EN|SDR_PIN_EN|SWD_PIN_EN|LOG_UART_PIN_EN ? +# mww 0x400002c0 0x00110000 + mww 0x400002c0 0x00110011 + sleep 10 +# GPIO_SHTDN_CTRL +# mww 0x40000320 0x00000033 + mww 0x40000320 0xffffffff + sleep 10 + + mww 0x40005008 0x00000000 + sleep 10 +# PESOC_MEM_CTRL + mww 0x40000300 0x0006005e + sleep 10 + + # set baudrate to 38400 +# mww 0x40003010 0x00000080 +# mww 0x40003008 0x00000022 +# mww 0x4000300C 0x00000000 +# mww 0x40003010 0x00000000 +} + +proc boot_from_flash {} { + echo "# skip sdram init, it has been init in openocd config" + mww 0x40000210 0x211157 +} + +proc boot_from_ram {} { + echo "# boot from ram, igonore loading flash" + mww 0x40000210 0x8011157 +} + +proc restart_from_falsh {} { + init + init_system + boot_from_flash + cortex_reboot +} + +proc load_ram_binary { local_filename address } { +# set address 0x10000BC8 + init + reset halt + set size [file size $local_filename] + load_image $local_filename $address bin $address $size + boot_from_ram + resume +} diff --git a/flasher/file_info.jlink b/flasher/file_info.jlink new file mode 100644 index 0000000..2b24f94 --- /dev/null +++ b/flasher/file_info.jlink @@ -0,0 +1,7 @@ +define call1 +set $ImageSize = 0xF1E0 +set $ImageAddr = 0x0D0000 +end +define call2 +FlasherWrite build/bin/WEBFiles.bin $ImageAddr $ImageSize +end diff --git a/flasher/flash_file.jlink b/flasher/flash_file.jlink new file mode 100644 index 0000000..83b04c0 --- /dev/null +++ b/flasher/flash_file.jlink @@ -0,0 +1,9 @@ +define call1 +SetFirwareSize build/bin/ram_all.bin +end +define call2 +FlasherWrite build/bin/ram_all.bin 0 $Image1Size +end +define call3 +FlasherWrite build/bin/ram_all.bin $Image2Addr $Image2Size +end diff --git a/flasher/gdb_flasher.jlink b/flasher/gdb_flasher.jlink new file mode 100644 index 0000000..f3aa851 --- /dev/null +++ b/flasher/gdb_flasher.jlink @@ -0,0 +1,198 @@ +#################################### +# J-LINK GDB SERVER initialization # +#################################### +define InitJlink +printf "Jlink Init:\n" +set verbose off +set complaints 0 +set confirm off +set exec-done-display off +show exec-done-display +set trace-commands off +#set debug aix-thread off +#set debug dwarf2-die 0 +set debug displaced off +set debug expression 0 +set debug frame 0 +set debug infrun 0 +set debug observer 0 +set debug overload 0 +#set debugvarobj 0 +set pagination off +set print address off +set print symbol-filename off +set print symbol off +set print pretty off +set print object off +#set debug notification off +set debug parser off +set debug remote 0 +target remote localhost:2331 +set remotetimeout 10000 +monitor device Cortex-M3 +monitor endian little +monitor reset +# Set max speed +monitor speed 4000 +set mem inaccessible-by-default off +# Setup GDB FOR FASTER DOWNLOADS +set remote memory-write-packet-size 8192 +set remote memory-write-packet-size fixed +end +############# +# Boot_Flash +define SetBootFlash +printf "SetBoot = Flash:\n" +monitor long 0x40000210 = 0x211157 +end +# Boot RAM start_addr0() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x80000000 ) +define SetBootCall0 +printf "SetBoot = Call0:\n" +monitor long 0x40000210 = 0x80111157 +end +# Boot RAM start_addr1() Run if ( v40000210 & 0x20000000 ) +define SetBootCall1 +printf "SetBoot = Call1:\n" +monitor long 0x40000210 = 0x20111157 +end +# Boot RAM start_addr2() Run if ( v40000210 & 0x10000000 ) +define SetBootCall2 +printf "SetBoot = Call2:\n" +monitor long 0x40000210 = 0x10111157 +end +# Boot RAM start_addr3() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x8000000 ) +define SetBootCall3 +printf "SetBoot = Call3:\n" +monitor long 0x40000210 = 0x8111157 +end +# Boot RAM start_addr4() Init console, Run if ( v40000210 & 0x4000000 ) +define SetBootCall4 +printf "SetBoot = Call4:\n" +monitor long 0x40000210 = 0x4111157 +end +# CPU CLK 166 MHz? +define SetClk166MHz +printf "SetCLK 166.66MHz:\n" +monitor long 0x40000014 = 0x00000011 +end +# CPU CLK 83 MHz? +define SetClk83MHz +printf "SetCLK 83.33MHz:\n" +monitor long 0x40000014 = 0x00000021 +end +############### +# System Init # +############### +define SystemInit +printf "System Init:\n" +monitor long 0x40000304 = 0x1FC00002 +monitor long 0x40000250 = 0x400 +monitor long 0x40000340 = 0x0 +monitor long 0x40000230 = 0xdcc4 +monitor long 0x40000210 = 0x11117 +monitor long 0x40000210 = 0x11157 +monitor long 0x400002c0 = 0x110011 +monitor long 0x40000320 = 0xffffffff +end +############ +# SPI Init # +############ +define SPI_Init +printf "Init SPI:\n" +#enable spi flash peripheral clock +set $Temp = {int}(0x40000230) +set $Temp = ($Temp | 0x300) +set {int}(0x40000230) = $Temp +#enable spi flash peripheral +set $Temp = {int}(0x40000210) +set $Temp = ($Temp | 0x10) +set {int}(0x40000210) = $Temp +#select spi flash pinout (0 - internal), enable spi flash pins +set $Temp = {int}(0x400002C0) +set $Temp = (($Temp & 0xFFFFFFF8) | 1) +set {int}(0x400002C0) = $Temp +#disable SPI FLASH operation +monitor long 0x40006008 = 0 +#disable all interrupts +monitor long 0x4000602C = 0 +#use first "slave select" pin +monitor long 0x40006010 = 1 +#baud rate, default value +monitor long 0x40006014 = 2 +#tx fifo threshold +monitor long 0x40006018 = 0 +#rx fifo threshold +monitor long 0x4000601C = 0 +#disable DMA +monitor long 0x4000604C = 0 +set $SPI_FLASH_BASE = 0x98000000 +end +################### +# SetFirwareSize # +################### +define SetFirwareSize +set $rambuffer = 0x10000300 +printf "Get ImagesSize:\n" +restore $arg0 binary $rambuffer 0 0x20 +set $Image1Size = {int}($rambuffer+0x10) + 32 +set $Image1LoadAddr = {int}($rambuffer+0x14) +set $Image2Addr = {short}($rambuffer+0x18) * 1024 +if $Image1Size != 0 && $Image1Size < 0x1000000 + if $Image2Addr == 0 + set $Image2Addr = $Image1Size + end + printf "Image1Size = %d\n", $Image1Size + printf "Image1LoadAddr = 0x%08x\n", $Image1LoadAddr + printf "Image2FlashAddr = 0x%08x\n", $Image2Addr + set $parms1 = $rambuffer - $Image2Addr + set $parms3 = $Image2Addr + 0x08 + restore $arg0 binary $parms1 $Image2Addr $parms3 + set $Image2Size = {int}($rambuffer) + set $Image2LoadAddr = {int}($rambuffer+0x4) + if $Image2Size != 0xFFFFFFFF && $Image2Size != 0 + set $Image2Size = $Image2Size + 16 + printf "Image2Size = %d\n", $Image2Size + printf "Image2LoadAddr = 0x%08x\n", $Image2LoadAddr + set $FirmwareSize = $Image2Addr + $Image2Size + printf "FirmwareSize = %d\n", $FirmwareSize + else + set $Image2Size = 0 + printf "Image2 - None\n" + set $FirmwareSize = $Image1Size + printf "FirmwareSize = %d\n", $FirmwareSize + end +else + set $Image1Size = 0 + set $Image2Size = 0 + set $Image2Addr = 0 + set $FirmwareSize = 0 + printf "Image not format Firmware!\n" +end +end +##################### +# Flash Images Info # +##################### +define FlashImagesInfo +printf "Flash Info:\n" +set $Image1Size = {int}($SPI_FLASH_BASE + 0x10) + 32 +set $Image1LoadAddr = {int}($SPI_FLASH_BASE + 0x14) +if $Image1LoadAddr == 0xFFFFFFFF +printf "Image1 - None\n" +else +set $Image2FlashAddr = {short}($SPI_FLASH_BASE + 0x18) * 1024 +if $Image2FlashAddr == 0 +$Image2FlashAddr = $Image1Size +end +set $Image2Size = {int}($Image2FlashAddr + $SPI_FLASH_BASE) +set $Image2LoadAddr = {int}($Image2FlashAddr + $SPI_FLASH_BASE + 0x4) +printf "Image1Size = %d\n", $Image1Size +printf "Image1LoadAddr = 0x%08x\n", $Image1LoadAddr +printf "Image2FlashAddr = 0x%08x\n", $Image2FlashAddr +if $Image2Size != 0xFFFFFFFF +printf "Image2Size = %d\n", $Image2Size +printf "Image2LoadAddr = 0x%08x\n", $Image2LoadAddr +else +printf "Image2 - None\n" +end +end +end diff --git a/flasher/gdb_init.jlink b/flasher/gdb_init.jlink new file mode 100644 index 0000000..0ffc247 --- /dev/null +++ b/flasher/gdb_init.jlink @@ -0,0 +1,30 @@ +# +# J-LINK GDB SERVER initialization +# +target remote localhost:2331 +set remotetimeout 10000 +monitor device Cortex-M3 +monitor endian little +monitor reset +# Set max speed +monitor speed 4000 +set mem inaccessible-by-default off +# Setup GDB FOR FASTER DOWNLOADS +#set remote memory-write-packet-size 4096 +#set remote memory-write-packet-size fixed +# Boot Flash +monitor long 0x40000210 = 0x211157 +# Boot RAM start_addr0() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x80000000 ) +#monitor long 0x40000210 = 0x80011117 +# Boot RAM start_addr1() Run if ( v40000210 & 0x20000000 ) +#monitor long 0x40000210 = 0x20011117 +# Boot RAM start_addr2() Run if ( v40000210 & 0x10000000 ) +#monitor long 0x40000210 = 0x10011117 +# Boot RAM start_addr3() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x8000000 ) +#monitor long 0x40000210 = 0x8011117 +# Boot RAM start_addr4() Init console, Run if ( v40000210 & 0x4000000 ) +monitor long 0x40000210 = 0x4011117 +# CPU CLK 166 MHz? +# monitor long 0x40000014 = 0x00000011 +# CPU CLK 83 MHz? +#monitor long 0x40000014 = 0x00000021 diff --git a/flasher/gdb_ota.jlink b/flasher/gdb_ota.jlink new file mode 100644 index 0000000..9ed1306 --- /dev/null +++ b/flasher/gdb_ota.jlink @@ -0,0 +1,374 @@ +#################################### +# J-LINK GDB SERVER initialization # +#################################### +define InitJlink +printf "Jlink Init:\n" +set verbose off +set complaints 0 +set confirm off +set exec-done-display off +show exec-done-display +set trace-commands off +#set debug aix-thread off +#set debug dwarf2-die 0 +set debug displaced off +set debug expression 0 +set debug frame 0 +set debug infrun 0 +set debug observer 0 +set debug overload 0 +#set debugvarobj 0 +set pagination off +set print address off +set print symbol-filename off +set print symbol off +set print pretty off +set print object off +#set debug notification off +set debug parser off +set debug remote 0 +target remote localhost:2331 +set remotetimeout 10000 +monitor device Cortex-M3 +monitor endian little +monitor reset +# Set max speed +monitor speed 4000 +set mem inaccessible-by-default off +# Setup GDB FOR FASTER DOWNLOADS +set remote memory-write-packet-size 8192 +set remote memory-write-packet-size fixed +end +############# +# Boot_Flash +define SetBootFlash +printf "SetBoot = Flash:\n" +monitor long 0x40000210 = 0x211157 +end +# Boot RAM start_addr0() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x80000000 ) +define SetBootCall0 +printf "SetBoot = Call0:\n" +monitor long 0x40000210 = 0x80011117 +end +# Boot RAM start_addr1() Run if ( v40000210 & 0x20000000 ) +define SetBootCall1 +printf "SetBoot = Call1:\n" +monitor long 0x40000210 = 0x20011117 +end +# Boot RAM start_addr2() Run if ( v40000210 & 0x10000000 ) +define SetBootCall2 +printf "SetBoot = Call2:\n" +monitor long 0x40000210 = 0x10011117 +end +# Boot RAM start_addr3() Run if ( v400001F4 & 0x8000000 ) && ( v40000210 & 0x8000000 ) +define SetBootCall3 +printf "SetBoot = Call3:\n" +monitor long 0x40000210 = 0x8011117 +end +# Boot RAM start_addr4() Init console, Run if ( v40000210 & 0x4000000 ) +define SetBootCall4 +printf "SetBoot = Call4:\n" +monitor long 0x40000210 = 0x4011117 +end +# CPU CLK 166 MHz? +define SetClk166MHz +printf "SetCLK 166.66MHz:\n" +monitor long 0x40000014 = 0x00000011 +end +# CPU CLK 83 MHz? +define SetClk83MHz +printf "SetCLK 83.33MHz:\n" +monitor long 0x40000014 = 0x00000021 +end +############### +# System Init # +############### +define SystemInit +printf "System Init:\n" +monitor long 0x40000304 = 0x1FC00002 +monitor long 0x40000250 = 0x400 +monitor long 0x40000340 = 0x0 +monitor long 0x40000230 = 0xdcc4 +monitor long 0x40000210 = 0x11117 +monitor long 0x40000210 = 0x11157 +monitor long 0x400002c0 = 0x110011 +monitor long 0x40000320 = 0xffffffff +end +############ +# SPI Init # +############ +define SPI_Init +printf "Init SPI:\n" +#enable spi flash peripheral clock +set $Temp = {int}(0x40000230) +set $Temp = ($Temp | 0x300) +set {int}(0x40000230) = $Temp +#enable spi flash peripheral +set $Temp = {int}(0x40000210) +set $Temp = ($Temp | 0x10) +set {int}(0x40000210) = $Temp +#select spi flash pinout (0 - internal), enable spi flash pins +set $Temp = {int}(0x400002C0) +set $Temp = (($Temp & 0xFFFFFFF8) | 1) +set {int}(0x400002C0) = $Temp +#disable SPI FLASH operation +monitor long 0x40006008 = 0 +#disable all interrupts +monitor long 0x4000602C = 0 +#use first "slave select" pin +monitor long 0x40006010 = 1 +#baud rate, default value +monitor long 0x40006014 = 2 +#tx fifo threshold +monitor long 0x40006018 = 0 +#rx fifo threshold +monitor long 0x4000601C = 0 +#disable DMA +monitor long 0x4000604C = 0 +set $SPI_FLASH_BASE = 0x98000000 +end +################### +# GetOtaSize # +################### +define GetOtaSize +set $rambuffer = 0x10000300 +printf "Get ImagesSize:\n" +set $ImageOtaSize = 0 +restore $arg0 binary $rambuffer 0 4 +set $ImageAddSize = {int}($rambuffer+0) +if $ImageAddSize != 0 + printf "Image2Size = %d\n", $ImageAddSize + set $ImageOtaSize = $ImageOtaSize + $ImageAddSize + 16 + set $parms1 = $rambuffer - $ImageOtaSize + set $parms3 = $ImageOtaSize + 4 + restore $arg0 binary $parms1 $ImageOtaSize $parms3 + set $ImageAddSize = {int}($rambuffer+0) + if $ImageAddSize < 0x200000 + printf "ImageSdramSize = %d\n", $ImageAddSize + set $ImageOtaSize = $ImageOtaSize + $ImageAddSize + 20 + end +else + printf "Image2Size = %d !\n", $ImageOtaSize +end +end +##################### +# Flash Images Info # +##################### +define FlashImagesInfo +printf "Flash Info:\n" +set $Image1Size = {int}($SPI_FLASH_BASE + 0x10) + 32 +set $Image1LoadAddr = {int}($SPI_FLASH_BASE + 0x14) +if $Image1LoadAddr == 0xFFFFFFFF +printf "Image1 - None\n" +else +set $Image2FlashAddr = {short}($SPI_FLASH_BASE + 0x18) * 1024 +if $Image2FlashAddr == 0 +$Image2FlashAddr = $Image1Size +end +set $Image2Size = {int}($Image2FlashAddr + $SPI_FLASH_BASE) +set $Image2LoadAddr = {int}($Image2FlashAddr + $SPI_FLASH_BASE + 0x4) +printf "Image1Size = %d\n", $Image1Size +printf "Image1LoadAddr = 0x%08x\n", $Image1LoadAddr +printf "Image2FlashAddr = 0x%08x\n", $Image2FlashAddr +if $Image2Size != 0xFFFFFFFF +printf "Image2Size = %d\n", $Image2Size +printf "Image2LoadAddr = 0x%08x\n", $Image2LoadAddr +else +printf "Image2 - None\n" +end +end +set $ImageOtaAddr = {int}($SPI_FLASH_BASE + 0x9000) +if $ImageOtaAddr != 0x80000 +printf "ImageOtaAddr = 0x%08x - Invalid!\n", $ImageOtaAddr +else +printf "ImageOtaAddr = 0x%08x\n", $ImageOtaAddr +end +end +############### +# FlasherInit # +############### +define FlasherInit +set $rtl8710_flasher_capacity = 0 +set $rtl8710_flasher_auto_erase = 1 +set $rtl8710_flasher_auto_verify = 1 +set $rtl8710_flasher_firmware_ptr = 0x10001000 +set $rtl8710_flasher_buffer = 0x10008000 +set $rtl8710_flasher_buffer_size = 421888 +set $rtl8710_flasher_sector_size = 4096 +set $rtl8710_flasher_auto_erase_sector = 0xFFFFFFFF +end +############### +# FlasherWait # +############### +define FlasherWait +set $fresult = {int}($rtl8710_flasher_buffer) +while ($fresult != 0) +set $fresult = {int}($rtl8710_flasher_buffer) +end +end +############### +# FlasherLoad # +############### +define FlasherLoad +if $rtl8710_flasher_capacity == 0 + printf "initializing RTL8710 flasher\n" + restore $arg0 binary $rtl8710_flasher_firmware_ptr 0 968 + monitor reset + set $pc = $rtl8710_flasher_firmware_ptr + set $sp = 0x1ffffffc + set {int}($rtl8710_flasher_buffer + 0x08) = 0 + set {int}($rtl8710_flasher_buffer + 0x00) = 1 + #continue + monitor go + FlasherWait + set $id = {int}($rtl8710_flasher_buffer + 0x0C) + set $rtl8710_flasher_capacity = 1 << (($id >> 16) & 0x0ff) + if ($id == 0x1420c2) + printf "Flash ID = 0x%08x : MX25L8006E (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + else + printf "Flash ID = 0x%08x : (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + end + printf "RTL8710 flasher initialized\n" +else +printf "reinitializing RTL8710 flasher\n" +end +end +################## +# FlasherRdBlock # +################## +define FlasherRdBlock +#printf "FlashRdBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 3 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + error "read error, offset 0x%08x", $arg0 +end +end +################## +# FlasherWrBlock # +################## +define FlasherWrBlock +#printf "FlashWrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 4 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + error "write error, offset 0x%08x", $arg0 +end +end +################## +# FlasherVrBlock # +################## +define FlasherVrBlock +#printf "FlashVrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 5 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + set $status = {int}($rtl8710_flasher_buffer + 0x0C) + set $status = {int}($status + $arg0) + error "verify error, offset 0x%08x", $status +end +end +################# +# FlashSecErase # +################# +define FlashSecErase +#printf "FlashSecErase 0x%08x, 0x%08x\n", $rtl8710_flasher_buffer, $arg0 +set {int}($rtl8710_flasher_buffer + 0x04) = 2 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +end +################ +# FlasherWrite # +################ +define FlasherWrite +set $sector = 0 +set $offset = 0 +set $size = $arg2 +while $offset < $size + set $len = $size - $offset + if $len > $rtl8710_flasher_buffer_size + set $len = $rtl8710_flasher_buffer_size + end + set $flash_offset = $arg1 + $offset + printf "write offset 0x%08x\n", $flash_offset + set $parms1 = $rtl8710_flasher_buffer + 0x20 - $offset + set $parms2 = $offset + set $parms3 = $offset + $len + restore $arg0 binary $parms1 $parms2 $parms3 + if $rtl8710_flasher_auto_erase != 0 + set $count_i = $flash_offset + while $count_i < ($flash_offset + $len) + set $sector = $count_i/$rtl8710_flasher_sector_size + if $rtl8710_flasher_auto_erase_sector != $sector + set $parms1 = $sector * $rtl8710_flasher_sector_size + printf "erase sector %d at 0x%08x\n", $sector, $parms1 + FlashSecErase $parms1 + set $rtl8710_flasher_auto_erase_sector = $sector + end + set $count_i = $count_i + 1 + end + end + FlasherWrBlock $flash_offset $len + printf "write %d bytes at 0x%08x\n", $len, $flash_offset + if $rtl8710_flasher_auto_verify != 0 + printf "verify offset 0x%08x len %d\n", $flash_offset, $len + FlasherVrBlock $flash_offset $len + end + set $offset = $offset + $rtl8710_flasher_buffer_size +end +end +######################################### +InitJlink +SystemInit +SetClk166MHz +SPI_Init +GetOtaSize build/bin/ota.bin +if $ImageOtaSize != 0 + FlasherInit + FlasherLoad flasher/rtl8710_flasher.bin + set $FixOtaAddr = 0x80000 + set $pbuffer = $rtl8710_flasher_buffer + 0x20 + FlasherRdBlock 0x9000 0x1000 + set $ImageOtaAddr = {int}($pbuffer) + if $ImageOtaAddr != $FixOtaAddr + printf "ImageOtaAddr = 0x%08x - Invalid!\n", $ImageOtaAddr + set {int}($pbuffer) = $FixOtaAddr + set $Temp = $ImageOtaAddr & $FixOtaAddr + if $Temp != $FixOtaAddr + printf "FlashSecErase at 0x9000\n" + FlashSecErase 0x9000 + printf "Write offset 0x9000 4096 bytes\n" + FlasherWrBlock 0x9000 0x1000 + else + printf "Write offset 0x9000 4 bytes\n" + FlasherWrBlock 0x9000 0x0004 + end + end + FlasherWrite build/bin/ota.bin $FixOtaAddr $ImageOtaSize + restore build/bin/ota.bin binary $pbuffer 0 0x1000 + set {int}($pbuffer + 0x08) = 0x35393138 + set {int}($pbuffer + 0x0C) = 0x31313738 + FlasherWrBlock $FixOtaAddr 0x10 + FlashImagesInfo +end +monitor reset +SetBootFlash +monitor go +quit diff --git a/flasher/gdb_rdflash.jlink b/flasher/gdb_rdflash.jlink new file mode 100644 index 0000000..674ce48 --- /dev/null +++ b/flasher/gdb_rdflash.jlink @@ -0,0 +1,17 @@ +# GDB Jlink read fullflash +# Init +source -v flasher/gdb_flasher.jlink +InitJlink +SystemInit +SPI_Init +monitor speed 12000 +#FlashInfo +# Read FullFlash +printf "Read FullFlash:\n" +set $dumpstartaddr = $SPI_FLASH_BASE +set $dumpendaddr = $SPI_FLASH_BASE + 0x100000 +printf "Start addr of dumping = 0x%08x\n", $dumpstartaddr +printf "End addr of dumping = 0x%08x\n", $dumpendaddr +dump binary memory ../fullflash.bin $dumpstartaddr $dumpendaddr +printf "FullFlash saved in ./build/bin/fullflash.bin - OK.\n" +quit diff --git a/flasher/gdb_run_ram.jlink b/flasher/gdb_run_ram.jlink new file mode 100644 index 0000000..ad6a86d --- /dev/null +++ b/flasher/gdb_run_ram.jlink @@ -0,0 +1,11 @@ +# +# J-LINK GDB SERVER initialization +# +source -v flasher/gdb_flasher.jlink +InitJlink +load build/obj/build.axf +SetBootCall4 +monitor reset +monitor go +quit + diff --git a/flasher/gdb_wrfile.jlink b/flasher/gdb_wrfile.jlink new file mode 100644 index 0000000..b81e8c4 --- /dev/null +++ b/flasher/gdb_wrfile.jlink @@ -0,0 +1,156 @@ +############### +# FlasherInit # +############### +define FlasherInit +set $rtl8710_flasher_capacity = 0 +set $rtl8710_flasher_auto_erase = 1 +set $rtl8710_flasher_auto_verify = 1 +set $rtl8710_flasher_firmware_ptr = 0x10001000 +set $rtl8710_flasher_buffer = 0x10008000 +set $rtl8710_flasher_buffer_size = 421888 +set $rtl8710_flasher_sector_size = 4096 +set $rtl8710_flasher_auto_erase_sector = 0xFFFFFFFF +end +############### +# FlasherWait # +############### +define FlasherWait +set $fresult = {int}($rtl8710_flasher_buffer) +while ($fresult != 0) +set $fresult = {int}($rtl8710_flasher_buffer) +end +end +############### +# FlasherLoad # +############### +define FlasherLoad +if $rtl8710_flasher_capacity == 0 + printf "initializing RTL8710 flasher\n" + restore $arg0 binary $rtl8710_flasher_firmware_ptr 0 968 + monitor reset + set $pc = $rtl8710_flasher_firmware_ptr + set $sp = 0x1ffffffc + set {int}($rtl8710_flasher_buffer + 0x08) = 0 + set {int}($rtl8710_flasher_buffer + 0x00) = 1 + #continue + monitor go + FlasherWait + set $id = {int}($rtl8710_flasher_buffer + 0x0C) + set $rtl8710_flasher_capacity = 1 << (($id >> 16) & 0x0ff) + if ($id == 0x1420c2) + printf "Flash ID = 0x%08x : MX25L8006E (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + else + printf "Flash ID = 0x%08x : (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + end + printf "RTL8710 flasher initialized\n" +else +printf "reinitializing RTL8710 flasher\n" +end +end +################## +# FlasherWrBlock # +################## +define FlasherWrBlock +#printf "FlashWrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 4 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + error "write error, offset 0x%08x", $arg0 +end +end +################## +# FlasherVrBlock # +################## +define FlasherVrBlock +#printf "FlashVrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 5 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + set $status = {int}($rtl8710_flasher_buffer + 0x0C) + set $status = {int}($status + $arg0) + error "verify error, offset 0x%08x", $status +end +end +################# +# FlashSecErase # +################# +define FlashSecErase +#printf "FlashSecErase 0x%08x, 0x%08x\n", $rtl8710_flasher_buffer, $arg0 +set {int}($rtl8710_flasher_buffer + 0x04) = 2 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +end +################ +# FlasherWrite # +################ +define FlasherWrite +set $sector = 0 +set $offset = 0 +set $size = $arg2 +while $offset < $size + set $len = $size - $offset + if $len > $rtl8710_flasher_buffer_size + set $len = $rtl8710_flasher_buffer_size + end + set $flash_offset = $arg1 + $offset + printf "write offset 0x%08x\n", $flash_offset + set $parms1 = $rtl8710_flasher_buffer + 0x20 - $offset + set $parms2 = $offset + set $parms3 = $offset + $len + restore $arg0 binary $parms1 $parms2 $parms3 + if $rtl8710_flasher_auto_erase != 0 + set $count_i = $flash_offset + while $count_i < ($flash_offset + $len) + set $sector = $count_i/$rtl8710_flasher_sector_size + if $rtl8710_flasher_auto_erase_sector != $sector + set $parms1 = $sector * $rtl8710_flasher_sector_size + printf "erase sector %d at 0x%08x\n", $sector, $parms1 + FlashSecErase $parms1 + set $rtl8710_flasher_auto_erase_sector = $sector + end + set $count_i = $count_i + 1 + end + end + FlasherWrBlock $flash_offset $len + printf "wrote %d bytes at 0x%08x\n", $len, $flash_offset + if $rtl8710_flasher_auto_verify != 0 + printf "verify offset 0x%08x len %d\n", $flash_offset, $len + FlasherVrBlock $flash_offset $len + end + set $offset = $offset + $rtl8710_flasher_buffer_size +end +end +######################################### +source -v flasher/gdb_flasher.jlink +source -v flasher/file_info.jlink +InitJlink +SystemInit +SetClk83MHz +SPI_Init +FlasherInit +FlasherLoad flasher/rtl8710_flasher.bin +call1 +if $ImageSize != 0 +set $ImageEnd = $ImageSize + $ImageAddr + 0x4000 +if $rtl8710_flasher_capacity >= $ImageEnd + printf "Write Image size %d to Flash addr 0x%08x:\n", $ImageSize, $ImageAddr + call2 +else + printf "Error: Image size is too big!\n" +end +else + printf "Error: Image size is zero!\n" +end +quit diff --git a/flasher/gdb_wrflash.jlink b/flasher/gdb_wrflash.jlink new file mode 100644 index 0000000..6d37642 --- /dev/null +++ b/flasher/gdb_wrflash.jlink @@ -0,0 +1,164 @@ +############### +# FlasherInit # +############### +define FlasherInit +set $rtl8710_flasher_capacity = 0 +set $rtl8710_flasher_auto_erase = 1 +set $rtl8710_flasher_auto_verify = 1 +set $rtl8710_flasher_firmware_ptr = 0x10001000 +set $rtl8710_flasher_buffer = 0x10008000 +set $rtl8710_flasher_buffer_size = 421888 +set $rtl8710_flasher_sector_size = 4096 +set $rtl8710_flasher_auto_erase_sector = 0xFFFFFFFF +end +############### +# FlasherWait # +############### +define FlasherWait +set $fresult = {int}($rtl8710_flasher_buffer) +while ($fresult != 0) +set $fresult = {int}($rtl8710_flasher_buffer) +end +end +############### +# FlasherLoad # +############### +define FlasherLoad +if $rtl8710_flasher_capacity == 0 + printf "initializing RTL8710 flasher\n" + restore $arg0 binary $rtl8710_flasher_firmware_ptr 0 968 + monitor reset + set $pc = $rtl8710_flasher_firmware_ptr + set $sp = 0x1ffffffc + set {int}($rtl8710_flasher_buffer + 0x08) = 0 + set {int}($rtl8710_flasher_buffer + 0x00) = 1 + #continue + monitor go + FlasherWait + set $id = {int}($rtl8710_flasher_buffer + 0x0C) + set $rtl8710_flasher_capacity = 1 << (($id >> 16) & 0x0ff) + if ($id == 0x1420c2) + printf "Flash ID = 0x%08x : MX25L8006E (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + else + printf "Flash ID = 0x%08x : (%d kbytes)\n", $id, $rtl8710_flasher_capacity>>10 + end + printf "RTL8710 flasher initialized\n" +else +printf "reinitializing RTL8710 flasher\n" +end +end +################## +# FlasherWrBlock # +################## +define FlasherWrBlock +#printf "FlashWrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 4 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + error "write error, offset 0x%08x", $arg0 +end +end +################## +# FlasherVrBlock # +################## +define FlasherVrBlock +#printf "FlashVrBlock 0x%08x, 0x%08x\n", $arg0, $arg1 +set {int}($rtl8710_flasher_buffer + 0x04) = 5 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x14) = $arg1 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +set $status = {int}($rtl8710_flasher_buffer + 0x08) +if $status > 0 + set $status = {int}($rtl8710_flasher_buffer + 0x0C) + set $status = {int}($status + $arg0) + error "verify error, offset 0x%08x", $status +end +end +################# +# FlashSecErase # +################# +define FlashSecErase +#printf "FlashSecErase 0x%08x, 0x%08x\n", $rtl8710_flasher_buffer, $arg0 +set {int}($rtl8710_flasher_buffer + 0x04) = 2 +set {int}($rtl8710_flasher_buffer + 0x08) = 0 +set {int}($rtl8710_flasher_buffer + 0x10) = $arg0 +set {int}($rtl8710_flasher_buffer + 0x00) = 1 +FlasherWait +end +################ +# FlasherWrite # +################ +define FlasherWrite +set $sector = 0 +set $offset = 0 +set $size = $arg2 +while $offset < $size + set $len = $size - $offset + if $len > $rtl8710_flasher_buffer_size + set $len = $rtl8710_flasher_buffer_size + end + set $flash_offset = $arg1 + $offset + printf "write offset 0x%08x\n", $flash_offset + set $parms1 = $rtl8710_flasher_buffer + 0x20 - $flash_offset + set $parms2 = $flash_offset + set $parms3 = $flash_offset + $len + restore $arg0 binary $parms1 $parms2 $parms3 + if $rtl8710_flasher_auto_erase != 0 + set $count_i = $flash_offset + while $count_i < ($flash_offset + $len) + set $sector = $count_i/$rtl8710_flasher_sector_size + if $rtl8710_flasher_auto_erase_sector != $sector + set $parms1 = $sector * $rtl8710_flasher_sector_size + printf "erase sector %d at 0x%08x\n", $sector, $parms1 + FlashSecErase $parms1 + set $rtl8710_flasher_auto_erase_sector = $sector + end + set $count_i = $count_i + 1 + end + end + FlasherWrBlock $flash_offset $len + printf "wrote %d bytes at 0x%08x\n", $len, $flash_offset + if $rtl8710_flasher_auto_verify != 0 + printf "verify offset 0x%08x len %d\n", $flash_offset, $len + FlasherVrBlock $flash_offset $len + end + set $offset = $offset + $rtl8710_flasher_buffer_size +end +end +######################################### +source -v flasher/gdb_flasher.jlink +source -v flasher/flash_file.jlink +InitJlink +SystemInit +SetClk166MHz +SPI_Init +FlashImagesInfo +#SetFirwareSize $wr_flile +call1 +if $FirmwareSize == 0 + error "FirmwareSize = 0!" +end +FlasherInit +FlasherLoad flasher/rtl8710_flasher.bin +if $Image1Size != 0 + printf "Write Image1 size %d to Flash addr 0x00000000:\n", $Image1Size + #FlasherWrite $wr_flile 0 $Image1Size + call2 + if $Image2Size != 0 && $Image2Addr >= $Image1Size + printf "Write Image2 size %d to Flash addr 0x%08x:\n", $Image2Size, $Image2Addr + #FlasherWrite $wr_flile $Image2Addr $Image2Size + call3 + end +end +FlashImagesInfo +monitor reset +SetBootFlash +monitor go +quit diff --git a/flasher/rtl8710.ocd b/flasher/rtl8710.ocd new file mode 100644 index 0000000..2f2b102 --- /dev/null +++ b/flasher/rtl8710.ocd @@ -0,0 +1,340 @@ +# +# OpenOCD script for RTL8710 +# Copyright (C) 2016 Rebane, rebane@alkohol.ee +# +set CHIPNAME rtl8195a +set CHIPSERIES ameba1 + +# Adapt based on what transport is active. + +source [find target/swj-dp.tcl] + +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME rtl8710 +} + +if { [info exists ENDIAN] } { + set _ENDIAN $ENDIAN +} else { + set _ENDIAN little +} + +if { [info exists WORKAREASIZE] } { + set _WORKAREASIZE $WORKAREASIZE +} else { + set _WORKAREASIZE 0x800 +} + +if { [info exists CPUTAPID] } { + set _CPUTAPID $CPUTAPID +} else { + set _CPUTAPID 0x2ba01477 +} + +swj_newdap $_CHIPNAME cpu -irlen 4 -expected-id $_CPUTAPID + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_TARGETNAME + +$_TARGETNAME configure -work-area-phys 0x10001000 -work-area-size $_WORKAREASIZE -work-area-backup 0 + +# adapter_khz 500 +adapter_nsrst_delay 100 + +if {![using_hla]} { + cortex_m reset_config sysresetreq vectreset +} + +set rtl8710_flasher_firmware_ptr 0x10001000 +set rtl8710_flasher_buffer 0x10008000 +set rtl8710_flasher_buffer_size 262144 +set rtl8710_flasher_sector_size 4096 + +array set rtl8710_flasher_code { + 0 0xB671B57F 1 0x25FF4B58 2 0x6B196B1A 3 0x7040F042 4 0x69D96318 5 0xF4414E55 + 6 0x69D97480 7 0xF8D361DC 8 0xF8C32120 9 0xF8D35120 10 0xF8C31124 11 0x47B05124 + 12 0x47B04E4F 13 0x47984B4F 14 0x60104A4F 15 0x484F47B0 16 0x60012100 17 0x2C006804 + 18 0x4D4DD0FC 19 0xB93E682E 20 0x60264C49 21 0x47B04E46 22 0x47984B46 23 0xE7ED6020 + 24 0x2B01682B 25 0x4E42D109 26 0x4C4647B0 27 0x47A02006 28 0x47904A45 29 0x47A020C7 + 30 0x682AE00D 31 0xD10E2A02 32 0x47B04E3B 33 0x20064C3F 34 0x483F47A0 35 0x493F4780 + 36 0x68084D3F 37 0x47B047A8 38 0x47A02004 39 0x6828E7CE 40 0xD1132803 41 0x47A04C32 + 42 0x24004838 43 0x4E396805 44 0x68311960 45 0xD206428C 46 0x4B384A37 47 0x221018A1 + 48 0x34104798 49 0x4D2AE7F3 50 0xE7B847A8 51 0x29046829 52 0x2400D11B 53 0x6806482F + 54 0xD2B042B4 55 0x47A84D24 56 0x20064E28 57 0x4B2847B0 58 0x49284798 59 0x680A4B2A + 60 0x18A018E1 61 0xF44F4B2A 62 0x47987280 63 0x200447A8 64 0xF50447B0 65 0x47A87480 + 66 0x682CE7E4 67 0xD1232C05 68 0x47984B17 69 0x4D1F2400 70 0x4294682A 71 0x481BD28F + 72 0x68012210 73 0x18604E1D 74 0x47B04669 75 0x1B19682B 76 0xBF282910 77 0x23002110 + 78 0xD011428B 79 0xF81D4A16 80 0x18A05003 81 0x42B55CC6 82 0x3301D101 83 0x4A15E7F4 + 84 0x60112101 85 0xE7726054 86 0x25014E12 87 0xE76E6035 88 0x47A84D03 89 0xE7D63410 + 90 0x40000200 91 0x100011BD 92 0x100013DD 93 0x10001289 94 0x1000800C 95 0x10008000 + 96 0x10008004 97 0x1000130D 98 0x100013ED 99 0x10008010 100 0x10001335 101 0x10008014 + 102 0x10008020 103 0x10001221 104 0x10001375 105 0x10008008 106 0x6A5A4B03 107 0xD0FB0512 + 108 0x0060F893 109 0xBF004770 110 0x40006000 111 0x6B194B17 112 0xF4416B1A 113 0x63187040 + 114 0x69186919 115 0x0110F041 116 0xF8D36119 117 0x220000C0 118 0x0106F020 119 0x00C0F8D3 + 120 0x10C0F8C3 121 0x00C0F8D3 122 0x0101F040 123 0x00C0F8D3 124 0x10C0F8C3 125 0x43BCF503 + 126 0x609A6899 127 0x20016AD9 128 0x691962DA 129 0x69596118 130 0x61592102 131 0x619A6999 + 132 0x61DA69D9 133 0x64DA6CD9 134 0xBF004770 135 0x40000200 136 0x460EB570 137 0xB34A4614 + 138 0xF3C04B15 139 0x681A4507 140 0x7240F44F 141 0x685A601A 142 0xF3C02103 143 0x2C102207 + 144 0x2410BF28 145 0x605CB2C0 146 0x1060F883 147 0x5060F883 148 0xF8832101 149 0xF8832060 + 150 0x689A0060 151 0x60992500 152 0x47984B08 153 0x35015570 154 0x42A2B2AA 155 0x4804D3F8 + 156 0xF0116A81 157 0xD1FA0301 158 0x60836881 159 0xBD704620 160 0x40006000 161 0x100011A9 + 162 0x4C10B5F8 163 0x68232003 164 0x7340F44F 165 0x68636023 166 0x60602101 167 0x68A3229F + 168 0x60A14D0B 169 0x2060F884 170 0x460647A8 171 0x460747A8 172 0x040347A8 173 0x2707EA43 + 174 0x0006EA47 175 0x4B036AA1 176 0x0201F011 177 0x6899D1FA 178 0xBDF8609A 179 0x40006000 + 180 0x100011A9 181 0x4C0BB510 182 0x68232001 183 0x7340F44F 184 0x68636023 185 0x60602105 + 186 0x60A068A2 187 0xF8844A06 188 0x47901060 189 0x4B036AA1 190 0x0201F011 191 0x6899D1FA + 192 0xBD10609A 193 0x40006000 194 0x100011A9 195 0x21014B08 196 0xF44F681A 197 0x601A7280 + 198 0x6099689A 199 0x0060F883 200 0x48036A9A 201 0x0101F012 202 0x6883D1FA 203 0x47706081 + 204 0x40006000 205 0x21014B0E 206 0xF44F681A 207 0x601A7280 208 0x2220689A 209 0xF8836099 + 210 0xF3C02060 211 0xF3C04107 212 0xB2C02207 213 0x1060F883 214 0x2060F883 215 0x0060F883 + 216 0x4A036A99 217 0x0001F011 218 0x6893D1FA 219 0x47706090 220 0x40006000 221 0xB36AB530 + 222 0x25014B17 223 0xF44F681C 224 0x601C7480 225 0x2402689C 226 0xF883609D 227 0xF3C04060 + 228 0xF3C04507 229 0xB2C02407 230 0x5060F883 231 0x7F80F5B2 232 0xF44FBF28 233 0xF8837280 + 234 0xF8834060 235 0x20000060 236 0x4C095C0D 237 0xF8843001 238 0xB2855060 239 0xD3F74295 + 240 0x07496A99 241 0x6AA0D5FC 242 0xF0104B03 243 0xD1FA0101 244 0x60996898 245 0xBD304610 + 246 0x40006000 247 0x4B02B508 248 0x07C04798 249 0xBD08D4FB 250 0x100012D5 251 0x4B04B508 + 252 0xF0004798 253 0xB2C10002 254 0xD0F82900 255 0xBF00BD08 256 0x100012D5 +} + +set rtl8710_flasher_command_read_id 0 +set rtl8710_flasher_command_mass_erase 1 +set rtl8710_flasher_command_sector_erase 2 +set rtl8710_flasher_command_read 3 +set rtl8710_flasher_command_write 4 +set rtl8710_flasher_command_verify 5 + +set rtl8710_flasher_ready 0 +set rtl8710_flasher_capacity 0 +set rtl8710_flasher_auto_erase 0 +set rtl8710_flasher_auto_verify 0 +set rtl8710_flasher_auto_erase_sector 0xFFFFFFFF + +proc rtl8710_flasher_init {} { + global rtl8710_flasher_firmware_ptr + global rtl8710_flasher_buffer + global rtl8710_flasher_capacity + global rtl8710_flasher_ready + global rtl8710_flasher_code + + if {[expr {$rtl8710_flasher_ready == 0}]} { + echo "initializing RTL8710 flasher" + halt + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + array2mem rtl8710_flasher_code 32 $rtl8710_flasher_firmware_ptr [array size rtl8710_flasher_code] + reg faultmask 0x01 + reg sp 0x20000000 + reg pc $rtl8710_flasher_firmware_ptr + resume + rtl8710_flasher_wait + set id [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x0C}]] + set rtl8710_flasher_capacity [expr {2 ** [expr {($id >> 16) & 0xFF}]}] + set rtl8710_flasher_ready 1 + echo "RTL8710 flasher initialized" + } + return "" +} + +proc rtl8710_flasher_mrw {reg} { + set value "" + mem2array value 32 $reg 1 + return $value(0) +} + +proc rtl8710_flasher_wait {} { + global rtl8710_flasher_buffer + while {[rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x00}]]} { } +} + +proc rtl8710_flasher_load_block {local_filename offset len} { + global rtl8710_flasher_buffer + load_image $local_filename [expr {$rtl8710_flasher_buffer + 0x20 - $offset}] bin [expr {$rtl8710_flasher_buffer + 0x20}] $len +} + +proc rtl8710_flasher_read_block {offset len} { + global rtl8710_flasher_buffer + global rtl8710_flasher_command_read + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_read + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x10}] $offset + mww [expr {$rtl8710_flasher_buffer + 0x14}] $len + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait + set status [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x08}]] + if {[expr {$status > 0}]} { + error "read error, offset $offset" + } +} + +proc rtl8710_flasher_write_block {offset len} { + global rtl8710_flasher_buffer + global rtl8710_flasher_command_write + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_write + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x10}] $offset + mww [expr {$rtl8710_flasher_buffer + 0x14}] $len + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait + set status [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x08}]] + if {[expr {$status > 0}]} { + error "write error, offset $offset" + } +} + +proc rtl8710_flasher_verify_block {offset len} { + global rtl8710_flasher_buffer + global rtl8710_flasher_command_verify + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_verify + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x10}] $offset + mww [expr {$rtl8710_flasher_buffer + 0x14}] $len + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait + set status [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x08}]] + if {[expr {$status > 0}]} { + set status [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x0C}]] + set status [expr {$status + $offset}] + error "verify error, offset $status" + } +} + +proc rtl8710_flash_read_id {} { + global rtl8710_flasher_buffer + global rtl8710_flasher_capacity + global rtl8710_flasher_command_read_id + rtl8710_flasher_init + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_read_id + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait + set id [rtl8710_flasher_mrw [expr {$rtl8710_flasher_buffer + 0x0C}]] + set manufacturer_id [format "0x%02X" [expr {$id & 0xFF}]] + set memory_type [format "0x%02X" [expr {($id >> 8) & 0xFF}]] + set memory_capacity [expr {2 ** [expr {($id >> 16) & 0xFF}]}] + echo "manufacturer ID: $manufacturer_id, memory type: $memory_type, memory capacity: $memory_capacity bytes" +} + +proc rtl8710_flash_mass_erase {} { + global rtl8710_flasher_buffer + global rtl8710_flasher_command_mass_erase + rtl8710_flasher_init + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_mass_erase + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait +} + +proc rtl8710_flash_sector_erase {offset} { + global rtl8710_flasher_buffer + global rtl8710_flasher_command_sector_erase + rtl8710_flasher_init + mww [expr {$rtl8710_flasher_buffer + 0x04}] $rtl8710_flasher_command_sector_erase + mww [expr {$rtl8710_flasher_buffer + 0x08}] 0x00000000 + mww [expr {$rtl8710_flasher_buffer + 0x10}] $offset + mww [expr {$rtl8710_flasher_buffer + 0x00}] 0x00000001 + rtl8710_flasher_wait +} + +proc rtl8710_flash_read {local_filename loc size} { + global rtl8710_flasher_buffer + global rtl8710_flasher_buffer_size + rtl8710_flasher_init + for {set offset 0} {$offset < $size} {set offset [expr {$offset + $rtl8710_flasher_buffer_size}]} { + set len [expr {$size - $offset}] + if {[expr {$len > $rtl8710_flasher_buffer_size}]} { + set len $rtl8710_flasher_buffer_size + } + set flash_offset [expr {$loc + $offset}] + echo "read offset $flash_offset" + rtl8710_flasher_read_block $flash_offset $len + dump_image _rtl8710_flasher.bin [expr {$rtl8710_flasher_buffer + 0x20}] $len + exec dd conv=notrunc if=_rtl8710_flasher.bin "of=$local_filename" bs=1 "seek=$offset" + echo "read $len bytes" + } +} + +proc rtl8710_flash_write {local_filename loc} { + global rtl8710_flasher_buffer_size + global rtl8710_flasher_sector_size + global rtl8710_flasher_auto_erase + global rtl8710_flasher_auto_verify + global rtl8710_flasher_auto_erase_sector + rtl8710_flasher_init + set sector 0 + set size [file size $local_filename] + for {set offset 0} {$offset < $size} {set offset [expr {$offset + $rtl8710_flasher_buffer_size}]} { + set len [expr {$size - $offset}] + if {[expr {$len > $rtl8710_flasher_buffer_size}]} { + set len $rtl8710_flasher_buffer_size + } + set flash_offset [expr {$loc + $offset}] + echo "write offset $flash_offset" + rtl8710_flasher_load_block $local_filename $offset $len + if {[expr {$rtl8710_flasher_auto_erase != 0}]} { + for {set i $flash_offset} {$i < [expr {$flash_offset + $len}]} {incr i} { + set sector [expr {$i / $rtl8710_flasher_sector_size}] + if {[expr {$rtl8710_flasher_auto_erase_sector != $sector}]} { + echo "erase sector $sector" + rtl8710_flash_sector_erase [expr {$sector * $rtl8710_flasher_sector_size}] + set rtl8710_flasher_auto_erase_sector $sector + } + } + } + rtl8710_flasher_write_block $flash_offset $len + echo "wrote $len bytes" + if {[expr {$rtl8710_flasher_auto_verify != 0}]} { + echo "verify offset $flash_offset" + rtl8710_flasher_verify_block $flash_offset $len + } + } +} + +proc rtl8710_flash_verify {local_filename loc} { + global rtl8710_flasher_buffer_size + rtl8710_flasher_init + set size [file size $local_filename] + for {set offset 0} {$offset < $size} {set offset [expr {$offset + $rtl8710_flasher_buffer_size}]} { + set len [expr {$size - $offset}] + if {[expr {$len > $rtl8710_flasher_buffer_size}]} { + set len $rtl8710_flasher_buffer_size + } + set flash_offset [expr {$loc + $offset}] + echo "read offset $flash_offset" + rtl8710_flasher_load_block $local_filename $offset $len + echo "verify offset $flash_offset" + rtl8710_flasher_verify_block $flash_offset $len + } +} + +proc rtl8710_flash_auto_erase {on} { + global rtl8710_flasher_auto_erase + if {[expr {$on != 0}]} { + set rtl8710_flasher_auto_erase 1 + echo "auto erase on" + } else { + set rtl8710_flasher_auto_erase 0 + echo "auto erase off" + } +} + +proc rtl8710_flash_auto_verify {on} { + global rtl8710_flasher_auto_verify + if {[expr {$on != 0}]} { + set rtl8710_flasher_auto_verify 1 + echo "auto verify on" + } else { + set rtl8710_flasher_auto_verify 0 + echo "auto verify off" + } +} + +proc rtl8710_reboot {} { + echo "# Set processor clock to default before system reset" + mww 0x40000014 0x00000021 + sleep 10 + echo "# Reboot (system reset)" + mww 0xE000ED0C 0x05FA0007 +} + diff --git a/flasher/rtl8710_flasher.bin b/flasher/rtl8710_flasher.bin new file mode 100644 index 0000000000000000000000000000000000000000..6c7e36d4c99d89ba7a7263e641d5777eedd594d1 GIT binary patch literal 968 zcmZ`%O-vI(6rSxb&|*etj4YCDx0tjgCWM4|@IbSa4($R>BvBL&Q{_9rWE=u=9ys6vrhY{{E8@KjdW9ml-cADxdwMfvCP(#GTT_L zyp|U51d`9|S)xuS<4%RUZc6jEC_D3i(=T4;#B&(5=iKg2s!#8qc>% zP>IDUpA&ki{oDNG?YPUii5&>~rzA_^*+d7^?L8gfid@f8g>!s>tX?`(QDC;+`aW7g zn@EQ|ORWe`w;*hdBmC|>!U)g+>kFK%1BW4Q1QY-V02=^p>KX8cz-T1DZ_%T!Uj*qR4|~&dx`r1NGJTDXEHed}y2H1hHz;-E4$KzITwld*a3*M`Ky+i)Fz-%(?`g&%rx*?!__|s&~kgb0p~-$1J1Wjl+w? z;9DdnSjgb{ZW4t4WL=31s5<{N8vP9!RmB4JGEGvWreK7ZK3ma{J?&`*H*s*ofp=}@ zNDJJ^agmc#6x?gGHJ`~5Y97yc&ZSk?|EtBB+p8DU`umqC*Evshosj8)JM-32+gp9< z!}z0I+PSGtqiWgU-c+P~JpEWQ4Ib?zBID+hPl;3C{v^ddtogth-H<}^=97;&n}PqK KwogEIZT|;|PH&e0 literal 0 HcmV?d00001 diff --git a/paths.bat b/paths.bat new file mode 100644 index 0000000..374f93e --- /dev/null +++ b/paths.bat @@ -0,0 +1 @@ +PATH=D:\MCU\GNU_Tools_ARM_Embedded\5.4_2016q2\bin;D:\MCU\SEGGER\JLink_V612i;%PATH% \ No newline at end of file diff --git a/paths.mk b/paths.mk new file mode 100644 index 0000000..a7154c4 --- /dev/null +++ b/paths.mk @@ -0,0 +1,44 @@ +#--------------------------- +# User defined +#--------------------------- +SDK_PATH = ../RTL00MP3/RTL00_SDKV35a/ +#GCC_PATH = d:/MCU/GNU_Tools_ARM_Embedded/5.2_2015q4/bin/# + or set in PATH +#OPENOCD_PATH = d:/MCU/OpenOCD/bin/# + or set in PATH +TOOLS_PATH ?= $(SDK_PATH)component/soc/realtek/8195a/misc/iar_utility/common/tools/ +FLASHER_TYPE ?= Jlink +#FLASHER_TYPE ?= OCD +FLASHER_PATH ?= flasher/ +JLINK_PATH ?= D:/MCU/SEGGER/JLink_V612i/ +JLINK_GDBSRV ?= JLinkGDBServer.exe +#--------------------------- +# Default +#--------------------------- +# Compilation tools +CROSS_COMPILE = $(GCC_PATH)arm-none-eabi- +AR = $(CROSS_COMPILE)ar +CC = $(CROSS_COMPILE)gcc +AS = $(CROSS_COMPILE)as +NM = $(CROSS_COMPILE)nm +LD = $(CROSS_COMPILE)gcc +GDB = $(CROSS_COMPILE)gdb +SIZE = $(CROSS_COMPILE)size +OBJCOPY = $(CROSS_COMPILE)objcopy +OBJDUMP = $(CROSS_COMPILE)objdump + +# TARGET dirs +TARGET ?= build +OBJ_DIR ?= $(TARGET)/obj +BIN_DIR ?= $(TARGET)/bin +ELFFILE ?= $(OBJ_DIR)/$(TARGET).axf + +# Make bunary tools +ifneq ($(shell uname), Linux) +EXE = .exe +endif +PICK = $(TOOLS_PATH)pick$(EXE) +PADDING = $(TOOLS_PATH)padding$(EXE) +CHCKSUM = $(TOOLS_PATH)checksum$(EXE) + +# openocd tools +OPENOCD = $(OPENOCD_PATH)openocd + diff --git a/project/inc/.gitignore b/project/inc/.gitignore new file mode 100644 index 0000000..ee42cd5 --- /dev/null +++ b/project/inc/.gitignore @@ -0,0 +1 @@ +build_info.h \ No newline at end of file diff --git a/project/inc/FreeRTOSConfig.h b/project/inc/FreeRTOSConfig.h new file mode 100644 index 0000000..3e414a1 --- /dev/null +++ b/project/inc/FreeRTOSConfig.h @@ -0,0 +1,210 @@ +/* + FreeRTOS V7.3.0 - Copyright (C) 2012 Real Time Engineers Ltd. + + FEATURES AND PORTS ARE ADDED TO FREERTOS ALL THE TIME. PLEASE VISIT + http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION. + + *************************************************************************** + * * + * FreeRTOS tutorial books are available in pdf and paperback. * + * Complete, revised, and edited pdf reference manuals are also * + * available. * + * * + * Purchasing FreeRTOS documentation will not only help you, by * + * ensuring you get running as quickly as possible and with an * + * in-depth knowledge of how to use FreeRTOS, it will also help * + * the FreeRTOS project to continue with its mission of providing * + * professional grade, cross platform, de facto standard solutions * + * for microcontrollers - completely free of charge! * + * * + * >>> See http://www.FreeRTOS.org/Documentation for details. <<< * + * * + * Thank you for using FreeRTOS, and thank you for your support! * + * * + *************************************************************************** + + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + >>>NOTE<<< The modification to the GPL is included to allow you to + distribute a combined work that includes FreeRTOS without being obliged to + provide the source code for proprietary components outside of the FreeRTOS + kernel. FreeRTOS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + *************************************************************************** + * * + * Having a problem? Start by reading the FAQ "My application does * + * not run, what could be wrong?" * + * * + * http://www.FreeRTOS.org/FAQHelp.html * + * * + *************************************************************************** + + + http://www.FreeRTOS.org - Documentation, training, latest versions, license + and contact details. + + http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products, + including FreeRTOS+Trace - an indispensable productivity tool. + + Real Time Engineers ltd license FreeRTOS to High Integrity Systems, who sell + the code with commercial support, indemnification, and middleware, under + the OpenRTOS brand: http://www.OpenRTOS.com. High Integrity Systems also + provide a safety engineered and independently SIL3 certified version under + the SafeRTOS brand: http://www.SafeRTOS.com. +*/ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H +#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__) +#include +extern uint32_t SystemCoreClock; +#endif + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ( SystemCoreClock ) +#define configTICK_RATE_HZ ( ( uint32_t ) 1000 ) +#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 70 ) +#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 90 * 1024 ) ) // use HEAP5 +#define configMAX_TASK_NAME_LEN ( 10 ) +#define configUSE_TRACE_FACILITY 0 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 0 +#define configUSE_CO_ROUTINES 1 +#define configUSE_MUTEXES 1 +#define configUSE_TIMERS 1 + +#define configMAX_PRIORITIES ( 11 ) +#define PRIORITIE_OFFSET ( 4 ) + +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) + +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_ALTERNATIVE_API 0 +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 0 +#define configGENERATE_RUN_TIME_STATS 1 + +#if configGENERATE_RUN_TIME_STATS +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() //( ulHighFrequencyTimerTicks = 0UL ) +#define portGET_RUN_TIME_COUNTER_VALUE() xTickCount //ulHighFrequencyTimerTicks +#undef configUSE_TRACE_FACILITY +#define configUSE_TRACE_FACILITY 1 +#define portCONFIGURE_STATS_PEROID_VALUE 1000 //unit Ticks +#endif + +#define configTIMER_TASK_PRIORITY ( 1 ) + +#ifdef CONFIG_UVC +#define configTIMER_QUEUE_LENGTH ( 20 ) +#else +#define configTIMER_QUEUE_LENGTH ( 10 ) +#endif + +#define configTIMER_TASK_STACK_DEPTH ( 512 ) //USE_MIN_STACK_SIZE modify from 512 to 256 + +#if (__IASMARM__ != 1) + +extern void freertos_pre_sleep_processing(unsigned int *expected_idle_time); +extern void freertos_post_sleep_processing(unsigned int *expected_idle_time); +extern int freertos_ready_to_sleep(); + +/* Enable tickless power saving. */ +#define configUSE_TICKLESS_IDLE 1 + +/* In wlan usage, this value is suggested to use value less than 80 milliseconds */ +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 + +/* It's magic trick that let us can use our own sleep function */ +#define configPRE_SLEEP_PROCESSING( x ) ( freertos_pre_sleep_processing(&x) ) + +#define configPOST_SLEEP_PROCESSING( x ) ( freertos_post_sleep_processing(&x) ) + +/* It's magic trick that let us can enable/disable tickless dynamically */ +#define traceLOW_POWER_IDLE_BEGIN(); do { \ + if (!freertos_ready_to_sleep()) { \ + mtCOVERAGE_TEST_MARKER(); \ + break; \ + } + + // portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + +#define traceLOW_POWER_IDLE_END(); } while (0); + +/* It's FreeRTOS related feature but it's not included in FreeRTOS design. */ +#define configUSE_WAKELOCK_PMU 1 + +#endif // #if (__IASMARM__ != 1) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_xTimerPendFunctionCall 1 + +/* Cortex-M specific definitions. */ +#ifdef __NVIC_PRIO_BITS + /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */ + #define configPRIO_BITS __NVIC_PRIO_BITS +#else + #define configPRIO_BITS 4 /* 15 priority levels */ +#endif + + +/* The lowest interrupt priority that can be used in a call to a "set priority" +function. */ +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0f + +/* The highest interrupt priority that can be used by any interrupt service +routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL +INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER +PRIORITY THAN THIS! (higher priorities are lower numeric values. */ +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 + + +/* Interrupt priorities used by the kernel port layer itself. These are generic +to all Cortex-M ports, and do not rely on any particular library functions. */ +#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) +/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!! +See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) ) + +//#define RTK_MODE_TIMER + + +#define INCLUDE_uxTaskGetStackHighWaterMark 1 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/project/inc/feep_config.h b/project/inc/feep_config.h new file mode 100644 index 0000000..3956a79 --- /dev/null +++ b/project/inc/feep_config.h @@ -0,0 +1,30 @@ +/* + * feep_config.h + * + * Created on: 06 ноÑб. 2016 г. + * Author: PVV + */ + +#ifndef _INC_FEEP_CONFIG_H_ +#define _INC_FEEP_CONFIG_H_ + +#define FEEP_ID_WIFI_CFG 0x5730 // id:'0W', type: struct wlan_fast_reconnect +#define FEEP_ID_WIFI_AP_CFG 0x5731 // id:'1W', type: struct rtw_wifi_config_t +#define FEEP_ID_UART_CFG 0x5530 // id:'0U', type: UART_LOG_CONF +#define FEEP_ID_LWIP_CFG 0x4C30 // id:'0L', type: struct atcmd_lwip_conf +#define FEEP_ID_DHCP_CFG 0x4430 // id:'0D', type: struct _sdhcp_cfg + +typedef struct _sdhcp_cfg { + u8 mode; // =0 dhcp off, =1 - dhcp on, =2 Static ip, =3 - auto + u32 ip; + u32 mask; + u32 gw; +}dhcp_cfg; + +/* +#define FEEP_WRITE_WIFI_CFG(x) flash_write_cfg(x, FEEP_ID_WIFI_CFG, sizeof(struct wlan_fast_reconnect)) +#define FEEP_READ_WIFI_CFG(x) flash_read_cfg(x, FEEP_ID_WIFI_CFG, sizeof(struct wlan_fast_reconnect)) +*/ + + +#endif /* _INC_FEEP_CONFIG_H_ */ diff --git a/project/inc/lwipopts.h b/project/inc/lwipopts.h new file mode 100644 index 0000000..54c7783 --- /dev/null +++ b/project/inc/lwipopts.h @@ -0,0 +1,381 @@ +/** + ****************************************************************************** + * @file lwipopts.h + * @author MCD Application Team + * @version V1.1.0 + * @date 07-October-2011 + * @brief lwIP Options Configuration. + * This file is based on Utilities\lwip_v1.3.2\src\include\lwip\opt.h + * and contains the lwIP configuration for the STM32F2x7 demonstration. + ****************************************************************************** + * @attention + * + * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + *

© COPYRIGHT 2011 STMicroelectronics

+ ****************************************************************************** + */ + +#ifndef __LWIPOPTS_H__ +#define __LWIPOPTS_H__ + +#include +#include "platform_opts.h" + +/** + * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first + * local TCP/UDP pcb (default==0). This can prevent creating predictable port + * numbers after booting a device. + */ +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 + +#define WIFI_LOGO_CERTIFICATION_CONFIG 1 //for ping 10k test buffer setting +/** + * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library + * instead of the lwip internal allocator. Can save code size if you + * already use it. + */ +#define MEM_LIBC_MALLOC 1 +/** +* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator. +* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution +* speed and usage from interrupts! +*/ +#define MEMP_MEM_MALLOC 1 + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT 1 + +/* Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores + should be used instead */ +#define LWIP_COMPAT_MUTEX 1 + +#define ETHARP_TRUST_IP_MAC 0 +#define IP_REASSEMBLY 1 +#define IP_FRAG 1 +#define ARP_QUEUEING 0 + +/** + * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname + * field. + */ +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_HOSTNAME_SIZE 16 +/** + * netif0: DEF_HOSTNAME "0", netif1: DEF_HOSTNAME "1", .. + */ +#define DEF_HOSTNAME "rtl871x" + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#define NO_SYS 0 + +/* ---------- Memory options ---------- */ +/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which + lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 + byte alignment -> define MEM_ALIGNMENT to 2. */ +#define MEM_ALIGNMENT 4 + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#if WIFI_LOGO_CERTIFICATION_CONFIG + #define MEM_SIZE (10*1024) //for ping 10k test +#else + #define MEM_SIZE (5*1024) +#endif + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 100 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 6 +/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB 10 +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 5 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG 20 +/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT 10 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#if WIFI_LOGO_CERTIFICATION_CONFIG + #define PBUF_POOL_SIZE 30 //for ping 10k test +#else + #define PBUF_POOL_SIZE 20 +#endif + +/* IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.*/ +#if WIFI_LOGO_CERTIFICATION_CONFIG + #define IP_REASS_MAX_PBUFS 30 //for ping 10k test +#else + #define IP_REASS_MAX_PBUFS 10 +#endif + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 500 + + +/* ---------- TCP options ---------- */ +#define LWIP_TCP 1 +#define TCP_TTL 255 + +/* Controls if TCP should queue segments that arrive out of + order. Define to 0 if your device is low on memory. */ +#define TCP_QUEUE_OOSEQ 1 + +/* TCP Maximum segment size. */ +#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF (5*TCP_MSS) + +/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ + +#define TCP_SND_QUEUELEN (4* TCP_SND_BUF/TCP_MSS) + +/* TCP receive window. */ +#define TCP_WND (4*TCP_MSS) // (2*TCP_MSS) + + +/* ---------- ICMP options ---------- */ +#define LWIP_ICMP 1 + +/* ---------- ARP options ----------- */ +#define LWIP_ARP 1 + +/* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. DHCP is not implemented in lwIP 0.5.1, however, so + turning this on does currently not work. */ +#define LWIP_DHCP 1 + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define UDP_TTL 255 +/* ---------- DNS options ---------- */ +#define LWIP_DNS 1 + +/* ---------- UPNP options --------- */ +#define LWIP_UPNP 0 + +/* Support Multicast */ +#define LWIP_IGMP 1 +#define LWIP_RAND() Rand() + +/* Support TCP Keepalive */ +#define LWIP_TCP_KEEPALIVE 1 + +/*LWIP_UART_ADAPTER==1: Enable LWIP_UART_ADAPTER when CONFIG_GAGENT is enabled, + because some GAGENT functions denpond on the following macro definitions.*/ +#if CONFIG_EXAMPLE_UART_ADAPTER +#define LWIP_UART_ADAPTER 1 +#else +#define LWIP_UART_ADAPTER 0 +#endif + +#if LWIP_UART_ADAPTER +#undef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 1 + +#undef SO_REUSE +#define SO_REUSE 1 + +#undef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 10 + +#undef TCP_WND +#define TCP_WND (4*TCP_MSS) + +#define TCP_KEEPIDLE_DEFAULT 10000UL +#define TCP_KEEPINTVL_DEFAULT 1000UL +#define TCP_KEEPCNT_DEFAULT 10U +#endif + +#if CONFIG_EXAMPLE_UART_ATCMD +#undef LWIP_SO_SNDTIMEO +#define LWIP_SO_SNDTIMEO 1 + +#undef SO_REUSE +#define SO_REUSE 1 + +#undef MEMP_NUM_NETCONN +#define MEMP_NUM_NETCONN 10 + +#undef MEMP_NUM_TCP_PCB +#define MEMP_NUM_TCP_PCB (MEMP_NUM_NETCONN) + +#undef MEMP_NUM_UDP_PCB +#define MEMP_NUM_UDP_PCB (MEMP_NUM_NETCONN) + +#undef TCP_WND +#define TCP_WND (4*TCP_MSS) + +#define TCP_KEEPIDLE_DEFAULT 10000UL +#define TCP_KEEPINTVL_DEFAULT 1000UL +#define TCP_KEEPCNT_DEFAULT 10U + +#define ERRNO 1 +#endif + +/* ---------- Statistics options ---------- */ +#define LWIP_STATS 0 +#define LWIP_PROVIDE_ERRNO 1 + + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ + +/* +The STM32F2x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: + - To use this feature let the following define uncommented. + - To disable it and process by CPU comment the the checksum. +*/ +//Do checksum by lwip - WLAN nic does not support Checksum offload +//#define CHECKSUM_BY_HARDWARE + + +#ifdef CHECKSUM_BY_HARDWARE + /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 0 + /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 0 + /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 0 + /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 0 + /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 0 + /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 0 +#else + /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 1 + /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 1 + /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 1 + /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 1 + /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 1 + /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 1 +#endif + + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#define LWIP_NETCONN 1 + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#define LWIP_SOCKET 1 + +/* + ----------------------------------- + ---------- DEBUG options ---------- + ----------------------------------- +*/ + +#define LWIP_DEBUG 0 + +/* + --------------------------------- + ---------- OS options ---------- + --------------------------------- +*/ + +/** + * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define TCPIP_THREAD_STACKSIZE 1000 +/** + * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when tcpip_init is called. + */ +#define TCPIP_MBOX_SIZE 6 +/** + * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_UDP_RECVMBOX_SIZE 6 +/** + * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_TCP_RECVMBOX_SIZE 6 +/** + * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a + * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed + * to sys_mbox_new() when the recvmbox is created. + */ +#define DEFAULT_RAW_RECVMBOX_SIZE 6 +/** + * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections. + * The queue size value itself is platform-dependent, but is passed to + * sys_mbox_new() when the acceptmbox is created. + */ +#define DEFAULT_ACCEPTMBOX_SIZE 6 +/** + * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread. + * The stack size value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define DEFAULT_THREAD_STACKSIZE 500 +/** + * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread. + * The priority value itself is platform-dependent, but is passed to + * sys_thread_new() when the thread is created. + */ +#define TCPIP_THREAD_PRIO (configMAX_PRIORITIES - 2) + +/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided + * by your system, set this to 0 and include in cc.h */ +#if defined(_SYS__TIMEVAL_H_) +#define LWIP_TIMEVAL_PRIVATE 0 +#endif + +#endif /* __LWIPOPTS_H__ */ + +/******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/ diff --git a/project/inc/main.h b/project/inc/main.h new file mode 100644 index 0000000..e05a0ab --- /dev/null +++ b/project/inc/main.h @@ -0,0 +1,119 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +#ifndef CONFIG_WLAN +#define CONFIG_WLAN 1 +#endif + +/* Header file declaration*/ +void wlan_network(); + +/* Interactive Mode */ +#define SERIAL_DEBUG_RX 1 + +/* WLAN and Netork */ +#define STA_MODE_SSID "ap" /* Set SSID here */ +#define AP_MODE_SSID "wlan_ap_ssid" /* Set SSID here */ +#define AP_DEFAULT_CH 6 +#define WLAN0_NAME "wlan0" +#define WLAN1_NAME "wlan1" +#define WPA_PASSPHRASE "1234567890" /* Max 32 cahracters */ +#define WEP40_KEY {0x12, 0x34, 0x56, 0x78, 0x90} + +#define ATVER_1 1 // For First AT command +#define ATVER_2 2 // For UART Module AT command + +#if CONFIG_EXAMPLE_UART_ATCMD +#define ATCMD_VER ATVER_2 +#else +#define ATCMD_VER ATVER_1 +#endif + +#if ATCMD_VER == ATVER_2 + +extern unsigned char sta_ip[4], sta_netmask[4], sta_gw[4]; +extern unsigned char ap_ip[4], ap_netmask[4], ap_gw[4]; + +/*Static IP ADDRESS*/ +#define IP_ADDR0 sta_ip[0] +#define IP_ADDR1 sta_ip[1] +#define IP_ADDR2 sta_ip[2] +#define IP_ADDR3 sta_ip[3] + +/*NETMASK*/ +#define NETMASK_ADDR0 sta_netmask[0] +#define NETMASK_ADDR1 sta_netmask[1] +#define NETMASK_ADDR2 sta_netmask[2] +#define NETMASK_ADDR3 sta_netmask[3] + +/*Gateway Address*/ +#define GW_ADDR0 sta_gw[0] +#define GW_ADDR1 sta_gw[1] +#define GW_ADDR2 sta_gw[2] +#define GW_ADDR3 sta_gw[3] + +/*******************************************/ + +/*Static IP ADDRESS*/ +#define AP_IP_ADDR0 ap_ip[0] +#define AP_IP_ADDR1 ap_ip[1] +#define AP_IP_ADDR2 ap_ip[2] +#define AP_IP_ADDR3 ap_ip[3] + +/*NETMASK*/ +#define AP_NETMASK_ADDR0 ap_netmask[0] +#define AP_NETMASK_ADDR1 ap_netmask[1] +#define AP_NETMASK_ADDR2 ap_netmask[2] +#define AP_NETMASK_ADDR3 ap_netmask[3] + +/*Gateway Address*/ +#define AP_GW_ADDR0 ap_gw[0] +#define AP_GW_ADDR1 ap_gw[1] +#define AP_GW_ADDR2 ap_gw[2] +#define AP_GW_ADDR3 ap_gw[3] + +#else + +/*Static IP ADDRESS*/ +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 3 +#define IP_ADDR3 80 + +/*NETMASK*/ +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 3 +#define GW_ADDR3 1 + +/*******************************************/ + +/*Static IP ADDRESS*/ +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 + +/*NETMASK*/ +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 + +#endif //#if ATCMD_VER == ATVER_2 + +#endif diff --git a/project/inc/platform_autoconf.h b/project/inc/platform_autoconf.h new file mode 100644 index 0000000..7377ec5 --- /dev/null +++ b/project/inc/platform_autoconf.h @@ -0,0 +1,251 @@ +/* + * Automatically generated by make menuconfig: don't edit + */ +#define AUTOCONF_INCLUDED + +#define RTL8710AF +//#define RTL8711AM +/* Image1 on project */ +#define PRESENT_IMAGE1 +/* Image2 on project */ +#define PRESENT_IMAGE2 +/* + * Target Platform Selection + */ +#define CONFIG_WITHOUT_MONITOR 1 + +#undef CONFIG_RTL8195A +#define CONFIG_RTL8195A 1 +#undef CONFIG_FPGA +#undef CONFIG_RTL_SIM +#undef CONFIG_POST_SIM +/* + * < Mass Production Option + */ +#undef CONFIG_MP +#undef CONFIG_CP +#undef CONFIG_FT +#define RTL8195A 1 +/* 0 - 166666666 Hz, 1 - 83333333 Hz, 2 - 41666666 Hz, 3 - 20833333 Hz, 4 - 10416666 Hz, 5 - 4000000? Hz, + 6 - 200000000 Hz, 7 - 10000000 Hz, 8 - 50000000 Hz, 9 - 25000000 Hz, 10 - 12500000 Hz, 11 - 4000000? Hz */ +#define CONFIG_CPU_CLK 1 +//166.6MHZ - RUN/IDLE/SLP ~63/21/6.4 mA +//83.3MHZ - RUN/IDLE/SLP ~55/15/6.4 mA +//41.6MHZ - RUN/IDLE ~51/11 mA +//20.8MHZ - RUN/IDLE ~49/9.5 mA +//4MHZ - IDLE ~8 mA +#undef CONFIG_FPGA_CLK +#define CONFIG_SDR_CLK 1 +#define CONFIG_SDR_100MHZ 1 +#undef CONFIG_SDR_50MHZ +#undef CONFIG_SDR_25MHZ +#undef CONFIG_SDR_12_5MHZ +#define SDR_CLOCK_SEL_VALUE (0) +#define CONFIG_BOOT_PROCEDURE 1 +#define CONFIG_IMAGE_PAGE_LOAD 1 +#undef CONFIG_IMAGE_AUTO_LOAD +#undef CONFIG_IMAGE_PAGE_LOAD +//#define CONFIG_IMAGE_AUTO_LOAD 1 +//#define CONFIG_BOOT_TO_UPGRADE_IMG2 1 +#undef CONFIG_PERI_UPDATE_IMG +#define CONFIG_BOOT_FROM_JTAG 1 +#undef CONFIG_ALIGNMENT_EXCEPTION_ENABLE +#define CONFIG_KERNEL 1 +#define PLATFORM_FREERTOS 1 +#undef PLATFORM_UCOSII +#undef PLATFORM_ECOS +#undef CONFIG_TASK_SCHEDUL_DIS +#define TASK_SCHEDULER_DISABLED (0) +#define CONFIG_NORMALL_MODE 1 +#undef CONFIG_MEMORY_VERIFY_MODE +#define CONFIG_TIMER_EN 1 +#define CONFIG_TIMER_NORMAL 1 +#undef CONFIG_TIMER_TEST +#define CONFIG_TIMER_MODULE 1 +#define CONFIG_WDG 1 +#undef CONFIG_WDG_NON +#define CONFIG_WDG_NORMAL 1 +#define CONFIG_WDG_ON_IDLE 10 // add pvvx: wdt on 10 s -> main.c + tasks.c +#define CONFIG_GDMA_EN 1 +#define CONFIG_GDMA_NORMAL 1 +#undef CONFIG_GDMA_TEST +#define CONFIG_GDMA_MODULE 1 +#define CONFIG_WIFI_EN 1 +#define CONFIG_WIFI_NORMAL 1 +#undef CONFIG_WIFI_TEST +#define CONFIG_WIFI_MODULE 1 +#define CONFIG_GPIO_EN 1 +#define CONFIG_GPIO_NORMAL 1 +#undef CONFIG_GPIO_TEST +#define CONFIG_GPIO_MODULE 1 +#if defined(CONFIG_INIC) || (CONFIG_SDIOD) +#define CONFIG_SDIO_DEVICE_EN 1 +#define CONFIG_SDIO_DEVICE_NORMAL 1 +#undef CONFIG_SDIO_DEVICE_TEST +#define CONFIG_SDIO_DEVICE_MODULE 1 +#else +#undef CONFIG_SDIO_DEVICE_EN +#endif +#define CONFIG_SDIO_HOST_EN 1 +//#define CONFIG_USB_EN 1 +#undef CONFIG_USB_NORMAL +#define CONFIG_USB_TEST 1 +#define CONFIG_USB_MODULE 1 +#define CONFIG_USB_VERIFY 1 +#undef CONFIG_USB_ROM_LIB +//#define CONFIG_USB_DBGINFO_EN 1 +#if defined(CONFIG_INIC) || (CONFIG_USBD) +#define DWC_DEVICE_ONLY 1 +#else +#define DWC_HOST_ONLY 1 +#define CONFIG_USB_HOST_ONLY 1 +#endif +#define CONFIG_SPI_COM_EN 1 +#define CONFIG_SPI_COM_NORMAL 1 +#undef CONFIG_SPI_COM_TEST +#define CONFIG_SPI_COM_MODULE 1 +#define CONFIG_UART_EN 1 +#define CONFIG_UART_NORMAL 1 +#undef CONFIG_UART_TEST +#define CONFIG_UART_MODULE 1 +#define CONFIG_I2C_EN 1 +#define CONFIG_I2C_NORMAL 1 +#undef CONFIG_I2C_TEST +#define CONFIG_I2C_MODULE 1 +#undef CONFIG_DEBUG_LOG_I2C_HAL +#undef CONFIG_PCM_EN +#define CONFIG_I2S_EN 1 +#define CONFIG_I2S_NORMAL 1 +#undef CONFIG_I2S_TEST +#define CONFIG_I2S_MODULE 1 +#undef CONFIG_DEBUG_LOG_I2S_HAL +#define CONFIG_NFC_EN 1 +#define CONFIG_NFC_NORMAL 1 +#undef CONFIG_NFC_TEST +#define CONFIG_NFC_MODULE 1 +#define CONFIG_SOC_PS_EN 1 +#define CONFIG_SOC_PS_NORMAL 1 +#undef CONFIG_SOC_PS_TEST +#define CONFIG_SOC_PS_MODULE 1 // hal_soc_ps_monitor.c +//#define CONFIG_SOC_PS_VERIFY 1 // hal_soc_ps_monitor.c +#define CONFIG_CRYPTO_EN 1 +#define CONFIG_CRYPTO_NORMAL 1 +#undef CONFIG_CRYPTO_TEST +#define CONFIG_CRYPTO_MODULE 1 +#define CONFIG_CRYPTO_STARTUP 1 +#define CONFIG_MII_EN 0 //1 +#define CONFIG_PWM_EN 1 +#define CONFIG_PWM_NORMAL 1 +#undef CONFIG_PWM_TEST +#define CONFIG_PWM_MODULE 1 +#define CONFIG_EFUSE_EN 1 // common/mbed/targets/hal/rtl8195a/efuse_api.c +#define CONFIG_EFUSE_NORMAL 1 +#undef CONFIG_EFUSE_TEST +#define CONFIG_EFUSE_MODULE 1 +#ifdef RTL8711AM +#define CONFIG_SDR_EN 1 +#endif +#define CONFIG_SDR_NORMAL 1 +#undef CONFIG_SDR_TEST +#define CONFIG_SDR_MODULE 1 +#define CONFIG_SPIC_EN 1 +#define CONFIG_SPIC_NORMAL 1 +#undef CONFIG_SPIC_TEST +#define CONFIG_SPIC_MODULE 1 +#define CONFIG_ADC_EN 1 +//#define CONFIG_DAC_EN 1 +#define CONFIG_NOR_FLASH 1 +#undef CONFIG_SPI_FLASH +#undef CONFIG_NAND_FLASH +#undef CONFIG_NONE_FLASH +#undef CONFIG_BTBX_EN + +// add pvvx +#define CONFIG_LOG_UART_EN 1 + +/* + * < Engineer Mode Config + */ +#undef CONFIG_JTAG +#undef CONFIG_COMPILE_FLASH_DOWNLOAD_CODE +#undef CONIFG_COMPILE_EXTERNAL_SRAM_CALIBRATE +#undef CONFIG_CMSIS_MATH_LIB_EN + +/* + * < Application Config + */ +#define CONFIG_NETWORK 1 +#define CONFIG_RTLIB_EN 1 +#define CONFIG_RTLIB_NORMAL 1 +#undef CONFIG_RTLIB_TEST +#define CONFIG_RTLIB_MODULE 1 + +/* + * < System Debug Message Config + */ +#define CONFIG_UART_LOG_HISTORY 1 +#undef CONFIG_CONSOLE_NORMALL_MODE +#define CONFIG_CONSOLE_VERIFY_MODE 1 + +/* CONFIG_DEBUG_LOG: +=0 Off all diag/debug msg, +=1 Only errors, +=2 errors + warning, (default) +=3 errors + warning + info, +=4 errors + warning + info + debug, +=5 full */ +#define CONFIG_DEBUG_LOG 2 +#if CONFIG_DEBUG_LOG > 0 +//#define CONFIG_DEBUG_ERR_MSG 1 +#define CONFIG_DEBUG_LOG_ADC_HAL 1 +#define CONFIG_DEBUG_LOG_I2S_HAL 1 +//#undef CONFIG_DEBUG_WARN_MSG +//#undef CONFIG_DEBUG_INFO_MSG +#endif // CONFIG_DEBUG_LOG +/* + * < SDK Option Config + */ +#undef CONFIG_MBED_ENABLED +#undef CONFIG_APP_DEMO + +/* + * < Select Chip Version + */ +#undef CONFIG_CHIP_A_CUT +#define CONFIG_CHIP_B_CUT 1 +#undef CONFIG_CHIP_C_CUT +#undef CONFIG_CHIP_E_CUT + +/* + * < Select toolchain + */ +#undef CONFIG_TOOLCHAIN_ASDK +#undef CONFIG_TOOLCHAIN_ARM_GCC + +/* + * < Build Option + */ +#define CONFIG_LINK_ROM_LIB 1 +#undef CONFIG_LINK_ROM_SYMB +#undef CONFIG_NORMAL_BUILD +#undef CONFIG_RELEASE_BUILD +#undef CONFIG_RELEASE_BUILD_LIBRARIES +#undef CONFIG_LIB_BUILD_RAM +#define CONFIG_RELEASE_BUILD_RAM_ALL 1 +#undef CONFIG_IMAGE_ALL +#define CONFIG_IMAGE_SEPARATE 1 + +#if CONFIG_CPU_CLK < 6 +#define CPU_CLOCK_SEL_DIV5_3 0 +#define CPU_CLOCK_SEL_VALUE CONFIG_CPU_CLK +#else +#define CPU_CLOCK_SEL_DIV5_3 1 +#define CPU_CLOCK_SEL_VALUE (CONFIG_CPU_CLK-6) +#endif + +#if CPU_CLOCK_SEL_DIV5_3 +#define PLATFORM_CLOCK (200000000ul>>CPU_CLOCK_SEL_VALUE) +#else +#define PLATFORM_CLOCK (((200000000ul*5ul)/6ul)>>CPU_CLOCK_SEL_VALUE) +#endif + diff --git a/project/inc/platform_opts.h b/project/inc/platform_opts.h new file mode 100644 index 0000000..90f29c8 --- /dev/null +++ b/project/inc/platform_opts.h @@ -0,0 +1,172 @@ +/** + ****************************************************************************** + *This file contains general configurations for ameba platform + ****************************************************************************** +*/ + +#ifndef __PLATFORM_OPTS_H__ +#define __PLATFORM_OPTS_H__ + +/*For MP mode setting*/ +#define SUPPORT_MP_MODE 0 + +/** + * For AT cmd Log service configurations + */ +#define SUPPORT_LOG_SERVICE 0 +#if SUPPORT_LOG_SERVICE +#define LOG_SERVICE_BUFLEN 100 //can't larger than UART_LOG_CMD_BUFLEN(127) +#define CONFIG_LOG_HISTORY 0 +#if CONFIG_LOG_HISTORY +#define LOG_HISTORY_LEN 5 +#endif +#define SUPPORT_INTERACTIVE_MODE 0 //on/off wifi_interactive_mode +#define CONFIG_LOG_SERVICE_LOCK 0 +#endif + +/** + * For interactive mode configurations, depends on log service + */ +#if SUPPORT_INTERACTIVE_MODE +#define CONFIG_INTERACTIVE_MODE 1 +#define CONFIG_INTERACTIVE_EXT 0 +#else +#define CONFIG_INTERACTIVE_MODE 0 +#define CONFIG_INTERACTIVE_EXT 0 +#endif + +/** + * For FreeRTOS tickless configurations + */ +#define FREERTOS_PMU_TICKLESS_PLL_RESERVED 0 // In sleep mode, 0: close PLL clock, 1: reserve PLL clock +#define FREERTOS_PMU_TICKLESS_SUSPEND_SDRAM 1 // In sleep mode, 1: suspend SDRAM, 0: no act + +/******************************************************************************/ + +/** +* For common flash usage +*/ +#define AP_SETTING_SECTOR 0x000FE000 +#define UART_SETTING_SECTOR 0x000FC000 +#define FAST_RECONNECT_DATA (0x80000 - 0x1000) + +/** + * For Wlan configurations + */ + +#define CONFIG_WLAN 1 +#if CONFIG_WLAN +#define CONFIG_LWIP_LAYER 1 +#define CONFIG_AT_USR 1 // add pvxx +//#define CONFIG_AT_LWIP 1 // add pvxx +//#define CONFIG_AT_SYS 1 // add pvxx +//#define CONFIG_AT_WIFI 1 // add pvxx +#define CONFIG_INIT_NET 1 // init lwip layer when start up +#define CONFIG_WIFI_IND_USE_THREAD 0 // wifi indicate worker thread + +//on/off relative commands in log service +#define CONFIG_SSL_CLIENT 0 +#define CONFIG_WEBSERVER 0 +#define CONFIG_OTA_UPDATE 0 +#define CONFIG_BSD_TCP 0 //NOTE : Enable CONFIG_BSD_TCP will increase about 11KB code size +#define CONFIG_AIRKISS 0 //on or off tencent airkiss +#define CONFIG_UART_SOCKET 0 // Set: CONFIG_UART_EN, CONFIG_UART_SOCKET +#define CONFIG_UART_XMODEM 0 //support uart xmodem upgrade or not +#define CONFIG_TRANSPORT 0 //on or off the at command for transport socket + +/* For WPS and P2P */ +#define CONFIG_ENABLE_WPS 0 +#define CONFIG_ENABLE_P2P 0 +#if CONFIG_ENABLE_P2P +#define CONFIG_ENABLE_WPS_AP 1 +#undef CONFIG_WIFI_IND_USE_THREAD +#define CONFIG_WIFI_IND_USE_THREAD 1 +#endif +#if (CONFIG_ENABLE_P2P && ((CONFIG_ENABLE_WPS_AP == 0) || (CONFIG_ENABLE_WPS == 0))) +#error "If CONFIG_ENABLE_P2P, need to define CONFIG_ENABLE_WPS_AP 1" +#endif + +/* For Simple Link */ +#define CONFIG_INCLUDE_SIMPLE_CONFIG 0 + +/*For wowlan service settings*/ +#define CONFIG_WOWLAN_SERVICE 0 + +#endif //end of #if CONFIG_WLAN +/*******************************************************************************/ + +/** + * For Ethernet configurations + */ +#define CONFIG_ETHERNET 0 +#if CONFIG_ETHERNET + +#define CONFIG_LWIP_LAYER 1 +#define CONFIG_INIT_NET 1 //init lwip layer when start up + +//on/off relative commands in log service +#define CONFIG_SSL_CLIENT 0 +#define CONFIG_BSD_TCP 0 //NOTE : Enable CONFIG_BSD_TCP will increase about 11KB code size + +#endif + + +/** + * For iNIC configurations + */ +#ifdef CONFIG_INIC //this flag is defined in IAR project +#define CONFIG_INIC_EN 1 //enable iNIC mode +#undef CONFIG_ENABLE_WPS +#define CONFIG_ENABLE_WPS 1 +#undef CONFIG_INCLUDE_SIMPLE_CONFIG +#define CONFIG_INCLUDE_SIMPLE_CONFIG 1 +#undef CONFIG_WOWLAN_SERVICE +#define CONFIG_WOWLAN_SERVICE 1 +#undef LOG_SERVICE_BUFLEN +#define LOG_SERVICE_BUFLEN 256 +#undef CONFIG_LWIP_LAYER +#define CONFIG_LWIP_LAYER 0 +#undef CONFIG_OTA_UPDATE +#define CONFIG_OTA_UPDATE 0 +#undef CONFIG_EXAMPLE_WLAN_FAST_CONNECT +#define CONFIG_EXAMPLE_WLAN_FAST_CONNECT 0 +#define CONFIG_INIC_SDIO_HCI 1 //for SDIO or USB iNIC +#define CONFIG_INIC_USB_HCI 0 +#define CONFIG_INIC_CMD_RSP 1 //need to return msg to host +#endif +/******************End of iNIC configurations*******************/ + +/* For UART Module AT command example */ +#define CONFIG_EXAMPLE_UART_ATCMD 0 +#if CONFIG_EXAMPLE_UART_ATCMD +#undef FREERTOS_PMU_TICKLESS_PLL_RESERVED +#define FREERTOS_PMU_TICKLESS_PLL_RESERVED 1 +#undef CONFIG_OTA_UPDATE +#define CONFIG_OTA_UPDATE 1 +#undef CONFIG_TRANSPORT +#define CONFIG_TRANSPORT 1 +#undef LOG_SERVICE_BUFLEN +#define LOG_SERVICE_BUFLEN 1600 +#undef CONFIG_LOG_SERVICE_LOCK +#define CONFIG_LOG_SERVICE_LOCK 1 +#else +#define CONFIG_EXAMPLE_WLAN_FAST_CONNECT 0 +#endif + +//#define CONFIG_EXAMPLE_UART_ADAPTER 1 +//#define CONFIG_EXAMPLE_MDNS +#define USE_FLASH_EEP 1 +#define CONFIG_WLAN_CONNECT_CB 1 + +//#define CONFIG_FATFS_EN 1 // FatFs & SD +#ifdef CONFIG_FATFS_EN +// fatfs version +#define FATFS_R_10C +// fatfs disk interface +#define FATFS_DISK_USB 0 +#define FATFS_DISK_SD 1 +#undef CONFIG_SDIO_HOST_EN +#define CONFIG_SDIO_HOST_EN 1 +#endif + +#endif //__PLATFORM_OPTS_H__ diff --git a/project/inc/rtl8195a/c_types.h b/project/inc/rtl8195a/c_types.h new file mode 100644 index 0000000..239d801 --- /dev/null +++ b/project/inc/rtl8195a/c_types.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2010 - 2011 Espressif System + * + */ + +#ifndef _C_TYPES_H_ +#define _C_TYPES_H_ + +typedef unsigned char uint8_t; +typedef signed char sint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short sint16_t; +typedef signed short int16_t; +typedef unsigned long uint32_t; +typedef signed long sint32_t; +typedef signed long int32_t; +typedef signed long long sint64_t; +typedef unsigned long long uint64_t; +typedef unsigned long long u_int64_t; +typedef float real32_t; +typedef double real64_t; + +typedef unsigned char uint8; +typedef unsigned char u8; +typedef signed char sint8; +typedef signed char int8; +typedef signed char s8; +typedef unsigned short uint16; +typedef unsigned short u16; +typedef signed short sint16; +typedef signed short s16; +typedef unsigned int uint32; +typedef unsigned int u_int; +typedef unsigned int u32; +typedef signed int sint32; +typedef signed int s32; +typedef int int32; +typedef signed long long sint64; +typedef unsigned long long uint64; +typedef unsigned long long u64; +typedef float real32; +typedef double real64; + +#define __le16 u16 + +typedef unsigned int size_t; +typedef int ssize_t; + +#ifndef _SYS_CDEFS_H_ +#define __packed __attribute__((packed)) +#endif + +#define LOCAL static + +#ifndef NULL +#define NULL (void *)0 +#endif /* NULL */ + +/* probably should not put STATUS here */ +typedef enum { + OK = 0, + FAIL, + PENDING, + BUSY, + CANCEL, +} STATUS; + +#define BIT(nr) (1UL << (nr)) + +#define REG_SET_BIT(_r, _b) (*(volatile uint32_t*)(_r) |= (_b)) +#define REG_CLR_BIT(_r, _b) (*(volatile uint32_t*)(_r) &= ~(_b)) + +#define DMEM_ATTR +#define SHMEM_ATTR + +#ifdef ICACHE_FLASH +#define ICACHE_FLASH_ATTR +#define ICACHE_RODATA_ATTR +#else +#define ICACHE_FLASH_ATTR +#define ICACHE_RODATA_ATTR +#endif /* ICACHE_FLASH */ + +#ifndef __cplusplus +typedef unsigned char bool; +//#define BOOL bool +#define true (1) +#define false (0) +#define TRUE true +#define FALSE false + + +#endif /* !__cplusplus */ + +#endif /* _C_TYPES_H_ */ diff --git a/project/inc/rtl8195a/os.h b/project/inc/rtl8195a/os.h new file mode 100644 index 0000000..2a0a4a7 --- /dev/null +++ b/project/inc/rtl8195a/os.h @@ -0,0 +1,593 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef OS_H +#define OS_H + +//#include "basic_types.h" +#include +#include "osdep_service.h" +#include "freertos/wrapper.h" +#include "utils/rom/rom_wps_os.h" + +typedef void* xqueue_handle_t; + +typedef long os_time_t; + +typedef _timer os_timer; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +struct os_reltime { + os_time_t sec; + os_time_t usec; +}; + +/** + * os_get_time - Get current time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_time(struct os_time *t); + +int os_get_reltime(struct os_reltime *t); +/* Helper macros for handling struct os_time */ +/* (&timeout->time, &tmp->time) */ +#define os_time_before(a, b) \ + ((a)->sec < (b)->sec || \ + ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) + +#define os_time_sub(a, b, res) do { \ + (res)->sec = (a)->sec - (b)->sec; \ + (res)->usec = (a)->usec - (b)->usec; \ + if ((res)->usec < 0) { \ + (res)->sec--; \ + (res)->usec += 1000000; \ + } \ +} while (0) + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + * + * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time + * which is used by POSIX mktime(). + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + +struct os_tm { + int sec; /* 0..59 or 60 for leap seconds */ + int min; /* 0..59 */ + int hour; /* 0..23 */ + int day; /* 1..31 */ + int month; /* 1..12 */ + int year; /* Four digit year */ +}; + +int os_gmtime(os_time_t t, struct os_tm *tm); + +/* Helpers for handling struct os_time */ + +/* Helpers for handling struct os_reltime */ + +static inline int os_reltime_before(struct os_reltime *a, + struct os_reltime *b) +{ + return os_time_before(a,b); +} + + +static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b, + struct os_reltime *res) +{ + os_time_sub(a,b,res); +} + + +static inline void os_reltime_age(struct os_reltime *start, + struct os_reltime *age) +{ + struct os_reltime now; + + os_get_time((struct os_time *)&now); + os_reltime_sub(&now, start, age); +} + + +static inline int os_reltime_expired(struct os_reltime *now, + struct os_reltime *ts, + os_time_t timeout_secs) +{ + struct os_reltime age; + + os_reltime_sub(now, ts, &age); + return (age.sec > timeout_secs) || + (age.sec == timeout_secs && age.usec > 0); +} + +/** + * os_daemonize - Run in the background (detach from the controlling terminal) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +//#if 0 +/** + * os_zalloc - Allocate and zero memory + * @size: Number of bytes to allocate + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_zalloc(size_t size); + +/** + * os_calloc - Allocate and zero memory for an array + * @nmemb: Number of members in the array + * @size: Number of bytes in each member + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * This function can be used as a wrapper for os_zalloc(nmemb * size) when an + * allocation is used for an array. The main benefit over os_zalloc() is in + * having an extra check to catch integer overflows in multiplication. + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +static inline void * os_calloc(size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_zalloc(nmemb * size); +} +//#endif + + +static inline int os_memcmp_const(const void *a, const void *b, size_t len) +{ + const u8 *aa = a; + const u8 *bb = b; + size_t i; + u8 res; + + for (res = 0, i = 0; i < len; i++) + res |= aa[i] ^ bb[i]; + + return res; +} + +/* + * The following functions are wrapper for standard ANSI C or POSIX functions. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifdef OS_NO_C_LIB_DEFINES + +/** + * os_malloc - Allocate dynamic memory + * @size: Size of the buffer to allocate + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_malloc(size_t size); + +/** + * os_realloc - Re-allocate dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc() + * @size: Size of the new buffer + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is + * not freed and caller is still responsible for freeing it. + */ +void * os_realloc(void *ptr, size_t size); + +/** + * os_free - Free dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL + */ +void os_free(void *ptr); + +/** + * os_memcpy - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst must not overlap. os_memmove() can be used with + * overlapping memory. + */ +void * os_memcpy(void *dest, const void *src, size_t n); + +/** + * os_memmove - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst may overlap. + */ +void *os_memmove(void *dest, const void *src, size_t n); + +/** + * os_memset - Fill memory with a constant byte + * @s: Memory area to be filled + * @c: Constant byte + * @n: Number of bytes started from s to fill with c + * Returns: s + */ +void *os_memset(void *s, int c, size_t n); + +/** + * os_memcmp - Compare memory areas + * @s1: First buffer + * @s2: Second buffer + * @n: Maximum numbers of octets to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_memcmp(const void *s1, const void *s2, size_t n); + +/** + * os_strdup - Duplicate a string + * @s: Source string + * Returns: Allocated buffer with the string copied into it or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char *os_strdup(const char *s); + +/** + * os_strlen - Calculate the length of a string + * @s: '\0' terminated string + * Returns: Number of characters in s (not counting the '\0' terminator) + */ +size_t os_strlen(const char *s); + +/** + * os_strcasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcasecmp(const char *s1, const char *s2); + +/** + * os_strncasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncasecmp(const char *s1, const char *s2, size_t n); + +/** + * os_strchr - Locate the first occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char *os_strchr(const char *s, int c); + +/** + * os_strrchr - Locate the last occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char *os_strrchr(const char *s, int c); + +/** + * os_strcmp - Compare two strings + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcmp(const char *s1, const char *s2); + +/** + * os_strncmp - Compare two strings + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncmp(const char *s1, const char *s2, size_t n); + +/** + * os_strncpy - Copy a string + * @dest: Destination + * @src: Source + * @n: Maximum number of characters to copy + * Returns: dest + */ +char *os_strncpy(char *dest, const char *src, size_t n); + +/** + * os_strstr - Locate a substring + * @haystack: String (haystack) to search from + * @needle: Needle to search from haystack + * Returns: Pointer to the beginning of the substring or %NULL if not found + */ +char *os_strstr(const char *haystack, const char *needle); + +/** + * os_snprintf - Print to a memory buffer + * @str: Memory buffer to print into + * @size: Maximum length of the str buffer + * @format: printf format + * Returns: Number of characters printed (not including trailing '\0'). + * + * If the output buffer is truncated, number of characters which would have + * been written is returned. Since some C libraries return -1 in such a case, + * the caller must be prepared on that value, too, to indicate truncation. + * + * Note: Some C library implementations of snprintf() may not guarantee null + * termination in case the output is truncated. The OS wrapper function of + * os_snprintf() should provide this guarantee, i.e., to null terminate the + * output buffer if a C library version of the function is used and if that + * function does not guarantee null termination. + * + * If the target system does not include snprintf(), see, e.g., + * http://www.ijs.si/software/snprintf/ for an example of a portable + * implementation of snprintf. + */ +int os_snprintf(char *str, size_t size, const char *format, ...); + +#else /* OS_NO_C_LIB_DEFINES */ + +#if !defined(CONFIG_PLATFORM_8195A) && !defined(CONFIG_PLATFORM_8711B) +#ifdef CONFIG_MEM_MONITOR + u8* os_malloc(u32 sz); + void os_mfree(u8 *pbuf, u32 sz); + #ifndef os_free + #define os_free(p, sz) os_mfree(((u8*)(p)), (sz)) + #endif +#else + #ifndef os_malloc + #define os_malloc(sz) _rtw_malloc(sz) + #endif + #ifndef os_free + #define os_free(p, sz) _rtw_mfree(((u8*)(p)), (sz)) + #endif +#endif +#endif + extern void *os_zalloc(size_t size); + extern char *os_strdup(const char *string_copy_from); + + #ifndef os_sleep + #define os_sleep(s, us) rtw_mdelay_os((s)*1000 + (us)/1000) + #endif + #ifndef os_memcpy + #define os_memcpy(d, s, n) rtw_memcpy((void*)(d), ((void*)(s)), (n)) + #endif + #ifndef os_memmove + #define os_memmove(d, s, n) memmove((d), (s), (n)) + #endif + #ifndef os_memset + #define os_memset(pbuf, c, sz) rtw_memset(pbuf, c, sz) + #endif + #ifndef os_memcmp + #define os_memcmp(s1, s2, n) rtw_memcmp(((void*)(s1)), ((void*)(s2)), (n)) + #endif + #ifndef os_memcmp_p2p + #define os_memcmp_p2p(s1, s2, n) memcmp((s1), (s2), (n)) + #endif + #ifndef os_get_random_bytes + #define os_get_random_bytes(d,sz) rtw_get_random_bytes(((void*)(d)), (sz)) + #endif + #ifndef os_strlen + #define os_strlen(s) strlen(s) + #endif + #ifndef os_strcasecmp + #ifdef _MSC_VER + #define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) + #else + #define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) + #endif + #endif + #ifndef os_strncasecmp + #ifdef _MSC_VER + #define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) + #else + #define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) + #endif + #endif + #ifndef os_init_timer + #define os_init_timer(t, p, f, x, n) rtw_init_timer((t), (p), (f), (x), (n)) + #endif + #ifndef os_set_timer + #define os_set_timer(t, d) rtw_set_timer((t), (d)) + #endif + #ifndef os_cancel_timer + #define os_cancel_timer(t) rtw_cancel_timer(t) + #endif + #ifndef os_del_timer + #define os_del_timer(t) rtw_del_timer(t) + #endif + #ifndef os_atoi + #define os_atoi(s) rtw_atoi(s) + #endif + +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strncpy +#define os_strncpy(d, s, n) strncpy((d), (s), (n)) +#endif +#ifndef os_strrchr +#define os_strrchr(s, c) strrchr((s), (c)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf + #ifdef _MSC_VER + #define os_snprintf _snprintf + #else + #define os_snprintf snprintf + #endif +#endif + +#endif /* OS_NO_C_LIB_DEFINES */ + + +static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size) +{ + if (size && nmemb > (~(size_t) 0) / size) + return NULL; + return os_realloc(ptr, nmemb * size, nmemb * size); +} + +void *os_xqueue_create(unsigned long uxQueueLength, unsigned long uxItemSize) ; + +int os_xqueue_receive(xqueue_handle_t xQueue, void * const pvBuffer, unsigned long xSecsToWait); + +void os_xqueue_delete(xqueue_handle_t xQueue ); + +int os_xqueue_send(xqueue_handle_t xQueue, const void * const pvItemToQueue, unsigned long xSecsToWait); + + +#endif /* OS_H */ diff --git a/project/inc/rtl8195a/queue.h b/project/inc/rtl8195a/queue.h new file mode 100644 index 0000000..a760c8d --- /dev/null +++ b/project/inc/rtl8195a/queue.h @@ -0,0 +1,204 @@ +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#define QMD_SAVELINK(name, link) +#define TRASHIT(x) + +/* + * Singly-linked List declarations. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List functions. + */ +#define SLIST_EMPTY(head) ((head)->slh_first == NULL) + +#define SLIST_FIRST(head) ((head)->slh_first) + +#define SLIST_FOREACH(var, head, field) \ + for ((var) = SLIST_FIRST((head)); \ + (var); \ + (var) = SLIST_NEXT((var), field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST((head)); \ + (var) && ((tvar) = SLIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \ + for ((varp) = &SLIST_FIRST((head)); \ + ((var) = *(varp)) != NULL; \ + (varp) = &SLIST_NEXT((var), field)) + +#define SLIST_INIT(head) do { \ + SLIST_FIRST((head)) = NULL; \ +} while (0) + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \ + SLIST_NEXT((slistelm), field) = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \ + SLIST_FIRST((head)) = (elm); \ +} while (0) + +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.sle_next); \ + if (SLIST_FIRST((head)) == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = SLIST_FIRST((head)); \ + while (SLIST_NEXT(curelm, field) != (elm)) \ + curelm = SLIST_NEXT(curelm, field); \ + SLIST_REMOVE_AFTER(curelm, field); \ + } \ + TRASHIT(*oldnext); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + SLIST_NEXT(elm, field) = \ + SLIST_NEXT(SLIST_NEXT(elm, field), field); \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ + struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ + } + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ + struct { \ + struct type *stqe_next; /* next element */ \ + } + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ + } while (0) + +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ + } while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ + } while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ + TRASHIT(*oldnext); \ + } while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ + } while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + } while (0) + +#define STAILQ_SWAP(head1, head2, type) do { \ + struct type *swap_first = STAILQ_FIRST(head1); \ + struct type **swap_last = (head1)->stqh_last; \ + STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_FIRST(head2) = swap_first; \ + (head2)->stqh_last = swap_last; \ + if (STAILQ_EMPTY(head1)) \ + (head1)->stqh_last = &STAILQ_FIRST(head1); \ + if (STAILQ_EMPTY(head2)) \ + (head2)->stqh_last = &STAILQ_FIRST(head2); \ + } while (0) + +#define STAILQ_INSERT_CHAIN_HEAD(head, elm_chead, elm_ctail, field) do { \ + if ((STAILQ_NEXT(elm_ctail, field) = STAILQ_FIRST(head)) == NULL ) { \ + (head)->stqh_last = &STAILQ_NEXT(elm_ctail, field); \ + } \ + STAILQ_FIRST(head) = (elm_chead); \ + } while (0) + +#endif /* !_SYS_QUEUE_H_ */ diff --git a/project/inc/rtl8195a/rom_wps_os.h b/project/inc/rtl8195a/rom_wps_os.h new file mode 100644 index 0000000..cd41061 --- /dev/null +++ b/project/inc/rtl8195a/rom_wps_os.h @@ -0,0 +1,24 @@ +/* + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef ROM_WPS_OS_H +#define ROM_WPS_OS_H + +#if defined(CONFIG_PLATFORM_8195A) || defined(CONFIG_PLATFORM_8711B) + +#include +extern struct _rom_wlan_ram_map rom_wlan_ram_map; +#define os_malloc(sz) rom_wlan_ram_map.rtw_malloc(sz) +#define os_free(p, sz) rom_wlan_ram_map.rtw_mfree(((u8*)(p)), (sz)) + +#endif + +extern u8 *WPS_realloc(u8 *old_buf, u32 old_sz, u32 new_sz); +#define os_realloc(p, os, ns) WPS_realloc(((u8*)(p)),(os),(ns)) + +#endif /* ROM_WPS_OS_H */ diff --git a/project/inc/rtl8195a/rtl_common.h b/project/inc/rtl8195a/rtl_common.h new file mode 100644 index 0000000..a107e16 --- /dev/null +++ b/project/inc/rtl8195a/rtl_common.h @@ -0,0 +1,15 @@ +/* + * Copyright (C) + * + */ + +#ifndef __RTL_COMMON_H__ +#define __RTL_COMMON_H__ + +#include +#include +#include + +#include "c_types.h" + +#endif diff --git a/project/inc/rtl8195a/rtl_libc.h b/project/inc/rtl8195a/rtl_libc.h new file mode 100644 index 0000000..15733fc --- /dev/null +++ b/project/inc/rtl8195a/rtl_libc.h @@ -0,0 +1,164 @@ +/* +* ROM calls +*/ + +#ifndef _INC_RTL_LIBC_ +#define _INC_RTL_LIBC_ + +//#undef malloc +#define malloc(size) pvPortMalloc(size) +//#undef free +#define free(pbuf) vPortFree(pbuf) + +#define atoi(str) prvAtoi(str) + +#define calloc(nelements, elementSize) calloc_freertos(nelements, elementSize) + +#define snprintf rtl_snprintf +#define sprintf rtl_sprintf +#define printf rtl_printf +#define vprintf rtl_vprintf +#define vsnprintf rtl_vsnprintf +#define vfprintf rtl_vfprintf +#define memchr rtl_memchr +#define memcmp rtl_memcmp +#define memcpy rtl_memcpy +#define memmove rtl_memmove +#define memset rtl_memset +#define strcat rtl_strcat +#define strchr rtl_strchr +#define strcmp rtl_strcmp +#define strcpy rtl_strcpy +#define strlen rtl_strlen +#define strncat rtl_strncat +#define strncmp rtl_strncmp +#define strncpy rtl_strncpy +#define strstr rtl_strstr +#define strsep rtl_strsep +#define strtok rtl_strtok + +#define dtoi rtl_dtoi +#define dtoui rtl_dtoui +#define i2f rtl_i2f +#define i2d rtl_i2d +#define ui2f rtl_ui2f +#define ui2d rtl_ui2d +#define itoa rtl_itoa +#define ltoa rtl_ltoa +#define utoa rtl_utoa +#define ultoa rtl_ultoa +#define ftol rtl_ftol +#define ftod rtl_ftod +#define dtof rtl_dtof +#define fadd rtl_fadd +#define fsub rtl_fsub +#define fmul rtl_fmul +#define fdiv rtl_fdiv +#define dadd rtl_dadd +#define dsub rtl_dsub +#define dmul rtl_dmul +#define ddiv rtl_ddiv +#define dcmpeq rtl_dcmpeq +#define dcmplt rtl_dcmplt +#define dcmple rtl_dcmple +#define dcmpgt rtl_dcmpgt +#define fcmplt rtl_fcmplt +#define fcmpgt rtl_fcmpgt + +#define fabsf rtl_fabsf +#define fabs rtl_fabs +#define cos_f32 rtl_cos_f32 +#define sin_f32 rtl_sin_f32 + + +#if 0 +extern void *calloc_freertos(size_t nelements, size_t elementSize); +// ram_libc.c +extern int rtl_snprintf(char *str, size_t size, const char *fmt, ...); +extern int rtl_sprintf(char *str, const char *fmt, ...); +extern int rtl_printf(const char *fmt, ...); +extern int rtl_vprintf(const char *fmt, void *param); +extern int rtl_vsnprintf(char *str, size_t size, const char *fmt, void *param); +extern int rtl_vfprintf(FILE *fp, const char *fmt0, va_list ap); +extern int rtl_memchr(const void *src_void, int c, size_t length); +extern int rtl_memcmp(const void *m1, const void *m2, size_t n); +extern int rtl_memcpy(void *dst0, const void *src0, size_t len0); +extern int rtl_memmove(void *dst_void, const void *src_void, size_t length); +extern int rtl_memset(void *m, int c, size_t n); +extern char * rtl_strcat(char *s1, const char *s2); +extern char * rtl_strchr(const char *s1, int i); +extern int rtl_strcmp(const char *s1, const char *s2); +extern char * rtl_strcpy(char *dst0, const char *src0); +extern int rtl_strlen(const char *str); +extern char * rtl_strncat(char *s1, const char *s2, size_t n); +extern int rtl_strncmp(const char *s1, const char *s2, size_t n); +extern char * rtl_strncpy(char *dst0, const char *src0, size_t count); +extern char * rtl_strstr(const char *searchee, const char *lookfor); +extern char * rtl_strsep(char **source_ptr, const char *delim); +extern char * rtl_strtok(char *s, const char *delim); + +//rtl_eabi_cast_ram.c +extern int rtl_dtoi(double d); +extern int rtl_dtoui(double d); +extern float rtl_i2f(int val); +extern int rtl_i2d(int val); +extern float rtl_ui2f(unsigned int val); +extern int rtl_ui2d(unsigned int val); +extern char *rtl_itoa(int value, char *string, int radix); +extern char *rtl_ltoa(int value, char *string, int radix); +extern char *rtl_utoa(unsigned int value, char *string, int radix); +extern char *rtl_ultoa(unsigned int value, char *string, int radix); +extern int rtl_ftol(float f); +extern int rtl_ftod(float f); +extern float rtl_dtof(double d); +extern float rtl_fadd(float a, float b); +extern float rtl_fsub(float a, float b); +extern float rtl_fmul(float a, float b); +extern float rtl_fdiv(float a, float b); +extern int rtl_dadd(double a, double b); +extern int rtl_dsub(double a, double b); +extern int rtl_dmul(double a, double b); +extern int rtl_ddiv(double a, double b); +extern int rtl_dcmpeq(double a, double b); +extern int rtl_dcmplt(double a, double b); +extern int rtl_dcmple(double a, double b); +extern int rtl_dcmpgt(double a, double b); +extern int rtl_fcmplt(float a, float b); +extern int rtl_fcmpgt(float a, float b); + +// rtl_math_ram.c +extern float rtl_fabsf(float a); +extern int rtl_fabs(double a); +extern float rtl_cos_f32(float a); +extern float rtl_sin_f32(float a); + +// ram_pvvx_libc.c +extern int snprintf(char *str, size_t size, const char *fmt, ...); +extern int sprintf(char *str, const char *fmt, ...); +extern int printf(const char *fmt, ...); +extern int vprintf(const char *fmt, void *param); +extern int vsnprintf(char *str, size_t size, const char *fmt, void *param); +extern int vfprintf(FILE *fp, const char *fmt0, va_list ap); +extern int memchr(const void *src_void, int c, size_t length); +extern int memcmp(const void *m1, const void *m2, size_t n); +extern int memcpy(void *dst0, const void *src0, size_t len0); +extern int memmove(void *dst_void, const void *src_void, size_t length); +extern int memset(void *m, int c, size_t n); +extern char * strcat(char *s1, const char *s2); +extern char * strchr(const char *s1, int i); +extern int strcmp(const char *s1, const char *s2); +extern char * strcpy(char *dst0, const char *src0); +extern int strlen(const char *str); +extern char * strncat(char *s1, const char *s2, size_t n); +extern int strncmp(const char *s1, const char *s2, size_t n); +extern char * strncpy(char *dst0, const char *src0, size_t count); +extern char * strstr(const char *searchee, const char *lookfor); +extern char * strsep(char **source_ptr, const char *delim); +extern char * strtok(char *s, const char *delim); +extern int sscanf(const char *buf, const char *fmt, ...); +extern char toupper(char ch); +extern int _stricmp (const char *s1, const char *s2); +extern unsigned long long __aeabi_llsr(unsigned long long val, unsigned int shift); +#endif + +#endif // _INC_RTL_LIBC_ diff --git a/project/inc/tcpsrv/tcp_srv_conn.h b/project/inc/tcpsrv/tcp_srv_conn.h new file mode 100644 index 0000000..4468e0f --- /dev/null +++ b/project/inc/tcpsrv/tcp_srv_conn.h @@ -0,0 +1,191 @@ +#ifndef __TCP_SERV_CONN_H__ +/*********************************** + * FileName: tcp_srv_conn.h + * Tcp Ñервачек Ð´Ð»Ñ ESP8266 + * PV` ver1.0 20/12/2014 + * PV` ver1.0 29/10/2016 Ð´Ð»Ñ RTL87xx + ***********************************/ +#define __TCP_SERV_CONN_H__ + +//#include "user_interface.h" +//#include "os_type.h" + +#include "lwip/err.h" + +#define mMIN(a, b) ((a < b)? a : b) + +enum srvconn_state { + SRVCONN_NONE =0, + SRVCONN_CLOSEWAIT, // ожидает Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ + SRVCONN_CLIENT, // уÑтановка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (клиент) + SRVCONN_LISTEN, // Ñоединение открыто, ждет rx или tx + SRVCONN_CONNECT, // Ñоединение открыто, было rx или tx + SRVCONN_CLOSED // Ñоединение закрыто +}; + +// приоритет pcb 1..127 1 - min, 127 - max +#ifndef TCP_SRV_PRIO +#define TCP_SRV_PRIO 99 //TCP_PRIO_MIN +#endif + +// макÑимальное кол-во TCP c TIME_WAIT +#ifndef MAX_TIME_WAIT_PCB +#define MAX_TIME_WAIT_PCB 10 +#endif + +// кол-во одновременно открытых Ñоединений по умолчанию +#ifndef TCP_SRV_MAX_CONNECTIONS + #define TCP_SRV_MAX_CONNECTIONS mMIN(MEMP_NUM_TCP_PCB, 10) +#endif + +// порт Ñервера по умолчанию +#ifndef TCP_SRV_SERVER_PORT + #define TCP_SRV_SERVER_PORT 80 +#endif + +/* уровень вывода отладочной инфы по умолчанию +#ifndef DEBUGSOO + #define DEBUGSOO 2 +#endif +*/ + +// Ð²Ñ€ÐµÐ¼Ñ (Ñек), по умолчанию, Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа (передачи пакета) от клиента, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑоединениÑ, +// при = 0 заменÑетÑÑ Ð½Ð° Ñти 5 Ñек. +#ifndef TCP_SRV_RECV_WAIT + #define TCP_SRV_RECV_WAIT 5 +#endif +// Ð²Ñ€ÐµÐ¼Ñ (Ñек), по умолчанию, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле приема или передачи, +// при = 0 заменÑетÑÑ Ð½Ð° Ñти 5 Ñек. +#ifndef TCP_SRV_END_WAIT + #define TCP_SRV_END_WAIT 5 +#endif + +// Ð²Ñ€ÐµÐ¼Ñ (в Ñек) до повтора Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (иÑполнÑетÑÑ Ð´Ð¾ 3-Ñ… раз). +#define TCP_SRV_CLOSE_WAIT 5 // 5 Ñек + +// минимальный размер heap по умолчанию, при открытии нового ÑоединениÑ, при = 0 заменÑетÑÑ Ð½Ð° Ñто: +#define TCP_SRV_MIN_HEAP_SIZE 14528 // Ñамый минимум от 6Kb + +// макÑимальный размер выделÑемого буфера в heap Ð´Ð»Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð° порции +#ifndef TCP_SRV_SERVER_MAX_RXBUF + #define TCP_SRV_SERVER_MAX_RXBUF (TCP_MSS*3) // 1460*2=2920, 1460*3=4380, 1460*4=5840 +#endif + +// размер выделÑемого буфера в heap Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ при буферизированном выводе +#ifndef TCP_SRV_SERVER_DEF_TXBUF + #define TCP_SRV_SERVER_DEF_TXBUF (TCP_MSS*3) // 1460*2=2920, 1460*3=4380, 1460*4=5840 +#endif + +#define ID_CLIENTS_PORT 3 // до 3-Ñ… clients +#define tcpsrv_init_client1() tcpsrv_init(1) // tcp2uart_client +#define tcpsrv_init_client2() tcpsrv_init(2) // mdb_tcp_client +#define tcpsrv_init_client3() tcpsrv_init(3) + +#define TCP_CLIENT_NEXT_CONNECT_S 5 // syscfg.tcp_client_twait // 5000 // через 5 Ñек +#define TCP_CLIENT_MAX_CONNECT_RETRY 7 // до 7 раз Ñ Ð¸Ð½Ñ‚ÐµÑ€Ð²Ð°Ð»Ð¾Ð¼ TCP_CLIENT_NEXT_CONNECT_MS + +//-------------------------------------------------------------------------- +// Структура ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ +// +typedef struct t_tcpsrv_conn_flags { + uint16 client: 1; //0001 данное Ñоединение не Ñервер, а клиент! + uint16 client_reconnect: 1; //0002 вечный реконнект + uint16 srv_reopen: 1; //0004 открытие нового ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð±Ð¾Ð»ÐµÐµ max_conn ведет к закрытию наиболее Ñтарого ÑоединениÑ. + uint16 pcb_time_wait_free: 1; //0008 проверка на Ð¼Ð°ÐºÑ ÐºÐ¾Ð»-во и уничтожение pcb Ñ TIME_WAIT при вызове disconnect() (иначе pcb TIME_WAIT 60 Ñек http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html) + uint16 nagle_disabled: 1; //0010 выключение nagle + uint16 rx_buf: 1; //0020 прием в буфер, иÑпользуетÑÑ Ñ€ÑƒÑ‡Ð½Ð¾Ðµ управление размером окна TCP + uint16 rx_null: 1; //0040 отключение вызова func_received_data() и прием в null (уÑтанавливаетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки при вызове tcpsrv_disconnect()) + uint16 tx_null: 1; //0080 отключение вызова func_sent_callback() и передача в null (уÑтанавливаетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки при вызове tcpsrv_disconnect()) + uint16 wait_sent: 1; //0100 ожидет завершениÑ/Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ от lwip + uint16 busy_bufo: 1; //0200 идет обработка bufo + uint16 busy_bufi: 1; //0400 идет обработка bufi + // далее идут биты не отноÑÑщиеÑÑ Ðº работе tcp_srv_conn + uint16 user_flg1: 1; //0800 Ð´Ð»Ñ Ð½ÑƒÐ¶Ð´ процедур уровнем выше (иÑпользован в tcp2uart.c) + uint16 user_flg2: 1; //1000 Ð´Ð»Ñ Ð½ÑƒÐ¶Ð´ процедур уровнем выше (пока Ñвободен) + uint16 user_option1: 1; //2000 Ð´Ð»Ñ Ð½ÑƒÐ¶Ð´ процедур обработки переменных (иÑпользован Ð´Ð»Ñ hexdump, xml_mdb в web_int_callbacks.c) + uint16 user_option2: 1; //4000 Ð´Ð»Ñ Ð½ÑƒÐ¶Ð´ процедур обработки переменных (иÑпользован Ð´Ð»Ñ xml_mdb в web_int_callbacks.c) +} __attribute__((packed)) tcpsrv_conn_flags; + +typedef struct t_TCP_SERV_CONN { + volatile tcpsrv_conn_flags flag;//+0 флаги ÑÐ¾ÐµÐ´Ð½ÐµÐ½Ð¸Ñ + enum srvconn_state state; //+4 ÑоÑтоÑние + struct t_TCP_SERV_CFG *pcfg; //+8 указатель на базовую Ñтруктуру Ñервера + uint16 recv_check; //+12 Ñчет тиков ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð² tcpsrv_poll + uint16 remote_port; //+16 номер порта клиента + union { //+20 ip клиента + uint32 dw; + uint8 b[4]; + } remote_ip; + struct t_TCP_SERV_CONN *next; //+24 указатель на Ñледующую Ñтруктуру + struct tcp_pcb *pcb; //+28 указатель на pcb в Lwip + uint8 *pbufo; //+32 указатель на Ñегмент Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°ÐµÐ¼Ñ‹Ð¼Ð¸ данными + uint8 *ptrtx; //+36 указатель на ещё не переданные данные + uint8 *pbufi; //+40 указатель на Ñегмент буфера Ñ Ð¿Ñ€Ð¸Ð½Ð¸Ð¼Ð°ÐµÐ¼Ñ‹Ð¼Ð¸ данными + uint16 sizeo; //+44 размер буфера передачи + uint16 sizei; //+48 размер приемного буфера (кол-во принÑÑ‚Ñ‹Ñ… и ещё не обработанных байт) + uint16 cntro; //+52 кол-во обработанных байт в буфере передачи + uint16 cntri; //+56 кол-во обработанных байт в буфере приема + uint16 unrecved_bytes; //+60 иÑпользуетÑÑ Ð¿Ñ€Ð¸ ручном управлении TCP WIN / This can be used to throttle data reception + // далее идут переменные не отноÑÑщиеÑÑ Ðº работе tcp_srv_conn + uint8 *linkd; //+64 указатель на прилепленные данные Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (при закрытии ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÑ‚ÑÑ os_close(linkd), еÑли linkd != NULL; +} TCP_SERV_CONN; + + +//-------------------------------------------------------------------------- +// Вызываемые функции Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ (calback-и) +// +typedef void (*func_disconect_calback)(TCP_SERV_CONN *ts_conn); // Ñоединение закрыто +typedef err_t (*func_listen)(TCP_SERV_CONN *ts_conn); // новый клиент +typedef err_t (*func_received_data)(TCP_SERV_CONN *ts_conn); // принÑто вÑего ts_conn->sizei байт, лежат в буфере по ts_conn->pbufi, по выходу принимаетÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ð½Ð½Ñ‹Ñ… ts_conn->cntri; +typedef err_t (*func_sent_callback)(TCP_SERV_CONN *ts_conn); // блок данных передан + +//-------------------------------------------------------------------------- +// Структура конфигурации tcp Ñервера +// +typedef struct t_TCP_SERV_CFG { + struct t_tcpsrv_conn_flags flag; // начальные флаги Ð´Ð»Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 port; // номер порта (=1 - client) + uint16 max_conn; // макÑимальное кол-во одновременных Ñоединений, еÑли client = кол-во повторов ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 conn_count; // кол-во текущих Ñоединений, при инициализации пропиÑывает 0, еÑли client = Ñчетчик повторов ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 min_heap; // минимальный размер heap при открытии нового ÑоединениÑ, при = 0 заменÑетÑÑ Ð½Ð° 8192. + uint16 time_wait_rec; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа (передачи пакета) от клиента, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑоединениÑ, по умолчанию TCP_SRV_RECV_WAIT Ñек. + uint16 time_wait_cls; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле приема или передачи, по умолчанию TCP_SRV_END_WAIT Ñек. + TCP_SERV_CONN * conn_links; // указатель на цепочку активных Ñоединений, при инициализации или отÑуÑтвии активных Ñоединений = NULL + struct tcp_pcb *pcb; // начальный pcb [LISTEN] еÑли Ñервер, иначе NULL + func_disconect_calback func_discon_cb; // Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ð¾Ñле Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑоединениÑ, еÑли = NULL - не вызываетÑÑ + func_listen func_listen; // Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ñ€Ð¸ приÑоединении клиента или коннекта к Ñерверу, еÑли = NULL - не вызываетÑÑ + func_sent_callback func_sent_cb; // Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ð¾Ñле передачи данных или наличию меÑта в ip Ñтеке Ð´Ð»Ñ Ñледушей передачи данных, еÑли = NULL - не вызываетÑÑ (+Ñм. флаги) + func_received_data func_recv; // Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ñ€Ð¸ приеме данных, еÑли = NULL - не вызываетÑÑ (+Ñм. флаги) + struct t_TCP_SERV_CFG *next; // Ñледующий ÑкземплÑÑ€ Ñтруктуры Ñервера/клиента +}TCP_SERV_CFG; +//-------------------------------------------------------------------------- +// Данные +// +extern TCP_SERV_CFG *phcfg; // указатель на цепочку TCP_SERV_CFG (Ñтартовавших Ñерверов) +//-------------------------------------------------------------------------- +// Функции +// +err_t tcpsrv_int_sent_data(TCP_SERV_CONN * ts_conn, uint8 *psent, uint16 length); // передать length байт (Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ - никаких проверок) +void tcpsrv_disconnect(TCP_SERV_CONN * ts_conn); // закрыть Ñоединение +void tcpsrv_print_remote_info(TCP_SERV_CONN *ts_conn); // выводит remote_ip:remote_port [conn_count] os_printf("srv x.x.x.x:x [n] ") +TCP_SERV_CFG * tcpsrv_server_port2pcfg(uint16 portn); // поиÑк Ñтруктуры конфига по номеру порта +TCP_SERV_CFG * tcpsrv_client_ip_port2conn(uint32 ip, uint16 portn); // поиÑк Ñтруктуры конфига по номеру порта Ð´Ð»Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð° +void tcpsrv_unrecved_win(TCP_SERV_CONN *ts_conn); // ВоÑÑтановить размер TCP WIN, еÑли иÑпользуетÑÑ Ñ€ÑƒÑ‡Ð½Ð¾Ðµ управление размером окна TCP + +void tcpsrv_disconnect_calback_default(TCP_SERV_CONN *ts_conn); +err_t tcpsrv_listen_default(TCP_SERV_CONN *ts_conn); +err_t tcpsrv_sent_callback_default(TCP_SERV_CONN *ts_conn); +err_t tcpsrv_received_data_default(TCP_SERV_CONN *ts_conn); + +TCP_SERV_CFG *tcpsrv_init(uint16 portn); +err_t tcpsrv_start(TCP_SERV_CFG *p); +err_t tcpsrv_client_start(TCP_SERV_CFG * p, uint32 remote_ip, uint16 remote_port); +err_t tcpsrv_close(TCP_SERV_CFG *p); +err_t tcpsrv_close_port(uint16 portn); +err_t tcpsrv_close_all(void); + +char * tspsrv_error_msg(err_t err); +char * tspsrv_tcp_state_msg(enum tcp_state state); +char * tspsrv_srvconn_state_msg(enum srvconn_state state); + +#endif // __TCP_SERV_CONN_H__ diff --git a/project/inc/user/main.h b/project/inc/user/main.h new file mode 100644 index 0000000..d3cdef6 --- /dev/null +++ b/project/inc/user/main.h @@ -0,0 +1,68 @@ +#ifndef MAIN_H +#define MAIN_H + +#include + +#ifndef CONFIG_WLAN +#define CONFIG_WLAN 1 +#endif + +/* Header file declaration*/ +void wlan_network(); + +/* Interactive Mode */ +#define SERIAL_DEBUG_RX 1 + + +#define ATVER_1 1 // For First AT command +#define ATVER_2 2 // For UART Module AT command + +#if CONFIG_EXAMPLE_UART_ATCMD +#define ATCMD_VER ATVER_2 +#else +#define ATCMD_VER ATVER_1 +#endif + + + +/*Static IP ADDRESS*/ +#define IP_ADDR0 192 +#define IP_ADDR1 168 +#define IP_ADDR2 3 +#define IP_ADDR3 80 + +/*NETMASK*/ +#define NETMASK_ADDR0 255 +#define NETMASK_ADDR1 255 +#define NETMASK_ADDR2 255 +#define NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define GW_ADDR0 192 +#define GW_ADDR1 168 +#define GW_ADDR2 3 +#define GW_ADDR3 1 + +/*******************************************/ + +/*Static IP ADDRESS*/ +#define AP_IP_ADDR0 192 +#define AP_IP_ADDR1 168 +#define AP_IP_ADDR2 43 +#define AP_IP_ADDR3 1 + +/*NETMASK*/ +#define AP_NETMASK_ADDR0 255 +#define AP_NETMASK_ADDR1 255 +#define AP_NETMASK_ADDR2 255 +#define AP_NETMASK_ADDR3 0 + +/*Gateway Address*/ +#define AP_GW_ADDR0 192 +#define AP_GW_ADDR1 168 +#define AP_GW_ADDR2 43 +#define AP_GW_ADDR3 1 + + + +#endif diff --git a/project/inc/user/sys_cfg.h b/project/inc/user/sys_cfg.h new file mode 100644 index 0000000..7b9117e --- /dev/null +++ b/project/inc/user/sys_cfg.h @@ -0,0 +1,64 @@ +/* + * sys_cfg.h + * + * Created on: 17/04/17 + * Author: pvvx + */ +#ifndef __SYS_CFG_H__ +#define __SYS_CFG_H__ +#include "user_config.h" +#define FEEP_ID_SYS_CFG 0x5359 + +struct sys_bits_config { + uint16 hi_speed_enable : 1; //0 0x0000001 =1 Set CPU 160 MHz ... + uint16 pin_clear_cfg_enable : 1; //1 0x0000002 =1 ПроверÑÑ‚ÑŒ ножку RX на ÑÐ±Ñ€Ð¾Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ WiFi + uint16 debug_print_enable : 1; //2 0x0000004 =1 Вывод отладочной информации на GPIO2 + uint16 web_time_wait_delete : 1; //3 0x0000008 =1 Закрывать Ñоединение и убивать pcb c TIME_WAIT + uint16 netbios_ena : 1; //4 0x0000010 =1 включить NetBios + uint16 sntp_ena : 1; //5 0x0000020 =1 включить SNTP + uint16 cdns_ena : 1; //6 0x0000040 =1 включить CAPDNS + uint16 tcp2uart_reopen : 1; //7 0x0000080 =1 открытие нового ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ tcp2uart ведет к закрытию Ñтарого ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (в режиме tcp2uart = Ñервер) + uint16 mdb_reopen : 1; //8 0x0000100 =1 открытие нового ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ modbus ведет к закрытию Ñтарого ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (в режиме modbus = Ñервер) +}; + +#define SYS_CFG_HI_SPEED 0x0000001 // Set CPU 160 MHz ... +#define SYS_CFG_PIN_CLR_ENA 0x0000002 // ПроверÑÑ‚ÑŒ ножку RX на ÑÐ±Ñ€Ð¾Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ð¸ WiFi +#define SYS_CFG_DEBUG_ENA 0x0000004 // Вывод отладочной информации на GPIO2 +#define SYS_CFG_TWPCB_DEL 0x0000008 // Закрывать Ñоединение и убивать pcb c TIME_WAIT +#define SYS_CFG_NETBIOS_ENA 0x0000010 // включить NetBios +#define SYS_CFG_SNTP_ENA 0x0000020 // включить SNTP +#define SYS_CFG_CDNS_ENA 0x0000040 // включить CAPDNS +#define SYS_CFG_T2U_REOPEN 0x0000080 // открытие нового ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ tcp2uart ведет к закрытию Ñтарого ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (Ñервер) +#define SYS_CFG_MDB_REOPEN 0x0000100 // открытие нового ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ modbus ведет к закрытию Ñтарого ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (Ñервер) + + +struct SystemCfg { // Ñтруктура ÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ ÑиÑтемных наÑтроек в Flash + union { + struct sys_bits_config b; + uint16 w; + }cfg; + uint16 tcp_client_twait; // Ð²Ñ€ÐµÐ¼Ñ (миллиÑек) до повтора ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ ÐºÐ»Ð¸ÐµÐ½Ñ‚Ð° +#ifdef USE_TCP2UART + uint16 tcp2uart_port; // номер порта TCP-UART (=0 - отключен) + uint16 tcp2uart_twrec; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) Ñтартового Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð°/передачи первого пакета, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 tcp2uart_twcls; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле приема или передачи +#endif +#ifdef USE_WEB + uint16 web_port; // номер порта WEB (=0 - отключен) + uint16 web_twrec; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) Ñтартового Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð°/передачи первого пакета, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 web_twcls; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле приема или передачи +#endif +#ifdef USE_MODBUS + uint16 mdb_port; // =0 - отключен + uint16 mdb_twrec; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) Ñтартового Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð°/передачи первого пакета, до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint16 mdb_twcls; // Ð²Ñ€ÐµÐ¼Ñ (Ñек) до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð¿Ð¾Ñле приема или передачи + uint8 mdb_id; // номер уÑтройÑтва ESP8266 по шине modbus +#endif +} __attribute__((packed)); + + +extern void sys_write_cfg(void); + +extern struct SystemCfg syscfg; + +#endif // #define __SYS_CFG_H__ diff --git a/project/inc/user_config.h b/project/inc/user_config.h new file mode 100644 index 0000000..d628f15 --- /dev/null +++ b/project/inc/user_config.h @@ -0,0 +1,34 @@ +#ifndef _user_config_h_ +#define _user_config_h_ + +//#include "sdk/sdk_config.h" + +#define SYS_VERSION "1.0.0" +#define SDK_VERSION "3.5.3" + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð° MODBUS-RS-485 +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#define USE_WEB 80 // включить в транÑÑлцию порт Web, еÑли =0 - по умолчанию выключен +#define WEBSOCKET_ENA 1 // включить WEBSOCKET + +//#define USE_CPU_SPEED 166 // уÑтановить чаÑтоту CPU (по умолчанию 83) +/* +#define USE_NETBIOS 1 // включить в транÑÑлцию драйвер NETBIOS, еÑли =0 - по умолчанию выключен. + +#define USE_SNTP 1 // включить в транÑÑлцию драйвер SNTP, еÑли =0 - по умолчанию выключен, = 1 - по умолчанию включен. + +#define USE_RS485DRV // иÑпользовать RS-485 драйвер +#define MDB_RS485_MASTER // Modbus RTU RS-485 master & slave +#define USE_MODBUS 502 // включить в транÑÑлцию Modbus TCP, еÑли =0 - по умолчанию выключен +#define MDB_ID_ESP 50 // номер уÑтройÑтва RTL на шине modbus + +//#define USE_CAPTDNS 0 // включить в транÑÑлцию DNS отвечающий на вÑÑ‘ запроÑÑ‹ клиента при Ñоединении к AP Ð¼Ð¾Ð´ÑƒÐ»Ñ + // указанием на данный WebHttp (http://aesp8266/), еÑли =0 - по умолчанию выключен + +*/ + +#endif // _user_config_h_ + + diff --git a/project/inc/web/web_srv.h b/project/inc/web/web_srv.h new file mode 100644 index 0000000..0126cec --- /dev/null +++ b/project/inc/web/web_srv.h @@ -0,0 +1,196 @@ +/* + * File: web_srv.h + * Description: The web server configration. + * Small WEB server ESP8266EX + * Author: PV` + */ + +#ifndef _INCLUDE_WEB_SRV_H_ +#define _INCLUDE_WEB_SRV_H_ + +#include "tcpsrv/tcp_srv_conn.h" +#ifdef WEBSOCKET_ENA +#include "websock.h" +#endif + +#define WEB_SVERSION "0.2.0" +#define DEFAULT_WEB_PORT USE_WEB // 80 + +/**************************************************************************** + Section: + Commands and Server Responses + ***************************************************************************/ + +// File type definitions +typedef enum +{ + HTTP_TXT = 0, // File is a text document + HTTP_HTML, // File is HTML (extension .htm) + HTTP_CGI, // File is HTML (extension .cgi) + HTTP_XML, // File is XML (extension .xml) + HTTP_CSS, // File is stylesheet (extension .css) + HTTP_ICO, // File is ICO vnd.microsoft.icon + HTTP_GIF, // File is GIF image (extension .gif) + HTTP_PNG, // File is PNG image (extension .png) + HTTP_JPG, // File is JPG image (extension .jpg) + HTTP_SVG, // File is SVG image (extension .svg) + HTTP_JAVA, // File is java (extension .js) + HTTP_SWF, // File is ShockWave-Flash (extension .swf) + HTTP_WAV, // File is audio (extension .wav) + HTTP_PDF, // File is PDF (extension .pdf) + HTTP_ZIP, // File is ZIP (extension .zip) + HTTP_BIN, // File is BIN (extension .bin) + HTTP_UNKNOWN // File type is unknown +} HTTP_FILE_TYPE; + + +// extended state data for each connection +#define FileNameSize 64 +#define VarNameSize 64 +#define CmdNameSize 32 + +typedef struct +{ + uint16 httpStatus; // Request method/status + uint16 uri_len; + uint16 head_len; + uint16 cookie_len; + uint8 pFilename[FileNameSize]; + uint8 *puri; // указатель на Ñтроку Ñ Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ñ‹Ð¼Ð¸ запроÑа к файлу + uint8 *phead; // HTTP Headers + uint8 *pcookie; // cookie + uint8 *pcontent; // content + uint32 content_len; // + uint8 httpver; // верÑÐ¸Ñ HTTP клиента в BCD (0x00 = неизвеÑтен; 0x09 = HTTP/0.9; 0x10 = HTTP/1.0; 0x11 = HTTP/1.1) + uint8 fileType; // File type to return with Content-Type +} HTTP_CONN; + + +typedef void (* web_func_cb)(TCP_SERV_CONN *ts_conn); +typedef uint32 (* web_func_disc_cb)(uint32 flg); // Ð¾Ñ‚Ð»Ð¾Ð¶ÐµÐ½Ð½Ð°Ñ Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ, когда Ñоединение закрыто + +typedef struct +{ + web_func_disc_cb fnk; + void * param; +} WEB_SRV_QFNK; + +typedef struct +{ + uint32 webflag; // флаги Ð´Ð»Ñ http/web Ñервера + uint8 bffiles[4]; // четыре Files pointers Ð´Ð»Ñ Ð¾Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ вложенных файлов include + uint32 udata_start; // udata "start=0x..." + uint32 udata_stop; // udata "stop=0x..." + uint8 *msgbuf; // указатель на текущий буфер вывода + uint16 msgbuflen; // кол-во занÑÑ‚Ñ‹Ñ… байт в буфере msgbuf + uint16 msgbufsize; // размер буфера + web_func_cb func_web_cb; // calback Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ñƒ httpd Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ приема/передачи куÑками + uint32 content_len; // размер файла Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ (GET/POST) или приема, еÑли принимаетÑÑ Ð²Ð½ÐµÑˆÐ½Ð¸Ð¹ файл (POST + SCB_RXDATA) + web_func_disc_cb web_disc_cb; // Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð²Ñ‹Ð·Ñ‹Ð²Ð°ÐµÐ¼Ð°Ñ Ð¿Ð¾ закрытию ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + uint32 web_disc_par; // параметры функции вызываемой по закрытию ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ +#ifdef WEBSOCKET_ENA + WS_FRSTAT ws; // параметры websoc +#endif + uint8 fileType; // File type to return with Content-Type (if SCB_FCALBACK) +} WEB_SRV_CONN; + +typedef enum +{ + WEBFS_MAX_HANDLE = 251, + WEBFS_NODISK_HANDLE, + WEBFS_WEBCGI_HANDLE, + WEBFS_UPLOAD_HANDLE +} WEBFS_NUM_HANDLE; + +// webflag: + +#define SCB_CLOSED 0x000001 // Ñоединение закрыто +#define SCB_DISCONNECT 0x000002 // выход на DISCONNECT +#define SCB_FCLOSE 0x000004 // закрыть файлы +#define SCB_FOPEN 0x000008 // файл(Ñ‹) открыт(Ñ‹) +#define SCB_FCALBACK 0x000010 // file use ~calback~ +#define SCB_FGZIP 0x000020 // файл GZIP +#define SCB_CHUNKED 0x000040 // передача шинковкой +#define SCB_RETRYCB 0x000080 // вызвать повтор CalBack +#define SCB_POST 0x000100 // POST +#define SCB_GET 0x000200 // GET +#define SCB_AUTH 0x000400 // необходима Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ +#define SCB_FINDCB 0x000800 // иÑпользуетÑÑ Ð¿Ð°Ñ€Ñингом ~calback~ +#define SCB_RXDATA 0x001000 // прием данных (файла) +#define SCB_HEAD_OK 0x002000 // заголовок HTTP принÑÑ‚ и обработан +#define SCB_BNDR 0x004000 // прилеплен Content-Type: multipart/form-data; boundary="..." +#define SCB_REDIR 0x008000 // Redirect 302 +#define SCB_WEBSOC 0x010000 // WebSocket +#define SCB_WSDATA 0x020000 // WebSocket data +#define SCB_SYSSAVE 0x040000 // по закрытию ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð·Ð²Ð°Ñ‚ÑŒ sys_write_cfg() + + +#define SCB_OPEN 0 + +#define SetSCB(a) web_conn->webflag |= a +#define FreeSCB() web_conn->webflag = SCB_FREE +#define SetNextFunSCB(a) web_conn->func_web_cb = a +#define ClrSCB(a) web_conn->webflag &= ~(a) +#define CheckSCB(a) (web_conn->webflag & (a)) + +#define FreeSCB() web_conn->webflag = SCB_FREE +#define OpenSCB() web_conn->webflag = SCB_OPEN + +#define MAXLENBOUNDARY 64 +typedef struct s_http_upload +{ + uint16 status; + uint16 sizeboundary; + uint8 boundary[MAXLENBOUNDARY+1]; + uint8 name[VarNameSize]; + uint8 filename[VarNameSize]; +#ifdef USE_OVERLAY + uint32 segs; // кол-во Ñегментов Ð¾Ð²ÐµÑ€Ð»ÐµÑ // пока в web_conn->web_disc_par + uint32 start; // Ð°Ð´Ñ€ÐµÑ Ð·Ð°Ð¿ÑƒÑка Ð¾Ð²ÐµÑ€Ð»ÐµÑ +#endif + uint32 fsize; + uint32 faddr; + uint8 *pbndr; + uint8 *pnext; +} HTTP_UPLOAD; + +typedef struct s_http_response +{ + uint32 status; + uint32 flag; + const char * headers; + const char * default_content; +} HTTP_RESPONSE; + +// HTTP_RESPONSE.flags: +#define HTTP_RESP_FLG_END 0x8000 +#define HTTP_RESP_FLG_NONE 0x0000 +#define HTTP_RESP_FLG_FINDFILE 0x0001 +#define HTTP_RESP_FLG_REDIRECT 0x0002 + +#define tcp_put(a) web_conn->msgbuf[web_conn->msgbuflen++] = a +#define tcp_htmlstrcpy(str, len) web_conn->msgbuflen += htmlcode(&web_conn->msgbuf[web_conn->msgbuflen], str, web_conn->msgbufsize - web_conn->msgbuflen - 1, len) +//#define tcp_urlstrcpy(str, len) web_conn->msgbuflen += urlencode(&web_conn->msgbuf[web_conn->msgbuflen], str, web_conn->msgbufsize - web_conn->msgbuflen - 1, len) +#define tcp_puts(...) web_conn->msgbuflen += rtl_sprintf((char *)&web_conn->msgbuf[web_conn->msgbuflen], __VA_ARGS__) +#define tcp_puts_fd(...) web_conn->msgbuflen += rtl_sprintf((char *)&web_conn->msgbuf[web_conn->msgbuflen], __VA_ARGS__) +/* +#define tcp_puts_fd(fmt, ...) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \ + web_conn->msgbuflen += rtl_sprintf((char *)&web_conn->msgbuf[web_conn->msgbuflen], (char *)flash_str, ##__VA_ARGS__); \ + } while(0) +*/ +//#define tcp_strcpy(a) web_conn->msgbuflen += ets_strlen((char *)ets_strcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], (char *)a)) +#define tcp_strcpy(a) web_conn->msgbuflen += rom_xstrcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], (const char *)a) +#define tcp_strcpy_fd(a) web_conn->msgbuflen += rom_xstrcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], (const char *)a) +/* +#define tcp_strcpy_fd(fmt) do { \ + static const char flash_str[] ICACHE_RODATA_ATTR = fmt; \ + web_conn->msgbuflen += rom_xstrcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], (char *)flash_str); \ + } while(0) +*/ +uint32 ahextoul(uint8 *s); +err_t webserver_init(uint16 portn); +err_t webserver_close(uint16 portn); +err_t webserver_reinit(uint16 portn); + +#endif /* _INCLUDE_WEB_SRV_H_ */ diff --git a/project/inc/web/web_srv_int.h b/project/inc/web/web_srv_int.h new file mode 100644 index 0000000..8606dc0 --- /dev/null +++ b/project/inc/web/web_srv_int.h @@ -0,0 +1,49 @@ +/* + * File: web_srv_int.h + * Description: The web server configration. + * Small WEB server ESP8266EX + * + * Author: PV` 12/2014 + */ + +#ifndef _INCLUDE_WEB_SRV_INT_H_ +#define _INCLUDE_WEB_SRV_INT_H_ + +#include "web_srv.h" + +#define WEB_NAME_VERSION "PVs/0.2" + +// #define WEBSOCKET_ENA 1 + +// lifetime (sec) of static responses as string 60*60*24*14=1209600" +#define FILE_CACHE_MAX_AGE_SEC 3600 // Ð²Ñ€ÐµÐ¼Ñ Ð´Ð»Ñ ÐºÐµÑˆÐ° файлов, Ñтавить 0 пока теÑÑ‚! + +#define MAX_HTTP_HEAD_BUF 3070 // макÑимальный размер HTTP запроÑа (GET) + +#define RESCHKS_SEND_SIZE 16 +#define RESCHKE_SEND_SIZE 8 +#define RESCHK_SEND_SIZE (RESCHKS_SEND_SIZE + RESCHKE_SEND_SIZE) + +#define MIN_SEND_SIZE (256 + RESCHK_SEND_SIZE) // минимальный размер буфера Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ файла +#define MAX_SEND_SIZE ((TCP_MSS*4) + RESCHK_SEND_SIZE) // макÑимальный размер буфера Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ 4*MSS = 5840 (MSS=1460) + +#define HTTP_SEND_SIZE 384 // минимальный размер буфера Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ заголовка HTTP +#define SCB_SEND_SIZE 128 // минимальный резерв в буфере Ð´Ð»Ñ callback + +#define webfile bffiles[0] // File pointer for main file + +//----------------------------------------------------------------------------- + +void web_int_vars(TCP_SERV_CONN *ts_conn, uint8 *pcmd, uint8 *pvar); +void web_int_cookie(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn); +void web_int_callback(TCP_SERV_CONN *ts_conn, uint8 *cstr); + +void web_hexdump(TCP_SERV_CONN *ts_conn); +bool web_inc_fopen(TCP_SERV_CONN *ts_conn, uint8 *cFile); +bool web_inc_fclose(WEB_SRV_CONN *web_conn); + +bool web_trim_bufi(TCP_SERV_CONN *ts_conn, uint8 *pdata, uint32 data_len); +bool web_feee_bufi(TCP_SERV_CONN *ts_conn); +//uint8 * head_find_ctr(HTTP_CONN *CurHTTP, const uint8 * c, int clen, int dlen); + +#endif /* _INCLUDE_WEB_SRV_INT_H_ */ diff --git a/project/inc/web/web_utils.h b/project/inc/web/web_utils.h new file mode 100644 index 0000000..b96d1c1 --- /dev/null +++ b/project/inc/web/web_utils.h @@ -0,0 +1,33 @@ + /****************************************************************************** + * FileName: web_utils.h + * Alternate SDK + * Author: PV` + * (c) PV` 2015 +*******************************************************************************/ + +#ifndef _INCLUDE_WEB_UTILS_H_ +#define _INCLUDE_WEB_UTILS_H_ + +int rom_atoi(const char *s); +void copy_align4(void *ptrd, void *ptrs, uint32 len); +uint32 hextoul(uint8 *s); +uint32 ahextoul(uint8 *s); +uint8 * cmpcpystr(uint8 *pbuf, uint8 *pstr, uint8 a, uint8 b, uint16 len); +uint8 * web_strnstr(const uint8* buffer, const uint8* token, int n); +bool base64decode(const uint8 *in, int len, uint8_t *out, int *outlen); +size_t base64encode(char* target, size_t target_len, const char* source, size_t source_len); +void strtomac(uint8 *s, uint8 *macaddr); +//uint32 strtoip(uint8 *s); // ipaddr_addr(); +int urldecode(uint8 *d, uint8 *s, uint16 lend, uint16 lens); +//int urlencode(uint8 *d, uint8 *s, uint16 lend, uint16 lens); +int htmlcode(uint8 *d, uint8 *s, uint16 lend, uint16 lens); +void print_hex_dump(uint8 *buf, uint32 len, uint8 k); +// char* str_to_upper_case(char* text); +uint32 str_array(uint8 *s, uint32 *buf, uint32 max_buf); +uint32 str_array_w(uint8 *s, uint16 *buf, uint32 max_buf); +uint32 str_array_b(uint8 *s, uint8 *buf, uint32 max_buf); +char* word_to_lower_case(char* text); +int rom_xstrcmp(char * pd, const char * ps); +int rom_xstrcpy(char * pd, const char * ps); + +#endif /* _INCLUDE_WEB_UTILS_H_ */ diff --git a/project/inc/web/web_websocket.h b/project/inc/web/web_websocket.h new file mode 100644 index 0000000..bb4c4cc --- /dev/null +++ b/project/inc/web/web_websocket.h @@ -0,0 +1,17 @@ +#ifndef _WEB_WEBSOCKET_H_ +/****************************************************************************** + * FileName: web_websocket.h + * Description: websocket for web ESP8266 + * Author: PV` + * (c) PV` 2016 +*******************************************************************************/ +#define _WEB_WEBSOCKET_H_ +#include "user_config.h" +#ifdef WEBSOCKET_ENA +#include "websock.h" + +err_t websock_tx_close_err(TCP_SERV_CONN *ts_conn, uint32 err); +bool websock_rx_data(TCP_SERV_CONN *ts_conn); + +#endif // WEBSOCKET_ENA +#endif /* _WEB_WEBSOCKET_H_ */ diff --git a/project/inc/web/websock.h b/project/inc/web/websock.h new file mode 100644 index 0000000..969837e --- /dev/null +++ b/project/inc/web/websock.h @@ -0,0 +1,108 @@ +/* + * File: websock.h + * Small WEB server ESP8266EX + * Author: PV` + */ + +#ifndef _WEBSOCK_H_ +#define _WEBSOCK_H_ + +//#define WS_NONBLOCK 0x02 + +/* + 0 1 2 3 + 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| опкод |Ðœ| Длина тела | РаÑÑˆÐ¸Ñ€ÐµÐ½Ð½Ð°Ñ Ð´Ð»Ð¸Ð½Ð° тела | + |I|S|S|S|(4бита)|Ð| (7бит) | (2 байта) | + |N|V|V|V| |С| |(еÑли длина тела==126 или 127) | + | |1|2|3| |К| | | + | | | | | |Ð| | | + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + |4 5 6 7 | + | Продолжение раÑширенной длины тела, еÑли длина тела = 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + |8 9 10 11 | + | | Ключ маÑки, еÑли ÐœÐСКР= 1 | + +-------------------------------+-------------------------------+ + |12 13 14 15 | + | Ключ маÑки (продолжение) | Данные фрейма ("тело") | + +-------------------------------- - - - - - - - - - - - - - - - + + |16 17 18 19 | + : Данные продолжаютÑÑ ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Данные продолжаютÑÑ ... | + +---------------------------------------------------------------+ + +*/ + +#define WS_FRAGMENT_FIN 0x80 +//#define WS_RSVD_BITS (7 << 4) +#define WS_OPCODE_BITS 0x7F +#define WS_OPCODE_CONTINUE 0x0 // фрейм-продолжение Ð´Ð»Ñ Ñ„Ñ€Ð°Ð³Ð¼ÐµÐ½Ñ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð½Ð¾Ð³Ð¾ ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ +#define WS_OPCODE_TEXT 0x1 // текÑтовый фрейм +#define WS_OPCODE_BINARY 0x2 // двоичный фрейм +#define WS_OPCODE_CLOSE 0x8 // закрытие ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ +#define WS_OPCODE_PING 0x9 +#define WS_OPCODE_PONG 0xa +#define WS_MASK_FLG (1 << 7) +#define WS_SIZE1_BITS 0x7F + + +#define WS_CLOSE_NORMAL 1000 +#define WS_CLOSE_GOING_AWAY 1001 +#define WS_CLOSE_PROTOCOL_ERROR 1002 +#define WS_CLOSE_NOT_ALLOWED 1003 +#define WS_CLOSE_RESERVED 1004 +#define WS_CLOSE_NO_CODE 1005 +#define WS_CLOSE_DIRTY 1006 +#define WS_CLOSE_WRONG_TYPE 1007 +#define WS_CLOSE_POLICY_VIOLATION 1008 +#define WS_CLOSE_MESSAGE_TOO_BIG 1009 +#define WS_CLOSE_UNEXPECTED_ERROR 1011 + +typedef struct _WS_FRSTAT +{ + uint32 frame_len; // размер данных в заголовке фрейма + uint32 cur_len; // Ñчетчик обработанных данных + union { + unsigned char uc[4]; + unsigned int ud; + } mask; // маÑка принимаемых данных + uint8 status; + uint8 flg; + uint8 head_len; +} WS_FRSTAT; // __attribute__((packed)) + +#define WS_FLG_MASK 0x01 +#define WS_FLG_FIN 0x02 +#define WS_FLG_CLOSE 0x04 // уже передано WS_CLOSE + +enum WS_FRAME_STATE { + sw_frs_none = 0, + sw_frs_text, + sw_frs_binary, + sw_frs_close, + sw_frs_ping, + sw_frs_pong +}; + +extern const uint8 WebSocketHTTPOkKey[]; // ICACHE_RODATA_ATTR = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept:%s\r\n\r\n" +extern const uint8 WebSocketAddKey[]; // ICACHE_RODATA_ATTR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +#define sizeWebSocketAddKey 36 +#define maxsizeWebSocketKey 64 +#define minsizeWebSocketKey 8 +extern const uint8 *HTTPUpgrade; // = "Upgrade:"; +#define sizeHTTPUpgrade 8 +extern const uint8 *HTTPwebsocket; // = "websocket"; +#define sizeHTTPwebsocket 9 +extern const uint8 *HTTPSecWebSocketKey; // = "Sec-WebSocket-Key:"; +#define sizeHTTPSecWebSocketKey 18 + + +bool WebSocketAcceptKey(uint8* dkey, uint8* skey); +void WebsocketMask(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len); +uint32 WebsocketHead(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len); +err_t WebsocketTxFrame(TCP_SERV_CONN *ts_conn, uint32 opcode, uint8 *raw_data, uint32 raw_len); + +#endif /* _WEBSOCK_H_ */ diff --git a/project/inc/webfs/webfs.h b/project/inc/webfs/webfs.h new file mode 100644 index 0000000..d7c6c23 --- /dev/null +++ b/project/inc/webfs/webfs.h @@ -0,0 +1,116 @@ +/********************************************************************* + * + * FileName: WEBFS.h + * Basis of MPFS2 (Microchip File System). + * WEBFS has its differences Based. + * + ********************************************************************/ +#ifndef __WEBFS1_H +#define __WEBFS1_H + +#include "flash_eep.h" + +extern uint32 _irom0_text_end; + +#define WEBFS_DISK_FADDR 0x0D0000 + +#define WEBFS_DISK_ID 0x42455746 // заголовок WEBFiles.bin +#define WEBFS_DISK_VER 0x0001 // верÑÐ¸Ñ + +#define MAX_FILE_NAME_SIZE 64 + +#ifndef MAX_WEBFS_OPENFILES + #define MAX_WEBFS_OPENFILES 31 +#endif + + #define WEBFS_FLAG_ISZIPPED 0x0001 // Indicates a file is compressed with GZIP compression + #define WEBFS_FLAG_HASINDEX 0x0002 // Indicates a file has an associated index of dynamic variables + #define WEBFS_INVALID 0xffffffff // Indicates a position pointer is invalid + #define WEBFS_INVALID_FAT 0xffff // Indicates an invalid FAT cache + typedef uint32 WEBFS_PTR; // WEBFS Pointers are currently uint32s + typedef uint8 WEBFS_HANDLE; // WEBFS Handles are currently stored as uint8s + #define WEBFS_INVALID_HANDLE 0xff // Indicates that a handle is not valid + + + // Stores each file handle's information + // Handles are free when addr = WEBFS_INVALID + typedef struct + { + WEBFS_PTR addr; // Current address in the file system + uint32 bytesRem; // How many uint8s remain in this file +#ifdef USE_MAX_IRAM + uint32 fatID; // ID of which file in the FAT was accessed +#else + uint16 fatID; // ID of which file in the FAT was accessed +#endif + } WEBFS_STUB; + + // Indicates the method for WEBFSSeek + typedef enum + { + WEBFS_SEEK_START = 0, // Seek forwards from the front of the file + WEBFS_SEEK_END, // Seek backwards from the end of the file + WEBFS_SEEK_FORWARD, // Seek forward from the current position + WEBFS_SEEK_REWIND // See backwards from the current position + } WEBFS_SEEK_MODE; + + + typedef struct __attribute__((packed)) + { + uint32 id; + uint16 ver; + uint16 numFiles; + uint32 disksize; + } WEBFS_DISK_HEADER ; + + typedef struct __attribute__((packed)) + { + uint32 blksize; // Length of file data - headlen + uint16 headlen; // headlen (Length of File Name + 0) + uint16 flags; // Flags for this file + } WEBFS_FHEADER; + + // Stores the data for an WEBFS1 FAT record + typedef struct + { + uint32 string; // Pointer to the file name + uint32 data; // Address of the file data + uint32 len; // Length of file data +#ifdef USE_MAX_IRAM + uint32 flags; // Flags for this file +#else + uint16 flags; // Flags for this file +#endif + } WEBFS_FAT_RECORD ; + + +void WEBFSInit(void); +WEBFS_HANDLE WEBFSOpen(uint8* cFile); +void WEBFSClose(WEBFS_HANDLE hWEBFS); + +uint16 WEBFSGetArray(WEBFS_HANDLE hWEBFS, uint8* cData, uint16 wLen); + +uint16 WEBFSGetFlags(WEBFS_HANDLE hWEBFS); +uint32 WEBFSGetSize(WEBFS_HANDLE hWEBFS); +uint32 WEBFSGetBytesRem(WEBFS_HANDLE hWEBFS); +WEBFS_PTR WEBFSGetStartAddr(WEBFS_HANDLE hWEBFS); +WEBFS_PTR WEBFSGetEndAddr(WEBFS_HANDLE hWEBFS); +bool WEBFSGetFilename(WEBFS_HANDLE hWEBFS, uint8* cName, uint16 wLen); +uint32 WEBFSGetPosition(WEBFS_HANDLE hWEBFS); +uint32 WEBFS_max_size(void); +uint32 WEBFS_curent_size(void); +uint32 WEBFS_base_addr(void); + +#ifdef USE_MAX_IRAM +extern int isWEBFSLocked; // Lock WEBFS access during the upgrade +extern uint32 numFiles; +#else +extern volatile bool isWEBFSLocked; // Lock WEBFS access during the upgrade +extern uint16 numFiles; +#endif + +extern WEBFS_FAT_RECORD fatCache; +extern WEBFS_STUB WEBFSStubs[MAX_WEBFS_OPENFILES+1]; +extern uint32 disk_base_addr; + +#endif diff --git a/project/inc/wifi_user_set.h b/project/inc/wifi_user_set.h new file mode 100644 index 0000000..da9a4df --- /dev/null +++ b/project/inc/wifi_user_set.h @@ -0,0 +1,67 @@ +/* + * wifi_user_set.h + * + * Created on: 01/04/2017 + * Author: pvvx + */ + +#ifndef _WIFI_USER_SET_H_ +#define _WIFI_USER_SET_H_ + +//========================================= +//==== Wlan Config ======================== +#define DEF_WIFI_MODE RTW_MODE_STA // RTW_MODE_STA_AP, RTW_MODE_AP, RTW_MODE_STA +#define DEF_WIFI_AP_STATIONS 3 // Max number of STAs, should be 1..3, default is 3 +#define DEF_WIFI_COUNTRY RTW_COUNTRY_RU +#define DEF_WIFI_TX_PWR RTW_TX_PWR_PERCENTAGE_25 // RTW_TX_PWR_PERCENTAGE_75 // RTW_TX_PWR_PERCENTAGE_100 +#define DEF_WIFI_BGN RTW_NETWORK_BGN // rtw_network_mode_t +#define DEF_WIFI_ST_SLEEP 0 // 0 - none, 1 - on +//#define USE_NETBIOS 3 // 0 - off, 1 - ST, 2 - AP, 3 - AP+ST +#define DEF_LOAD_CFG ( 0 \ + | BID_WIFI_AP_CFG \ + | BID_WIFI_ST_CFG \ + | BID_AP_DHCP_CFG \ + | BID_ST_DHCP_CFG \ + | BID_WIFI_CFG \ +) +#define DEF_SAVE_CFG ( 0 \ + | BID_WIFI_AP_CFG \ + | BID_WIFI_ST_CFG \ + | BID_AP_DHCP_CFG \ + | BID_ST_DHCP_CFG \ + | BID_WIFI_CFG \ +) +//==== Interface 0 - wlan0 = AP =========== +#define DEF_AP_SSID "RTL871X" +#define DEF_AP_PASSWORD "0123456789" +#define DEF_AP_SECURITY RTW_SECURITY_WPA2_AES_PSK // RTW_SECURITY_OPEN, RTW_SECURITY_WEP_PSK +#define DEF_AP_BEACON 100 // 100...6000 ms +#define DEF_AP_CHANNEL 1 // 1..14 +#define DEF_AP_CHANNEL 1 // 1..14 +#define DEF_AP_DHCP_MODE 1 // =0 dhcp off, =1 - dhcp on +#define DEF_AP_IP IP4ADDR(192,168,4,1) +#define DEF_AP_MSK IP4ADDR(255,255,255,0) +#define DEF_AP_GW IP4ADDR(192,168,4,1) +#define DEF_AP_DHCP_START 2 +#define DEF_AP_DHCP_STOP 15 +//==== Interface 1 - wlan1 = STA ========== +#define DEF_ST_SSID "HOMEAP" +#define DEF_ST_PASSWORD "0123456789" +#define DEF_ST_SECURITY RTW_SECURITY_WPA_WPA2_MIXED +#define DEF_ST_BSSID { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } // If bssid set is not ff.ff.ff.ff.ff.ff, +// station will connect to the router with both ssid[] and bssid[] matched. +#define DEF_ST_CHANNEL 1 // 1..14 +#define DEF_ST_AUTORECONNECT 1 // 0 - none, 1..254 - count, 255 - all +#define DEF_ST_RECONNECT_PAUSE 1 // 5 sec +#define DEF_ST_DHCP_MODE 1 // =0 dhcp off, =1 - dhcp on, =2 Static ip, =3 - auto +#define DEF_ST_IP IP4ADDR(192,168,1,100) +#define DEF_ST_MSK IP4ADDR(255,255,255,0) +#define DEF_ST_GW IP4ADDR(192,168,1,1) +//==== Interface 2 - eth0 ================= +#define DEF_EH_DHCP_MODE 1 // =0 dhcp off, =1 - dhcp on +#define DEF_EH_IP IP4ADDR(192,168,7,200) +#define DEF_EH_MSK IP4ADDR(255,255,255,0) +#define DEF_EH_GW IP4ADDR(192,168,7,1) +//========================================= + +#endif /* _WIFI_USER_SET_H_ */ diff --git a/project/src/console/adc_tst.c b/project/src/console/adc_tst.c new file mode 100644 index 0000000..115ead1 --- /dev/null +++ b/project/src/console/adc_tst.c @@ -0,0 +1,258 @@ +/* + * adc_tst.c + * + * Created on: 04/04/2017. + * Author: pvvx + */ + +#include +#include "rtl8195a.h" +#include "FreeRTOS.h" +#include "rtl8195a/rtl_libc.h" + +//------------------------------------------------------------------------------ +#include "objects.h" +#include "PinNames.h" +#include "hal_adc.h" +#include "analogin_api.h" +#include "strproc.h" +//------------------------------------------------------------------------------ +analogin_t adc; + +LOCAL void fATADI(int argc, char *argv[]) { + + int count = 8; + int channel = 2; + union { + unsigned int ui[2]; + unsigned short us[4]; + }x; + uint16_t adcdat; + memset(&adc, 0, sizeof(adc)); + + + // ConfigDebugErr |= (_DBG_ADC_|_DBG_GDMA_); + // ConfigDebugInfo |= (_DBG_ADC_|_DBG_GDMA_); + + if (argc > 1) { + channel = atoi(argv[1]); + channel &= 0x03; + if(!channel) channel = 2; + } + + if (argc > 2) { + count = atoi(argv[2]); + } + + analogin_init(&adc, (channel+1) | (PORT_V << 4)); + + PSAL_ADC_HND pSalADCHND = &((&(adc.SalADCMngtAdpt))->pSalHndPriv->SalADCHndPriv); + + uint32_t sum = 0; + for (uint32_t i = 1; i <= count; i++) { + RtkADCReceiveBuf(pSalADCHND, &x.ui); + adcdat = x.us[channel]; + if((i % 8) == 0 || (i == count)) { + printf("0x%04x\n", adcdat); + } else { + printf("0x%04x, ", adcdat); + } + sum += adcdat; + } + analogin_deinit(&adc); + printf("ADC%d = 0x%04x\n", channel, sum / count); + // sys_adc_calibration(0, &channel, &count); +} + +LOCAL void fATADD(int argc, char *argv[]) { + + int count = 64; + int channel = 2; + uint16_t adcdat; + memset(&adc, 0, sizeof(adc)); + + +// ConfigDebugErr |= (_DBG_ADC_|_DBG_GDMA_); +// ConfigDebugInfo |= (_DBG_ADC_|_DBG_GDMA_); + + if (argc > 1) { + channel = atoi(argv[1]); + channel &= 0x03; + if(!channel) channel = 1; + } + + if (argc > 2) { + count = atoi(argv[2]); + if (count <= 2) { + count = 64; + } + }; + + analogin_init(&adc, (channel+1) | (PORT_V << 4)); + SAL_ADC_TRANSFER_BUF trbuf; + trbuf.pDataBuf = zalloc(count*4); + if(trbuf.pDataBuf) { + trbuf.DataLen = count/2; // x32 bit ? + trbuf.RSVD = 0; + adc.SalADCHndPriv.SalADCHndPriv.pRXBuf = &trbuf; + adc.SalADCHndPriv.SalADCHndPriv.OpType = ADC_DMA_TYPE; + + adc.HalADCInitData.ADCEndian = ADC_DATA_ENDIAN_LITTLE; //ADC endian selection, + //but actually it's for 32-bit ADC data swap control + //1'b0: no swap, + //1'b1: swap the upper 16-bit and the lower 16-bit +// adc.HalADCInitData.ADCCompOnly = ADC_FEATURE_DISABLED; //ADC compare mode only enable (without FIFO enable) +// adc.HalADCInitData.ADCEnManul = ADC_FEATURE_ENABLED; // ADC_FEATURE_DISABLED; //ADC enable manually +// adc.HalADCInitData.ADCIdx = channel+1; //ADC index used (1..3 ?) +// adc.HalADCInitData.ADCBurstSz = 8; //ADC DMA operation threshold +// adc.HalADCInitData.ADCOneShotTD = 8; //ADC one shot mode threshold +// adc.HalADCInitData.ADCDataRate = 0; // 0xff; // ADC down sample data rate ?? + adc.HalADCInitData.ADCAudioEn = ADC_FEATURE_ENABLED; //ADC audio mode enable // ADC_FEATURE_DISABLED +// adc.HalADCInitData.ADCOneShotEn = ADC_FEATURE_DISABLED; //ADC one shot mode threshold + adc.HalADCInitData.ADCInInput = ADC_FEATURE_ENABLED; //ADC Input is internal? +// adc.HalADCInitData.ADCEn = ADC_DISABLE; //ADC_ENABLE; + + HalADCInit8195a(&adc.HalADCInitData); + /* Read Content */ + HAL_ADC_READ32(REG_ADC_FIFO_READ); + HAL_ADC_READ32(REG_ADC_INTR_STS); + RtkADCReceive(&adc.SalADCHndPriv.SalADCHndPriv); + while(adc.SalADCHndPriv.SalADCHndPriv.DevSts != ADC_STS_IDLE); + uint16 * ptr = (uint16 *) trbuf.pDataBuf; + // RtkADCDMAInit(&adc.SalADCHndPriv.SalADCHndPriv); + for (uint32_t i = 1; i <= count; i++) { + if((i % 16) == 0 || (i == count)) { + printf("%04x\n", *ptr); + } else { + printf("%04x ", *ptr); + } + ptr++; + } + uint32_t sum = 0; + ptr = (uint16 *) trbuf.pDataBuf; + for (uint32_t i = 1; i <= count; i++) { + printf("%d\n", *ptr); + sum += *ptr; + ptr++; + if((i%512)==0) vTaskDelay(10); + } +/* + printf("OpType:\t\t%p\n", adc.SalADCHndPriv.SalADCHndPriv.OpType); + printf("pRXBuf:\t\t%p\n", adc.SalADCHndPriv.SalADCHndPriv.pRXBuf); + printf("pDataBuf:\t%p\n", adc.SalADCHndPriv.SalADCHndPriv.pRXBuf->pDataBuf); + printf("DataLen:\t%p\n", adc.SalADCHndPriv.SalADCHndPriv.pRXBuf->DataLen); + printf("ADCDataRate:\t%p\n", adc.HalADCInitData.ADCDataRate); + printf("ADCData:\t%p\n", adc.HalADCInitData.ADCData); + printf("ADCIdx:\t\t%p\n", adc.HalADCInitData.ADCIdx); + printf("ADCPWCtrl:\t%p\n", adc.HalADCInitData.ADCPWCtrl); + printf("ADCAnaParAd3:\t%p\n", adc.HalADCInitData.ADCAnaParAd3); + printf("ADC%d = 0x%04x\n", channel, analogin_read_u16(&adc)); + printf("ADC%d = 0x%04x\n", channel, analogin_read_u16(&adc)); +*/ + analogin_deinit(&adc); + free(trbuf.pDataBuf); + printf("ADC%d = 0x%04x\n", channel, sum / count); + + } + else { + error_printf("%s: malloc failed!\n", __func__); + }; + +// sys_adc_calibration(0, &channel, &count); +} + +LOCAL void fATADC(int argc, char *argv[]) { + + int count = 8; + int channel = 2; + uint16_t adcdat; + memset(&adc, 0, sizeof(adc)); + + +// ConfigDebugErr |= (_DBG_ADC_|_DBG_GDMA_); +// ConfigDebugInfo |= (_DBG_ADC_|_DBG_GDMA_); + + if (argc > 1) { + channel = atoi(argv[1]); + channel &= 0x03; + if(!channel) channel = 1; + } + + if (argc > 2) { + count = atoi(argv[2]); + } + + analogin_init(&adc, (channel+1) | (PORT_V << 4)); + + uint32_t sum = 0; + for (uint32_t i = 1; i <= count; i++) { + adcdat = analogin_read_u16(&adc); + if((i % 8) == 0 || (i == count)) { + printf("0x%04x\n", adcdat); + } else { + printf("0x%04x, ", adcdat); + } + sum += adcdat; + } + analogin_deinit(&adc); + printf("ADC%d = 0x%04x\n", channel, sum / count); +// sys_adc_calibration(0, &channel, &count); +} + +LOCAL void fATSA(int argc, char *argv[]) { +// u32 tConfigDebugInfo = ConfigDebugInfo; + int channel; + char *ptmp; + u16 offset, gain, adcdat; + memset(&adc, 0, sizeof(adc)); + + if (argc < 2) { + printf("Usage: ATSA=CHANNEL(0~2)\n"); + printf("Usage: ATSA=k_get\n"); + printf("Usage: ATSA=k_set[offet(hex),gain(hex)]\n"); + return; + } + + if (strcmp(argv[1], "k_get") == 0) { + sys_adc_calibration(0, &offset, &gain); +// printf("[ATSA] offset = 0x%04X, gain = 0x%04X", offset, gain); + } else if (strcmp(argv[1], "k_set") == 0) { + if (argc != 4) { + printf("Usage: ATSA=k_set[offet(hex),gain(hex)]\n"); + return; + } + offset = strtoul(argv[2], &ptmp, 16); + gain = strtoul(argv[3], &ptmp, 16); + sys_adc_calibration(1, &offset, &gain); +// printf("[ATSA] offset = 0x%04X, gain = 0x%04X", offset, gain); + } else { + channel = atoi(argv[1]); + if (channel < 0 || channel > 2) { + printf("Usage: ATSA=CHANNEL(0~2)\n"); + return; + } + // Remove debug info massage +// ConfigDebugInfo = 0; + if (channel == 0) + analogin_init(&adc, AD_1); + else if (channel == 1) + analogin_init(&adc, AD_2); + else + analogin_init(&adc, AD_3); +// analogin_read_u16(&adc); + adcdat = analogin_read_u16(&adc) >> 4; + analogin_deinit(&adc); + // Recover debug info massage +// ConfigDebugInfo = tConfigDebugInfo; + + printf("A%d = 0x%04X\n", channel, adcdat); + } +} + +//------------------------------------------------------------------------------ +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_adc[] = { + { "ATADC", 0, fATADC, ": ADC Test" }, + { "ATADD", 0, fATADD, ": ADC DMA Test" }, + { "ATADI", 0, fATADI, ": ADC Irq Test" }, + { "ATSA" , 0, fATSA , ": ADC at" } +}; diff --git a/project/src/console/atcmd_user.c b/project/src/console/atcmd_user.c new file mode 100644 index 0000000..15b3ff2 --- /dev/null +++ b/project/src/console/atcmd_user.c @@ -0,0 +1,359 @@ +#include + +#ifdef CONFIG_AT_USR + +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "at_cmd/log_service.h" +#include "at_cmd/atcmd_wifi.h" +#include +#include "tcpip.h" +#include +#include +#include +#include "tcm_heap.h" +#include "rtl8195a/rtl_libc.h" + +#include "sleep_ex_api.h" + +#include "lwip/tcp_impl.h" + +extern char str_rom_57ch3Dch0A[]; // "=========================================================\n" 57 + +#define printf rtl_printf // DiagPrintf + +/* RAM/TCM/Heaps info */ +extern void ShowMemInfo(void); +/* +void ShowMemInfo(void) +{ + printf("\nCLK CPU\t\t%d Hz\nRAM heap\t%d bytes\nTCM heap\t%d bytes\n", + HalGetCpuClk(), xPortGetFreeHeapSize(), tcm_heap_freeSpace()); +} + */ +//------------------------------------------------------------------------------ +// Mem, Tasks info +//------------------------------------------------------------------------------ +LOCAL void fATST(int argc, char *argv[]) { + ShowMemInfo(); +#if 0 //CONFIG_DEBUG_LOG > 1 + dump_mem_block_list(); + tcm_heap_dump(); +#endif; + printf("\n"); +#if (configGENERATE_RUN_TIME_STATS == 1) + char *cBuffer = pvPortMalloc(512); + if(cBuffer != NULL) { + vTaskGetRunTimeStats((char *)cBuffer); + printf("%s", cBuffer); + } + vPortFree(cBuffer); +#endif +#if defined(configUSE_TRACE_FACILITY) && (configUSE_TRACE_FACILITY == 1) && (configUSE_STATS_FORMATTING_FUNCTIONS == 1) + { + char * pcWriteBuffer = malloc(1024); + if(pcWriteBuffer) { + vTaskList((char*)pcWriteBuffer); + printf("\nTask List:\n"); + printf(&str_rom_57ch3Dch0A[7]); // "==========================================\n" + printf("Name\t Status Priority HighWaterMark TaskNumber\n%s\n", pcWriteBuffer); + free(pcWriteBuffer); + } + } +#endif +} +/*------------------------------------------------------------------------------------- + Копирует данные из облаÑти align(4) (flash, registers, ...) в облаÑÑ‚ÑŒ align(1) (ram) +--------------------------------------------------------------------------------------*/ +extern void copy_align4_to_align1(unsigned char * pd, void * ps, unsigned int len); +/* +static void copy_align4_to_align1(unsigned char * pd, void * ps, unsigned int len) +{ + union { + unsigned char uc[4]; + unsigned int ud; + }tmp; + unsigned int *p = (unsigned int *)((unsigned int)ps & (~3)); + unsigned int xlen = (unsigned int)ps & 3; + // unsigned int size = len; + + if(xlen) { + tmp.ud = *p++; + while (len) { + len--; + *pd++ = tmp.uc[xlen++]; + if(xlen & 4) break; + } + } + xlen = len >> 2; + while(xlen) { + tmp.ud = *p++; + *pd++ = tmp.uc[0]; + *pd++ = tmp.uc[1]; + *pd++ = tmp.uc[2]; + *pd++ = tmp.uc[3]; + xlen--; + } + if(len & 3) { + tmp.ud = *p; + pd[0] = tmp.uc[0]; + if(len & 2) { + pd[1] = tmp.uc[1]; + if(len & 1) { + pd[2] = tmp.uc[2]; + } + } + } + // return size; +} +*/ +int print_hex_dump(uint8_t *buf, int len, unsigned char k) { + uint32_t ss[2]; + ss[0] = 0x78323025; // "%02x" + ss[1] = k; // ","...'\0' + uint8_t * ptr = buf; + int result = 0; + while (len--) { + if (len == 0) + ss[1] = 0; + result += printf((uint8_t *) &ss, *ptr++); + } + return result; +} + +extern char str_rom_hex_addr[]; // in *.ld "[Addr] .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F\n" +void dump_bytes(uint32 addr, int size) +{ + uint8 buf[17]; + u32 symbs_line = sizeof(buf)-1; + printf(str_rom_hex_addr); + while (size) { + if (symbs_line > size) symbs_line = size; + printf("%08X ", addr); + copy_align4_to_align1(buf, addr, symbs_line); + print_hex_dump(buf, symbs_line, ' '); + int i; + for(i = 0 ; i < symbs_line ; i++) { + if(buf[i] < 0x20 || buf[i] > 0x7E) { + buf[i] = '.'; + } + } + buf[symbs_line] = 0; + i = (sizeof(buf)-1) - symbs_line; + while(i--) printf(" "); + printf(" %s\r\n", buf); + addr += symbs_line; + size -= symbs_line; + } +} +//------------------------------------------------------------------------------ +// Dump byte register +//------------------------------------------------------------------------------ +LOCAL void fATSB(int argc, char *argv[]) +{ + int size = 16; + uint32 addr = Strtoul(argv[1],0,16); + if (argc > 2) { + size = Strtoul(argv[2],0,10); + if (size <= 0 || size > 16384) + size = 16; + } + if(addr + size > SPI_FLASH_BASE) { + flash_turnon(); + dump_bytes(addr, size); + SpicDisableRtl8195A(); + } + else { + dump_bytes(addr, size); + } +} + +//------------------------------------------------------------------------------ +// Dump dword register +//------------------------------------------------------------------------------ +LOCAL void fATSD(int argc, char *argv[]) +{ +/* + if (argc > 2) { + int size = Strtoul(argv[2],0,10); + if (size <= 0 || size > 16384) + argv[2] = "16"; + } +*/ + CmdDumpWord(argc-1, (unsigned char**)(argv+1)); +} +//------------------------------------------------------------------------------ +// Write dword register +//------------------------------------------------------------------------------ +LOCAL void fATSW(int argc, char *argv[]) +{ + CmdWriteWord(argc-1, (unsigned char**)(argv+1)); +} + +/* Get one byte from the 4-byte address */ +#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0]) +#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1]) +#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2]) +#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3]) +/* These are cast to u16_t, with the intent that they are often arguments + * to printf using the U16_F format from cc.h. */ +#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr)) +#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr)) +#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr)) +#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr)) + +#define IP2STR(ipaddr) ip4_addr1_16(ipaddr), \ + ip4_addr2_16(ipaddr), \ + ip4_addr3_16(ipaddr), \ + ip4_addr4_16(ipaddr) + +#define IPSTR "%d.%d.%d.%d" + +extern const char * const tcp_state_str[]; +/* +static const char * const tcp_state_str[] = { + "CLOSED", + "LISTEN", + "SYN_SENT", + "SYN_RCVD", + "ESTABLISHED", + "FIN_WAIT_1", + "FIN_WAIT_2", + "CLOSE_WAIT", + "CLOSING", + "LAST_ACK", + "TIME_WAIT" +}; +*/ +/****************************************************************************** + * FunctionName : debug + * Parameters : + * Returns : +*******************************************************************************/ +void print_udp_pcb(void) +{ + struct udp_pcb *pcb; + bool prt_none = true; + rtl_printf("UDP pcbs:\n"); + for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { + rtl_printf("flg:%02x\t" IPSTR ":%d\t" IPSTR ":%d\trecv:%p\n", pcb->flags, IP2STR(&pcb->local_ip), pcb->local_port, IP2STR(&pcb->remote_ip), pcb->remote_port, pcb->recv ); + prt_none = false; + }; + if(prt_none) rtl_printf("none\n"); +} +/****************************************************************************** + * FunctionName : debug + * Parameters : + * Returns : +*******************************************************************************/ +void print_tcp_pcb(void) +{ + struct tcp_pcb *pcb; + rtl_printf("Active PCB states:\n"); + bool prt_none = true; + for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]); + prt_none = false; + }; + if(prt_none) rtl_printf("none\n"); + rtl_printf("Listen PCB states:\n"); + prt_none = true; + for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) { + rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p\t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]); + prt_none = false; + }; + if(prt_none) rtl_printf("none\n"); + rtl_printf("TIME-WAIT PCB states:\n"); + prt_none = true; + for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + rtl_printf("Port %d|%d\tflg:%02x\ttmr:%p \t%s\n", pcb->local_port, pcb->remote_port, pcb->flags, pcb->tmr, tcp_state_str[pcb->state]); + prt_none = false; + }; + if(prt_none) rtl_printf("none\n"); +} +/****************************************************************************** + * FunctionName : debug + * Parameters : + * Returns : +*******************************************************************************/ +LOCAL void fATLW(int argc, char *argv[]) // Info Lwip +{ + print_udp_pcb(); + print_tcp_pcb(); +} +//------------------------------------------------------------------------------ +// Deep sleep +//------------------------------------------------------------------------------ +LOCAL void fATDS(int argc, char *argv[]) +{ + uint32 sleep_ms = 10000; + if(argc > 1) sleep_ms = atoi(argv[1]); +#if 0 + if(argc > 2) { + printf("%u ms waiting low level on PB_1 before launching Deep-Sleep...\n", sleep_ms); + // turn off log uart + HalDeinitLogUart(); // sys_log_uart_off(); + + // initialize wakeup pin + gpio_t gpio_wake; + gpio_init(&gpio_wake, PB_1); + gpio_dir(&gpio_wake, PIN_INPUT); + gpio_mode(&gpio_wake, PullDown); + TickType_t sttime = xTaskGetTickCount(); + + do { + if(gpio_read(&gpio_wake) == 0) { + // Enter deep sleep... Wait give rising edge at PB_1 to wakeup system. + deepsleep_ex(DSLEEP_WAKEUP_BY_GPIO, 0); + }; + vTaskDelay(1); + } while(xTaskGetTickCount() - sttime < sleep_ms); + HalInitLogUart(); // sys_log_uart_on(); + printf("No set pin low in deep sleep!\n"); + } + else { + printf("Deep-Sleep %u ms\n", sleep_ms); + HalLogUartWaitTxFifoEmpty(); + // Enter deep sleep... Wait timer ms + deepsleep_ex(DSLEEP_WAKEUP_BY_TIMER, sleep_ms); + } +#else + HalLogUartWaitTxFifoEmpty(); + deepsleep_ex(DSLEEP_WAKEUP_BY_TIMER, sleep_ms); +#endif +} +/*------------------------------------------------------------------------------ + * power saving mode + *----------------------------------------------------------------------------*/ +LOCAL void fATSP(int argc, char *argv[]) +{ + if(argc > 2) { + switch (argv[1][0]) { + case 'a': // acquire + { + acquire_wakelock(atoi(argv[2])); + break; + } + case 'r': // release + { + release_wakelock(atoi(argv[2])); + break; + } + }; + }; + printf("WakeLock Status %d\n", get_wakelock_status()); +} +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_at[] = { + {"ATST", 0, fATST, ": Memory info"}, + {"ATLW", 0, fATLW, ": LwIP Info"}, + {"ATSB", 1, fATSB, "=[,COUNT(dec)]: Dump byte register"}, + {"ATSD", 1, fATSD, "=[,COUNT(dec)]: Dump dword register"}, + {"ATSW", 2, fATSW, "=,: Set register"}, + {"ATDS", 0, fATDS, "=[TIME(ms)]: Deep sleep"}, + {"ATSP", 0, fATSP, "=,: Power"} +}; + +#endif //#ifdef CONFIG_AT_USR diff --git a/project/src/console/flash_tst.c b/project/src/console/flash_tst.c new file mode 100644 index 0000000..2499e0d --- /dev/null +++ b/project/src/console/flash_tst.c @@ -0,0 +1,38 @@ +/* + * flash_tst.c + * + * Created on: 10/04/2017 + * Author: pvvx + */ + +#include +#include "rtl8195a.h" +#include "flash_api.h" +#include "rtl8195a/rtl_libc.h" + +extern void dump_bytes(uint32 addr, int size); + +LOCAL void FlashDump(int argc, char *argv[]) { + if (argc > 1) { + int addr; + sscanf(argv[1], "%x", &addr); + int size = 16; + if (argc > 2) { + size = atoi(argv[2]); + if (size <= 0 || size > 16384) { + size = 16; + }; + }; + flash_turnon(); + dump_bytes(addr + SPI_FLASH_BASE, size); + SpicDisableRtl8195A(); + } +} + +MON_RAM_TAB_SECTION COMMAND_TABLE console_flash_tst[] = { + {"FLASHDB", 1, FlashDump, ": [,size]: Flash Dump"} +}; + + + + diff --git a/project/src/console/gpio_irq_test.c b/project/src/console/gpio_irq_test.c new file mode 100644 index 0000000..d67e0ec --- /dev/null +++ b/project/src/console/gpio_irq_test.c @@ -0,0 +1,99 @@ +/* + * test.c + * + * Created on: 12 марта 2017 г. + * Author: PVV + */ + +#include +#include "device.h" +#include "gpio_api.h" // mbed +#include "gpio_irq_api.h" // mbed +#include "gpio_irq_ex_api.h" // mbed +#include "timer_api.h" +#include "diag.h" +#include "main.h" + +#include "hal_diag.h" +#include "rtl8195a/rtl_libc.h" + +#define GPIO_LED_PIN PA_4 +#define GPIO_IRQ_PIN PC_4 + +gpio_irq_t gpio_btn; +gpio_t gpio_led; +gtimer_t my_timer; + +uint32_t lo_time_us, hi_time_us; +uint32_t lo_time_cnt, hi_time_cnt; +uint32_t old_tsf; +uint32_t lo, hi, fr; + +uint32_t io_irq_count; + +LOCAL void gpio_demo_irq_handler(uint32_t id, gpio_irq_event event) { + +// gpio_irq_disable(&gpio_btn); + io_irq_count++; + uint32_t new_tsf = get_tsf(); + uint32_t delta_us = (uint32_t) new_tsf - (uint32_t) old_tsf; + if (event & 1) { + lo_time_us += delta_us; + lo_time_cnt++; + gpio_irq_set(&gpio_btn, IRQ_LOW, 1); + } else { + hi_time_us += delta_us; + hi_time_cnt++; + gpio_irq_set(&gpio_btn, IRQ_HIGH, 1); + } + old_tsf = new_tsf; +// gpio_irq_enable(&gpio_btn); +} + +LOCAL void timer1_timeout_handler(uint32_t id) { + + if (lo_time_cnt && hi_time_cnt) { + lo = lo_time_us / lo_time_cnt; + hi = hi_time_us / hi_time_cnt; + fr = hi + lo; + lo_time_cnt = 0; + lo_time_us = 0; + hi_time_cnt = 0; + hi_time_us = 0; + printf("Period: %lu us, Lo: %lu us, Hi: %lu us\n", fr, lo, hi); + } +} + +/** + * @brief Main program. + * @param None + * @retval None + */ +LOCAL void fATTT(int argc, char *argv[]) { + + lo_time_cnt = 0; + lo_time_us = 0; + hi_time_cnt = 0; + hi_time_us = 0; + + // Init LED control pin + gpio_init(&gpio_led, GPIO_LED_PIN); + gpio_dir(&gpio_led, PIN_OUTPUT); // Direction: Output + gpio_mode(&gpio_led, PullNone); // No pull + gpio_write(&gpio_led, 0); + + // Initial Push Button pin as interrupt source + gpio_irq_init(&gpio_btn, GPIO_IRQ_PIN, gpio_demo_irq_handler, + (uint32_t) (&gpio_led)); + gpio_irq_set(&gpio_btn, IRQ_FALL, 1); // Falling Edge Trigger + gpio_irq_enable(&gpio_btn); + + // Initial a periodical timer + gtimer_init(&my_timer, TIMER1); + gtimer_start_periodical(&my_timer, 1000000, (void*) timer1_timeout_handler, + (uint32_t) &gpio_led); +} + +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_test[] = { { "ATTT", 0, + fATTT, ": Test" } }; + diff --git a/project/src/console/power_tst.c b/project/src/console/power_tst.c new file mode 100644 index 0000000..7445b6e --- /dev/null +++ b/project/src/console/power_tst.c @@ -0,0 +1,39 @@ +/* + * power_tst.c + * + * Created on: 04 апр. 2017 г. + * Author: PVV + */ + +#include "rtl8195a.h" +#include "freertos_pmu.h" +#include "rtl8195a/rtl_libc.h" +/*------------------------------------------------------------------------------ + * power saving mode + *----------------------------------------------------------------------------*/ +void fATSP(int argc, char *argv[]) +{ + if(argc > 2) { + switch (argv[1][0]) { + case 'a': // acquire + { + acquire_wakelock(atoi(argv[2])); + break; + } + case 'r': // release + { + release_wakelock(atoi(argv[2])); + break; + } + } + } + printf("WakeLock Status %d\n", get_wakelock_status()); +} + + +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_pwrs[] = { + {"ATSP", 0, fATSP, "=,: Power"} + +}; + + diff --git a/project/src/console/pwm_tst.c b/project/src/console/pwm_tst.c new file mode 100644 index 0000000..5e995c3 --- /dev/null +++ b/project/src/console/pwm_tst.c @@ -0,0 +1,59 @@ +/* + * pwm_tst.c + * + * Created on: 19/04/2017. + * Author: pvvx + */ + +#include +#include "rtl8195a.h" +#include "FreeRTOS.h" +#include "rtl8195a/rtl_libc.h" + +//#include "device.h" +#include "pwmout_api.h" // mbed +//#include "main.h" +#include "web_utils.h" +#include "objects.h" +#include "pinmap.h" + +extern const PinMap PinMap_PWM[]; +extern u32 gTimerRecord; + +HAL_PWM_ADAPTER pwm_hal_adp; + +LOCAL void fATPWM(int argc, char *argv[]) { + + uint8_t pin = ahextoul(argv[1]); + uint32_t period = ahextoul(argv[2]); + uint32_t pulse = ahextoul(argv[3]); + + uint32_t peripheral = pinmap_peripheral(pin, PinMap_PWM); + + if(pwm_hal_adp.enable) { + HAL_Pwm_Disable(&pwm_hal_adp); + gTimerRecord &= ~(1 << pwm_hal_adp.gtimer_id); + rtl_memset((void *)&pwm_hal_adp, 0, sizeof(HAL_PWM_ADAPTER)); + }; + if((period) && (unlikely(peripheral != NC)) + && (HAL_Pwm_Init(&pwm_hal_adp, RTL_GET_PERI_IDX(peripheral), RTL_GET_PERI_SEL(peripheral)) == HAL_OK)) { + HAL_Pwm_SetDuty(&pwm_hal_adp, period, pulse); + HAL_Pwm_Enable(&pwm_hal_adp); + } else { + printf("Error parameters!"); + }; +} + +LOCAL void fATWLED(int argc, char *argv[]) { + HalPinCtrlRtl8195A(WL_LED, 2, 1); + HalPinCtrlRtl8195A(EGTIM, ahextoul(argv[1]), 1); +} +//------------------------------------------------------------------------------ +// atpwm=34,1048575,524287 +// atpwm=34,122,61 (8.187kHz) +// atsw 40000368 85001002 (8.187kHz) +// atsd 40000360 6 +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_adc[] = { + { "ATWLED", 0, fATWLED, ": WLED Test" }, + { "ATPWM", 3, fATPWM, "=,,: PWM Test" } +}; diff --git a/project/src/console/spi_tst.c b/project/src/console/spi_tst.c new file mode 100644 index 0000000..7548065 --- /dev/null +++ b/project/src/console/spi_tst.c @@ -0,0 +1,83 @@ +/* + * spi_test.c + */ +#include +#include "rtl8195a.h" +#include "spi_api.h" +#include "spi_ex_api.h" +#include "rtl8195a/rtl_libc.h" + +#define SPI0_MOSI PC_2 +#define SPI0_MISO PC_3 +#define SPI0_SCLK PC_1 +#define SPI0_CS PC_0 + +spi_t spi_master; + +LOCAL void show_reg_spi(int i) { + rtl_printf("Regs SPI:\n"); + for(int x = 0; x < 64 ; x += 4) { + rtl_printf("0x%08x ", HAL_SSI_READ32(i, x)); + if((x & 0x0F) == 0x0C) rtl_printf("\n"); + } +} + + +LOCAL void fATSSI(int argc, char *argv[]) +{ + int len = 128; + int count = 32; + int clk = 1000000; + int ssn = 0; + if(argc > 1) { + len = atoi(argv[1]); + if(len > 32768 || len <= 0) { + len = 128; + error_printf("%s: len = %u!\n", __func__, len); + }; + }; + if(argc > 2) { + count = atoi(argv[2]); + if(count > 10000 || count <= 0) { + count = 32; + error_printf("%s: count = %u!\n", __func__, count); + }; + }; + if(argc > 3) { + clk = atoi(argv[3]); + if(clk <= 0) { + clk = 1000000; + error_printf("%s: clk = %u!\n", __func__, clk); + }; + }; + if(argc > 4) { + ssn = atoi(argv[4]); + if(ssn > 7 || ssn < 0) { + ssn = 0; + error_printf("%s: ssn = %u!\n", __func__, ssn); + }; + }; + char* buff = pvPortMalloc(len); + if(buff) { + spi_init(&spi_master, SPI0_MOSI, SPI0_MISO, SPI0_SCLK, SPI0_CS); // CS заданный тут нигде не иÑпользуетÑÑ + spi_format(&spi_master, 16, 3, 0); + spi_frequency(&spi_master, clk); + spi_slave_select(&spi_master, ssn); // выбор CS + for(int i = 0; i < len; i++) buff[i] = (char)i; + while(count--) { + spi_master_write_stream(&spi_master, buff, len); + while(spi_busy(&spi_master)); + rtl_printf("Master write: %d\n", count); + }; +// show_reg_spi(spi_master.spi_adp.Index); + spi_free(&spi_master); + free(buff); + } + else { + error_printf("%s: error malloc!\n", __func__); + }; +} + +MON_RAM_TAB_SECTION COMMAND_TABLE console_commands_spitst[] = { + {"ATSSI", 0, fATSSI, "[len[,count[,clk[,ssn]]]]: Spi test"} +}; diff --git a/project/src/console/wifi_console.c b/project/src/console/wifi_console.c new file mode 100644 index 0000000..0f522fa --- /dev/null +++ b/project/src/console/wifi_console.c @@ -0,0 +1,338 @@ +/* + * wifi_console.c + * + * Created on: 03/04/2017 + * Author: pvvx + */ + +#include +#include "FreeRTOS.h" +#include "diag.h" +#include "wifi_api.h" +#include "wifi_conf.h" +#include "rtl8195a/rtl_libc.h" +#include "hal_platform.h" + +#include "section_config.h" +#include "hal_diag.h" +#include "lwip/netif.h" + + +extern struct netif xnetif[NET_IF_NUM]; + +//========================================================== +//--- CONSOLE -------------------------- + +// ATPN=[,password[,encryption[,auto reconnect[,reconnect pause]]]: WIFI Connect to AP +LOCAL void fATPN(int argc, char *argv[]){ + if(argc > 1) { + if(argv[1][0] == '?') { + show_wifi_st_cfg(); + } + else { + strncpy(wifi_st_cfg.ssid, argv[1], NDIS_802_11_LENGTH_SSID); + if(argc > 2) { + strncpy(wifi_st_cfg.password, argv[2], NDIS_802_11_LENGTH_SSID); + int i = strlen(wifi_st_cfg.password); + if(i > 7) { + wifi_st_cfg.security_type = RTW_SECURITY_WPA2_AES_PSK; + } + else if(!i) { + wifi_st_cfg.security_type = RTW_SECURITY_OPEN; + } + else { + printf("password len < 8!\n"); + wifi_st_cfg.security_type = RTW_SECURITY_OPEN; + } + } + else { + wifi_st_cfg.password[0] = 0; + wifi_st_cfg.security_type = RTW_SECURITY_OPEN; + } + if(argc > 3) { + wifi_ap_cfg.security_type = translate_rtw_security(atoi(argv[3])); + } + if(argc > 4) { + wifi_st_cfg.autoreconnect = atoi(argv[3]); + } + else wifi_st_cfg.autoreconnect = 0; + if(argc > 5) { + wifi_st_cfg.reconnect_pause = atoi(argv[3]); + } + else wifi_st_cfg.reconnect_pause = 5; + show_wifi_st_cfg(); + wifi_run(wifi_run_mode | RTW_MODE_STA); + } + } +} + +// ATPA=[,password[,encryption[,channel[,hidden[,max connections]]]]]: Start WIFI AP +LOCAL void fATPA(int argc, char *argv[]){ + if(argc > 1) { + if(argv[1][0] == '?') { + show_wifi_ap_cfg(); + } + else { + strncpy(wifi_ap_cfg.ssid, argv[1], NDIS_802_11_LENGTH_SSID); + if(argc > 2) { + strncpy(wifi_ap_cfg.password, argv[2], NDIS_802_11_LENGTH_SSID); + int i = strlen(wifi_ap_cfg.password); + if(i > 7) { + wifi_ap_cfg.security_type = RTW_SECURITY_WPA2_AES_PSK; + } + else if(i == 0) { + wifi_ap_cfg.security_type = RTW_SECURITY_OPEN; + } + else { + printf("password len < 8!\n"); + wifi_ap_cfg.security_type = RTW_SECURITY_OPEN; + } + } + else { + wifi_ap_cfg.password[0] = 0; + wifi_ap_cfg.security_type = RTW_SECURITY_OPEN; + } + if(argc > 3) { + if(argv[3][0]=='0') wifi_st_cfg.security_type = RTW_SECURITY_OPEN; + else wifi_st_cfg.security_type = RTW_SECURITY_WEP_PSK; + } + if(argc > 4) { + wifi_ap_cfg.channel = atoi(argv[4]); + } + else wifi_ap_cfg.channel = 1; + if(argc > 5) { + wifi_ap_cfg.ssid_hidden = atoi(argv[5]); + } + else wifi_ap_cfg.ssid_hidden = 0; + + if(argc > 6) { + wifi_ap_cfg.max_sta = atoi(argv[6]); + } + else wifi_ap_cfg.max_sta = 3; + + show_wifi_ap_cfg(); + wifi_run(wifi_run_mode | RTW_MODE_AP); + } + } +} + +// WIFI Connect, Disconnect +LOCAL void fATWR(int argc, char *argv[]){ + rtw_mode_t mode = RTW_MODE_NONE; + if(argc > 1) mode = atoi(argv[1]); + wifi_run(mode); +} + +// Close connections +LOCAL void fATOF(int argc, char *argv[]){ + connect_close(); +} + +// Open connections +LOCAL void fATON(int argc, char *argv[]){ + connect_start(); +} + +LOCAL void fATWI(int argc, char *argv[]) { + rtw_wifi_setting_t Setting; + if((wifi_run_mode & RTW_MODE_AP) + && wifi_get_setting(wlan_ap_name, &Setting) == 0) { + wifi_show_setting(wlan_ap_name, &Setting); +// show_wifi_ap_ip(); + printf("\tIP: " IPSTR "\n", IP2STR(&xnetif[WLAN_AP_NETIF_NUM].ip_addr)); + } + if((wifi_run_mode & RTW_MODE_STA) + && wifi_get_setting(wlan_st_name, &Setting) == 0) { + wifi_show_setting(wlan_st_name, &Setting); +// show_wifi_st_ip(); + printf("\tIP: " IPSTR "\n", IP2STR(&xnetif[WLAN_ST_NETIF_NUM].ip_addr)); + } + printf("\nWIFI config:\n"); + printf(&str_rom_57ch3Dch0A[25]); // "================================\n" + show_wifi_cfg(); + printf("\nWIFI AP config:\n"); + printf(&str_rom_57ch3Dch0A[25]); // "================================\n" + show_wifi_ap_cfg(); + printf("\nWIFI ST config:\n"); + printf(&str_rom_57ch3Dch0A[25]); // "================================\n" + show_wifi_st_cfg(); + printf("\n"); +#if 1 + if(argc > 2) { + uint8_t c = argv[1][0] | 0x20; + if(c == 's') { + int i = atoi(argv[2]); + printf("Save configs(%d)..\n", i); + write_wifi_cfg(atoi(argv[2])); + } + else if(c == 'l') { + wifi_cfg.load_flg = atoi(argv[2]); + } + else if(c == 'm') { + wifi_cfg.mode = atoi(argv[2]); + } + } +#endif +} + +extern uint8_t rtw_power_percentage_idx; + +LOCAL void fATWT(int argc, char *argv[]) { + if(argc > 1) { + int txpwr = atoi(argv[1]); + debug_printf("set tx power (%d)...\n", txpwr); + if(rltk_set_tx_power_percentage(txpwr) != RTW_SUCCESS) { + error_printf("Error set tx power (%d)!", wifi_cfg.tx_pwr); + } + } + printf("TX power = %d\n", rtw_power_percentage_idx); +} + +//-- Test tsf (64-bits counts, 1 us step) --- + +#include "hal_com_reg.h" + +#define ReadTSF_Lo32() (*((volatile unsigned int *)(WIFI_REG_BASE + REG_TSFTR))) +#define ReadTSF_Hi32() (*((volatile unsigned int *)(WIFI_REG_BASE + REG_TSFTR1))) + +LOCAL uint64_t get_tsf(void) +{ + return *((uint64_t *)(WIFI_REG_BASE + REG_TSFTR)); +} + +LOCAL void fATSF(int argc, char *argv[]) +{ + uint64_t tsf = get_tsf(); + printf("\nTSF: %08x%08x\n", (uint32_t)(tsf>>32), (uint32_t)(tsf)); +} + +/* -------- WiFi Scan ------------------------------- */ +unsigned char *tab_txt_rtw_secyrity[] = { + "OPEN ", + "WEP ", + "WPA TKIP", + "WPA AES", + "WPA2 AES", + "WPA2 TKIP", + "WPA2 Mixed", + "WPA/WPA2 AES", + "Unknown" +}; +unsigned int *tab_code_rtw_secyrity[] = { + RTW_SECURITY_OPEN, + RTW_SECURITY_WEP_PSK, + RTW_SECURITY_WPA_TKIP_PSK, + RTW_SECURITY_WPA_AES_PSK, + RTW_SECURITY_WPA2_AES_PSK, + RTW_SECURITY_WPA2_TKIP_PSK, + RTW_SECURITY_WPA2_MIXED_PSK, + RTW_SECURITY_WPA_WPA2_MIXED, + RTW_SECURITY_UNKNOWN +}; + +volatile uint8_t scan_end; + +/* -------- WiFi Scan ------------------------------- */ +LOCAL rtw_result_t _scan_result_handler( rtw_scan_handler_result_t* malloced_scan_result ) +{ + if (malloced_scan_result->scan_complete != RTW_TRUE) { + rtw_scan_result_t* record = &malloced_scan_result->ap_details; + record->SSID.val[record->SSID.len] = 0; /* Ensure the SSID is null terminated */ + if(scan_end == 1) { + printf("\nScan networks:\n\n"); + printf("N\tType\tMAC\t\t\tSignal\tCh\tWPS\tSecyrity\tSSID\n\n"); + }; + printf("%d\t", scan_end++); + printf("%s\t", (record->bss_type == RTW_BSS_TYPE_ADHOC)? "Adhoc": "Infra"); + printf(MAC_FMT, MAC_ARG(record->BSSID.octet)); + printf("\t%d\t", record->signal_strength); + printf("%d\t", record->channel); + printf("%d\t", record->wps_type); + int i = 0; + for(; record->security != tab_code_rtw_secyrity[i] && tab_code_rtw_secyrity[i] != RTW_SECURITY_UNKNOWN; i++); + printf("%s \t", tab_txt_rtw_secyrity[i]); + printf("%s\n", record->SSID.val); + } else { + scan_end = 0; + printf("\n"); + } + return RTW_SUCCESS; +} +/* -------- WiFi Scan ------------------------------- */ +#define scan_channels 14 +LOCAL void fATSN(int argc, char *argv[]) +{ + int i; + u8 *channel_list = (u8*)pvPortMalloc(scan_channels*2); + if(channel_list) { + scan_end = 1; + u8 * pscan_config = &channel_list[scan_channels]; + //parse command channel list + for(i = 1; i <= scan_channels; i++){ + *(channel_list + i - 1) = i; + *(pscan_config + i - 1) = PSCAN_ENABLE; + }; + if(wifi_set_pscan_chan(channel_list, pscan_config, scan_channels) < 0){ + printf("ERROR: wifi set partial scan channel fail\n"); + } else if(wifi_scan_networks(_scan_result_handler, NULL ) != RTW_SUCCESS){ + printf("ERROR: wifi scan failed\n"); + } else { + i = 300; + while(i-- && scan_end) { + vTaskDelay(10); + }; + }; + vPortFree(channel_list); + } else { + printf("ERROR: Can't malloc memory for channel list\n"); + }; +} + +#if defined(CONFIG_ENABLE_WPS_AP) && CONFIG_ENABLE_WPS_AP +extern void cmd_ap_wps(int argc, char **argv); +extern void cmd_wps(int argc, char **argv); +//extern void cmd_wifi_on(int argc, char **argv); +#endif +#if CONFIG_ENABLE_P2P +extern void cmd_wifi_p2p_start(int argc, char **argv); +extern void cmd_wifi_p2p_stop(int argc, char **argv); +extern void cmd_p2p_listen(int argc, char **argv); +extern void cmd_p2p_find(int argc, char **argv); +extern void cmd_p2p_peers(int argc, char **argv); +extern void cmd_p2p_info(int argc, char **argv); +extern void cmd_p2p_disconnect(int argc, char **argv); +extern void cmd_p2p_connect(int argc, char **argv); +extern void cmd_wifi_p2p_auto_go_start(int argc, char **argv); +extern void cmd_p2p_peers(int argc, char **argv); +#endif //CONFIG_ENABLE_P2P + + +MON_RAM_TAB_SECTION COMMAND_TABLE console_cmd_wifi_api[] = { + {"ATPN", 1, fATPN, "=[,password[,encryption[,auto-reconnect[,reconnect pause]]]: WIFI Connect to AP"}, + {"ATPA", 1, fATPA, "=[,password[,encryption[,channel[,hidden[,max connections]]]]]: Start WIFI AP"}, +#if defined(CONFIG_ENABLE_WPS_AP) && CONFIG_ENABLE_WPS_AP + {"WPS_AP", 1, cmd_ap_wps, "=[,pin]: WiFi AP WPS"}, + {"WPS_ST", 1, cmd_wps, "=[,pin]: WiFi Station WPS"}, +#endif +#if CONFIG_ENABLE_P2P + {"P2P_START", 0, cmd_wifi_p2p_start, ": p2p start" }, + {"P2P_ASTART", 0, cmd_wifi_p2p_auto_go_start, ": p2p auto go start" }, + {"P2P_STOP", 0, cmd_wifi_p2p_stop, ": p2p stop"}, + {"P2P_PEERS", 0, cmd_p2p_peers, ": p2p peers" }, + {"P2P_FIND", 0, cmd_p2p_find, ": p2p find"}, + {"P2P_INFO", 0, cmd_p2p_info, ": p2p info"}, + {"P2P_DISCCONNECT", 0, cmd_p2p_disconnect, ": p2p disconnect"}, + {"P2P_CONNECT", 0, cmd_p2p_connect, ": p2p connect"}, +#endif + {"ATWR", 0, fATWR, ": WIFI Connect, Disconnect"}, +// {"ATON", 0, fATON, ": Open connections"}, +// {"ATOF", 0, fATOF, ": Close connections"}, + {"ATWI", 0, fATWI, ": WiFi Info"}, +#if CONFIG_DEBUG_LOG > 3 + {"ATWT", 1, fATWT, "=: WiFi tx power: 0 - 100%, 1 - 75%, 2 - 50%, 3 - 25%, 4 - 12.5%"}, + {"ATSF", 0, fATSF, ": Test TSF value"}, +#endif + {"ATSN", 0, fATSN, ": Scan networks"} +}; + + diff --git a/project/src/console/wlan_tst.c b/project/src/console/wlan_tst.c new file mode 100644 index 0000000..5dfea23 --- /dev/null +++ b/project/src/console/wlan_tst.c @@ -0,0 +1,50 @@ +/* + * wlan_tst.c + * + * Created on: 10 апр. 2017 г. + * Author: PVV + */ +#include +#include "rtl8195a.h" +#include "drv_types.h" +//#include "section_config.h" +//#include "hal_diag.h" +#include "rtl8195a/rtl_libc.h" + +extern void dump_bytes(uint32 addr, int size); +extern Rltk_wlan_t rltk_wlan_info[2]; // in wrapper.h + +LOCAL void tst_wlan_struct(int argc, char *argv[]) +{ + printf("Test: sizeof(struct _ADAPTER) = %d\n", sizeof(struct _ADAPTER)); //6088 + printf("mlmeextpriv\t+%d\n", offsetof(struct _ADAPTER, mlmeextpriv)); //+1256 + printf("TSFValue\t+%d\n", offsetof(struct _ADAPTER, mlmeextpriv.TSFValue)); //+1992 + printf("stapriv\t\t+%d\n", offsetof(struct _ADAPTER, stapriv)); //+3024 [164] + printf("pwrctrlpriv.bInternalAutoSuspend +%d\n", offsetof(struct _ADAPTER, pwrctrlpriv.bInternalAutoSuspend)); //+5061 + printf("eeprompriv\t+%d\n", offsetof(struct _ADAPTER, eeprompriv)); // +5128 + printf("HalData\t\t+%d\n", offsetof(struct _ADAPTER, HalData)); //+5656 + printf("HalFunc\t\t+%d\n", offsetof(struct _ADAPTER, HalFunc)); //+5664 + printf("bDriverStopped\t+%d\n", offsetof(struct _ADAPTER, bDriverStopped)); //+5880 + printf("hw_init_completed +%d\n", offsetof(struct _ADAPTER, hw_init_completed)); //+5905 + printf("stats\t\t+%d\n", offsetof(struct _ADAPTER, stats)); //+6024 + printf("hw_init_mutex\t+%d\n", offsetof(struct _ADAPTER, hw_init_mutex)); //+6060 + printf("fix_rate\t+%d\n", offsetof(struct _ADAPTER, fix_rate)); //+6084 + + printf("rltk_wlan_info = %p\n", &rltk_wlan_info); + dump_bytes((u32)&rltk_wlan_info, sizeof(rltk_wlan_info)); + _adapter * ad = *(_adapter **)((rltk_wlan_info[0].dev)->priv); + printf("adapter0 = %p, %p\n", ad, ad->pbuddy_adapter); + ad = *(_adapter **)((rltk_wlan_info[1].dev)->priv); + printf("adapter1 = %p, %p\n", ad, ad->pbuddy_adapter); + vTaskDelay(5); + dump_bytes((u32)ad,sizeof(struct _ADAPTER)); + vTaskDelay(5); + + if (sizeof(struct _ADAPTER) != 6088) { + printf("Error: Check WiFi adapter struct!\n"); + }; +} + +MON_RAM_TAB_SECTION COMMAND_TABLE console_wlan_tst[] = { + {"CHKWL", 0, tst_wlan_struct, ": Chk wlan struct"} +}; diff --git a/project/src/tcpsrv/tcp_srv_conn.c b/project/src/tcpsrv/tcp_srv_conn.c new file mode 100644 index 0000000..35760ee --- /dev/null +++ b/project/src/tcpsrv/tcp_srv_conn.c @@ -0,0 +1,1269 @@ +/****************************************************************************** + * FileName: tcp_srv_conn.c + * TCP Ñервачек Ð´Ð»Ñ ESP8266 + * PV` ver1.0 20/12/2014 + ******************************************************************************/ +#include "user_config.h" +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +//#include "bios.h" +//#include "sdk/add_func.h" +//#include "osapi.h" +//#include "user_interface.h" +//#include +//#include "rtl8195a/rtl_common.h" +//#include "rtl_lib.h" +#include "lwip/tcp.h" +#include "lwip/tcp_impl.h" +#include "lwip/memp.h" +#include "lwip/ip_addr.h" +#include "flash_eep.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "rtl8195a/rtl_libc.h" +#include "esp_comp.h" +//#include "web_iohw.h" +//#include "wifi.h" + +#ifdef CONFIG_DEBUG_LOG +#define DEBUGSOO 2 // уровень вывода отладочной инфы по умолчанию = 2, =1 только error +#else +#define DEBUGSOO 0 +#endif +// Lwip funcs - http://www.ecoscentric.com/ecospro/doc/html/ref/lwip.html + +#define TCP_SRV_BSSDATA_ATTR +#define TCP_SRV_RODATA_ATTR +#define TCP_SRV_CODE_ATTR + +#define ts_printf(...) rtl_printf(__VA_ARGS__) +#define system_get_free_heap_size xPortGetFreeHeapSize + +#define os_free(p) vPortFree(p) +#define os_malloc(p) pvPortMalloc(p) +#define os_zalloc(p) pvPortZalloc(p) +#define os_realloc(p,s) pvPortReAlloc(p,s) +/* +extern __rtl_memsetw_v1_00(void *, uint32, size_t); + +void *pvPortZalloc( size_t xWantedSize ) +{ + void *pvReturn = pvPortMalloc(xWantedSize); + if(pvReturn != NULL) __rtl_memsetw_v1_00(pvReturn, 0, (xWantedSize + 3)>>2); + return pvReturn; +} +*/ + +TCP_SERV_CFG *phcfg TCP_SRV_BSSDATA_ATTR; // = NULL; // начальный указатель в памÑти на Ñтруктуры открытых Ñервачков + +#if DEBUGSOO > 0 +const uint8 txt_tcpsrv_NULL_pointer[] TCP_SRV_RODATA_ATTR = "tcpsrv: NULL pointer!\n"; +const uint8 txt_tcpsrv_already_initialized[] TCP_SRV_RODATA_ATTR = "tcpsrv: already initialized!\n"; +const uint8 txt_tcpsrv_out_of_mem[] TCP_SRV_RODATA_ATTR = "tcpsrv: out of mem!\n"; +#endif + +#define mMIN(a, b) ((a 2 +static const char srvContenErr00[] TCP_SRV_RODATA_ATTR = "Ok"; // ERR_OK 0 +static const char srvContenErr01[] TCP_SRV_RODATA_ATTR = "Out of memory error"; // ERR_MEM -1 +static const char srvContenErr02[] TCP_SRV_RODATA_ATTR = "Buffer error"; // ERR_BUF -2 +static const char srvContenErr03[] TCP_SRV_RODATA_ATTR = "Timeout"; // ERR_TIMEOUT -3 +static const char srvContenErr04[] TCP_SRV_RODATA_ATTR = "Routing problem"; // ERR_RTE -4 +static const char srvContenErr05[] TCP_SRV_RODATA_ATTR = "Operation in progress"; // ERR_INPROGRESS -5 +static const char srvContenErr06[] TCP_SRV_RODATA_ATTR = "Illegal value"; // ERR_VAL -6 +static const char srvContenErr07[] TCP_SRV_RODATA_ATTR = "Operation would block"; // ERR_WOULDBLOCK -7 +static const char srvContenErr08[] TCP_SRV_RODATA_ATTR = "Connection aborted"; // ERR_ABRT -8 +static const char srvContenErr09[] TCP_SRV_RODATA_ATTR = "Connection reset"; // ERR_RST -9 +static const char srvContenErr10[] TCP_SRV_RODATA_ATTR = "Connection closed"; // ERR_CLSD -10 +static const char srvContenErr11[] TCP_SRV_RODATA_ATTR = "Not connected"; // ERR_CONN -11 +static const char srvContenErr12[] TCP_SRV_RODATA_ATTR = "Illegal argument"; // ERR_ARG -12 +static const char srvContenErr13[] TCP_SRV_RODATA_ATTR = "Address in use"; // ERR_USE -13 +static const char srvContenErr14[] TCP_SRV_RODATA_ATTR = "Low-level netif error"; // ERR_IF -14 +static const char srvContenErr15[] TCP_SRV_RODATA_ATTR = "Already connected"; // ERR_ISCONN -15 +const char * srvContenErr[] = { + srvContenErr00, + srvContenErr01, + srvContenErr02, + srvContenErr03, + srvContenErr04, + srvContenErr05, + srvContenErr06, + srvContenErr07, + srvContenErr08, + srvContenErr09, + srvContenErr10, + srvContenErr11, + srvContenErr12, + srvContenErr13, + srvContenErr14, + srvContenErr15 +}; +#endif +static const char srvContenErrX[] TCP_SRV_RODATA_ATTR = "?"; +/****************************************************************************** + * FunctionName : tspsrv_error_msg + * Description : Ñтрока ошибки по номеру + * Parameters : LwIP err + * Returns : указатель на Ñтроку ошибки + *******************************************************************************/ +char * tspsrv_error_msg(err_t err) +{ + if((err > -16) && (err < 1)) { +#ifdef LWIP_DEBUG + return lwip_strerr(err); +#else + return srvContenErr[-err]; +#endif + } + else return srvContenErrX; +} + +extern const char * const tcp_state_str[]; +/****************************************************************************** + * FunctionName : tspsrv_tcp_state_msg + * Description : Ñтрока tcp_state по номеру + * Parameters : LwIP tcp_state + * Returns : указатель на Ñтроку + *******************************************************************************/ +char * tspsrv_tcp_state_msg(enum tcp_state state) +{ + if(state > TIME_WAIT && state < CLOSED) return srvContenErrX; + return tcp_state_str[state]; +} +/****************************************************************************** + * FunctionName : tspsrv_tcp_state_msg + * Description : Ñтрока tcp_state по номеру + * Parameters : LwIP tcp_state + * Returns : указатель на Ñтроку + *******************************************************************************/ +static char *msg_srvconn_state[] = { + "NONE", + "CLOSEWAIT", + "CLIENT", + "LISTEN", + "CONNECT", + "CLOSED" +}; +char * tspsrv_srvconn_state_msg(enum srvconn_state state) +{ + if(state > SRVCONN_CLOSED && state < SRVCONN_NONE) return srvContenErrX; + return msg_srvconn_state[state]; +} +//#endif +/****************************************************************************** + * FunctionName : tcpsrv_print_remote_info + * Description : выводит remote_ip:remote_port [conn_count] ts_printf("srv x.x.x.x:x [n] ") + * Parameters : TCP_SERV_CONN * ts_conn + * Returns : none + *******************************************************************************/ +void TCP_SRV_CODE_ATTR tcpsrv_print_remote_info(TCP_SERV_CONN *ts_conn) { +//#if DEBUGSOO > 0 + uint16 port; + if(ts_conn->pcb != NULL) port = ts_conn->pcb->local_port; + else port = ts_conn->pcfg->port; + ts_printf("srv[%u] %d.%d.%d.%d:%d [%d] ", port, + ts_conn->remote_ip.b[0], ts_conn->remote_ip.b[1], + ts_conn->remote_ip.b[2], ts_conn->remote_ip.b[3], + ts_conn->remote_port, ts_conn->pcfg->conn_count); +//#endif +} +/****************************************************************************** + * Default call back functions + ******************************************************************************/ +//------------------------------------------------------------------------------ +void TCP_SRV_CODE_ATTR tcpsrv_disconnect_calback_default(TCP_SERV_CONN *ts_conn) { + ts_conn->pcb = NULL; +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("disconnect\n"); +#endif +} +//------------------------------------------------------------------------------ +err_t TCP_SRV_CODE_ATTR tcpsrv_listen_default(TCP_SERV_CONN *ts_conn) { +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("listen\n"); +#endif + return ERR_OK; +} +//------------------------------------------------------------------------------ +err_t TCP_SRV_CODE_ATTR tcpsrv_connected_default(TCP_SERV_CONN *ts_conn) { +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("connected\n"); +#endif + return ERR_OK; +} +//------------------------------------------------------------------------------ +err_t TCP_SRV_CODE_ATTR tcpsrv_sent_callback_default(TCP_SERV_CONN *ts_conn) { +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("sent_cb\n"); +#endif + return ERR_OK; +} +//------------------------------------------------------------------------------ +err_t TCP_SRV_CODE_ATTR tcpsrv_received_data_default(TCP_SERV_CONN *ts_conn) { +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("received, buffer %d bytes\n", ts_conn->sizei); +#endif + return ERR_OK; +} +/****************************************************************************** + * FunctionName : tcpsrv_check_max_tm_tcp_pcb + * Description : Ограничение неактивных pcb в ÑпиÑках lwip до MAX_TIME_WAIT_PCB + * Parameters : none + * Returns : none + *******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_check_max_tm_tcp_pcb(void) +{ + struct tcp_pcb *pcb; + int i = 0; + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) i++; +#if DEBUGSOO > 4 + ts_printf("tcpsrv: check %d tm pcb\n", i); +#endif + while(i-- > MAX_TIME_WAIT_PCB) { + struct tcp_pcb *inactive = NULL; + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + inactive = pcb; + } +#if DEBUGSOO > 3 + ts_printf("tcpsrv: kill %d tm pcb\n", i); +#endif + if(inactive != NULL) { + tcp_pcb_remove(&tcp_tw_pcbs, inactive); + memp_free(MEMP_TCP_PCB, inactive); + } + } +} +/****************************************************************************** + * FunctionName : find_tcp_pcb + * Description : поиÑк pcb в ÑпиÑках lwip + * Parameters : TCP_SERV_CONN * ts_conn + * Returns : *pcb or NULL + *******************************************************************************/ +struct tcp_pcb * TCP_SRV_CODE_ATTR find_tcp_pcb(TCP_SERV_CONN * ts_conn) { + struct tcp_pcb *pcb = ts_conn->pcb; + if (pcb) { + uint16 remote_port = ts_conn->remote_port; + uint16 local_port = ts_conn->pcfg->port; + uint32 ip = ts_conn->remote_ip.dw; + if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) + && (pcb->remote_ip.addr == ip)) + return pcb; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { + if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) + && (pcb->remote_ip.addr == ip)) + return pcb; + }; + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { + if ((pcb->remote_port == remote_port) && (pcb->local_port == local_port) + && (pcb->remote_ip.addr == ip)) + return pcb; + }; + } + return NULL; +} +/****************************************************************************** + * FunctionName : tcpsrv_disconnect + * Description : disconnect + * Parameters : TCP_SERV_CONN * ts_conn + * Returns : none + *******************************************************************************/ +void TCP_SRV_CODE_ATTR tcpsrv_disconnect(TCP_SERV_CONN * ts_conn) { + if (ts_conn == NULL || ts_conn->state == SRVCONN_CLOSEWAIT) return; // уже закрываетÑÑ + ts_conn->pcb = find_tcp_pcb(ts_conn); // ещё жива Ð´Ð°Ð½Ð½Ð°Ñ pcb ? + if (ts_conn->pcb != NULL) tcpsrv_server_close(ts_conn); +} +/****************************************************************************** + * FunctionName : internal fun: tcpsrv_int_sent_data + * Description : передача данных (не буферизированнаÑ! только передача в tcp Lwip-у) + * вызывать только из call back Ñ Ñ‚ÐµÐºÑƒÑ‰Ð¸Ð¼ pcb! + * Parameters : TCP_SERV_CONN * ts_conn + * uint8* psent - буфер Ñ Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ + * uint16 length - кол-во передаваемых байт + * Returns : tcp error + ******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_int_sent_data(TCP_SERV_CONN * ts_conn, uint8 *psent, uint16 length) { + err_t err = ERR_ARG; + if(ts_conn == NULL) return err; + ts_conn->pcb = find_tcp_pcb(ts_conn); // ещё жива Ð´Ð°Ð½Ð½Ð°Ñ pcb ? + struct tcp_pcb *pcb = ts_conn->pcb; + if(pcb == NULL || ts_conn->state == SRVCONN_CLOSEWAIT) return ERR_CONN; + ts_conn->flag.busy_bufo = 1; // буфер bufo занÑÑ‚ + if(tcp_sndbuf(pcb) < length) { +#if DEBUGSOO > 1 + ts_printf("sent_data length (%u) > sndbuf (%u)!\n", length, tcp_sndbuf(pcb)); +#endif + return err; + } + if (length) { + if(ts_conn->flag.nagle_disabled) tcp_nagle_disable(pcb); + err = tcp_write(pcb, psent, length, 1); + if (err == ERR_OK) { + ts_conn->ptrtx = psent + length; + ts_conn->cntro -= length; + ts_conn->flag.wait_sent = 1; // ожидать Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ (sent_cb) + err = tcp_output(pcb); // передать что влезло + } else { // ts_conn->state = SRVCONN_CLOSE; +#if DEBUGSOO > 1 + ts_printf("tcp_write(%p, %p, %u) = %d! pbuf = %u\n", pcb, psent, length, err, tcp_sndbuf(pcb)); +#endif + ts_conn->flag.wait_sent = 0; + tcpsrv_server_close(ts_conn); + }; + } else { // Ñоздать вызов tcpsrv_server_sent() + tcp_nagle_enable(pcb); + err = tcp_output(pcb); // передать пуÑтое + } + ts_conn->flag.busy_bufo = 0; // буфер bufo Ñвободен + return err; +} +/****************************************************************************** + * FunctionName : tcpsrv_server_sent + * Description : Data has been sent and acknowledged by the remote host. + * This means that more data can be sent. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb for which data has been acknowledged + * len -- The amount of bytes acknowledged + * Returns : ERR_OK: try to send some data by calling tcp_output + * ERR_ABRT: if you have called tcp_abort from within the function! + ******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_server_sent(void *arg, struct tcp_pcb *pcb, u16_t len) { + sint8 ret_err = ERR_OK; + TCP_SERV_CONN * ts_conn = arg; + if (ts_conn == NULL || pcb == NULL) return ERR_ARG; + ts_conn->state = SRVCONN_CONNECT; + ts_conn->recv_check = 0; + ts_conn->flag.wait_sent = 0; // блок передан + if ((ts_conn->flag.tx_null == 0) + && (ts_conn->pcfg->func_sent_cb != NULL)) { + ret_err = ts_conn->pcfg->func_sent_cb(ts_conn); + } + return ret_err; +} +/****************************************************************************** + * FunctionName : recv_trim_bufi + * Description : увеличение размера буфера приема на newadd + * Parameters : ts_conn, newadd - требуемое дополнение размера буфера + * Returns : err_t + ******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR recv_trim_bufi(TCP_SERV_CONN * ts_conn, uint32 newadd) +{ + uint32 len = 0; +// while(ts_conn->flag.busy_bufi) vTaskDelay(1); + ts_conn->flag.busy_bufi = 1; // идет обработка bufi + if(newadd) { + // требуетÑÑ Ð´Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ðµ размера буфера + if(ts_conn->pbufi != NULL) { // уже еÑÑ‚ÑŒ какой-то буфер + if ((ts_conn->flag.rx_buf) && ts_conn->cntri < ts_conn->sizei) { // буфер не пуÑÑ‚ + len = ts_conn->sizei - ts_conn->cntri; // размер необработанных данных + if(ts_conn->cntri >= newadd) { // кол-во обработанных байт (пуÑтого меÑта в буфере) больше чем дополнение + os_memcpy(ts_conn->pbufi, &ts_conn->pbufi[ts_conn->cntri], len ); // Ñкопировать необработанную чаÑÑ‚ÑŒ в начало буфера + ts_conn->sizei = len; + if(ts_conn->cntri != newadd) { // кол-во обработанных байт (пуÑтого меÑта в буфере) равно требуемому дополнению + // уменьшение занимаемого буфера в памÑти + len += newadd; // + ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, len + 1); //mem_trim(ts_conn->pbufi, len); + if(ts_conn->pbufi == NULL) { +#if DEBUGSOO > 2 + ts_printf("memtrim err %p[%d] ", ts_conn->pbufi, len + 1); +#endif + return ERR_MEM; + } +#if DEBUGSOO > 2 + ts_printf("memi%p[%d] ", ts_conn->pbufi, len); +#endif + } + ts_conn->pbufi[len] = '\0'; // вмеÑто os_zalloc; + ts_conn->cntri = 0; + ts_conn->flag.busy_bufi = 0; // обработка bufi окончена + return ERR_OK; + } + } + else { // буфер пуÑÑ‚ или не тот режим + // удалить буфер + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + } + } + // подготовка нового буфера, еÑли его нет или не лезет дополнение + uint8 * newbufi = (uint8 *) os_malloc(len + newadd + 1); + if (newbufi == NULL) { +#if DEBUGSOO > 2 + ts_printf("memerr %p[%d] ", newbufi, len + newadd); +#endif + ts_conn->flag.busy_bufi = 0; // обработка bufi окончена + return ERR_MEM; + } +#if DEBUGSOO > 2 + ts_printf("memi%p[%d] ", newbufi, len + newadd); +#endif + newbufi[len + newadd] = '\0'; // вмеÑто os_zalloc + if(len) { + os_memcpy(newbufi, &ts_conn->pbufi[ts_conn->cntri], len); + os_free(ts_conn->pbufi); + }; + ts_conn->pbufi = newbufi; + ts_conn->sizei = len; + } + else { + // Ð¾Ð¿Ñ‚Ð¸Ð¼Ð¸Ð·Ð°Ñ†Ð¸Ñ Ð±ÑƒÑ„ÐµÑ€Ð° (уменьшение занимаемого размера) + if((!ts_conn->flag.rx_buf) || ts_conn->cntri >= ts_conn->sizei) { // буфер обработан + ts_conn->sizei = 0; + if (ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); // оÑвободить буфер. + ts_conn->pbufi = NULL; + }; + } + else if(ts_conn->cntri) { // еÑÑ‚ÑŒ обработанные данные + // уменьшение занимаемого размера на обработанные данные + len = ts_conn->sizei - ts_conn->cntri; + os_memcpy(ts_conn->pbufi, &ts_conn->pbufi[ts_conn->cntri], len ); + ts_conn->sizei = len; + ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, len + 1); //mem_trim(ts_conn->pbufi, len); + if(ts_conn->pbufi == NULL) { +#if DEBUGSOO > 2 + ts_printf("memtrim err %p[%d] ", ts_conn->pbufi, len + 1); +#endif + ts_conn->flag.busy_bufi = 0; // обработка bufi окончена + return ERR_MEM; + } + ts_conn->pbufi[len] = '\0'; // вмеÑто os_zalloc; + } + } + ts_conn->cntri = 0; + ts_conn->flag.busy_bufi = 0; // обработка bufi окончена + return ERR_OK; +} +/****************************************************************************** + * FunctionName : tcpsrv_server_recv + * Description : Data has been received on this pcb. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb which received data + * p -- The received data (or NULL when the connection has been closed!) + * err -- An error code if there has been an error receiving + * Returns : ERR_ABRT: if you have called tcp_abort from within the function! + ******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { + // Sets the callback function that will be called when new data arrives on the connection associated with pcb. + // The callback function will be passed a NULL pbuf to indicate that the remote host has closed the connection. + TCP_SERV_CONN * ts_conn = arg; + if (ts_conn == NULL || pcb == NULL) return ERR_ARG; + +// if(syscfg.cfg.b.hi_speed_enable) set_cpu_clk(); + + if (p == NULL || err != ERR_OK) { // the remote host has closed the connection. + tcpsrv_server_close(ts_conn); + return err; + }; + // еÑли нет функции обработки или ожидаем Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ ÑоединениÑ, то принимаем данные в трубу + if ((ts_conn->flag.rx_null != 0) || (ts_conn->pcfg->func_recv == NULL) + || (ts_conn->state == SRVCONN_CLOSEWAIT)) { // Ñоединение закрыто? нет. + tcp_recved(pcb, p->tot_len + ts_conn->unrecved_bytes); // Ñообщает Ñтеку, что Ñъели len и можно поÑылать ACK и принимать новые данные. + ts_conn->unrecved_bytes = 0; +#if DEBUGSOO > 3 + ts_printf("rec_null %d of %d\n", ts_conn->cntri, p->tot_len); +#endif + pbuf_free(p); // данные выели + return ERR_OK; + }; + ts_conn->state = SRVCONN_CONNECT; // был прием + ts_conn->recv_check = 0; // Ñчет времени до авто-Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ñ Ð½ÑƒÐ»Ñ + if(p->tot_len) { + err = recv_trim_bufi(ts_conn, p->tot_len); // увеличить буфер + if(err != ERR_OK) return err; + // добавление вÑего что отдал Lwip в буфер + uint32 len = pbuf_copy_partial(p, &ts_conn->pbufi[ts_conn->sizei], p->tot_len, 0); + ts_conn->sizei += len; + pbuf_free(p); // вÑе данные выели + if(!ts_conn->flag.rx_buf) { + tcp_recved(pcb, len); // Ñообщает Ñтеку, что Ñъели len и можно поÑылать ACK и принимать новые данные. + } + else ts_conn->unrecved_bytes += len; +#if DEBUGSOO > 3 + ts_printf("rec %d of %d :\n", p->tot_len, ts_conn->sizei); +#endif + err = ts_conn->pcfg->func_recv(ts_conn); + err_t err2 = recv_trim_bufi(ts_conn, 0); // оптимизировать размер занимаемый буфером + if(err2 != ERR_OK) return err2; + } + return err; +} +/****************************************************************************** + * FunctionName : tcpsrv_unrecved_win + * Description : Update the TCP window. + * This can be used to throttle data reception (e.g. when received data is + * programmed to flash and data is received faster than programmed). + * Parameters : TCP_SERV_CONN * ts_conn + * Returns : none + * ПоÑле Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ throttle, будет принÑто до 5840 (MAX WIN) + 1460 (MSS) байт? + ******************************************************************************/ +void TCP_SRV_CODE_ATTR tcpsrv_unrecved_win(TCP_SERV_CONN *ts_conn) { + if (ts_conn->unrecved_bytes) { + // update the TCP window +#if DEBUGSOO > 3 + ts_printf("recved_bytes=%d\n", ts_conn->unrecved_bytes); +#endif +#if 1 + if(ts_conn->pcb != NULL) tcp_recved(ts_conn->pcb, ts_conn->unrecved_bytes); +#else + struct tcp_pcb *pcb = find_tcp_pcb(ts_conn); // уже закрыто? + if(pcb != NULL) tcp_recved(ts_conn->pcb, ts_conn->unrecved_bytes); +#endif + } + ts_conn->unrecved_bytes = 0; +} +/****************************************************************************** + * FunctionName : tcpsrv_disconnect + * Description : disconnect with host + * Parameters : ts_conn + * Returns : none + ******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_disconnect_successful(TCP_SERV_CONN * ts_conn) { + ts_conn->pcb = NULL; + if(ts_conn->flag.client && ts_conn->flag.client_reconnect) + tcpsrv_client_reconnect(ts_conn); + else + tcpsrv_list_delete(ts_conn); // remove the node from the server's connection list +} +/****************************************************************************** + * FunctionName : tcpsrv_server_close + * Description : The connection shall be actively closed. + * Parameters : ts_conn + * Returns : none + ******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_server_close(TCP_SERV_CONN * ts_conn) { + + struct tcp_pcb *pcb = ts_conn->pcb; + if(pcb == NULL) { +#if DEBUGSOO > 3 + ts_printf("tcpsrv_server_close, state: %s, pcb = NULL!\n", tspsrv_srvconn_state_msg(ts_conn->state)); +#endif + return; + } +#if DEBUGSOO > 3 + ts_printf("tcpsrv_server_close[%d], state: %s\n", pcb->local_port, tspsrv_srvconn_state_msg(ts_conn->state)); +#endif + if(ts_conn->state != SRVCONN_CLOSEWAIT && ts_conn->state != SRVCONN_CLOSED) { +#if DEBUGSOO > 2 + tcpsrv_print_remote_info(ts_conn); + ts_printf("start close...\n"); +#endif + ts_conn->state = SRVCONN_CLOSEWAIT; + ts_conn->recv_check = 0; + ts_conn->flag.wait_sent = 0; // блок передан + ts_conn->flag.rx_null = 1; // отключение вызова func_received_data() и прием в null + ts_conn->flag.tx_null = 1; // отключение вызова func_sent_callback() и передача в null + // отключение функций приема, передачи и poll + tcp_recv(pcb, NULL); // отключение приема + tcp_sent(pcb, NULL); // отключение передачи + tcp_poll(pcb, NULL, 0); // отключение poll + tcp_err(pcb, NULL); + // + if(ts_conn->unrecved_bytes) { + tcp_recved(pcb, TCP_WND); + ts_conn->unrecved_bytes = 0; + } + // оÑвободить буфера + if (ts_conn->pbufo != NULL) { + os_free(ts_conn->pbufo); + ts_conn->pbufo = NULL; + } + ts_conn->sizeo = 0; + ts_conn->cntro = 0; + if (ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + } + ts_conn->sizei = 0; + ts_conn->cntri = 0; + } + if(ts_conn->state == SRVCONN_CLOSEWAIT || ts_conn->state == SRVCONN_CLOSED) { + if (pcb->state == CLOSED || pcb->state == TIME_WAIT) { + /*remove the node from the server's active connection list*/ +#if DEBUGSOO > 3 + ts_printf("close[%d]\n", pcb->local_port); +#endif + tcpsrv_disconnect_successful(ts_conn); + } else { + if (ts_conn->recv_check > 3) { // Ñчет до принудительного Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ 3 раза по TCP_SRV_CLOSE_WAIT +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("tcp_abandon!\n"); +#endif + tcp_poll(pcb, NULL, 0); +/// tcp_err(pcb, NULL); + tcp_abandon(pcb, 0); +// ts_conn->pcb = NULL; + // remove the node from the server's active connection list + tcpsrv_disconnect_successful(ts_conn); + } + else if (tcp_close(pcb) != ERR_OK) { // поÑлать закрытие ÑоединениÑ, closing failed +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("+ncls+[%d]\n", pcb->local_port); +#endif + // closing failed, try again later + tcp_poll(pcb, tcpsrv_poll, 2*(TCP_SRV_CLOSE_WAIT)); + } + else { +#if DEBUGSOO > 3 + ts_printf("tcp_close[%d] ok.\n", pcb->local_port); +#endif + tcpsrv_disconnect_successful(ts_conn); + } + } + } +#if DEBUGSOO > 2 + else { + tcpsrv_print_remote_info(ts_conn); + ts_printf("already close!\n"); + } +#endif +} +/****************************************************************************** + * FunctionName : tcpsrv_poll (server and client) + * Description : The poll function is called every 1 second. + * This could be increased, but we don't want to waste resources for bad connections. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb for which data has been acknowledged + * Returns : ERR_OK: try to send some data by calling tcp_output + * ERR_ABRT: if you have called tcp_abort from within the function! + *******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_poll(void *arg, struct tcp_pcb *pcb) { + TCP_SERV_CONN * ts_conn = arg; + if (ts_conn == NULL) { +#if DEBUGSOO > 3 + ts_printf("poll, ts_conn = NULL! - abandon\n"); +#endif + tcp_poll(pcb, NULL, 0); + tcp_abandon(pcb, 0); + return ERR_ABRT; + } +#if DEBUGSOO > 3 + tcpsrv_print_remote_info(ts_conn); + ts_printf("poll: %d %s#%s, %d,%d, pcb:%p\n", ts_conn->recv_check, tspsrv_srvconn_state_msg(ts_conn->state), tspsrv_tcp_state_msg(pcb->state), ts_conn->pcfg->time_wait_rec, ts_conn->pcfg->time_wait_cls), pcb; +#endif + if (ts_conn->pcb != NULL && ts_conn->state != SRVCONN_CLOSEWAIT) { + ts_conn->recv_check++; + if (pcb->state == ESTABLISHED) { + if ((ts_conn->state == SRVCONN_LISTEN + && (ts_conn->pcfg->time_wait_rec) + && ts_conn->recv_check > ts_conn->pcfg->time_wait_rec) + || (ts_conn->state == SRVCONN_CONNECT + && (ts_conn->pcfg->time_wait_cls) + && ts_conn->recv_check > ts_conn->pcfg->time_wait_cls)) { + if(ts_conn->flag.client) tcpsrv_client_reconnect(ts_conn); + else tcpsrv_server_close(ts_conn); + } +// else tcpsrv_server_close(ts_conn); + } + else if(ts_conn->state == SRVCONN_CLIENT) { + if(ts_conn->pcfg->time_wait_rec == 0) { + if(ts_conn->recv_check > TCP_CLIENT_MAX_CONNECT_RETRY) + tcpsrv_client_reconnect(ts_conn); + } + else if(ts_conn->recv_check > ts_conn->pcfg->time_wait_rec) { + tcpsrv_client_reconnect(ts_conn); + } + } + else tcpsrv_server_close(ts_conn); + } + else tcpsrv_server_close(ts_conn); + return ERR_OK; +} +/****************************************************************************** + * FunctionName : tcpsrv_list_delete + * Description : remove the node from the connection list + * Parameters : ts_conn + * Returns : none + *******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_list_delete(TCP_SERV_CONN * ts_conn) { +// if (ts_conn == NULL) return; + if(ts_conn->state != SRVCONN_CLOSED) { + ts_conn->state = SRVCONN_CLOSED; // иÑключить повторное вхождение из запроÑов в func_discon_cb() + if (ts_conn->pcfg->func_discon_cb != NULL) + ts_conn->pcfg->func_discon_cb(ts_conn); + if(phcfg == NULL || ts_conn->pcfg == NULL) return; // еÑли в func_discon_cb() было вызвано tcpsrv_close_all() и Ñ‚.д. + } +#if DEBUGSOO > 3 + ts_printf("tcpsrv_list_delete (%p)\n", ts_conn->pcb); +#endif + TCP_SERV_CONN ** p = &ts_conn->pcfg->conn_links; + TCP_SERV_CONN *tcpsrv_cmp = ts_conn->pcfg->conn_links; + while (tcpsrv_cmp != NULL) { + if (tcpsrv_cmp == ts_conn) { + *p = ts_conn->next; + ts_conn->next = NULL; + ts_conn->pcfg->conn_count--; + if (ts_conn->linkd != NULL) { + os_free(ts_conn->linkd); + ts_conn->linkd = NULL; + } + if (ts_conn->pbufo != NULL) { + os_free(ts_conn->pbufo); + ts_conn->pbufo = NULL; + } + if (ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + } + os_free(ts_conn); + return; // break; + } + p = &tcpsrv_cmp->next; + tcpsrv_cmp = tcpsrv_cmp->next; + }; +} +/****************************************************************************** + * FunctionName : tcpsrv_client_reconnect (client) + * Description : оÑвобождение занимаемых реÑурÑов и + * включение задержки до Ñледующего tcpsrv_client_connect + * Parameters : ts_conn + * Returns : none + *******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_client_reconnect(TCP_SERV_CONN * ts_conn) +{ + if (ts_conn != NULL) { + if(ts_conn->state != SRVCONN_CLIENT) { // не уÑтановка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (клиент)? +#if DEBUGSOO > 3 + ts_printf("Client free\n"); +#endif + if(ts_conn->state != SRVCONN_CLOSED) { + ts_conn->state = SRVCONN_CLOSED; // иÑключить повторное вхождение из запроÑов в func_discon_cb() + if (ts_conn->pcfg->func_discon_cb != NULL) ts_conn->pcfg->func_discon_cb(ts_conn); + } + if (ts_conn->linkd != NULL) { + os_free(ts_conn->linkd); + ts_conn->linkd = NULL; + } + if (ts_conn->pbufo != NULL) { + os_free(ts_conn->pbufo); + ts_conn->pbufo = NULL; + } + if (ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + } + ts_conn->cntri = 0; + ts_conn->cntro = 0; + ts_conn->sizei = 0; + ts_conn->sizeo = 0; + ts_conn->unrecved_bytes = 0; + ts_conn->ptrtx = NULL; + ts_conn->flag = ts_conn->pcfg->flag; + } + if(++ts_conn->pcfg->conn_count == 0) ts_conn->pcfg->conn_count = 0xffff; + if(ts_conn->pcfg->conn_count < ts_conn->pcfg->max_conn || ts_conn->pcfg->max_conn == 0) { +#if DEBUGSOO > 1 + ts_printf("Waiting next connection %u sec...\n", TCP_CLIENT_NEXT_CONNECT_S); +#endif + tcpsrv_client_connect(ts_conn); + } + else tcpsrv_server_close(ts_conn); //tcpsrv_disconnect(ts_conn); + } +} +//----------------------------------------------------------------------------- +static void tspsrv_delete_pcb(TCP_SERV_CONN * ts_conn) +{ + struct tcp_pcb *pcb = find_tcp_pcb(ts_conn); + if(pcb) { + tcp_arg(pcb, NULL); + tcp_recv(pcb, NULL); + tcp_err(pcb, NULL); + tcp_poll(pcb, NULL, 0); + tcp_sent(pcb, NULL); + tcp_recved(pcb, TCP_WND); + tcp_close(pcb); + ts_conn->pcb = NULL; + } + tcpsrv_list_delete(ts_conn); +} +/****************************************************************************** + * FunctionName : tcpsrv_error (server and client) + * Description : The pcb had an error and is already deallocated. + * The argument might still be valid (if != NULL). + * Parameters : arg -- Additional argument to pass to the callback function + * err -- Error code to indicate why the pcb has been closed + * Returns : none + *******************************************************************************/ +static void TCP_SRV_CODE_ATTR tcpsrv_error(void *arg, err_t err) { + TCP_SERV_CONN * ts_conn = arg; +// struct tcp_pcb *pcb = NULL; + if (ts_conn != NULL) { +#if DEBUGSOO > 2 + tcpsrv_print_remote_info(ts_conn); + ts_printf("error %d (%s)\n", err, tspsrv_error_msg(err)); +#elif DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); +#ifdef LWIP_DEBUG + ts_printf("error %d (%s)\n", err, tspsrv_error_msg(err)); +#else + ts_printf("error %d\n", err); +#endif +#endif + if (ts_conn->state != SRVCONN_CLOSEWAIT) { + if(ts_conn->flag.client && // еÑли данное Ñоединение клиент + (ts_conn->flag.client_reconnect // вечный реконнект + || (ts_conn->state == SRVCONN_CLIENT // уÑтановка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ + && (ts_conn->pcfg->max_conn == 0 // макÑимальное кол-во одновременных Ñоединений + || ts_conn->recv_check < ts_conn->pcfg->max_conn)))) { + ts_conn->recv_check++; +#if DEBUGSOO > 3 + ts_printf("go_reconnect\n"); +#endif + tcpsrv_client_reconnect(ts_conn); + } + else if(ts_conn->pcb != NULL) { +// && ts_conn->state != SRVCONN_CLOSED) { +// && (ts_conn->state != SRVCONN_CONNECT || ts_conn->state == SRVCONN_LISTEN)) { +#if DEBUGSOO > 1 + ts_printf("eclose[%d]\n", ts_conn->pcfg->port); +#endif + tcpsrv_list_delete(ts_conn); // remove the node from the server's connection list + }; + }; + } +} +/****************************************************************************** + * FunctionName : tcpsrv_client_connect + * Returns : err + *******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_client_connect(TCP_SERV_CONN * ts_conn) +{ + err_t err = ERR_ARG; + if (ts_conn != NULL) { +#if DEBUGSOO > 3 + ts_printf("tcpsrv_client_connect()\n"); +#endif + struct tcp_pcb *pcb = ts_conn->pcb; + if(pcb != NULL) { // повторный? + pcb = find_tcp_pcb(ts_conn); // ещё жива pcb? + if(pcb != NULL) { + tcp_abandon(pcb, 0); +// ts_conn->pcb = NULL; + } + } + pcb = tcp_new(); + if(pcb != NULL) { + ts_conn->pcb = pcb; + ts_conn->state = SRVCONN_CLIENT; // уÑтановка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (клиент) +// ts_conn->recv_check = 0; + err_t err = tcp_bind(pcb, IP_ADDR_ANY, 0); // Binds pcb to a local IP address and new port number. // &netif_default->ip_addr +#if DEBUGSOO > 2 + ts_printf("tcp_bind() = %s, port = %d\n", tspsrv_error_msg(err), ts_conn->pcb->local_port); +#endif + if (err == ERR_OK) { // If another connection is bound to the same port, the function will return ERR_USE, otherwise ERR_OK is returned. + ts_conn->pcfg->port = ts_conn->pcb->local_port; + tcp_arg(pcb, ts_conn); // Allocate client-specific session structure, set as callback argument + // Set up the various callback functions + tcp_err(pcb, tcpsrv_error); + tcp_poll(pcb, tcpsrv_poll, 2); // every 1/2 seconds + err = tcp_connect(pcb, (ip_addr_t *)&ts_conn->remote_ip, ts_conn->remote_port, tcpsrv_connected); +#if DEBUGSOO > 2 + ts_printf("tcp_connect() = %s\n", tspsrv_error_msg(err)); +#endif + if(err == ERR_OK) { +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("start client - Ok\n"); +#endif + return err; + } + } + tcp_abandon(pcb, 0); + } + else err = ERR_MEM; // ERR_CONN; + ts_conn->pcb = NULL; +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("start client - error!\n"); +#endif + } + return err; +} +/****************************************************************************** + tcpsrv_connected_fn (client) + *******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_connected(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + TCP_SERV_CONN * ts_conn = arg; + err_t merr = ERR_OK; + if (ts_conn != NULL) { +// os_timer_disarm(&ts_conn->ptimer); + tcp_err(tpcb, tcpsrv_error); + ts_conn->state = SRVCONN_LISTEN; + ts_conn->recv_check = 0; + tcp_sent(tpcb, tcpsrv_server_sent); + tcp_recv(tpcb, tcpsrv_server_recv); + tcp_poll(tpcb, tcpsrv_poll, 2); // every 1/2 seconds + if(ts_conn->pcfg->func_listen != NULL) merr = ts_conn->pcfg->func_listen(ts_conn); + else { +#if DEBUGSOO > 2 + tcpsrv_print_remote_info(ts_conn); + char serr[24]; + if((err > -16) && (err < 1)) { + os_memcpy(serr, srvContenErr[-err], 24); + } + else { + serr[0] = '?'; + serr[1] = '\0'; + } + ts_printf("error %d (%s)\n", err, serr); +#elif DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + ts_printf("connected, error %d\n", err); +#endif + // test + // tcpsrv_int_sent_data(ts_conn, wificonfig.st.config.password, os_strlen(wificonfig.st.config.password)); + } + } + return merr; +} +/****************************************************************************** + * FunctionName : tcpsrv_tcp_accept + * Description : A new incoming connection has been accepted. + * Parameters : arg -- Additional argument to pass to the callback function + * pcb -- The connection pcb which is accepted + * err -- An unused error code, always ERR_OK currently + * Returns : acception result + *******************************************************************************/ +static err_t TCP_SRV_CODE_ATTR tcpsrv_server_accept(void *arg, struct tcp_pcb *pcb, err_t err) { + struct tcp_pcb_listen *lpcb = (struct tcp_pcb_listen*) arg; + TCP_SERV_CFG *p = tcpsrv_server_port2pcfg(pcb->local_port); + TCP_SERV_CONN * ts_conn; + if (p == NULL) return ERR_ARG; + if (system_get_free_heap_size() < p->min_heap) { +#if DEBUGSOO > 1 + ts_printf("srv[%u] new listen - low heap size!\n", p->port); +#endif + return ERR_MEM; + } + if (p->conn_count >= p->max_conn) { + if(p->flag.srv_reopen) { +#if DEBUGSOO > 1 + ts_printf("srv[%u] reconnect!\n", p->port); +#endif + if (p->conn_links != NULL) { + tspsrv_delete_pcb(p->conn_links); + }; + } + else { +#if DEBUGSOO > 1 + ts_printf("srv[%u] new listen - max connection!\n", p->port); +#endif + return ERR_CONN; + } + } + ts_conn = (TCP_SERV_CONN *) os_zalloc(sizeof(TCP_SERV_CONN)); + if (ts_conn == NULL) { +#if DEBUGSOO > 1 + ts_printf("srv[%u] new listen - out of mem!\n", ts_conn->pcfg->port); +#endif + return ERR_MEM; + } + ts_conn->pcfg = p; + // перенеÑти флаги по умолчанию на данное Ñоединение + ts_conn->flag = p->flag; + tcp_accepted(lpcb); // Decrease the listen backlog counter + // tcp_setprio(pcb, TCP_PRIO_MIN); // Set priority ? + // init/copy data ts_conn + ts_conn->pcb = pcb; + ts_conn->remote_port = pcb->remote_port; + ts_conn->remote_ip.dw = pcb->remote_ip.addr; +// *(uint16 *) &ts_conn->flag = 0; //zalloc +// ts_conn->recv_check = 0; //zalloc +// ts_conn->linkd = NULL; //zalloc + // Insert new ts_conn + ts_conn->next = ts_conn->pcfg->conn_links; + ts_conn->pcfg->conn_links = ts_conn; + ts_conn->pcfg->conn_count++; + ts_conn->state = SRVCONN_LISTEN; + // Tell TCP that this is the structure we wish to be passed for our callbacks. + tcp_arg(pcb, ts_conn); + // Set up the various callback functions + tcp_err(pcb, tcpsrv_error); + tcp_sent(pcb, tcpsrv_server_sent); + tcp_recv(pcb, tcpsrv_server_recv); + tcp_poll(pcb, tcpsrv_poll, 2); /* every 1/2 seconds */ +#if MEMP_MEM_MALLOC // tcp_alloc() уже управлÑет убийÑтвом TIME_WAIT еÑли MEMP_MEM_MALLOC == 0 + if(ts_conn->flag.pcb_time_wait_free) tcpsrv_check_max_tm_tcp_pcb(); + // http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html +#endif + if (p->func_listen != NULL) + return p->func_listen(ts_conn); + return ERR_OK; +} +/****************************************************************************** + * FunctionName : tcpsrv_server_port2pcfg + * Description : поиÑк конфига servera по порту + * Parameters : номер порта + * Returns : указатель на TCP_SERV_CFG или NULL + *******************************************************************************/ +TCP_SERV_CFG * TCP_SRV_CODE_ATTR tcpsrv_server_port2pcfg(uint16 portn) { + TCP_SERV_CFG * p; + for (p = phcfg; p != NULL; p = p->next) + if (p->port == portn + && (!(p->flag.client))) + return p; + return NULL; +} +/****************************************************************************** + * FunctionName : tcpsrv_client_ip_port2conn + * Description : поиÑк конфига clienta по ip + порту + * Parameters : номер порта + * Returns : указатель на TCP_SERV_CFG или NULL + *******************************************************************************/ +TCP_SERV_CFG * TCP_SRV_CODE_ATTR tcpsrv_client_ip_port2conn(uint32 ip, uint16 portn) { + TCP_SERV_CFG * p; + for (p = phcfg; p != NULL; p = p->next) + if (p->flag.client + && p->conn_links != NULL + && p->conn_links->remote_ip.dw == ip + && p->conn_links->remote_port == portn) + return p; + return NULL; +} +/****************************************************************************** + tcpsrv_init server or client. + client -> port = 1 ? + *******************************************************************************/ +TCP_SERV_CFG * TCP_SRV_CODE_ATTR tcpsrv_init(uint16 portn) { + // if (portn == 0) portn = 80; + if (portn == 0) return NULL; + TCP_SERV_CFG * p; + for (p = phcfg; p != NULL; p = p->next) { + if (p->port == portn) { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_already_initialized); +#endif + return NULL; + } + } + p = (TCP_SERV_CFG *) os_zalloc(sizeof(TCP_SERV_CFG)); + if (p == NULL) { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_out_of_mem); +#endif + return NULL; + } + p->port = portn; + p->conn_count = 0; + p->min_heap = TCP_SRV_MIN_HEAP_SIZE; + p->time_wait_rec = TCP_SRV_RECV_WAIT; + p->time_wait_cls = TCP_SRV_END_WAIT; + // p->phcfg->conn_links = NULL; // zalloc + // p->pcb = NULL; // zalloc + // p->lnk = NULL; // zalloc + if(portn > ID_CLIENTS_PORT) { + p->max_conn = TCP_SRV_MAX_CONNECTIONS; + p->func_listen = tcpsrv_listen_default; + } + else { + p->max_conn = TCP_CLIENT_MAX_CONNECT_RETRY; + p->flag.client = 1; // данное Ñоединение не Ñервер, а клиент! + // insert new tcpsrv_config + p->next = phcfg; + phcfg = p; + } + p->func_discon_cb = tcpsrv_disconnect_calback_default; + p->func_sent_cb = tcpsrv_sent_callback_default; + p->func_recv = tcpsrv_received_data_default; + return p; +} +/****************************************************************************** + tcpsrv_start + *******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_start(TCP_SERV_CFG *p) { + err_t err = ERR_OK; + if (p == NULL) { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_NULL_pointer); +#endif + return ERR_ARG; + } + if (p->pcb != NULL) { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_already_initialized); +#endif + return ERR_USE; + } + p->pcb = tcp_new(); + + if (p->pcb != NULL) { + tcp_setprio(p->pcb, TCP_SRV_PRIO); + err = tcp_bind(p->pcb, IP_ADDR_ANY, p->port); // Binds pcb to a local IP address and port number. + if (err == ERR_OK) { // If another connection is bound to the same port, the function will return ERR_USE, otherwise ERR_OK is returned. + p->pcb = tcp_listen(p->pcb); // Commands pcb to start listening for incoming connections. + // When an incoming connection is accepted, the function specified with the tcp_accept() function + // will be called. pcb must have been bound to a local port with the tcp_bind() function. + // The tcp_listen() function returns a new connection identifier, and the one passed as an + // argument to the function will be deallocated. The reason for this behavior is that less + // memory is needed for a connection that is listening, so tcp_listen() will reclaim the memory + // needed for the original connection and allocate a new smaller memory block for the listening connection. + // tcp_listen() may return NULL if no memory was available for the listening connection. + // If so, the memory associated with pcb will not be deallocated. + if (p->pcb != NULL) { + tcp_arg(p->pcb, p->pcb); + // insert new tcpsrv_config + p->next = phcfg; + phcfg = p; + // initialize callback arg and accept callback + tcp_accept(p->pcb, tcpsrv_server_accept); + return err; + } + } + tcp_abandon(p->pcb, 0); + p->pcb = NULL; + } else + err = ERR_MEM; +#if DEBUGSOO > 0 + ts_printf("tcpsrv: not new tcp!\n"); +#endif + return err; +} +/****************************************************************************** + tcpsrv_start_client + TCP_SERV_CFG * p = tcpsrv_init(ID_CLIENTS_PORT); + // insert new tcpsrv_config + p->next = phcfg; + phcfg = p; + *******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_client_start(TCP_SERV_CFG * p, uint32 remote_ip, uint16 remote_port) { + err_t err = ERR_ARG; + if (p == NULL) return err; + if (system_get_free_heap_size() >= p->min_heap) { + TCP_SERV_CONN * ts_conn = (TCP_SERV_CONN *) os_zalloc(sizeof(TCP_SERV_CONN)); + if (ts_conn != NULL) { + ts_conn->flag = p->flag; // перенеÑти флаги по умолчанию на данное Ñоединение + ts_conn->pcfg = p; + ts_conn->state = SRVCONN_CLIENT; // уÑтановка ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ (клиент) + ts_conn->remote_port = remote_port; + ts_conn->remote_ip.dw = remote_ip; + err = tcpsrv_client_connect(ts_conn); +// if(ts_conn->pcb != NULL) { + if(err == ERR_OK) { + // Insert new ts_conn + ts_conn->next = p->conn_links; + p->conn_links = ts_conn; + p->conn_count++; +// err = ERR_OK; + } else { +#if DEBUGSOO > 0 + tcpsrv_print_remote_info(ts_conn); + ts_printf("tcpsrv: connect error = %s\n", tspsrv_error_msg(err)); +#endif + os_free(ts_conn); +// err = ERR_CONN; + }; + } + else { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_out_of_mem); +#endif + err = ERR_MEM; + }; + } else { +#if DEBUGSOO > 0 + ts_printf("tcpsrv: low heap size!\n"); +#endif + err = ERR_MEM; + }; + return err; +} +/****************************************************************************** + tcpsrv_close + *******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_close(TCP_SERV_CFG *p) { + if (p == NULL) { +#if DEBUGSOO > 0 + ts_printf(txt_tcpsrv_NULL_pointer); +#endif + return ERR_ARG; + }; + TCP_SERV_CFG **pwr = &phcfg; + TCP_SERV_CFG *pcmp = phcfg; + while (pcmp != NULL) { + if (pcmp == p) { + *pwr = p->next; + while (p->conn_links != NULL) { + tspsrv_delete_pcb(p->conn_links); + }; + if(p->pcb != NULL) tcp_close(p->pcb); + os_free(p); + p = NULL; + return ERR_OK; // break; + } + pwr = &pcmp->next; + pcmp = pcmp->next; + }; +#if DEBUGSOO > 2 + ts_printf("tcpsrv: srv_cfg not find!\n"); +#endif + return ERR_CONN; +} +/****************************************************************************** + tcpsrv_close_port + *******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_close_port(uint16 portn) +{ + if(portn) return tcpsrv_close(tcpsrv_server_port2pcfg(portn)); + else return ERR_ARG; +} +/****************************************************************************** + tcpsrv_close_all_tcp_pcb + *******************************************************************************/ +int TCP_SRV_CODE_ATTR tcpsrv_close_all_tcp_pcb(void) +{ + struct tcp_pcb *pcb; + int ret = 0; + for (pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { +#if DEBUGSOO > 3 + ts_printf("tcpsrv: abort %p pcb\n", pcb); +#endif + tcp_abort(pcb); + ret = 1; + } + return ret; +} +/****************************************************************************** + tcpsrv_delete_all_tm_tcp_pcb + *******************************************************************************/ +void TCP_SRV_CODE_ATTR tcpsrv_delete_all_tm_tcp_pcb(void) +{ + struct tcp_pcb *pcb; + for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { +#if DEBUGSOO > 3 + ts_printf("tcpsrv: del tm %p pcb\n", pcb); +#endif + tcp_pcb_remove(&tcp_tw_pcbs, pcb); + memp_free(MEMP_TCP_PCB, pcb); + } +} +/****************************************************************************** + tcpsrv_close_all + *******************************************************************************/ +err_t TCP_SRV_CODE_ATTR tcpsrv_close_all(void) +{ + err_t err = ERR_OK; + while(phcfg != NULL && err == ERR_OK) err = tcpsrv_close(phcfg); + if(tcpsrv_close_all_tcp_pcb()) vTaskDelay(50); + tcpsrv_delete_all_tm_tcp_pcb(); + return err; +} + diff --git a/project/src/user/main.c b/project/src/user/main.c new file mode 100644 index 0000000..3cd3427 --- /dev/null +++ b/project/src/user/main.c @@ -0,0 +1,97 @@ +/* + * + */ + +#include "platform_autoconf.h" +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "hal_crypto.h" +#include "hal_log_uart.h" +#include "hal_misc.h" +#include "diag.h" +//#include "wdt_api.h" +//#include +#include "hal_platform.h" +#include "rtl8195a_sys_on.h" + +#ifdef CONFIG_WDG_ON_IDLE +#include "hal_peri_on.h" +#include "rtl8195a_peri_on.h" +#endif + +/* --------------------------------------------------- + * Customized Signature (Image Name) + * ---------------------------------------------------*/ +#include "section_config.h" +SECTION(".custom.validate.rodata") +const unsigned char cus_sig[32] = "WEB Sample"; + +#ifdef CONFIG_DEBUG_LOG +#define DEBUG_MAIN_LEVEL CONFIG_DEBUG_LOG +#else +#define DEBUG_MAIN_LEVEL 0 +#endif + +#ifndef CONFIG_INIT_NET +#define CONFIG_INIT_NET 1 +#endif +#ifndef CONFIG_INTERACTIVE_MODE +#define CONFIG_INTERACTIVE_MODE 1 +#endif + +extern void user_init_thrd(void); + +/* RAM/TCM/Heaps info */ +void ShowMemInfo(void) +{ + DiagPrintf("\nCLK CPU\t\t%d Hz\nRAM heap\t%d bytes\nTCM heap\t%d bytes\n", + HalGetCpuClk(), xPortGetFreeHeapSize(), tcm_heap_freeSpace()); +} + +/* main */ +void main(void) +{ +#if DEBUG_MAIN_LEVEL > 3 + ConfigDebugErr = -1; + ConfigDebugInfo = ~(_DBG_SPI_FLASH_);//|_DBG_TCM_HEAP_); + ConfigDebugWarn = -1; + CfgSysDebugErr = -1; + CfgSysDebugInfo = -1; + CfgSysDebugWarn = -1; +#endif + +#ifdef CONFIG_WDG_ON_IDLE + HAL_PERI_ON_WRITE32(REG_SOC_FUNC_EN, HAL_PERI_ON_READ32(REG_SOC_FUNC_EN) & 0x1FFFFF); +#if CONFIG_DEBUG_LOG > 3 + WDGInitial(CONFIG_WDG_ON_IDLE * 3000); // 30 s +#else + WDGInitial(CONFIG_WDG_ON_IDLE * 1000); // 10 s +#endif + WDGStart(); +#endif + +#if (defined(CONFIG_CRYPTO_STARTUP) && (CONFIG_CRYPTO_STARTUP)) + if(rtl_cryptoEngine_init() != 0 ) { + DBG_8195A("Crypto engine init failed!\n"); + } +#endif + +#if DEBUG_MAIN_LEVEL > 1 + vPortFree(pvPortMalloc(4)); // Init RAM heap + ShowMemInfo(); // RAM/TCM/Heaps info +#endif + + /* wlan & user_start intialization */ + xTaskCreate(user_init_thrd, "user_init", 1024, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, NULL); + + /*Enable Schedule, Start Kernel*/ +#if defined(CONFIG_KERNEL) && !TASK_SCHEDULER_DISABLED +#ifdef PLATFORM_FREERTOS + vTaskStartScheduler(); +#endif +#else + RtlConsolTaskRom(NULL); +#endif +} diff --git a/project/src/user/user_start.c b/project/src/user/user_start.c new file mode 100644 index 0000000..06a0ef7 --- /dev/null +++ b/project/src/user/user_start.c @@ -0,0 +1,77 @@ +/* + * user_start.c + * + * Created on: 26/03/2017 + * Author: pvvx + */ +#include "user_config.h" +#include "platform_autoconf.h" +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "netbios/netbios.h" +#include "user/sys_cfg.h" +#include "web/web_srv.h" +#include "webfs/webfs.h" + +struct SystemCfg syscfg = { + .cfg.w = SYS_CFG_DEBUG_ENA | SYS_CFG_NETBIOS_ENA, +#if defined(USE_WEB) + .web_port = USE_WEB, +#else + .web_port = 0, +#endif + .web_twrec = 5, + .web_twcls = 5 +}; + +void connect_start(void) +{ + info_printf("\%s: Time at start %d ms.\n", __func__, xTaskGetTickCount()); +} + +void connect_close(void) +{ + info_printf("\%s: Time at start %d ms.\n", __func__, xTaskGetTickCount()); +} + +void user_start(void) +{ + info_printf("\%s: Time at start %d ms.\n", __func__, xTaskGetTickCount()); + +} + +void sys_write_cfg(void) +{ + flash_write_cfg(&syscfg, FEEP_ID_SYS_CFG, sizeof(syscfg)); +} + + +void user_init_thrd(void) { + + flash_read_cfg(&syscfg, FEEP_ID_SYS_CFG, sizeof(syscfg)); + + if(!syscfg.cfg.b.debug_print_enable) print_off = 1; + + /* Initilaize the console stack */ + + console_init(); + + /* Web Disk Init */ + WEBFSInit(); + + /* Load cfg, init WiFi + LwIP init, WiFi start if wifi_cfg.mode != RTW_MODE_NONE */ + wifi_init(); + + if(syscfg.cfg.b.netbios_ena) netbios_init(); + + // webstuff_init(); // httpd_init(); + webserver_init(syscfg.web_port); + + // xTaskCreate(x_init_thrd, "wifi_init", 1024, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, NULL); + + /* Kill init thread after all init tasks done */ + vTaskDelete(NULL); +} + diff --git a/project/src/web/web_int_callbacks.c b/project/src/web/web_int_callbacks.c new file mode 100644 index 0000000..aa26a07 --- /dev/null +++ b/project/src/web/web_int_callbacks.c @@ -0,0 +1,688 @@ +/****************************************************************************** + * FileName: web_int_callbacks.c + * Description: The web server inernal callbacks. +*******************************************************************************/ + +#include "user_config.h" +#ifdef USE_WEB +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "lwip/tcp.h" +#include "flash_eep.h" +#include "device_lock.h" +#include "ethernetif.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "web_srv_int.h" +#include "web_utils.h" +#include "webfs/webfs.h" +#include "rtl8195a/rtl_libc.h" +#include "user/sys_cfg.h" +#include "wifi_api.h" +#include "sys_api.h" +#include "esp_comp.h" + +#ifdef USE_NETBIOS +#include "netbios.h" +#endif + +#ifdef USE_SNTP +#include "sntp.h" +#endif + +#ifdef USE_CAPTDNS +#include "captdns.h" +#endif + +#ifdef USE_MODBUS +#include "modbustcp.h" +#include "mdbtab.h" +#endif + +#ifdef USE_RS485DRV +#include "driver/rs485drv.h" +#endif + +#ifdef USE_OVERLAY +#include "overlay.h" +#endif + +#define atoi rom_atoi + +#define mMIN(a, b) ((alinkd; + unsigned int len = web_conn->msgbufsize - web_conn->msgbuflen; + if(len > WAV_HEADER_SIZE + 10) { + len -= WAV_HEADER_SIZE; + WAV_HEADER * ptr = (WAV_HEADER *) &web_conn->msgbuf[web_conn->msgbuflen]; + os_memcpy(ptr, &wav_header, WAV_HEADER_SIZE); + ptr->dsize = len; + web_conn->msgbuflen += WAV_HEADER_SIZE; + len >>= 1; + read_adcs((uint16 *)(web_conn->msgbuf + web_conn->msgbuflen), len, 0x0808); + web_conn->msgbuflen += len << 1; + } + SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // connection close +} +#endif // TEST_SEND_WAVE +#if 0 +//=============================================================================== +// WiFi Saved Aps XML +//------------------------------------------------------------------------------- +void ICACHE_FLASH_ATTR wifi_aps_xml(TCP_SERV_CONN *ts_conn) +{ + struct buf_html_string { + uint8 ssid[32*6 + 1]; + uint8 psw[64*6 + 1]; + }; + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *) ts_conn->linkd; + struct station_config config[5]; + struct buf_html_string * buf = (struct buf_html_string *)os_malloc(sizeof(struct buf_html_string)); + if(buf == NULL) return; + int total_aps = wifi_station_get_ap_info(config); + // Check if this is a first round call + if(CheckSCB(SCB_RETRYCB)==0) { + tcp_puts_fd("%u%u", total_aps, wifi_station_get_current_ap_id()); + if(total_aps == 0) return; + web_conn->udata_start = 0; + } + while(web_conn->msgbuflen + 74 + 32 <= web_conn->msgbufsize) { + if(web_conn->udata_start < total_aps) { + struct station_config *p = (struct station_config *)&config[web_conn->udata_start]; + if(web_conn->msgbuflen + 74 + htmlcode(buf->ssid, p->ssid, 32*6, 32) + htmlcode(buf->psw, p->password, 64*6, 64) > web_conn->msgbufsize) break; + tcp_puts_fd("%s%s" MACSTR "%d", + web_conn->udata_start, buf->ssid, buf->psw, MAC2STR(p->bssid), p->bssid_set); + web_conn->udata_start++; + if(web_conn->udata_start >= total_aps) { + ClrSCB(SCB_RETRYCB); + os_free(buf); + return; + } + } + else { + ClrSCB(SCB_RETRYCB); + os_free(buf); + return; + } + } + // repeat in the next call ... + SetSCB(SCB_RETRYCB); + SetNextFunSCB(wifi_aps_xml); + os_free(buf); + return; +} +//=============================================================================== +// WiFi Scan XML +//------------------------------------------------------------------------------- +void ICACHE_FLASH_ATTR web_wscan_xml(TCP_SERV_CONN *ts_conn) +{ + struct bss_scan_info si; + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *) ts_conn->linkd; + // Check if this is a first round call + if(CheckSCB(SCB_RETRYCB)==0) { + tcp_puts_fd("%d", total_scan_infos); + if(total_scan_infos == 0) return; + web_conn->udata_start = 0; + } + while(web_conn->msgbuflen + 96 + 32 <= web_conn->msgbufsize) { + if(web_conn->udata_start < total_scan_infos) { + struct bss_scan_info *p = (struct bss_scan_info *)buf_scan_infos; + p += web_conn->udata_start; + ets_memcpy(&si, p, sizeof(si)); +/* + uint8 ssid[33]; + ssid[32] = '\0'; + ets_memcpy(ssid, si.ssid, 32); */ + uint8 ssid[32*6 + 1]; + if(web_conn->msgbuflen + 96 + htmlcode(ssid, si.ssid, 32*6, 32) > web_conn->msgbufsize) break; + tcp_puts_fd("%d%d" MACSTR "%s%d%d", web_conn->udata_start, si.channel, si.authmode, MAC2STR(si.bssid), ssid, si.rssi, si.is_hidden); + web_conn->udata_start++; + if(web_conn->udata_start >= total_scan_infos) { + ClrSCB(SCB_RETRYCB); + return; + } + } + else { + ClrSCB(SCB_RETRYCB); + return; + } + } + // repeat in the next call ... + SetSCB(SCB_RETRYCB); + SetNextFunSCB(web_wscan_xml); + return; +} +//=============================================================================== +// WiFi Probe Request XML +//------------------------------------------------------------------------------- +void ICACHE_FLASH_ATTR web_ProbeRequest_xml(TCP_SERV_CONN *ts_conn) +{ + struct s_probe_requests pr; + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *) ts_conn->linkd; + // Check if this is a first round call + uint32 cnt = (probe_requests_count < MAX_COUNT_BUF_PROBEREQS)? probe_requests_count : MAX_COUNT_BUF_PROBEREQS; + if(CheckSCB(SCB_RETRYCB)==0) { + if(cnt == 0) { + tcp_strcpy_fd("0"); + return; + } + web_conn->udata_start = 0; + } + while(web_conn->msgbuflen + 92 <= web_conn->msgbufsize) { + if(web_conn->udata_start < cnt) { + struct s_probe_requests *p = (struct s_probe_requests *)&buf_probe_requests; + p += web_conn->udata_start; + ets_memcpy(&pr, p, sizeof(struct s_probe_requests)); + tcp_puts_fd("" MACSTR "%d%d", web_conn->udata_start, MAC2STR(pr.mac), pr.rssi_min, pr.rssi_max); + web_conn->udata_start++; + if(web_conn->udata_start >= cnt) { + tcp_puts_fd("%d", cnt); + ClrSCB(SCB_RETRYCB); + return; + } + } + else { + ClrSCB(SCB_RETRYCB); + return; + } + } + // repeat in the next call ... + SetSCB(SCB_RETRYCB); + SetNextFunSCB(web_ProbeRequest_xml); + return; +} +#endif +#ifdef USE_MODBUS +//=============================================================================== +// Mdb XML +//------------------------------------------------------------------------------- +void ICACHE_FLASH_ATTR web_modbus_xml(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *) ts_conn->linkd; + while(web_conn->msgbuflen + 24 <= web_conn->msgbufsize) { + if(web_conn->udata_start < web_conn->udata_stop) { + uint16 val16; + if(RdMdbData((uint8 *)&val16, web_conn->udata_start, 1) != 0) tcp_puts_fd("?", web_conn->udata_start, web_conn->udata_start); + else { + if(ts_conn->flag.user_option2) { + if(ts_conn->flag.user_option1) { + tcp_puts_fd("0x%04x", web_conn->udata_start, val16, web_conn->udata_start); + } + else { + tcp_puts_fd("%04x", web_conn->udata_start, val16, web_conn->udata_start); + }; + } + else { + if(ts_conn->flag.user_option1) { + tcp_puts_fd("%d", web_conn->udata_start, (sint32)((sint16)val16), web_conn->udata_start); + } + else { + tcp_puts_fd("%u", web_conn->udata_start, val16, web_conn->udata_start); + }; + }; + }; + web_conn->udata_start++; + } + else { + ClrSCB(SCB_RETRYCB); + return; + } + } + // repeat in the next call ... + SetSCB(SCB_RETRYCB); + SetNextFunSCB(web_modbus_xml); + return; +} +#endif +//=============================================================================== +// RAM hexdump +//------------------------------------------------------------------------------- +void ICACHE_FLASH_ATTR web_hexdump(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *) ts_conn->linkd; + union { + uint32 dw[4]; + uint8 b[16]; + }data; + web_conn->udata_start &= 0xfffffff0; + uint32 *addr = (uint32 *)web_conn->udata_start; + int i; + web_conn->udata_stop &= 0xfffffff0; + while(web_conn->msgbuflen + (9+3*16+17+2) <= web_conn->msgbufsize) { + if((uint32)addr < 0x9A000000) { + tcp_puts("%08x", addr); + for(i=0 ; i < 4 ; i++) data.dw[i] = *addr++; + web_conn->udata_start = (uint32)addr; + if(ts_conn->flag.user_option1) { + for(i=0 ; i < 4 ; i++) tcp_puts(" %08x", data.dw[i]); + } + else { + for(i=0 ; i < 16 ; i++) tcp_puts(" %02x", data.b[i]); + } + tcp_put(' '); tcp_put(' '); + for(i=0 ; i < 16 ; i++) tcp_put((data.b[i] >=' ' && data.b[i] != 0x7F)? data.b[i] : '.'); + tcp_puts("\r\n"); + if((uint32)addr >= web_conn->udata_stop) { + ClrSCB(SCB_RETRYCB); + SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // connection close + return; + } + } + else { + tcp_puts("%p = Bad address!\r\n", addr); + ClrSCB(SCB_RETRYCB); + SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // connection close + return; + }; + } + // repeat in the next call ... + SetSCB(SCB_RETRYCB); + SetNextFunSCB(web_hexdump); + return; +} +/****************************************************************************** + * FunctionName : web saved flash + * Description : Processing the flash data send + * Parameters : none (Calback) + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR web_get_flash(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + // Check if this is a first round call + if(CheckSCB(SCB_RETRYCB)==0) { + if(web_conn->udata_start == web_conn->udata_stop) return; +#if DEBUGSOO > 2 + os_printf("file_size:%08x ", web_conn->udata_stop - web_conn->udata_start ); +#endif + } + // Get/put as many bytes as possible + unsigned int len = mMIN(web_conn->msgbufsize - web_conn->msgbuflen, web_conn->udata_stop - web_conn->udata_start); + // Read Flash addr = web_conn->webfinc_offsets, len = x, buf = sendbuf +#if DEBUGSOO > 2 + os_printf("%08x..%08x ",web_conn->udata_start, web_conn->udata_start + len ); +#endif +// device_mutex_lock(RT_DEV_LOCK_FLASH); + if(spi_flash_read(web_conn->udata_start, web_conn->msgbuf, len)) { + web_conn->udata_start += len; + web_conn->msgbuflen += len; + if(web_conn->udata_start < web_conn->udata_stop) { + SetNextFunSCB(web_get_flash); +// device_mutex_unlock(RT_DEV_LOCK_FLASH); + SetSCB(SCB_RETRYCB); + return; + }; + }; +// device_mutex_unlock(RT_DEV_LOCK_FLASH); + ClrSCB(SCB_RETRYCB); +// SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + return; +} +/****************************************************************************** + * FunctionName : web saved flash + * Description : Processing the flash data send + * Parameters : none (Calback) + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR web_get_ram(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + // Check if this is a first round call + if(CheckSCB(SCB_RETRYCB)==0) { // On initial call, проверка параметров + if(web_conn->udata_start == web_conn->udata_stop) { +// SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + return; + } +#if DEBUGSOO > 2 + os_printf("file_size:%08x ", web_conn->udata_stop - web_conn->udata_start ); +#endif + } + // Get/put as many bytes as possible + uint32 len = mMIN(web_conn->msgbufsize - web_conn->msgbuflen, web_conn->udata_stop - web_conn->udata_start); + copy_align4(web_conn->msgbuf, (void *)(web_conn->udata_start), len); + web_conn->msgbuflen += len; + web_conn->udata_start += len; +#if DEBUGSOO > 2 + os_printf("%08x-%08x ",web_conn->udata_start, web_conn->udata_start + len ); +#endif + if(web_conn->udata_start != web_conn->udata_stop) { + SetSCB(SCB_RETRYCB); + SetNextFunSCB(web_get_ram); + return; + }; + ClrSCB(SCB_RETRYCB); +// SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + return; +} +/****************************************************************************** + * FunctionName : web_callback + * Description : callback + * Parameters : struct TCP_SERV_CONN + * Returns : none + ******************************************************************************/ +void ICACHE_FLASH_ATTR web_int_callback(TCP_SERV_CONN *ts_conn, uint8 *cstr) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; +// uint8 *cstr = &web_conn->msgbuf[web_conn->msgbuflen]; + { + uint8 *vstr = os_strchr(cstr, '='); + if(vstr != NULL) { + *vstr++ = '\0'; + web_int_vars(ts_conn, cstr, vstr); + return; + } + } +#if DEBUGSOO > 3 + os_printf("[%s]\n", cstr); +#endif + ifcmp("start") tcp_puts("0x%08x", web_conn->udata_start); + else ifcmp("stop") tcp_puts("0x%08x", web_conn->udata_stop); + else ifcmp("xml_") { + cstr+=4; + web_conn->udata_start&=~3; + ifcmp("ram") tcp_puts("0x%08x", *((uint32*)web_conn->udata_start)); + else tcp_put('?'); + web_conn->udata_start += 4; + } + else ifcmp("sys_") { + cstr+=4; + ifcmp("cid") tcp_puts("%08x", HalGetChipId()); + else ifcmp("fid") tcp_puts("%08x", spi_flash_get_id()); + else ifcmp("fsize") tcp_puts("%u", spi_flash_real_size()); // flashchip->chip_size + else ifcmp("sdkver") tcp_strcpy_fd(SDK_VERSION); + else ifcmp("sysver") tcp_strcpy_fd(SYS_VERSION); + else ifcmp("webver") tcp_strcpy_fd(WEB_SVERSION); + else ifcmp("heap") tcp_puts("%u", xPortGetFreeHeapSize()); + else ifcmp("heapm") tcp_puts("%u", xPortGetMinimumEverFreeHeapSize()); + else ifcmp("tcmh") tcp_puts("%u", tcm_heap_freeSpace()); + else ifcmp("time") tcp_puts("%u", xTaskGetTickCount()); + else ifcmp("mactime") { + if(wifi_mode) { + union { + uint32 dw[2]; + uint64 dd; + }ux; + ux.dd = *((uint64_t *)(WIFI_REG_BASE + 0x0560)); // REG_TSFTR -> #include "hal_com_reg.h" + tcp_puts("0x%08x%08x", ux.dw[1], ux.dw[0]); + } + } + else ifcmp("clkcpu") tcp_puts("%u", HalGetCpuClk()); + else ifcmp("debug") tcp_put('1' - (print_off & 1)); // rtl_print on/off + else ifcmp("restart") web_conn->web_disc_cb = (web_func_disc_cb)sys_reset; + else ifcmp("ram") tcp_puts("0x%08x", *((uint32 *)(ahextoul(cstr+3)&(~3)))); + else ifcmp("rdec") tcp_puts("%d", *((uint32 *)(ahextoul(cstr+4)&(~3)))); + else ifcmp("ip") { + uint32 cur_ip; + if(netif_default != NULL) cur_ip = netif_default->ip_addr.addr; + tcp_puts(IPSTR, IP2STR(&cur_ip)); + } +#ifdef USE_NETBIOS + else ifcmp("netbios") { + if(syscfg.cfg.b.netbios_ena) tcp_strcpy(netbios_name); + } +#endif + else tcp_put('?'); + } + else ifcmp("cfg_") { + cstr += 4; + ifcmp("web_") { + cstr += 4; + ifcmp("port") tcp_puts("%u", syscfg.web_port); + else ifcmp("twrec") tcp_puts("%u", syscfg.web_twrec); + else ifcmp("twcls") tcp_puts("%u", syscfg.web_twcls); + else ifcmp("twd") tcp_put((syscfg.cfg.b.web_time_wait_delete)? '1' : '0'); + else tcp_put('?'); + } + else ifcmp("pinclr") tcp_put((syscfg.cfg.b.pin_clear_cfg_enable)? '1' : '0'); + else ifcmp("debug") tcp_put((syscfg.cfg.b.debug_print_enable)? '1' : '0'); +#ifdef USE_NETBIOS + else ifcmp("netbios") tcp_put((syscfg.cfg.b.netbios_ena)? '1' : '0'); +#endif +#ifdef USE_SNTP + else ifcmp("sntp") tcp_put((syscfg.cfg.b.sntp_ena)? '1' : '0'); +#endif +#ifdef USE_CAPTDNS + else ifcmp("cdns") tcp_put((syscfg.cfg.b.cdns_ena)? '1' : '0'); +#endif + else tcp_put('?'); + } + else ifcmp("wifi_") { + cstr+=5; + ifcmp("rdcfg") read_wifi_cfg(-1); + else ifcmp("newcfg") { + web_conn->web_disc_cb = (web_func_disc_cb)wifi_run; + web_conn->web_disc_par = wifi_cfg.mode; + } + else ifcmp("cmode") tcp_puts("%d", wifi_mode); + else ifcmp("mode") tcp_puts("%d", wifi_cfg.mode); + else ifcmp("bgn") tcp_puts("%d", wifi_cfg.bgn); + else ifcmp("sleep") tcp_puts("%d", wifi_cfg.sleep); + else ifcmp("txpow") tcp_puts("%u", wifi_cfg.tx_pwr); + else ifcmp("lflg") tcp_puts("%u", wifi_cfg.load_flg); + else ifcmp("sflg") tcp_puts("%u", wifi_cfg.save_flg); + else ifcmp("country") tcp_puts("%u", wifi_cfg.country_code); + else ifcmp("ap_") { + cstr+=3; + ifcmp("ssid") { + int len = os_strlen(wifi_ap_cfg.ssid); + if(len > sizeof(wifi_ap_cfg.ssid)) { + len = sizeof(wifi_ap_cfg.ssid); + } + os_memcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], wifi_ap_cfg.ssid, len); + web_conn->msgbuflen += len; + } + else ifcmp("psw") { + int len = os_strlen(wifi_ap_cfg.password); + if(len > sizeof(wifi_ap_cfg.password)) { + len = sizeof(wifi_ap_cfg.password); + } + os_memcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], wifi_ap_cfg.password, len); + web_conn->msgbuflen += len; + } + else ifcmp("chl") tcp_puts("%u", wifi_ap_cfg.channel); + else ifcmp("mcns") tcp_puts("%u", wifi_ap_cfg.max_sta); + else ifcmp("auth") tcp_put((wifi_ap_cfg.security_type == RTW_SECURITY_OPEN) ? '0' : '1'); + else ifcmp("hssid") tcp_put((wifi_ap_cfg.ssid_hidden & 1) + '0'); + else ifcmp("bint") tcp_puts("%u", wifi_ap_cfg.beacon_interval); + else ifcmp("mac") tcp_puts(MACSTR, MAC2STR(xnetif[wlan_ap_netifn].hwaddr)); + else ifcmp("hostname") tcp_strcpy(lwip_host_name[wlan_ap_netifn]); + else ifcmp("dhcp") tcp_puts("%u", wifi_ap_dhcp.mode); + else ifcmp("ip") tcp_puts(IPSTR, IP2STR(&wifi_ap_dhcp.ip)); + else ifcmp("gw") tcp_puts(IPSTR, IP2STR(&wifi_ap_dhcp.gw)); + else ifcmp("msk") tcp_puts(IPSTR, IP2STR(&wifi_ap_dhcp.mask)); + else ifcmp("cip") tcp_puts(IPSTR, IP2STR(&xnetif[wlan_st_netifn].ip_addr.addr)); + + // else ifcmp("mac") strtomac(pvar, wifi_ap_cfg.macaddr); + // else ifcmp("sip") tcp_puts(IPSTR, IP2STR(&wifi_ap_dhcp.start_ip)); + // else ifcmp("eip") tcp_puts(IPSTR, IP2STR(&wifi_ap_dhcp.end_ip)); + #if DEBUGSOO > 2 + else os_printf(" - none! "); + #endif + } + else ifcmp("st_") { + cstr+=3; + ifcmp("rssi") { + int rssi; + wifi_get_rssi(&rssi); + tcp_puts("%d", rssi); + } + else ifcmp("arec") tcp_puts("%u", wifi_st_cfg.autoreconnect); + else ifcmp("rect") tcp_puts("%u", wifi_st_cfg.reconnect_pause); + else ifcmp("ssid") { + int len = os_strlen(wifi_st_cfg.ssid); + if(len > sizeof(wifi_st_cfg.ssid)) { + len = sizeof(wifi_st_cfg.ssid); + } + os_memcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], wifi_st_cfg.ssid, len); + web_conn->msgbuflen += len; + } + else ifcmp("psw") { + int len = os_strlen(wifi_st_cfg.password); + if(len > sizeof(wifi_st_cfg.password)) { + len = sizeof(wifi_st_cfg.password); + } + os_memcpy((char *)&web_conn->msgbuf[web_conn->msgbuflen], wifi_st_cfg.password, len); + web_conn->msgbuflen += len; + } + else ifcmp("mac") tcp_puts(MACSTR, MAC2STR(xnetif[wlan_st_netifn].hwaddr)); + else ifcmp("bssid") tcp_puts(MACSTR, MAC2STR(wifi_st_cfg.bssid)); + else ifcmp("sbss") tcp_puts("%u", wifi_st_cfg.flg); +#if LWIP_NETIF_HOSTNAME + else ifcmp("hostname") tcp_strcpy(lwip_host_name[wlan_st_netifn]); +#endif + else ifcmp("auth") tcp_puts("%u", wifi_st_cfg.security_type); + else ifcmp("dhcp") tcp_puts("%u", wifi_st_dhcp.mode); + else ifcmp("ip") tcp_puts(IPSTR, IP2STR(&wifi_st_dhcp.ip)); + else ifcmp("gw") tcp_puts(IPSTR, IP2STR(&wifi_st_dhcp.gw)); + else ifcmp("msk") tcp_puts(IPSTR, IP2STR(&wifi_st_dhcp.mask)); + #if DEBUGSOO > 5 + else os_printf(" - none!\n"); + #endif + } +#if DEBUGSOO > 5 + else os_printf(" - none!\n"); +#endif + } + else ifcmp("bin_") { + cstr+=4; + ifcmp("flash") { + cstr+=5; + if(*cstr == '_') { + cstr++; + ifcmp("all") { + web_conn->udata_start = 0; + web_conn->udata_stop = spi_flash_real_size(); + web_get_flash(ts_conn); + } + else ifcmp("sec_") { + web_conn->udata_start = ahextoul(cstr+4) << 12; + web_conn->udata_stop = web_conn->udata_start + FLASH_SECTOR_SIZE; + web_get_flash(ts_conn); + } + else ifcmp("disk") { + web_conn->udata_start = WEBFS_base_addr(); + web_conn->udata_stop = web_conn->udata_start + WEBFS_curent_size(); + web_get_flash(ts_conn); + } + else tcp_put('?'); + } + else web_get_flash(ts_conn); + } + else ifcmp("ram") web_get_ram(ts_conn); + else tcp_put('?'); + } + else ifcmp("hexdmp") { + if(cstr[6]=='d') ts_conn->flag.user_option1 = 1; + else ts_conn->flag.user_option1 = 0; + web_hexdump(ts_conn); + } + else ifcmp("web_") { + cstr+=4; + ifcmp("port") tcp_puts("%u", ts_conn->pcfg->port); + else ifcmp("host") tcp_puts(IPSTR ":%d", IP2STR(&(ts_conn->pcb->local_ip.addr)), ts_conn->pcb->local_port); + else ifcmp("remote") tcp_puts(IPSTR ":%d", IP2STR(&(ts_conn->remote_ip.dw)), ts_conn->remote_port); + else ifcmp("twrec") tcp_puts("%u", ts_conn->pcfg->time_wait_rec); + else ifcmp("twcls") tcp_puts("%u", ts_conn->pcfg->time_wait_cls); + else tcp_put('?'); + } + else ifcmp("wfs_") { + cstr+=4; + ifcmp("files") tcp_puts("%u", numFiles); + else ifcmp("addr") tcp_puts("0x%08x", WEBFS_base_addr()); + else ifcmp("size") tcp_puts("%u", WEBFS_curent_size()); + else ifcmp("max_size") tcp_puts("%u", WEBFS_max_size()); + else tcp_put('?'); + } +#ifdef USE_OVERLAY + else ifcmp("ovl") { + cstr += 3; + if(*cstr == ':') { + int i = ovl_loader(cstr + 1); + if (i == 0) { + if(CheckSCB(SCB_WEBSOC)) { + tcp_puts("%d", ovl_call(1)); + } + else { + web_conn->web_disc_cb = (web_func_disc_cb)ovl_call; // Ð°Ð´Ñ€ÐµÑ Ñтарта Ð¾Ð²ÐµÑ€Ð»ÐµÑ + web_conn->web_disc_par = 1; // параметр функции - Ð¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ + } + } + tcp_puts("%d", i); + } + else if(*cstr == '$') { + if(ovl_call != NULL) tcp_puts("%d", ovl_call(ahextoul(cstr + 1))); + else tcp_put('?'); + } + else if(*cstr == '@') { + if(ovl_call != NULL) tcp_puts("%d", ovl_call((int) cstr + 1)); + else tcp_put('?'); + } + else tcp_put('?'); + } +#endif +#ifdef USE_SNTP + else ifcmp("sntp_") { + cstr += 5; + ifcmp("time") tcp_puts("%u", get_sntp_time()); + else tcp_put('?'); + } +#endif +#ifdef TEST_SEND_WAVE + else ifcmp("test_adc") web_test_adc(ts_conn); +#endif + else ifcmp("hellomsg") tcp_puts_fd("Web on RTL871x!"); + else tcp_put('?'); +} + + +#endif // USE_WEB diff --git a/project/src/web/web_int_vars.c b/project/src/web/web_int_vars.c new file mode 100644 index 0000000..947eb52 --- /dev/null +++ b/project/src/web/web_int_vars.c @@ -0,0 +1,356 @@ +/****************************************************************************** + * FileName: webserver.c + * Description: The web server mode configuration. +*******************************************************************************/ + +#include "user_config.h" +#ifdef USE_WEB +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "lwip/ip.h" +#include "lwip/ip_addr.h" +#include "lwip/tcp.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "ethernetif.h" +#include "web_srv_int.h" +#include "web_utils.h" +#include "flash_eep.h" +#include "device_lock.h" +#include "rtl8195a/rtl_libc.h" +#include "user/sys_cfg.h" +#include "wifi_api.h" +#include "sys_api.h" +#include "esp_comp.h" + +#ifdef USE_NETBIOS +#include "netbios.h" +#endif + +#ifdef USE_SNTP +#include "sntp.h" +#endif + +#ifdef USE_LWIP_PING +#include "lwip/app/ping.h" +struct ping_option pingopt; // for test +#endif + +#ifdef USE_CAPTDNS +#include "captdns.h" +#endif + +#ifdef USE_MODBUS +#include "modbustcp.h" +#include "mdbtab.h" +#endif + +#ifdef USE_RS485DRV +#include "driver/rs485drv.h" +#include "mdbrs485.h" +#endif + +#ifdef USE_OVERLAY +#include "overlay.h" +#endif + +extern void web_get_ram(TCP_SERV_CONN *ts_conn); +extern void web_get_flash(TCP_SERV_CONN *ts_conn); +extern void web_hexdump(TCP_SERV_CONN *ts_conn); + +#define ifcmp(a) if(rom_xstrcmp(cstr, a)) + +extern int rom_atoi(const char *); +#define atoi rom_atoi + +typedef uint32 (* call_func)(uint32 a, uint32 b, uint32 c); + +/****************************************************************************** + * FunctionName : parse_url + * Description : parse the received data from the server + * Parameters : CurHTTP -- the result of parsing the url + * Returns : none +*******************************************************************************/ +void ICACHE_FLASH_ATTR web_int_vars(TCP_SERV_CONN *ts_conn, uint8 *pcmd, uint8 *pvar) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + uint32 val = ahextoul(pvar); + char *cstr = pcmd; +#if DEBUGSOO > 1 + os_printf("[%s=%s]\n", pcmd, pvar); +#endif + ifcmp("start") web_conn->udata_start = val; + else ifcmp("stop") web_conn->udata_stop = val; + else ifcmp("sys_") { + cstr+=4; + ifcmp("restart") { + if(val == 12345) web_conn->web_disc_cb = (web_func_disc_cb)sys_reset; + } + else ifcmp("ram") { uint32 *ptr = (uint32 *)(ahextoul(cstr+3)&(~3)); str_array(pvar, ptr, 32); } + else ifcmp("debug") print_off = (!val) & 1; // rtl_print on/off +#ifdef USE_LWIP_PING + else ifcmp("ping") { +// struct ping_option *pingopt = (struct ping_option *)UartDev.rcv_buff.pRcvMsgBuff; + pingopt.ip = ipaddr_addr(pvar); + pingopt.count = 3; + pingopt.recv_function=NULL; + pingopt.sent_function=NULL; + ping_start(&pingopt); + } +#endif + } + else ifcmp("cfg_") { + cstr += 4; + ifcmp("web_") { + cstr += 4; + ifcmp("port") { + if(syscfg.web_port != val) { + web_conn->web_disc_par = syscfg.web_port; // ts_conn->pcfg->port + syscfg.web_port = val; + web_conn->web_disc_cb = (web_func_disc_cb)webserver_reinit; + } + } + else ifcmp("twd") { + if(val) { + syscfg.cfg.b.web_time_wait_delete = 1; + ts_conn->pcfg->flag.pcb_time_wait_free = 1; + } + else { + syscfg.cfg.b.web_time_wait_delete = 0; + ts_conn->pcfg->flag.pcb_time_wait_free = 0; + } + } + else ifcmp("twrec") { + syscfg.web_twrec = val; + ts_conn->pcfg->time_wait_rec = val; + } + else ifcmp("twcls") { + syscfg.web_twcls = val; + ts_conn->pcfg->time_wait_cls = val; + } +#if DEBUGSOO > 5 + else os_printf(" - none!\n"); +#endif + } + else ifcmp("pinclr") syscfg.cfg.b.pin_clear_cfg_enable = (val)? 1 : 0; + else ifcmp("debug") { + syscfg.cfg.b.debug_print_enable = val; + print_off = (!val) & 1; // rtl_print on/off + } + else ifcmp("save") { + if(val == 2) SetSCB(SCB_SYSSAVE); // по закрытию ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ð²Ñ‹Ð·Ð²Ð°Ñ‚ÑŒ sys_write_cfg() + else if(val == 1) sys_write_cfg(); + } +#ifdef USE_NETBIOS + else ifcmp("netbios") { + syscfg.cfg.b.netbios_ena = (val)? 1 : 0; + if(syscfg.cfg.b.netbios_ena) netbios_init(); + else netbios_off(); + } +#endif +#ifdef USE_SNTP + else ifcmp("sntp") { + syscfg.cfg.b.sntp_ena = (val)? 1 : 0; + if(syscfg.cfg.b.sntp_ena) sntp_inits(); + else sntp_close(); + } +#endif +#ifdef USE_CAPTDNS + else ifcmp("cdns") { + syscfg.cfg.b.cdns_ena = (val)? 1 : 0; + if(syscfg.cfg.b.cdns_ena && wifi_softap_get_station_num()) captdns_init(); + else captdns_close(); + } +#endif +#if DEBUGSOO > 5 + else os_printf(" - none!\n"); +#endif + // sys_write_cfg(); + } + else ifcmp("wifi_") { + cstr+=5; + ifcmp("rdcfg") web_conn->udata_stop = read_wifi_cfg(val); + else ifcmp("newcfg") { + web_conn->web_disc_cb = (web_func_disc_cb)wifi_run; + web_conn->web_disc_par = wifi_cfg.mode; + } + else ifcmp("mode") wifi_cfg.mode = val; + else ifcmp("bgn") wifi_cfg.bgn = val; + else ifcmp("lflg") wifi_cfg.load_flg = val; + else ifcmp("sflg") wifi_cfg.save_flg = val; + else ifcmp("sleep") wifi_cfg.sleep = val; + else ifcmp("txpow") wifi_cfg.tx_pwr = val; + else ifcmp("country") wifi_cfg.country_code = val; +// else ifcmp("scan") { +// web_conn->web_disc_par = val; +// web_conn->web_disc_cb = (web_func_disc_cb)wifi_start_scan; +// } + else ifcmp("save") { write_wifi_cfg(val); } + else ifcmp("ap_") { + cstr+=3; + ifcmp("ssid") { + if(pvar[0]!='\0') { + int len = os_strlen(pvar); + if(len > sizeof(wifi_ap_cfg.ssid)) { + len = sizeof(wifi_ap_cfg.ssid); + } + else os_memset(wifi_ap_cfg.ssid, 0, sizeof(wifi_ap_cfg.ssid)); + os_memcpy(wifi_ap_cfg.ssid, pvar, len); +#ifdef USE_NETBIOS + netbios_set_name(wifi_ap_cfg.ssid); +#endif + } + } + else ifcmp("psw") { + int len = os_strlen(pvar); + if(len > sizeof(wifi_ap_cfg.password)) { + len = sizeof(wifi_ap_cfg.password); + } + else os_memset(wifi_ap_cfg.password, 0, sizeof(wifi_ap_cfg.password)); + os_memcpy(wifi_ap_cfg.password, pvar, len); + } + else ifcmp("chl") wifi_ap_cfg.channel = val; + else ifcmp("mcns") wifi_ap_cfg.max_sta = val; + else ifcmp("auth") wifi_ap_cfg.security_type = (val)? RTW_SECURITY_WEP_PSK : RTW_SECURITY_OPEN; + else ifcmp("hssid") wifi_ap_cfg.ssid_hidden = val; + else ifcmp("bint") wifi_ap_cfg.beacon_interval = val; +#if LWIP_NETIF_HOSTNAME + else ifcmp("hostname") { + int len = os_strlen(pvar); + if(len >= LWIP_NETIF_HOSTNAME_SIZE) { + len = LWIP_NETIF_HOSTNAME_SIZE-1; + } + os_memcpy(lwip_host_name[wlan_ap_netifn], pvar, len); + lwip_host_name[wlan_ap_netifn][len] = 0; + netbios_set_name(wlan_ap_netifn, pvar); + } +#endif + else ifcmp("dhcp") wifi_ap_dhcp.mode = val; + else ifcmp("ip") wifi_ap_dhcp.ip = ipaddr_addr(pvar); + else ifcmp("gw") wifi_ap_dhcp.gw = ipaddr_addr(pvar); + else ifcmp("msk") wifi_ap_dhcp.mask = ipaddr_addr(pvar); +// else ifcmp("mac") strtomac(pvar, wifi_ap_cfg.macaddr); +// else ifcmp("sip") wifi_ap_dhcp.start_ip = ipaddr_addr(pvar); +// else ifcmp("eip") wifi_ap_dhcp.end_ip = ipaddr_addr(pvar); +#if DEBUGSOO > 2 + else os_printf(" - none! "); +#endif + } + else ifcmp("st_") { + cstr+=3; + ifcmp("arec") wifi_st_cfg.autoreconnect = val; + else ifcmp("rect") wifi_st_cfg.reconnect_pause = val; + else ifcmp("ssid") { + if(pvar[0]!='\0') { + int len = os_strlen(pvar); + if(len > sizeof(wifi_st_cfg.ssid)) { + len = sizeof(wifi_st_cfg.ssid); + } + else os_memset(wifi_st_cfg.ssid, 0, sizeof(wifi_st_cfg.ssid)); + os_memcpy(wifi_st_cfg.ssid, pvar, len); + } + } + else ifcmp("psw") { + int len = os_strlen(pvar); + if(len > sizeof(wifi_st_cfg.password)) { + len = sizeof(wifi_st_cfg.password); + } + else os_memset(wifi_st_cfg.password, 0, sizeof(wifi_st_cfg.password)); + os_memcpy(wifi_st_cfg.password, pvar, len); + } + else ifcmp("auth") wifi_st_cfg.security_type = val; + else ifcmp("bssid") strtomac(pvar, wifi_st_cfg.bssid); + else ifcmp("sbss") wifi_st_cfg.flg = val; +#if LWIP_NETIF_HOSTNAME + else ifcmp("hostname") { + int len = os_strlen(pvar); + if(len >= LWIP_NETIF_HOSTNAME_SIZE) { + len = LWIP_NETIF_HOSTNAME_SIZE-1; + } + os_memcpy(lwip_host_name[wlan_st_netifn], pvar, len); + lwip_host_name[wlan_st_netifn][len] = 0; + netbios_set_name(wlan_st_netifn, pvar); + } +#endif + else ifcmp("dhcp") wifi_st_dhcp.mode = val; + else ifcmp("ip") wifi_st_dhcp.ip = ipaddr_addr(pvar); + else ifcmp("gw") wifi_st_dhcp.gw = ipaddr_addr(pvar); + else ifcmp("msk") wifi_st_dhcp.mask = ipaddr_addr(pvar); +// else ifcmp("mac") strtomac(pvar, wifi_st_cfg.mac); +// else ifcmp("sbss") wifi_st_cfg.bssidx = val; +#if DEBUGSOO > 5 + else os_printf(" - none!\n"); +#endif + } +#if DEBUGSOO > 5 + else os_printf(" - none!\n"); +#endif + } + else if(web_conn->bffiles[0]==WEBFS_WEBCGI_HANDLE && CheckSCB(SCB_GET)) { + ifcmp("hexdmp") { +#if DEBUGSOO > 5 + os_printf("hexdmp(%p)\n", val); +#endif + if(val > 0) { + if(cstr[6]=='d') ts_conn->flag.user_option1 = 1; + else ts_conn->flag.user_option1 = 0; + uint32 x = ahextoul(cstr+7); + web_conn->udata_start = x; + web_conn->udata_stop = val + web_conn->udata_start; +#if DEBUGSOO > 5 + os_printf("start=%p, stop=%p\n", web_conn->udata_start, web_conn->udata_stop); +#endif + web_conn->fileType = HTTP_TXT; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_hexdump); + }; + } + else ifcmp("flash") { + cstr+=5; + if(*cstr == '_') { + cstr++; + ifcmp("all") { + web_conn->udata_start = 0; + web_conn->udata_stop = spi_flash_real_size(); + web_conn->fileType = HTTP_BIN; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_get_flash); + } + else ifcmp("sec_") { + web_conn->udata_start = ahextoul(cstr+4) << 12; + web_conn->udata_stop = web_conn->udata_start + FLASH_SECTOR_SIZE*val; + web_conn->fileType = HTTP_BIN; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_get_flash); + } + else ifcmp("disk") { + web_conn->udata_start = WEBFS_base_addr(); + web_conn->udata_stop = web_conn->udata_start + WEBFS_curent_size(); + web_conn->fileType = HTTP_BIN; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_get_flash); + } + else tcp_put('?'); + } + else { + web_conn->fileType = HTTP_BIN; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_get_flash); + } + } + else ifcmp("bin_ram") { + web_conn->fileType = HTTP_BIN; + SetSCB(SCB_RETRYCB | SCB_FCALBACK); + SetNextFunSCB(web_get_ram); + } +#if DEBUGSOO > 5 + else os_printf(" - none! "); +#endif + } +#if DEBUGSOO > 5 + else os_printf(" - none! "); +#endif +} + +#endif // USE_WEB diff --git a/project/src/web/web_srv.c b/project/src/web/web_srv.c new file mode 100644 index 0000000..93154e0 --- /dev/null +++ b/project/src/web/web_srv.c @@ -0,0 +1,2073 @@ +/****************************************************************************** + * FileName: webserver.c + * Description: Small WEB server + WebSocket + * Author: pvvx + * ver1.0 25/12/2014 SDK 0.9.4 + * ver1.1 02/04/2015 SDK 1.0.0 + * ver2.0 14/14/2017 RTL871x +*******************************************************************************/ +#include "user_config.h" +#ifdef USE_WEB +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "lwip/tcp.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "web_srv_int.h" +#include "web_utils.h" +#include "flash_eep.h" +#include "device_lock.h" +#include "webfs/webfs.h" +#include "user/sys_cfg.h" +#include "wifi_api.h" +#include "rtl8195a/rtl_libc.h" +#include "esp_comp.h" + +#ifdef WEBSOCKET_ENA +#include "web_websocket.h" +#endif + +#ifdef USE_CAPTDNS +#include "captdns.h" +#endif + +#ifdef USE_OVERLAY +#include "overlay.h" +#endif + +#define USE_WEB_NAGLE // https://en.wikipedia.org/wiki/Nagle%27s_algorithm +#define MIN_REQ_LEN 7 // Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes +#define CRLF "\r\n" + +#define max_len_buf_write_flash 2048 // размер буфера при запиÑи flash. Увеличение/уменньшение размера (до Ñектора 4096) уÑÐºÐ¾Ñ€ÐµÐ½Ð¸Ñ Ð½Ðµ дает (1..2%) + +#define mMIN(a, b) ((ab)?a:b) + +#define atoi(s) rom_atoi(s) + +LOCAL void web_print_headers(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR ; + +//LOCAL void webserver_discon(void *arg) ICACHE_FLASH_ATTR; +//LOCAL void webserver_recon(void *arg, sint8 err) ICACHE_FLASH_ATTR; +LOCAL void webserver_send_fdata(TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR; +LOCAL void web_int_disconnect(TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR; +LOCAL bool webserver_open_file(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR; +LOCAL void webserver_file_ext(HTTP_CONN *CurHTTP, uint8 *pfname) ICACHE_FLASH_ATTR; + +const char http_default_file[] ICACHE_RODATA_ATTR = "index.htm"; +const char web_cgi_fname[] ICACHE_RODATA_ATTR = "web.cgi"; +const char fsupload_fname[] ICACHE_RODATA_ATTR = "fsupload"; +#ifdef USE_CAPTDNS +const char ncsi_txt_fname[] ICACHE_RODATA_ATTR = "ncsi.txt"; +//const char generate_204_fname[] ICACHE_RODATA_ATTR = "generate_204"; +const char *HTTPHost ="Host:"; +#define sizeHTTPHost 5 +#endif +#define ProtectedFilesName "protect" + +#define MAX_NO_DATA_BUF_SIZE (8192) // if(ts_conn->sizei > MAX_NO_DATA_BUF_SIZE) CurHTTP->httpStatus = 418; // 418: Out of Coffee + +QueueHandle_t xQueueWebSrv; + +/**************************************************************************** + Section: + File and Content Type Settings + ***************************************************************************/ + // File type extensions corresponding to HTTP_FILE_TYPE +static const char *httpFileExtensions[] = { + "txt", // HTTP_TXT + "htm", // HTTP_HTML + "cgi", // HTTP_CGI + "xml", // HTTP_XML + "css", // HTTP_CSS + "ico", // HTTP_ICO + "gif", // HTTP_GIF + "png", // HTTP_PNG + "jpg", // HTTP_JPG + "svg", // HTTP_SVG + "js", // HTTP_JAVA + "swf", // HTTP_SWF + "wav", // HTTP_WAV + "pdf", // HTTP_PDF + "zip", // HTTP_ZIP + "bin", // HTTP_BIN + "\0\0\0" // HTTP_UNKNOWN +}; + +// Content-type strings corresponding to HTTP_FILE_TYPE +static const char *httpContentTypes[] = { + "text/plain", // HTTP_TXT "txt", + "text/html", // HTTP_HTM "htm", + "magnus-internal/cgi", // HTTP_CGI "cgi", + "text/xml", // HTTP_XML "xml", + "text/css", // HTTP_CSS "css", + "image/vnd.microsoft.icon", // HTTP_ICO "ico", + "image/gif", // HTTP_GIF "gif", + "image/png", // HTTP_PNG "png", + "image/jpeg", // HTTP_JPG "jpg", + "image/svg+xml", // HTTP_SVG "svg", + "text/javascript", // HTTP_JAVA "js", + "application/x-shockwave-flash", // HTTP_SWF "swf", + "audio/x-wave", // HTTP_WAV "wav", + "application/pdf", // HTTP_PDF "pdf", + "application/zip", // HTTP_ZIP "zip", + "application/octet-stream", // HTTP_BIN "bin", + "" // HTTP_UNKNOWN +}; +/**************************************************************************** + Section: + Commands and Server Responses + ***************************************************************************/ +const char HTTPresponse_200_head[] ICACHE_RODATA_ATTR = "OK"; +const char HTTPresponse_302_head[] ICACHE_RODATA_ATTR = "Found"; +const char HTTPresponse_304_head[] ICACHE_RODATA_ATTR = "Not Modified"; +const char HTTPresponse_400_head[] ICACHE_RODATA_ATTR = "Bad Request"; +const char HTTPresponse_401_head[] ICACHE_RODATA_ATTR = "Unauthorized\r\nWWW-Authenticate: Basic realm=\"Protected\""; +const char HTTPresponse_404_head[] ICACHE_RODATA_ATTR = "Not found"; +const char HTTPresponse_411_head[] ICACHE_RODATA_ATTR = "Length Required"; +const char HTTPresponse_413_head[] ICACHE_RODATA_ATTR = "Request Entity Too Large"; +const char HTTPresponse_414_head[] ICACHE_RODATA_ATTR = "Request-URI Too Long"; +const char HTTPresponse_418_head[] ICACHE_RODATA_ATTR = "I'm a teapot"; +const char HTTPresponse_429_head[] ICACHE_RODATA_ATTR = "Too Many Requests\r\nRetry-After: 30"; +const char HTTPresponse_500_head[] ICACHE_RODATA_ATTR = "Internal Server Error"; +const char HTTPresponse_501_head[] ICACHE_RODATA_ATTR = "Not Implemented\r\nAllow: GET, POST"; + +const char HTTPresponse_401_content[] ICACHE_RODATA_ATTR = "401 Unauthorized: Password required\r\n"; +const char HTTPresponse_404_content[] ICACHE_RODATA_ATTR = "404: File not found\r\n"; +const char HTTPresponse_411_content[] ICACHE_RODATA_ATTR = "411 The request must have a content length\r\n"; +const char HTTPresponse_413_content[] ICACHE_RODATA_ATTR = "413 Request Entity Too Large: There's too many letters :)\r\n"; +const char HTTPresponse_414_content[] ICACHE_RODATA_ATTR = "414 Request-URI Too Long: Buffer overflow detected\r\n"; +const char HTTPresponse_418_content[] ICACHE_RODATA_ATTR = "418: Out of Coffee\r\n"; +const char HTTPresponse_500_content[] ICACHE_RODATA_ATTR = "500 Internal Server Error\r\n"; +const char HTTPresponse_501_content[] ICACHE_RODATA_ATTR = "501 Not Implemented: Only GET and POST supported\r\n"; + +// Initial response strings (Corresponding to HTTP_STATUS) +static const HTTP_RESPONSE ICACHE_RODATA_ATTR HTTPResponse[] ICACHE_RODATA_ATTR = { + { 200, HTTP_RESP_FLG_NONE, + HTTPresponse_200_head, + NULL }, + // уÑпешный запроÑ. ЕÑли клиентом были запрошены какие-либо данные, то они находÑÑ‚ÑÑ Ð² заголовке и/или теле ÑообщениÑ. + { 302, HTTP_RESP_FLG_NONE | HTTP_RESP_FLG_REDIRECT, + HTTPresponse_302_head, + NULL }, +// "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: ", + // 302 Found, 302 Moved Temporarily - запрошенный документ временно + // доÑтупен по другому URI, указанному в заголовке в поле Location. + // Этот код может быть иÑпользован, например, при управлÑемом Ñервером + // ÑоглаÑовании Ñодержимого. Ðекоторые клиенты некорректно ведут ÑÐµÐ±Ñ + // при обработке данного кода. + { 304, HTTP_RESP_FLG_NONE, + HTTPresponse_304_head, + NULL }, +///"304 Redirect: ", // If-Modified-Since If-None-Match + // Ñервер возвращает такой код, еÑли клиент запроÑил документ методом GET, + // иÑпользовал заголовок If-Modified-Since или If-None-Match и документ + // не изменилÑÑ Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ момента. При Ñтом Ñообщение Ñервера не должно Ñодержать тела. + { 400, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_400_head, + NULL} , + // Ñервер обнаружил в запроÑе клиента ÑинтакÑичеÑкую ошибку. + { 401, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_401_head, + HTTPresponse_401_content }, + // Ð´Ð»Ñ Ð´Ð¾Ñтупа к запрашиваемому реÑурÑу требуетÑÑ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ. + // Ð’ заголовке ответ должен Ñодержать поле WWW-Authenticate Ñ Ð¿ÐµÑ€ÐµÑ‡Ð½ÐµÐ¼ + // уÑловий аутентификации. Клиент может повторить запроÑ, + // включив в заголовок ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»Ðµ Authorization Ñ Ñ‚Ñ€ÐµÐ±ÑƒÐµÐ¼Ñ‹Ð¼Ð¸ Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ данными. +//"HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n403 Forbidden: SSL Required - use HTTPS\r\n" + { 404, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_404_head, + HTTPresponse_404_content }, + // Сервер понÑл запроÑ, но не нашёл ÑоответÑтвующего реÑурÑа по указанному URI. + { 411, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_411_head, + HTTPresponse_411_content }, + // Ð´Ð»Ñ ÑƒÐºÐ°Ð·Ð°Ð½Ð½Ð¾Ð³Ð¾ реÑурÑа клиент должен указать Content-Length в заголовке запроÑа. + // Без ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ñтого Ð¿Ð¾Ð»Ñ Ð½Ðµ Ñтоит делать повторную попытку запроÑа к Ñерверу по данному URI. + // Такой ответ еÑтеÑтвенен Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñов типа POST и PUT. + // Ðапример, еÑли по указанному URI производитÑÑ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ° файлов, а на Ñервере Ñтоит + // ограничение на их объём. Тогда разумней будет проверить в Ñамом начале заголовок + // Content-Length и Ñразу отказать в загрузке, чем провоцировать беÑÑмыÑленную нагрузку, + // Ñ€Ð°Ð·Ñ€Ñ‹Ð²Ð°Ñ Ñоединение, когда клиент дейÑтвительно пришлёт Ñлишком объёмное Ñообщение. + { 413, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_413_head, + HTTPresponse_413_content }, + // возвращаетÑÑ Ð² Ñлучае, еÑли Ñервер отказываетÑÑ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚Ð°Ñ‚ÑŒ Ð·Ð°Ð¿Ñ€Ð¾Ñ + // по причине Ñлишком большого размера тела запроÑа. Сервер может закрыть Ñоединение, + // чтобы прекратить дальнейшую передачу запроÑа. + { 414, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_414_head, + HTTPresponse_414_content }, + // Ñервер не может обработать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¸Ð·-за Ñлишком длинного указанного URL. + // Такую ошибку можно Ñпровоцировать, например, когда клиент пытаетÑÑ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‚ÑŒ длинные + // параметры через метод GET, а не POST. + { 429, HTTP_RESP_FLG_NONE, + HTTPresponse_429_head, + NULL }, + // клиент попыталÑÑ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²Ð¸Ñ‚ÑŒ Ñлишком много запроÑов за короткое времÑ, что может указывать, + // например, на попытку DoS-атаки. Может ÑопровождатьÑÑ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð¼ Retry-After, указывающим, + // через какое Ð²Ñ€ÐµÐ¼Ñ Ð¼Ð¾Ð¶Ð½Ð¾ повторить запроÑ. + { 501, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_501_head, + HTTPresponse_501_content }, + // Ñервер не поддерживает возможноÑтей, необходимых Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ¸ запроÑа. + // Типичный ответ Ð´Ð»Ñ Ñлучаев, когда Ñервер не понимает указанный в запроÑе метод. + Ñм 405 + { 418, HTTP_RESP_FLG_FINDFILE, + HTTPresponse_418_head, + HTTPresponse_418_content }, + // http://en.wikipedia.org/wiki/Hyper_Text_Coffee_Pot_Control_Protocol + { 500, HTTP_RESP_FLG_END, + HTTPresponse_500_head, + HTTPresponse_500_content } + // Ð»ÑŽÐ±Ð°Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÑÑ Ð¾ÑˆÐ¸Ð±ÐºÐ° Ñервера, ÐºÐ¾Ñ‚Ð¾Ñ€Ð°Ñ Ð½Ðµ входит в рамки оÑтальных ошибок клаÑÑа. +}; +const char HTTPfsupload[] ICACHE_RODATA_ATTR = "
File Upload

"; +#define sizeHTTPfsupload 220 +const char HTTPdefault[] ICACHE_RODATA_ATTR = "

RTL871X Built-in Web server ©

"; +#define sizeHTTPdefault 73 +const char HTTPfserror[] ICACHE_RODATA_ATTR = "

Web-disk error. Upload the WEBFiles.bin!

"; +#define sizeHTTPfserror 62 + +const char HTTPAccessControlAllowOrigin[] ICACHE_RODATA_ATTR = "Access-Control-Allow-Origin: *\r\n"; +// const uint8 *HTTPCacheControl = "Cache-Control:"; +const char *HTTPContentLength = "Content-Length:"; +#define sizeHTTPContentLength 15 +// const uint8 *HTTPConnection = "Connection: "; +// #define sizeHTTPConnection 12 +// const uint8 *HTTPkeepalive = "keep-alive"; +// #define sizeHTTPkeepalive 10 +// const uint8 *HTTPIfNoneMatch = "If-None-Match:" +// #define sizeHTTPIfNoneMatch 14 +const char *HTTPContentType = "Content-Type:"; +#define sizeHTTPContentType 13 +const char *HTTPmultipartformdata = "multipart/form-data"; +#define sizeHTTPmultipartformdata 19 +const char *HTTPboundary = "boundary="; +#define sizeHTTPboundary 9 +const char *HTTPAuthorization = "Authorization:"; +#define sizeHTTPAuthorization 14 +const char *HTTPCookie = "Cookie:"; +#define sizeHTTPCookie 7 + +/****************************************************************************** + * FunctionName : Close_web_conn + * Description : Free ts_conn + * Parameters : struct TCP_SERV_CONN + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR Close_web_conn(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + int i = 0; + do { + if(web_conn->bffiles[i] != WEBFS_INVALID_HANDLE) { +#if DEBUGSOO > 1 + os_printf("cf%d ", web_conn->bffiles[i]); +#endif + if(web_conn->bffiles[i] <= WEBFS_MAX_HANDLE) WEBFSClose(web_conn->bffiles[i]); + web_conn->bffiles[i] = WEBFS_INVALID_HANDLE; + }; + i++; + }while(i < 4); + ClrSCB(SCB_FOPEN | SCB_FGZIP | SCB_FCALBACK); +} +/****************************************************************************** + * FunctionName : ReNew_web_conn + * Description : + * Parameters : struct TCP_SERV_CONN + * Returns : none +*******************************************************************************/ +LOCAL WEB_SRV_CONN * ICACHE_FLASH_ATTR ReNew_web_conn(TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(web_conn == NULL) { + web_conn = (WEB_SRV_CONN *)os_zalloc(sizeof(WEB_SRV_CONN)); + if(web_conn != NULL) { + web_conn->bffiles[0] = WEBFS_INVALID_HANDLE; + web_conn->bffiles[1] = WEBFS_INVALID_HANDLE; + web_conn->bffiles[2] = WEBFS_INVALID_HANDLE; + web_conn->bffiles[3] = WEBFS_INVALID_HANDLE; + // web_conn->webflag = 0; //zalloc + // web_conn->func_web_cb = NULL; //zalloc + OpenSCB(); // ÑброÑить флаги + ts_conn->linkd = (void *)web_conn; + }; + } + return web_conn; +} +//============================================================================= +// Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n" +// The resulting string is then encoded using the RFC2045-MIME variant of Base64, +// except not limited to 76 uint8/line +// /ssl/crypto/ssl_crypto_misc.c: +// EXP_FUNC int STDCALL base64_decode(const uint8 *in, int len, uint8_t *out, int *outlen); +// Username and password are combined into a string "username:password" +LOCAL bool ICACHE_FLASH_ATTR CheckAuthorization(uint8* base64str) +{ + uint8 *pcmp = base64str; + int len = 0; + while(*pcmp++ >= '+') len++; +// struct softap_config apcfg; + uint8 pbuf[77]; + int declen = 76; + if((len >= 4)&&(len <= 128) + &&(base64decode(base64str, len, pbuf, &declen))) { + pbuf[declen]='\0'; + uint8 ppsw[32+64+1]; + cmpcpystr(ppsw, wifi_ap_cfg.ssid, '\0','\0', 32); + len = rtl_strlen((char*)ppsw); + ppsw[len++] = ':'; + cmpcpystr(&ppsw[len], wifi_ap_cfg.password, '\0','\0', 64); +#if DEBUGSOO > 1 + os_printf("'%s' ", pbuf); +#endif +#if DEBUGSOO > 2 + os_printf("<%s>[%u] ", ppsw, declen); +#endif + if(os_strncmp(pbuf, (char *)ppsw , declen) == 0) return true; + }; + return false; +} +//============================================================================= + +//============================================================================= +LOCAL void ICACHE_FLASH_ATTR +web_parse_cookie(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + if((CurHTTP->pcookie == NULL)||(CurHTTP->cookie_len == 0)) return; + uint8 pcmd[CmdNameSize]; + uint8 pvar[VarNameSize*3]; + uint8 *pcmp = CurHTTP->pcookie - 1; + do { + pcmp = cmpcpystr(pvar, ++pcmp, '\0', '=', sizeof(pvar)-1); + if(pcmp == NULL) return; + urldecode(pcmd, pvar, CmdNameSize - 1, sizeof(pvar)); + pcmp = cmpcpystr(pvar, pcmp, '=', ';', sizeof(pvar)-1); + if(pcmd[0]!='\0') { + urldecode(pvar, pvar, VarNameSize - 1, sizeof(pvar)); + web_int_vars(ts_conn, pcmd, pvar); + } + } while(pcmp != NULL); +} +//============================================================================= +LOCAL void ICACHE_FLASH_ATTR +web_parse_uri_vars(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + if((CurHTTP->puri == NULL)||(CurHTTP->uri_len == 0)) return; + uint8 pcmd[CmdNameSize]; + uint8 pvar[VarNameSize*3]; + uint8 *pcmp = CurHTTP->puri; + uint8 c = '?'; + pcmp = cmpcpystr(NULL, pcmp, '\0', c, CurHTTP->uri_len); + while(pcmp != NULL) { + pcmp = cmpcpystr(pvar, pcmp, c, '=', sizeof(pvar)-1); + if(pcmp == NULL) return; + urldecode(pcmd, pvar, CmdNameSize - 1, sizeof(pvar)); + c = '&'; + pcmp = cmpcpystr(pvar, pcmp, '=', c, sizeof(pvar)-1); + if(pcmd[0]!='\0') { + urldecode(pvar, pvar, VarNameSize - 1, sizeof(pvar)); + web_int_vars(ts_conn, pcmd, pvar); + } + }; +} +//============================================================================= +LOCAL void ICACHE_FLASH_ATTR +web_parse_content(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + if((CurHTTP->pcontent == NULL)||(CurHTTP->content_len == 0)) return; + uint8 pcmd[CmdNameSize]; + uint8 pvar[VarNameSize*3]; + uint8 *pcmp = CurHTTP->pcontent; + uint8 c = '\0'; + do { + pcmp = cmpcpystr(pvar, pcmp, c, '=', sizeof(pvar)-1); + if(pcmp == NULL) return; + urldecode(pcmd, pvar, CmdNameSize - 1, sizeof(pvar)); + c = '&'; + pcmp = cmpcpystr(pvar, pcmp, '=', c, sizeof(pvar)-1); + if(pcmd[0]!='\0') { + urldecode(pvar, pvar, VarNameSize - 1, sizeof(pvar)); + web_int_vars(ts_conn, pcmd, pvar); + } + } while(pcmp != NULL); +} +//============================================================================= +// Разбор имени файла и перевод в вид отноÑительного URI. +// (выкидывание HTTP://Host) +// Проверка на обращение в папку или Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° требующее пароль +//============================================================================= +LOCAL void ICACHE_FLASH_ATTR +web_parse_fname(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + if(CurHTTP->puri == NULL) return; + if(CurHTTP->uri_len < 2) { // = "/"? + CurHTTP->pFilename[0] = CurHTTP->puri[0]; + return; + } + { + uint8 cbuf[FileNameSize+16]; + uint8 *pcbuf = cbuf; + urldecode(pcbuf, CurHTTP->puri, sizeof(cbuf) - 1, CurHTTP->uri_len); + if((os_strncmp((char *)pcbuf, "HTTP://", 7) == 0)||(os_strncmp((char *)pcbuf, "http://", 7) == 0)) { + pcbuf += 7; + uint8 *pcmp = os_strchr((char *)pcbuf, '/'); + if(pcmp != NULL) pcbuf = pcmp; + }; + cmpcpystr(CurHTTP->pFilename, pcbuf, '\0', '?', FileNameSize); + }; + { // Проверка на обращение в папку или Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° требующее пароль + uint8 *pcmp = web_strnstr(CurHTTP->pFilename, ProtectedFilesName, os_strlen(CurHTTP->pFilename)); + if(pcmp != NULL) { + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + SetSCB(SCB_AUTH); + } + }; +} +//============================================================================= +//============================================================================= +uint8 * ICACHE_FLASH_ATTR head_find_ctr(HTTP_CONN *CurHTTP, const uint8 * c, int clen, int dlen) +{ + if(CurHTTP->head_len < clen + dlen + 2) return NULL; // + "\r\n" + uint8 * pstr = web_strnstr((char *)CurHTTP->phead, c, CurHTTP->head_len); + if(pstr != NULL) { + pstr += clen; + uint8 *pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr); + if(pend == NULL) { + CurHTTP->httpStatus = 400; // 400 Bad Request + return NULL; + } + while(*pstr == ' ' && pstr < pend) pstr++; + if(pend - pstr < dlen) { + CurHTTP->httpStatus = 400; // 400 Bad Request + return NULL; + } + } + return pstr; +} +//============================================================================= +// Func: parse_header +// Разбирает докачан или нет заголовок HTTP, что там принÑто, GET или POST, +// открывает файл и проверÑет content, еÑли Ñто POST и не прием файла. +//============================================================================= +LOCAL bool ICACHE_FLASH_ATTR +parse_header(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + CurHTTP->httpStatus = 501; // 501 Not Implemented (not a GET or POST command) + + uint8 *pstr = ts_conn->pbufi; + uint8 *pend = &ts_conn->pbufi[ts_conn->sizei]; + CurHTTP->pcontent = pend; + + if(pstr == NULL) { + CurHTTP->httpStatus = 500; // 500 Internal Server Error + return false; + }; + if(ts_conn->sizei < MIN_REQ_LEN) return false; // 501 Not Implemented (not a GET or POST command) + uint8 *pnext = web_strnstr(pstr, CRLF, ts_conn->sizei); // "\r\n" +// if(pnext != NULL) *pnext = '\0'; + if(pnext == NULL) { + CurHTTP->httpStatus = 400; // 400 Bad Request + return false; + }; + pnext += 2; + if(pnext - pstr < MIN_REQ_LEN) return false; // 501 размер Ñтроки запроÑа менее "GET /" + if(os_strncmp(pstr, "GET ", 4) == 0) { + SetSCB(SCB_GET); + CurHTTP->httpStatus = 200; + pstr += 4; + } + else if(os_strncmp(pstr, "POST ", 5) == 0) { + SetSCB(SCB_POST); + CurHTTP->httpStatus = 200; + pstr += 5; + } + else return false; // 501 Not Implemented (not a GET or POST command) + CurHTTP->puri = pstr; + CurHTTP->uri_len = pnext - pstr; + + if(CurHTTP->uri_len > 10) { // "/ HTTP/1.0\r\n" + pstr = web_strnstr(CurHTTP->puri, " HTTP/", CurHTTP->uri_len); + if(pstr != NULL) { + if((pstr[7] == '.')&&(pstr[6] <= '9')&&(pstr[6] >= '0')&&(pstr[8] >= '0')&&(pstr[8] <= '9')) + CurHTTP->httpver = ((pstr[6]-'0')<<4) + pstr[8]-'0'; + // else CurHTTP->ver = 0x00; + }; + }; +#if DEBUGSOO > 3 + os_printf("http_ver=%02x ", CurHTTP->httpver); +#endif + if(CurHTTP->httpver < 0x10) { // HTTP/0.9 ? + if(CheckSCB(SCB_POST)) { + CurHTTP->httpStatus = 400; // 400 HTTP/0.9 does not support POST + return false; // HTTP/0.9 + }; + }; + // здеÑÑŒ уже надо глÑдеть - Ñледует или нет докачивать данные + pstr = web_strnstr(pnext-2, CRLF CRLF, pend - pnext + 2 ); // find "\r\n\r\n" + if(pstr == NULL) return true; // докачивать! + // разбираем дальше Header, раз уже Ñкачан + pstr += 2; + if(pstr != pnext) { // еÑÑ‚ÑŒ Headers + CurHTTP->phead = pnext; + CurHTTP->head_len = pstr - pnext; + if(CheckSCB(SCB_POST)){ + pstr += 2; + CurHTTP->pcontent = pstr; + CurHTTP->content_len = pend - pstr; + }; + }; + if(!CheckSCB(SCB_FOPEN)) { // файл уже открыт? нет + web_parse_fname(CurHTTP, ts_conn); + if(!webserver_open_file(CurHTTP, ts_conn)) { + CurHTTP->httpStatus = 404; // "404: File not found" + return false; // + }; + }; + if((CurHTTP->phead == NULL)||(CurHTTP->head_len == 0)) { + // еÑли требуетÑÑ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ, но нет передачи паролÑ... + if(CheckSCB(SCB_AUTH)) CurHTTP->httpStatus = 401; // 401 Unauthorized + return false; // нет Header + }; + if(CheckSCB(SCB_POST)) { + pstr = head_find_ctr(CurHTTP, HTTPContentLength, sizeHTTPContentLength, 1); + if(pstr == NULL || CurHTTP->httpStatus != 200) { + CurHTTP->httpStatus = 411; // no "Content Length:", 411 Length Required + return false; + } + uint32 cnlen = atoi(pstr); +#if DEBUGSOO > 1 + os_printf("content_len = %d of %d ", cnlen, CurHTTP->content_len); +#endif + if(cnlen) { + web_conn->content_len = cnlen; // запомнить размер, Ð´Ð»Ñ Ð¿Ñ€Ð¸ÐµÐ¼Ð° файла + if(!CheckSCB(SCB_BNDR) && (CurHTTP->head_len > sizeHTTPContentType + sizeHTTPmultipartformdata + sizeHTTPboundary + 2 + 2)) { //"x\r\n" + pstr = head_find_ctr(CurHTTP, HTTPContentType, sizeHTTPContentType, sizeHTTPmultipartformdata + sizeHTTPboundary + 2); + if(CurHTTP->httpStatus != 200) return false; + if(pstr != NULL) { + pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr); + pstr = web_strnstr(pstr, HTTPmultipartformdata, pend - pstr); + if(pstr != NULL) { + pstr += sizeHTTPmultipartformdata; + pstr = web_strnstr(pstr, HTTPboundary, pend - pstr); + if(pstr != NULL) { + // Ñохраним Ñтот "мультипаÑпорт" (Ñ) 5-ый Ñлемент :) + pstr += sizeHTTPboundary; + HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)os_zalloc(sizeof(HTTP_UPLOAD)); + if(pupload == NULL) { + CurHTTP->httpStatus = 500; // 500 Internal Server Error + return false; + } + uint8 x = *pend; + *pend = '\0'; +#if DEBUGSOO > 4 + os_printf("[%s] ", pstr); +#endif + rtl_memcpy(pupload->boundary, pstr, MAXLENBOUNDARY); + *pend = x; + pupload->sizeboundary = os_strlen(pupload->boundary); + ts_conn->pbufo = (uint8 *)pupload; + SetSCB(SCB_BNDR); +// if(cnlen > ((pupload->sizeboundary * 2) + 18)) { + SetSCB(SCB_RXDATA); +// } + }; + }; + }; + }; + if((!CheckSCB(SCB_BNDR)) && cnlen > CurHTTP->content_len) { // обычный контент и недокачан заголовок? да. + CurHTTP->content_len = cnlen; +#if DEBUGSOO > 2 + os_printf("wait content "); +#endif + CurHTTP->httpStatus = 413; // 413 Request Entity Too Large // пока так + return true; // докачивать + }; + } + else CurHTTP->content_len = cnlen; // уточнить, что Content Length = 0 + }; + if(CheckSCB(SCB_AUTH)) { + pstr = head_find_ctr(CurHTTP, HTTPAuthorization, sizeHTTPAuthorization, 5 + 3); // "Authorization: Basic 1234\r\n" + if(pstr == NULL || CurHTTP->httpStatus != 200) { + CurHTTP->httpStatus = 401; // 401 Unauthorized + return false; + } + if(os_strncmp(pstr, "Basic", 5) == 0) { // The authorization method and a space i.e. "Basic" is then put before the encoded string. + pstr += 5; + while(*pstr == ' ') pstr++; + if(CheckAuthorization(pstr)) ClrSCB(SCB_AUTH); + else { + CurHTTP->httpStatus = 401; // 401 Unauthorized + return false; + }; + } + else { + CurHTTP->httpStatus = 401; // 401 Unauthorized + return false; + }; + }; + + if(CurHTTP->head_len > sizeHTTPCookie + 4) { // "Cookie: a=\r\n" + pstr = head_find_ctr(CurHTTP, HTTPCookie, sizeHTTPCookie, 2); + if(pstr != NULL) { + pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr); + if(pend != NULL) { + CurHTTP->pcookie = pstr; + CurHTTP->cookie_len = pend - pstr; +#if DEBUGSOO > 3 + *pend = '\0'; + os_printf("cookie:[%s] ", pstr); + *pend = '\r'; +#endif + } +#if DEBUGSOO > 3 + else os_printf("cookie not crlf! "); +#endif + }; + }; +#ifdef WEBSOCKET_ENA + if(CheckSCB(SCB_GET) && web_conn->bffiles[0] == WEBFS_WEBCGI_HANDLE) { +#if DEBUGSOO > 3 + os_printf("hdlen=%d ", CurHTTP->head_len); +#endif + if(CurHTTP->head_len > sizeHTTPUpgrade + sizeHTTPwebsocket + 2 + sizeHTTPSecWebSocketKey + minsizeWebSocketKey + 2) { // + "\r\n" + pstr = head_find_ctr(CurHTTP, HTTPUpgrade, sizeHTTPUpgrade, sizeHTTPwebsocket); + if(CurHTTP->httpStatus != 200) return false; + if(pstr != NULL) { + if(!rom_xstrcmp(word_to_lower_case(pstr), HTTPwebsocket)) { + CurHTTP->httpStatus = 400; // 400 Bad Request + return false; + } + pstr = head_find_ctr(CurHTTP, HTTPSecWebSocketKey, sizeHTTPSecWebSocketKey, minsizeWebSocketKey); + if(pstr == NULL || CurHTTP->httpStatus != 200) return false; + { + if(WebSocketAcceptKey(CurHTTP->pFilename, pstr)) SetSCB(SCB_WEBSOC); + } + } + } + } +#endif + return false; +} +/****************************************************************************** + * FunctionName : web_inc_fp + * Parameters : fp +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR web_inc_fp(WEB_SRV_CONN *web_conn, WEBFS_HANDLE fp) +{ + if(web_conn->bffiles[3] != WEBFS_INVALID_HANDLE) { + #if DEBUGSOO > 1 + os_printf("cf%d ", web_conn->bffiles[3]); + #endif + if(web_conn->bffiles[3] <= WEBFS_MAX_HANDLE) { + web_conn->content_len -= WEBFSGetBytesRem(web_conn->bffiles[3]); + WEBFSClose(web_conn->bffiles[3]); + } + }; + web_conn->bffiles[3] = web_conn->bffiles[2]; + web_conn->bffiles[2] = web_conn->bffiles[1]; + web_conn->bffiles[1] = web_conn->bffiles[0]; + web_conn->bffiles[0] = fp; + SetSCB(SCB_FOPEN); // файл открыт +} +/****************************************************************************** + * FunctionName : web_inc_fopen + * Description : web include file open + * Parameters : struct + * Returns : true - open OK +*******************************************************************************/ +bool ICACHE_FLASH_ATTR web_inc_fopen(TCP_SERV_CONN *ts_conn, uint8 *cFile) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(CheckSCB(SCB_FOPEN) && (!CheckSCB(SCB_FCALBACK))) { // файл уже открыт и он не парÑитÑÑ? + return false; // такое не поддерживаетÑÑ Ð² "~inc:filename~" + }; + WEBFS_HANDLE fp = WEBFSOpen(cFile); +#if DEBUGSOO > 1 + os_printf("of%d[%s] ", fp, cFile); +#endif + if(fp != WEBFS_INVALID_HANDLE) { + if(fatCache.flags & WEBFS_FLAG_HASINDEX) SetSCB(SCB_FCALBACK); // файл надо парÑить + web_conn->content_len += WEBFSGetBytesRem(fp); // указать размер файла Ð´Ð»Ñ Ð²Ñ‹Ð²Ð¾Ð´Ð° + if(fatCache.flags & WEBFS_FLAG_ISZIPPED) { + if(CheckSCB(SCB_FOPEN)) { // файл уже открыт и "~inc:filename~" не поддерживает GZIP! + WEBFSClose(fp); +#if DEBUGSOO > 1 + os_printf("Not inc GZIP! "); +#endif + return false; + }; + SetSCB(SCB_FGZIP); // файл Ñжат GZIP + } + } + else { // File not found + return false; + }; + web_inc_fp(web_conn, fp); + return true; +}; +/****************************************************************************** + * FunctionName : web_inc_file + * Description : web include file close + * Parameters : struct + * Returns : true - вÑе файлы закрыты +*******************************************************************************/ +bool ICACHE_FLASH_ATTR web_inc_fclose(WEB_SRV_CONN *web_conn) +{ + if(web_conn->bffiles[0] != WEBFS_INVALID_HANDLE) { +#if DEBUGSOO > 1 + os_printf("cf%d ", web_conn->bffiles[0]); +#endif + if(web_conn->bffiles[0] <= WEBFS_MAX_HANDLE) { + WEBFSClose(web_conn->bffiles[0]); + ClrSCB(SCB_FGZIP); + } + web_conn->bffiles[0] = web_conn->bffiles[1]; + web_conn->bffiles[1] = web_conn->bffiles[2]; + web_conn->bffiles[2] = web_conn->bffiles[3]; + web_conn->bffiles[3] = WEBFS_INVALID_HANDLE; + if(web_conn->bffiles[0] != WEBFS_INVALID_HANDLE) return false; + }; + ClrSCB(SCB_FOPEN | SCB_FGZIP | SCB_FCALBACK); + return true; // больше нет файлов +}; +/****************************************************************************** + * FunctionName : webserver_open_file + * Description : Compare to known extensions to determine Content-Type + * Parameters : filename -- file name + * Returns : 1 - open, 0 - no +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR webserver_file_ext(HTTP_CONN *CurHTTP, uint8 *pfname) +{ + uint8 *pfext = NULL; + while(*pfname >= ' ') if(*pfname++ == '.') pfext = pfname; + if(pfext != NULL) { + for(CurHTTP->fileType = HTTP_TXT; CurHTTP->fileType < HTTP_UNKNOWN; CurHTTP->fileType++) + if(rom_xstrcmp(pfext, httpFileExtensions[CurHTTP->fileType])) break; + }; +} +/*----------------------------------------------------------------------*/ +#ifdef USE_CAPTDNS +/* = flase, еÑли включен redirect, и Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð¾Ñ‚ ip адреÑа из подÑети AP, + * и Host name не равен aesp8266 или ip AP. */ +LOCAL bool ICACHE_FLASH_ATTR web_cdns_no_redir(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + if(syscfg.cfg.b.cdns_ena + && pcb_cdns != NULL + &&((ts_conn->pcb->remote_ip.addr ^ info.ap_ip) & info.ap_mask) == 0 + && CurHTTP->phead != NULL + && CurHTTP->head_len != 0) { + uint8 * ps = head_find_ctr(CurHTTP, HTTPHost, sizeHTTPHost, 7); + if(ps != NULL) { +#if DEBUGSOO > 1 + os_printf("Host: '%s' ", ps); +#endif + uint8 strip[4*4]; + os_sprintf_fd(strip, IPSTR, IP2STR(&info.ap_ip)); + if((rom_xstrcmp(ps, HostNameLocal) == 0) && (rom_xstrcmp(ps, strip) == 0)) { + rtl_sprintf(CurHTTP->pFilename, httpHostNameLocal, HostNameLocal); // "http://esp8266/" + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + SetSCB(SCB_REDIR); + return false; + } + } + } + return true; +} +#endif +/****************************************************************************** + * FunctionName : webserver_open_file + * Description : Open file + * Parameters : filename -- file name + * Returns : 1 - open, 0 - no +*******************************************************************************/ +LOCAL bool ICACHE_FLASH_ATTR webserver_open_file(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + uint8 pbuf[MAX_FILE_NAME_SIZE]; + uint8 *pstr = pbuf; + if(CurHTTP->pFilename[0] == '/') { + if(CurHTTP->pFilename[1] == '\0') { + if(isWEBFSLocked) { + web_inc_fp(web_conn, WEBFS_NODISK_HANDLE); // желательно допиÑать ответ, что нет диÑка. + web_conn->content_len = sizeHTTPfserror; + CurHTTP->fileType = HTTP_HTML; +#if DEBUGSOO > 1 + os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename); +#endif + return true; + } + else { +#ifdef USE_CAPTDNS + if(web_cdns_no_redir(CurHTTP, ts_conn)) rom_xstrcpy(pstr, http_default_file); + else return false; +#else + rom_xstrcpy(pstr, http_default_file); +#endif + } + } + else { + rtl_memcpy(pstr, &CurHTTP->pFilename[1], MAX_FILE_NAME_SIZE-1); + if(rom_xstrcmp(pstr, web_cgi_fname)) { + web_inc_fp(web_conn, WEBFS_WEBCGI_HANDLE); + web_conn->content_len = sizeHTTPdefault; + CurHTTP->fileType = HTTP_HTML; +#if DEBUGSOO > 1 + os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename); +#endif + return true; + } + else if(rom_xstrcmp(pstr, fsupload_fname)) { + SetSCB(SCB_AUTH); + web_inc_fp(web_conn, WEBFS_UPLOAD_HANDLE); + web_conn->content_len = sizeHTTPfsupload; + CurHTTP->fileType = HTTP_HTML; +#if DEBUGSOO > 1 + os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename); +#endif + return true; + } + } + if(isWEBFSLocked) return false; + // поиÑк файла на диÑке + if(!web_inc_fopen(ts_conn, pstr)) { + uint32 i = os_strlen(pbuf); + if(i + sizeof(http_default_file) < MAX_FILE_NAME_SIZE - 1) { + // добавить к имени папки "/index.htm" + pbuf[i] = '/'; + rom_xstrcpy(&pbuf[i+1], http_default_file); + if(!web_inc_fopen(ts_conn, pstr)) { +#ifdef USE_CAPTDNS + web_cdns_no_redir(CurHTTP, ts_conn); +#endif + return false; + } + }; + }; + // Compare to known extensions to determine Content-Type + webserver_file_ext(CurHTTP, pstr); + return true; + }; + return false; // файл не открыт +} +/****************************************************************************** +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR web_send_fnohanle(TCP_SERV_CONN *ts_conn) { + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + uint32 pdata = 0; +// uint8 pbuf[mMAX(mMAX(sizeHTTPdefault,sizeHTTPfserror), sizeHTTPfsupload)]; + uint32 size = 0; + switch(web_conn->webfile) { + case WEBFS_WEBCGI_HANDLE: + pdata = (uint32)((void *)HTTPdefault); + size = sizeHTTPdefault; + break; + case WEBFS_UPLOAD_HANDLE: + pdata = (uint32)((void *)HTTPfsupload); + size = sizeHTTPfsupload; + break; + case WEBFS_NODISK_HANDLE: + pdata = (uint32)((void *)HTTPfserror); + size = sizeHTTPfserror; + break; + } + if(pdata != 0 && size != 0) { +// spi_flash_read(pdata & MASK_ADDR_FLASH_ICACHE_DATA, pbuf, size); + tcpsrv_int_sent_data(ts_conn, pdata, size); + } +#if DEBUGSOO > 1 + os_printf("%u ", size); +#endif + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); +} +/****************************************************************************** +*******************************************************************************/ +LOCAL int ICACHE_FLASH_ATTR web_find_cbs(uint8 * chrbuf, uint32 len) { + int i; + for(i = 0; i < len; i++) if(chrbuf[i] == '~') return i; + return -1; +} +/****************************************************************************** + * FunctionName : webserver_send_fdata + * Description : Sent callback function to call for this espconn when data + * is successfully sent + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR webserver_send_fdata(TCP_SERV_CONN *ts_conn) { + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(web_conn->webfile == WEBFS_INVALID_HANDLE) { + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); + return; + } +#if DEBUGSOO > 1 + os_printf("send: "); +#endif +#ifdef SET_CPU_CLK_SPEED +// set_cpu_clk(); +#endif + web_conn->msgbufsize = tcp_sndbuf(ts_conn->pcb); + +#if DEBUGSOO > 5 + os_printf("sndbuf=%u ", web_conn->msgbufsize); +#endif + + if (web_conn->msgbufsize < MIN_SEND_SIZE) { +#if DEBUGSOO > 1 + os_printf("sndbuf=%u! ", web_conn->msgbufsize); + if(ts_conn->flag.wait_sent) os_printf("wait_sent! "); // блок передан? +#endif + ts_conn->pcb->flags &= ~TF_NODELAY; + tcpsrv_int_sent_data(ts_conn, (uint8 *)ts_conn, 0); + return; + } + if((web_conn->webfile > WEBFS_MAX_HANDLE)&&(!CheckSCB(SCB_RETRYCB))) { + web_send_fnohanle(ts_conn); + return; + } + web_conn->msgbufsize = mMIN(MAX_SEND_SIZE, web_conn->msgbufsize); + uint8 *pbuf = (uint8 *) os_malloc(web_conn->msgbufsize); + if (pbuf == NULL) { +#if DEBUGSOO > 0 + os_printf("out of memory - disconnect!\n"); +#endif + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); + return; + }; + web_conn->msgbuf = pbuf; + web_conn->msgbuflen = 0; + if (CheckSCB(SCB_CHUNKED)) { // is chunked + web_conn->msgbuf += RESCHKS_SEND_SIZE; + web_conn->msgbufsize -= RESCHK_SEND_SIZE; + }; + if(CheckSCB(SCB_FCALBACK) == 0) { // передача файла без парÑинга + // Get/put as many bytes as possible + web_conn->msgbuflen = WEBFSGetArray(web_conn->webfile, web_conn->msgbuf, web_conn->msgbufsize); + if(web_conn->msgbuflen < web_conn->msgbufsize ) SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + } + else { // парÑинг потока передачи + do { // начинаем Ñ Ð¿ÑƒÑтого буфера + if(CheckSCB(SCB_RETRYCB)) { // повторный callback? да +#if DEBUGSOO > 2 + os_printf("rcb "); +#endif + if(web_conn->func_web_cb != NULL) web_conn->func_web_cb(ts_conn); + if(CheckSCB(SCB_RETRYCB)) break; // повторить ещё раз? да. + } + else { + uint8 *pstr = &web_conn->msgbuf[web_conn->msgbuflen]; // указатель в буфере + // запомнить указатель в файле. ftell(fp) + uint32 max = mMIN(web_conn->msgbufsize - web_conn->msgbuflen, SCB_SEND_SIZE); // читаем по 128 байт ? + uint32 len = WEBFSGetArray(web_conn->webfile, pstr, max); + // прочитано len байт в буфер по указателю &sendbuf[msgbuflen] + if(len) { // еÑÑ‚ÑŒ байты Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸, ищем string "~calback~" + int cmp = web_find_cbs(pstr, len); + if(cmp >= 0) { // найден calback + // откат файла + WEBFSStubs[web_conn->webfile].addr -= len; + WEBFSStubs[web_conn->webfile].bytesRem += len; + // передвинуть указатель в файле на Ñчитанные байты Ñ ÑƒÑ‡ÐµÑ‚Ð¾Ð¼ маркера, без добавки длины Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸ + WEBFSStubs[web_conn->webfile].addr += cmp+1; + WEBFSStubs[web_conn->webfile].bytesRem -= cmp+1; + // Ñто второй маркер? + if(CheckSCB(SCB_FINDCB)) { // в файле найден закрывающий маркер calback + ClrSCB(SCB_FINDCB); // прочитали string calback-а + if(cmp != 0) { // Ñто дубль маркера ? нет. + // запуÑтить calback + pstr[cmp] = '\0'; // закрыть string calback-а + if(!os_memcmp((void*)pstr, "inc:", 4)) { // "inc:file_name" + if(!web_inc_fopen(ts_conn, &pstr[4])) { + tcp_strcpy_fd("file not found!"); + }; + } + else web_int_callback(ts_conn, pstr); + } + else { // Дубль маркера. + web_conn->msgbuflen++; // передать только маркер ('~') + }; + } + else { + SetSCB(SCB_FINDCB); // в файле найден Ñтартовый маркер calback + web_conn->msgbuflen += cmp; // передать до Ñтартового маркера calback + }; + } + else { // проÑто данные + ClrSCB(SCB_FINDCB); + if(len < max) { + if(web_inc_fclose(web_conn)) SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // файл(Ñ‹) закончилÑÑŒ ÑовÑем? да. + }; + web_conn->msgbuflen += len; // добавить кол-во Ñчитанных байт Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‡Ð¸. + }; + } + else if(web_inc_fclose(web_conn)) SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // файл(Ñ‹) закончилÑÑŒ ÑовÑем? да. + }; // not SCB_RETRYCB + } // набираем буфер + while((web_conn->msgbufsize - web_conn->msgbuflen >= SCB_SEND_SIZE)&&(!CheckSCB(SCB_FCLOSE | SCB_RETRYCB | SCB_DISCONNECT))); + }; +#if DEBUGSOO > 3 + os_printf("#%04x %d ", web_conn->webflag, web_conn->msgbuflen); +#elif DEBUGSOO > 1 + os_printf("%u ", web_conn->msgbuflen); +#endif + if(web_conn->msgbuflen) { + web_conn->content_len -= web_conn->msgbuflen; // пока только Ð´Ð»Ñ Ð¸Ð½Ñ„Ñ‹ + if(CheckSCB(SCB_CHUNKED)) { // greate chunked + uint8 cbuf[16]; + static const char chunks[] ICACHE_RODATA_ATTR = "\r\n%X\r\n"; + unsigned int len = rtl_sprintf(cbuf, chunks, web_conn->msgbuflen); + web_conn->msgbuf -= len; + rtl_memcpy(web_conn->msgbuf, cbuf, len); + web_conn->msgbuflen += len; + if(CheckSCB(SCB_FCLOSE)) { // close file? -> add 'end chunked' + tcp_strcpy_fd("\r\n0\r\n\r\n"); + }; + }; + ts_conn->pcb->flags |= TF_NODELAY; + tcpsrv_int_sent_data(ts_conn, web_conn->msgbuf, web_conn->msgbuflen); + }; + os_free(pbuf); + web_conn->msgbuf = NULL; +} +/****************************************************************************** + * FunctionName : web_print_headers + * Description : Print HTTP Response Header + * Parameters : * + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR +web_print_headers(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + HTTP_RESPONSE *CurResp = (HTTP_RESPONSE *)HTTPResponse; +#if DEBUGSOO > 3 + os_printf("prh#%04x,%d,%d ", web_conn->webflag, CurHTTP->httpStatus, CurHTTP->fileType); +#endif + web_conn->msgbuf = (uint8 *)os_malloc(HTTP_SEND_SIZE); + if(web_conn->msgbuf == NULL) + { +#if DEBUGSOO == 1 + os_printf("web: out of memory!\n"); +#elif DEBUGSOO > 1 + os_printf("out of memory! "); +#endif + SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + return; + } + web_conn->msgbufsize = HTTP_SEND_SIZE; + web_conn->msgbuflen = 0; + if(CheckSCB(SCB_REDIR)) { + CurHTTP->httpStatus = 302; // редирект + } +#ifdef WEBSOCKET_ENA + if(CheckSCB(SCB_WEBSOC) && CurHTTP->httpStatus == 200) { +#if DEBUGSOO > 1 + CurHTTP->httpStatus = 101; +#endif + tcp_puts(WebSocketHTTPOkKey, CurHTTP->pFilename); + } + else { +#endif + while(!(CurResp->flag & HTTP_RESP_FLG_END)) { + if(CurResp->status == CurHTTP->httpStatus) break; + CurResp++; + }; + tcp_puts_fd("HTTP/1.1 %u ", CurResp->status); + tcp_strcpy(CurResp->headers); + tcp_strcpy_fd("\r\nServer: " WEB_NAME_VERSION "\r\nConnection: close\r\n"); + if(CheckSCB(SCB_REDIR)) { + tcp_puts_fd("Location: %s\r\n\r\n", CurHTTP->pFilename); + ts_conn->flag.pcb_time_wait_free = 1; // закрыть Ñоединение + SetSCB(SCB_DISCONNECT); + } + else { + if(CurResp->status != 200) { + web_inc_fclose(web_conn); + ClrSCB(SCB_FCALBACK | SCB_FGZIP | SCB_CHUNKED | SCB_RXDATA | SCB_FCLOSE); + if(CurResp->flag & HTTP_RESP_FLG_FINDFILE) { + os_sprintf_fd(CurHTTP->pFilename, "/%u.htm", CurResp->status); + webserver_open_file(CurHTTP, ts_conn); + // CurHTTP->httpStatus = CurResp->status; // вернуть ÑтатуÑ! + }; + } + if((!CheckSCB(SCB_FOPEN)) && (CurResp->default_content != NULL) ) { + tcp_puts_fd("%s %u\r\n%s %s\r\n\r\n", HTTPContentLength, rtl_strlen(CurResp->default_content), + HTTPContentType, httpContentTypes[HTTP_TXT]); + tcp_strcpy(CurResp->default_content); + SetSCB(SCB_DISCONNECT); + } + else if(CheckSCB(SCB_FOPEN)) { + if(web_conn->content_len) { + // Указать, что данные могут пользовать вÑе (очень актуально Ð´Ð»Ñ XML, ...) + tcp_strcpy_fd("Access-Control-Allow-Origin: *\r\n"); + if(CurHTTP->fileType != HTTP_UNKNOWN) { + if(web_conn->bffiles[0] == WEBFS_WEBCGI_HANDLE && CheckSCB(SCB_FCALBACK)) CurHTTP->fileType = web_conn->fileType; + tcp_puts_fd("Content-Type: %s\r\n", httpContentTypes[CurHTTP->fileType]); + }; + // Output the cache-control + ContentLength + if(CheckSCB(SCB_FCALBACK)) { // длина неизветÑна + // file is callback index + tcp_strcpy_fd("Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n"); + if(CurHTTP->httpver >= 0x11) SetSCB(SCB_CHUNKED); + } + else { // длина изветÑна + tcp_puts_fd("%s %d\r\n", HTTPContentLength, web_conn->content_len); + if(CurResp->status == 200 && (!isWEBFSLocked) && web_conn->bffiles[0] != WEBFS_WEBCGI_HANDLE) { + // lifetime (sec) of static responses as string 60*60*24*14=1209600" + tcp_puts_fd("Cache-Control: smax-age=%d\r\n", FILE_CACHE_MAX_AGE_SEC); + } + else { + tcp_strcpy_fd("Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n"); + } + }; + if(CheckSCB(SCB_FGZIP)) { + // Output the gzip encoding header if needed + tcp_strcpy_fd("Content-Encoding: gzip\r\n"); + } + else if(CheckSCB(SCB_CHUNKED)) { + tcp_strcpy_fd("Transfer-Encoding: chunked\r\n"); + } + if(!CheckSCB(SCB_CHUNKED)) tcp_strcpy_fd(CRLF); + } + else { + tcp_puts_fd("%s 0\r\n\r\n", HTTPContentLength); + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); + } + } + else SetSCB(SCB_DISCONNECT); + } // CheckSCB(SCB_REDIR) +#ifdef WEBSOCKET_ENA + } +#endif +#if DEBUGSOO > 3 + os_printf("#%04x (%d) %d ", web_conn->webflag, web_conn->msgbuflen, CurHTTP->httpStatus); + web_conn->msgbuf[web_conn->msgbuflen] = 0; + os_printf("\n2?%d[%s]\n", web_conn->msgbuflen, web_conn->msgbuf); +#elif DEBUGSOO > 1 + os_printf("head[%d]:%d ", web_conn->msgbuflen, CurHTTP->httpStatus); +#endif + if(web_conn->msgbuflen) { + if(CheckSCB(SCB_DISCONNECT)) SetSCB(SCB_CLOSED); + tcpsrv_int_sent_data(ts_conn, web_conn->msgbuf, web_conn->msgbuflen); +#ifdef USE_WEB_NAGLE + ts_conn->flag.nagle_disabled = 1; +#endif + }; + os_free(web_conn->msgbuf); + web_conn->msgbuf = NULL; +} +/******************************************************************************/ +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +// поиÑк boundary +// 0 - разделитель (boundary) не найден, докачивать или ... +// 1 - boundary найден +// 200 - найден завершаюший boundary +// 400 - неверный формат +// ... +//----------------------------------------------------------------------------- +/* Пример M-Explorer: Load blk len: 399 +-----------------------------7df22f37711be\r\n +Content-Disposition: form-data; name="test"; filename="readme.txt"\r\n +Content-Type: text/plain\r\n\r\n +1234567890\r\n +-----------------------------7df22f37711be\r\n +Content-Disposition: form-data; name="start"\r\n\r\n +0x1B000\r\n +-----------------------------7df22f37711be\r\n +Content-Disposition: form-data; name="stop"\r\n\r\n +0x1B000\r\n +-----------------------------7df22f37711be--\r\n */ +/* Пример Google Chrome: Load blk len: 391 +------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n +Content-Disposition: form-data; name="test"; filename="readme.txt"\r\n +Content-Type: text/plain\r\n\r\n +1234567890\r\n +------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n +Content-Disposition: form-data; name="start"\r\n\r\n +0x1B000\r\n +------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n +Content-Disposition: form-data; name="stop"\r\n\r\n +0x1B000\r\n +------WebKitFormBoundaryugGNBVFOk6qxfe22--\r\n */ +//----------------------------------------------------------------------------- +const char crlf_end_boundary[] ICACHE_RODATA_ATTR = "--" CRLF; +LOCAL int ICACHE_FLASH_ATTR find_boundary(HTTP_UPLOAD *pupload, uint8 *pstr, uint32 len) +{ + int x = len - 6 - pupload->sizeboundary; + if(x <= 0) return 0; // разделитель (boundary) не найден - докачивать буфер + int i; + uint8 *pcmp; + for(i = 0; i <= x; i++) { + if(pstr[i] == '-' && pstr[i+1] == '-') { + pcmp = pstr + i; +// if((pstr + len - pcmp) < pupload->sizeboundary + 6) return 0; // разделитель (boundary) не найден - докачивать буфер + pupload->pbndr = pcmp; // указатель на заголовок boundary (конец блока данных); + pcmp += 2; + if(os_memcmp(pcmp, pupload->boundary, pupload->sizeboundary)) return 0; // разделитель (boundary) не найден + pcmp += pupload->sizeboundary; + if(rom_xstrcmp(pcmp, crlf_end_boundary)) { + pcmp += 4; + pupload->pnext = pcmp; // указатель в заголовке boundary (опиÑание новых данных); + return 200; // найден завершающий разделитель + } + if(pcmp[0] != '\r' || pcmp[1] != '\n') return 400; // неверный формат + pcmp += 2; + pupload->pnext = pcmp; // указатель в заголовке boundary (опиÑание новых данных); + return 1; + }; + }; + return 0; // разделитель (boundary) не найден - докачивать буфер +} +//----------------------------------------------------------------------------- +// Function: cmp_next_boundary +// return: +// 0 - разделитель (boundary) не найден, докачивать +// 1 - далее обработка данных +// 200 - найден завершающий разделитель: "\r\n--boundary--" +// 400 - неизвеÑтный формат content-а +//----------------------------------------------------------------------------- +const char disk_ok_filename[] ICACHE_RODATA_ATTR = "/disk_ok.htm"; +const char disk_err1_filename[] ICACHE_RODATA_ATTR = "/disk_er1.htm"; +const char disk_err2_filename[] ICACHE_RODATA_ATTR = "/disk_er2.htm"; +const char disk_err3_filename[] ICACHE_RODATA_ATTR = "/disk_er3.htm"; +const char sysconst_filename[] ICACHE_RODATA_ATTR = "sysconst"; +#ifdef USE_OVERLAY +const char overlay_filename[] ICACHE_RODATA_ATTR = "overlay"; +#endif +const char sector_filename[] ICACHE_RODATA_ATTR = "fsec_"; +#define sector_filename_size 5 +const char file_label[] ICACHE_RODATA_ATTR = "file"; + +LOCAL int ICACHE_FLASH_ATTR upload_boundary(TCP_SERV_CONN *ts_conn) // HTTP_UPLOAD pupload, uint8 pstr, uint16 len) +{ + HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)ts_conn->pbufo; + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(pupload == NULL) return 500; // ошибка Ñервера + uint32 ret; + uint32 len; + uint8 *pnext; + uint8 *pstr; + while(web_conn->content_len && ts_conn->pbufi != NULL) { + pstr = ts_conn->pbufi; + len = ts_conn->sizei; +#if DEBUGSOO > 4 + os_printf("bufi[%u]%u, cont:%u ", ts_conn->sizei, ts_conn->cntri, web_conn->content_len); +#endif + if(len < (8 + pupload->sizeboundary)) return 0; // разделитель (boundary) не влезет - докачивать буфер + switch(pupload->status) { + case 0: // поиÑк boundary + { +#if DEBUGSOO > 4 + os_printf("find_bndr "); +#endif + pnext = web_strnstr(pstr, CRLF CRLF , len); + if(pnext == NULL) return 0; // докачивать + len = pnext - pstr; + ret = find_boundary(pupload, pstr, len); +#if DEBUGSOO > 4 + os_printf("len=%u,ret=%u ", len, ret ); +#endif + if(ret != 1) return ret; + pstr = pupload->pnext; // +"\r\n" Ð°Ð´Ñ€ÐµÑ Ð·Ð° заголовком boundary + pupload->name[0] = '\0'; + pupload->filename[0] = '\0'; + pstr = web_strnstr(pstr, "name=", pnext - pstr); + if(pstr == NULL) return 400; // неизвеÑтный формат content-а + pstr += 5; + if(pstr >= pnext) return 400; // неизвеÑтный формат content-а + uint8 *pcmp = cmpcpystr(pupload->name, pstr, '"', '"', VarNameSize); + if(pcmp == NULL) { + pcmp = cmpcpystr(pupload->name, pstr, 0x22, 0x22, VarNameSize); + if(pcmp == NULL) return 400; // неизвеÑтный формат content-а + }; + pstr = pcmp; +#if DEBUGSOO > 4 + os_printf("name:'%s' ", pupload->name); +#endif + if(pstr >= pnext) return 400; // неизвеÑтный формат content-а + pcmp = web_strnstr(pstr, "filename=", pnext - pstr); + if(pcmp != NULL) { + pcmp += 9; + if(pcmp < pnext) { + if(cmpcpystr(pupload->filename, pcmp, '"', '"', VarNameSize) == NULL) + cmpcpystr(pupload->filename, pcmp, 0x22, 0x22, VarNameSize); + }; +#if DEBUGSOO > 1 + if(pupload->filename[0]!= '\0') os_printf("filename:'%s' ", pupload->filename); +#endif + }; + len += 4; + pupload->status++; +#if DEBUGSOO > 4 + os_printf("trim#%u\n", len ); +#endif + ts_conn->cntri += len; // далее идут данные + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500; + web_conn->content_len -= len; + break; + } + case 1: // прием данных, первый заход, проверка форматов и Ñ‚.д. + { +#if DEBUGSOO > 4 + os_printf("tst,fn='%s' ", pupload->filename); +#endif + if(pupload->filename[0]!='\0') { // загрузка файла? + if(rom_xstrcmp(pupload->name, file_label)) { // !os_memcmp((void*)pupload->name, "file", 4) + if(len < sizeof(WEBFS_DISK_HEADER)) return 0; // докачивать + WEBFS_DISK_HEADER *dhead = (WEBFS_DISK_HEADER *)pstr; + if(dhead->id != WEBFS_DISK_ID || dhead->ver != WEBFS_DISK_VER + || (web_conn->content_len - pupload->sizeboundary - 8 < dhead->disksize)) { + if(isWEBFSLocked) return 400; + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // неверный формат + return 200; + }; + if(dhead->disksize > WEBFS_max_size()) { + if(isWEBFSLocked) return 400; + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err2_filename); // rtl_memcpy(pupload->filename,"/disk_er2.htm\0",14); // не влезет + return 200; + }; + pupload->fsize = dhead->disksize; + pupload->faddr = WEBFS_base_addr(); +#if DEBUGSOO > 4 + os_printf("updisk[%u]=ok ", dhead->disksize); +#endif + pupload->status = 3; // = 3 загрузка WebFileSystem во flash + isWEBFSLocked = true; + break; + } +#ifdef USE_OVERLAY + else if(rom_xstrcmp(pupload->name, overlay_filename)) { + if(len < sizeof(struct SPIFlashHeader)) return 0; // докачивать + struct SPIFlashHeader *fhead = (struct SPIFlashHeader *)pstr; + if(web_conn->content_len - pupload->sizeboundary < sizeof(fhead) + || fhead->head.id != LOADER_HEAD_ID) { + if(isWEBFSLocked) return 400; + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // неверный формат + return 200; + }; + if(fhead->entry_point >= IRAM_BASE && ovl_call != NULL) { + ovl_call(0); // close прошлый оверлей + ovl_call = NULL; + } + pupload->start = fhead->entry_point; + pupload->segs = fhead->head.number_segs; + if(pupload->segs) { + pupload->fsize = sizeof(struct SPIFlashHeadSegment); + pupload->status = 5; // = 5 загрузка файла оверлеÑ, начать Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ заголовка Ñегмента + } + else { + pupload->fsize = 0; + pupload->status = 4; // = 4 загрузка файла оверлеÑ, запуÑк entry_point + } + // + len = sizeof(struct SPIFlashHeader); + ts_conn->cntri += len; + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500; + web_conn->content_len -= len; + // + break; + } +#endif + else if(rom_xstrcmp(pupload->name, sysconst_filename)) { + pupload->fsize = FLASH_SECTOR_SIZE; + pupload->faddr = FLASH_RESERVED_DATA_BASE; // FLASH_SYSTEM_DATA_ADDR + pupload->status = 2; // = 2 загрузка файла во flash + break; + } + else if(rom_xstrcmp(pupload->name, sector_filename)) { + pupload->fsize = FLASH_SECTOR_SIZE; + pupload->faddr = ahextoul(&pupload->name[sector_filename_size]) << 12; + pupload->status = 2; // = 2 загрузка файла Ñектора во flash + break; + }; + if(isWEBFSLocked) return 400; + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err3_filename); // rtl_memcpy(pupload->filename,"/disk_er3.htm\0",14); // неизвеÑтный тип + return 200; + } + else { + uint8 *pcmp = web_strnstr(pstr, CRLF, len); + if(pcmp == NULL) return 0; // докачивать + ret = find_boundary(pupload, pstr, len); +#if DEBUGSOO > 4 + os_printf("ret=%u ", ret ); +#endif + if((ret != 1 && ret != 200)) { // не найден конец или новый boundary? + return ret; // догружать + } + *pcmp = '\0'; + web_int_vars(ts_conn, pupload->name, pstr); + if(ret == 200) return ret; + // найден Ñледующий boundary + len = pupload->pbndr - ts_conn->pbufi; + pupload->status = 0; // = 0 найден Ñледующий boundary +#if DEBUGSOO > 4 + os_printf("trim#%u\n", len ); +#endif + ts_conn->cntri += len; // далее идут данные + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500; + web_conn->content_len -= len; + break; + } +// return 400; + } +// default: + case 2: // загрузка файла во flash + case 3: // загрузка WebFileSystem во flash (ÑкороÑÑ‚ÑŒ запиÑи W25Q128 ~175 килобайт в Ñек, полный диÑк на 15,5МБ пишетÑÑ 90..100 Ñек ) + { +#if DEBUGSOO > 4 + os_printf("fdata "); +#endif + uint32 block_size = mMIN(max_len_buf_write_flash + 8 + pupload->sizeboundary, web_conn->content_len); + if(ts_conn->sizei < block_size) return 0; // докачивать + ret = find_boundary(pupload, pstr, block_size); +#if DEBUGSOO > 4 + os_printf("ret=%u ", ret); +#endif + if((ret == 1 || ret == 200)) { // найден конец или новый boundary? + len = mMIN(block_size, pupload->pbndr - 2 - ts_conn->pbufi); + } + else { + len = mMIN(max_len_buf_write_flash, web_conn->content_len - 8 - pupload->sizeboundary); + } +#if DEBUGSOO > 4 + os_printf("\nlen=%d, block_size=%d, content_len=%d, sizeboundary= %d, ret=%d, data = %d, load=%d", len, block_size, web_conn->content_len, pupload->sizeboundary, ret, pupload->pbndr - ts_conn->pbufi, ts_conn->sizei); +#endif + if(pupload->fsize < len) block_size = pupload->fsize; + else block_size = len; + if(block_size) { // идут данные файла +// tcpsrv_unrecved_win(ts_conn); // Ð´Ð»Ñ ÑƒÑкорениÑ, пока ÑтрираетÑÑ-пишетÑÑ ÑƒÐ¶Ðµ обновит окно (включено в web_rx_buf) + + device_mutex_lock(RT_DEV_LOCK_FLASH); + if(pupload->faddr >= flash_get_size(&flashobj) && pupload->status == 3) { + if((pupload->faddr & 0x0000FFFF)==0) { + +#if DEBUGSOO > 2 + os_printf("Clear flash page addr %p... ", pupload->faddr); +#endif + flash_erase_block(&flashobj, pupload->faddr); + } + } + else if((pupload->faddr & 0x00000FFF) == 0) { +#if DEBUGSOO > 2 + os_printf("Clear flash sector addr %p... ", pupload->faddr); +#endif + flash_erase_sector(&flashobj, pupload->faddr); + } +#if DEBUGSOO > 2 + os_printf("Write flash addr:%p[0x%04x]\n", pupload->faddr, block_size); +#endif + flash_stream_write(&flashobj, pupload->faddr, (block_size + 3)&(~3), (uint32 *)pstr); + + device_mutex_unlock(RT_DEV_LOCK_FLASH); + + pupload->fsize -= block_size; + pupload->faddr += block_size; + } +#if DEBUGSOO > 4 + os_printf("trim#%u\n", len); +#endif + if(len) { + ts_conn->cntri += len; + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500; + web_conn->content_len -= len; + } +#ifdef SET_CPU_CLK_SPEED +// if(syscfg.cfg.b.hi_speed_enable) set_cpu_clk(); +#endif + if((ret == 1 || ret == 200)) { // найден конец или новый boundary? + if(pupload->status == 3) WEBFSInit(); + if(pupload->fsize != 0) { + if(!isWEBFSLocked) { + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не вÑÑ‘ передано или неверный формат + return 200; + } + return 400; // не вÑÑ‘ передано или неверный формат + } + else { + if(!isWEBFSLocked) { + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_ok_filename); // rtl_memcpy(pupload->filename,"/disk_ok.htm\0",13); + }; + }; + if(ret == 1) pupload->status = 0; // = 0 найден Ñледующий boundary + if(ret == 200) return ret; + } + break; + } +#ifdef USE_OVERLAY + case 4: // загрузка данных/кода Ð¾Ð²ÐµÑ€Ð»ÐµÑ + case 5: // загрузка заголовка данных Ð¾Ð²ÐµÑ€Ð»ÐµÑ + { + uint32 block_size = mMIN(max_len_buf_write_flash + 8 + pupload->sizeboundary, web_conn->content_len); + if(ts_conn->sizei < block_size) return 0; // докачивать + ret = find_boundary(pupload, pstr, block_size); + if((ret == 1 || ret == 200)) { // найден конец или новый boundary? + len = mMIN(block_size, pupload->pbndr - 2 - ts_conn->pbufi); + } + else { + len = mMIN(max_len_buf_write_flash, web_conn->content_len - 8 - pupload->sizeboundary); + } + block_size = len; + while(block_size) { +#if DEBUGSOO > 5 + os_printf("blk:%d,st:%d,fs:%d,%d ", block_size, pupload->status, pupload->fsize, pupload->segs); +#endif + if(pupload->status == 5) { + if(block_size >= sizeof(struct SPIFlashHeadSegment)) { // размер данных + if(pupload->segs) { // + rtl_memcpy(&pupload->faddr, pstr, 4); + rtl_memcpy(&pupload->fsize, &pstr[4], 4); +#if DEBUGSOO > 4 + os_printf("New seg ovl addr:%p[%p] ", pupload->faddr, pupload->fsize); +#endif + } + } + else if(ret != 1 && ret != 200) { // не найден конец или boundary? + return 0; // докачивать + } + else { +#if DEBUGSOO > 5 + os_printf("err_load_fseg "); +#endif +// if(block_size < sizeof(struct SPIFlashHeadSegment) +// || pupload->segs == 0 // +// || pupload->fsize > USE_OVERLAY) { + if(!isWEBFSLocked) { + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не вÑÑ‘ передано или неверный формат + return 200; + } + return 400; // не вÑÑ‘ передано или неверный формат + } + pupload->segs--; // Ñчет Ñегментов + pupload->status = 4; // загрузка данных/кода Ð¾Ð²ÐµÑ€Ð»ÐµÑ + pstr += sizeof(struct SPIFlashHeadSegment); + block_size -= sizeof(struct SPIFlashHeadSegment); + }; + uint32 i = mMIN(pupload->fsize, block_size); + if(i) { +#if DEBUGSOO > 1 + os_printf("Wr:%p[%p] ", pupload->faddr, i); +#endif + copy_s1d4((void *)pupload->faddr, pstr, i); + block_size -= i; + pupload->faddr += i; + pstr += i; + pupload->fsize -= i; + }; + if(pupload->fsize == 0) { + if(pupload->segs) { // вÑе Ñегменты загружены? + pupload->status = 5; // загрузка заголовка данных Ð¾Ð²ÐµÑ€Ð»ÐµÑ + } + else { // вÑе Ñегменты загружены + block_size = 0; + break; // break while(block_size) + } + }; + }; // while(block_size) + if(len) { + ts_conn->cntri += len; + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500; + web_conn->content_len -= len; + }; + if((ret == 1 || ret == 200)) { // найден конец или новый boundary? +#if DEBUGSOO > 5 + os_printf("fs:%d,%d ", pupload->fsize, pupload->segs); +#endif + if(pupload->fsize != 0 || pupload->segs != 0) { // + if(!isWEBFSLocked) { + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не вÑÑ‘ передано или неверный формат + return 200; + } + return 400; // не вÑÑ‘ передано или неверный формат + } + else { +#if DEBUGSOO > 1 + os_printf("Run%p ", pupload->start); +#endif + if(pupload->start >= IRAM_BASE) { + ovl_call = (tovl_call *)pupload->start; + web_conn->web_disc_cb = (web_func_disc_cb)pupload->start; // Ð°Ð´Ñ€ÐµÑ Ñтарта Ð¾Ð²ÐµÑ€Ð»ÐµÑ + web_conn->web_disc_par = 1; // параметр функции - Ð¸Ð½Ð¸Ñ†Ð¸Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ + } + if(!isWEBFSLocked) { + SetSCB(SCB_REDIR); + rom_xstrcpy(pupload->filename, disk_ok_filename); // rtl_memcpy(pupload->filename,"/disk_ok.htm\0",13); + }; + }; + if(ret == 1) pupload->status = 0; // = 0 найден Ñледующий boundary + if(ret == 200) return ret; + }; + break; + }; +#endif + }; + }; + return 0; // +} +//----------------------------------------------------------------------------- +// web_rx_buf +// +//----------------------------------------------------------------------------- +LOCAL bool ICACHE_FLASH_ATTR web_rx_buf(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) +{ + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + ts_conn->flag.rx_buf = 1; // указать, что вÑегда в режиме докачивать +// CurHTTP->fileType = HTTP_UNKNOWN; +// ts_conn->pbufi, ts_conn->cntri; +#if DEBUGSOO > 3 + os_printf("rx:%u[%u] ", web_conn->content_len, ts_conn->sizei); +#endif + if(ts_conn->sizei == 0) return true; // докачивать + tcpsrv_unrecved_win(ts_conn); + int ret = upload_boundary(ts_conn); + if(ret > 1) { + CurHTTP->httpStatus = ret; + web_conn->content_len = 0; + if(ret == 200) { + if(CheckSCB(SCB_REDIR)) { + HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)ts_conn->pbufo; + if(pupload != NULL) { + rtl_memcpy(CurHTTP->pFilename, pupload->filename, VarNameSize); +// SetSCB(SCB_DISCONNECT); + } + } + else if((!isWEBFSLocked) && CheckSCB(SCB_FOPEN) + && web_conn->webfile <= WEBFS_MAX_HANDLE + && WEBFSGetFilename(web_conn->webfile, CurHTTP->pFilename, FileNameSize)) { + SetSCB(SCB_REDIR); +// web_conn->content_len = WEBFSGetBytesRem(web_conn->webfile); // WEBFSGetSize(web_conn->webfile); +// webserver_file_ext(CurHTTP, CurHTTP->pFilename); +// return false; // ok 200 + file + } + } + SetSCB(SCB_DISCONNECT); + return false; // неизвеÑтный content или end + } + else { +#if DEBUGSOO > 2 + os_printf("no boundary "); +#endif + if(ts_conn->sizei > MAX_NO_DATA_BUF_SIZE) { + CurHTTP->httpStatus = 418; // 418: Out of Coffee + SetSCB(SCB_DISCONNECT); + return false; // неизвеÑтный content или end + } + }; + if(web_conn->content_len > ts_conn->cntri) return true; // докачивать + CurHTTP->httpStatus = 400; + SetSCB(SCB_DISCONNECT); + web_conn->content_len = 0; + return false; // неизвеÑтный content +} +//----------------------------------------------------------------------------- +//--- web_trim_bufi ----------------------------------------------------------- +//----------------------------------------------------------------------------- +bool ICACHE_FLASH_ATTR web_trim_bufi(TCP_SERV_CONN *ts_conn, uint8 *pdata, uint32 data_len) +{ + if(pdata != NULL && data_len != 0 && ts_conn->sizei > data_len) { + rtl_memcpy(ts_conn->pbufi, pdata, data_len); // перемеÑтим куÑок в начало буфера + ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, data_len + 1); // mem_trim(ts_conn->pbufi, data_len + 1); + if(ts_conn->pbufi != NULL) { + ts_conn->sizei = data_len; // размер куÑка + ts_conn->cntri = 0; + } + else return false; // CurHTTP.httpStatus = 500; // 500 Internal Server Error + } + else if(ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + ts_conn->sizei = 0; + ts_conn->cntri = 0; + }; + return true; +} +/****************************************************************************** + * web_feee_bufi + * оÑвободить приемный буфер +*******************************************************************************/ +bool ICACHE_FLASH_ATTR web_feee_bufi(TCP_SERV_CONN *ts_conn) +{ + if(ts_conn->pbufi != NULL) { + os_free(ts_conn->pbufi); + ts_conn->pbufi = NULL; + ts_conn->sizei = 0; + ts_conn->cntri = 0; + return true; + } + return false; +} +/****************************************************************************** + * FunctionName : webserver_recv + * Description : Processing the received data from the server + * Parameters : arg -- Additional argument to pass to the callback function + * pusrdata -- The received data (or NULL when the connection has been closed!) + * length -- The length of received data + * Returns : none + * + * For HTTP 1.0, this should normally only happen once (if the request fits in one packet). + * +*******************************************************************************/ +LOCAL err_t ICACHE_FLASH_ATTR webserver_received_data(TCP_SERV_CONN *ts_conn) +{ +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); + os_printf("read: %d ", ts_conn->sizei); +#endif + HTTP_CONN CurHTTP; // Current HTTP connection state + WEB_SRV_CONN *web_conn = ReNew_web_conn(ts_conn); + if(web_conn == NULL) { +#if DEBUGSOO > 1 + os_printf("err mem!\n"); +#endif + return ERR_MEM; + } + if(CheckSCB(SCB_CLOSED | SCB_DISCONNECT | SCB_FCLOSE )) // обрабатывать нечего + return ERR_OK; + if(!CheckSCB(SCB_WEBSOC)) { + web_conn->udata_start = 0; + web_conn->udata_stop = 0; + } + os_memset(&CurHTTP, 0, sizeof(CurHTTP)); + CurHTTP.httpStatus = 200; // OK + CurHTTP.fileType = HTTP_UNKNOWN; + // прием и обработка заголовка HHTP + if(!CheckSCB(SCB_HEAD_OK)) { // заголовок уже принÑÑ‚ и обработан? нет + ts_conn->flag.rx_buf = 1; // докачивать буфер + tcpsrv_unrecved_win(ts_conn); + // разбираем докачан или нет заголовок HTTP, что там принÑто GET или POST и открываем файл и прием content, еÑли Ñто POST и не прием файла. + if(parse_header(&CurHTTP, ts_conn)) { // заголовок полный? нет + if(ts_conn->sizei < MAX_HTTP_HEAD_BUF) { +#if DEBUGSOO > 4 + os_printf("buf"); +#endif +#if DEBUGSOO > 1 + os_printf("...\n"); +#endif + return ERR_OK; // будем принимать ещё. + }; + CurHTTP.httpStatus = 413; // 413 Request Entity Too Large // пока так + }; + // разбор заголовка +#if DEBUGSOO > 1 +#ifdef WEBSOCKET_ENA + os_printf("%s f[%s] ", (CheckSCB(SCB_POST))? "POST" : (CheckSCB(SCB_WEBSOC))? "WEBSOC" : "GET", CurHTTP.pFilename); +#else + os_printf("%s f[%s] ", (CheckSCB(SCB_POST))? "POST" : "GET", CurHTTP.pFilename); +#endif +#endif +#if DEBUGSOO > 3 + os_printf("hcn:%p[%d],wcn:%d ", CurHTTP.pcontent, CurHTTP.content_len, web_conn->content_len); +#endif + if(CurHTTP.httpStatus == 200) { // && CheckSCB(SCB_FOPEN)) { // еÑли файл открыт и вÑÑ‘ OK + if(CurHTTP.cookie_len != 0) web_parse_cookie(&CurHTTP, ts_conn); + web_parse_uri_vars(&CurHTTP, ts_conn); + if(CurHTTP.pcontent != NULL) { + if(CheckSCB(SCB_RXDATA)) { + if(web_conn->content_len) { // Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð¼ принÑли куÑок данных файла? +#if DEBUGSOO > 3 + os_printf("trim:%u[%u] ", web_conn->content_len, CurHTTP.content_len); +#endif + if(!web_trim_bufi(ts_conn, CurHTTP.pcontent, CurHTTP.content_len)) { +#if DEBUGSOO > 1 + os_printf("trim error!\n"); +#endif + CurHTTP.httpStatus = 500; + }; + }; + } + else { + if(CurHTTP.content_len != 0) web_parse_content(&CurHTTP, ts_conn); + }; + }; + }; + SetSCB(SCB_HEAD_OK); // заголовок принÑÑ‚ и обработан + }; +#if DEBUGSOO > 3 + os_printf("tst_rx: %u, %u, %u ", CurHTTP.httpStatus, (CheckSCB(SCB_RXDATA) != 0), web_conn->content_len ); +#endif + // проверка на прием данных (content) + if(CurHTTP.httpStatus == 200 && CheckSCB(SCB_RXDATA) && (web_conn->content_len) && web_rx_buf(&CurHTTP, ts_conn)) { +#if DEBUGSOO > 1 + os_printf("...\n"); +#endif + return ERR_OK; // докачивать content + }; +#ifdef WEBSOCKET_ENA + if(CheckSCB(SCB_WEBSOC) && CurHTTP.httpStatus == 200 && (!CheckSCB(SCB_REDIR))) { + if(!CheckSCB(SCB_WSDATA)) { + // Ñоздание и вывод заголовка ответа websock + ClrSCB(SCB_RXDATA); + Close_web_conn(ts_conn); // закрыть вÑе файлы + web_print_headers(&CurHTTP, ts_conn); + if(CheckSCB(SCB_DISCONNECT)) { + ts_conn->flag.rx_null = 1; // вÑÑ‘ - больше не принимаем! + ts_conn->flag.rx_buf = 0; // не докачивать буфер + if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер + } + else { + SetSCB(SCB_WSDATA); + ts_conn->flag.rx_buf = 1; // указать, что вÑегда в режиме докачивать + tcpsrv_unrecved_win(ts_conn); + tcp_output(ts_conn->pcb); + + if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер +/* + if(ts_conn->pbufi != NULL && ts_conn->sizei != 0) { // что-то ещё еÑÑ‚ÑŒ в буфере? +#if DEBUGSOO > 1 + os_printf("ws_rx[%u]? ", ts_conn->sizei); +#endif + websock_rx_data(ts_conn); + } +*/ + } + } + else { + websock_rx_data(ts_conn); + } + } + else +#endif + { + ts_conn->flag.rx_null = 1; // вÑÑ‘ - больше не принимаем! + ts_conn->flag.rx_buf = 0; // не докачивать буфер + if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер + if(tcp_sndbuf(ts_conn->pcb) >= HTTP_SEND_SIZE) { // возможна втавка ответа? + // Ñоздание и вывод заголовка ответа. + web_print_headers(&CurHTTP, ts_conn); + // начало предачи файла, еÑли еÑÑ‚ÑŒ + if((!CheckSCB(SCB_CLOSED | SCB_DISCONNECT | SCB_FCLOSE))&&CheckSCB(SCB_FOPEN)) webserver_send_fdata(ts_conn); + } + else { +#if DEBUGSOO > 1 + os_printf("sndbuf=%u! ", tcp_sndbuf(ts_conn->pcb)); +#endif + SetSCB(SCB_FCLOSE | SCB_DISCONNECT); + }; + } + if(CheckSCB(SCB_FCLOSE)) { + tcp_output(ts_conn->pcb); + Close_web_conn(ts_conn); + SetSCB(SCB_DISCONNECT); + } + if(CheckSCB(SCB_DISCONNECT)) web_int_disconnect(ts_conn); +#if DEBUGSOO > 1 + else os_printf("...\n"); +#endif + return ERR_OK; +} +/****************************************************************************** + * web_int_disconnect +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR web_int_disconnect(TCP_SERV_CONN *ts_conn) +{ +#if DEBUGSOO > 1 + os_printf("dis\n"); +#endif + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + ts_conn->flag.tx_null = 1; + ts_conn->flag.rx_null = 1; + tcpsrv_unrecved_win(ts_conn); + if(ts_conn->flag.pcb_time_wait_free) tcpsrv_disconnect(ts_conn); + SetSCB(SCB_CLOSED); +} +/****************************************************************************** + * FunctionName : webserver_sent_cb + * Description : Sent callback function to call for this espconn when data + * is successfully sent + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL err_t ICACHE_FLASH_ATTR webserver_sent_callback(TCP_SERV_CONN *ts_conn) +{ +#if DEBUGSOO > 1 + tcpsrv_print_remote_info(ts_conn); +#endif + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(web_conn == NULL) return ERR_ARG; + if(CheckSCB(SCB_CLOSED) == 0) { // No SCB_CLOSED + if(!CheckSCB(SCB_DISCONNECT)) { +#ifdef WEBSOCKET_ENA + if(CheckSCB(SCB_WSDATA)) { + websock_rx_data(ts_conn); + } + else +#endif + if((!CheckSCB(SCB_FCLOSE))&&CheckSCB(SCB_FOPEN)) webserver_send_fdata(ts_conn); + } + if(CheckSCB(SCB_FCLOSE)) { + Close_web_conn(ts_conn); + SetSCB(SCB_DISCONNECT); + } + if(CheckSCB(SCB_DISCONNECT)) web_int_disconnect(ts_conn); + #if DEBUGSOO > 1 + else os_printf("...\n"); + #endif + } + else { // SCB_CLOSED +#if DEBUGSOO > 1 + os_printf("#%04x ?\n", web_conn->webflag); +#endif + ts_conn->flag.tx_null = 1; + ts_conn->flag.rx_null = 1; + }; + return ERR_OK; +} +/****************************************************************************** + * FunctionName : webserver_disconnect + * Description : calback disconnect + * Parameters : arg -- Additional argument to pass to the callback function + * Returns : none +*******************************************************************************/ +LOCAL void ICACHE_FLASH_ATTR webserver_disconnect(TCP_SERV_CONN *ts_conn) +{ +#if DEBUGSOO > 1 + tcpsrv_disconnect_calback_default(ts_conn); +#endif + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(web_conn == NULL) return; + Close_web_conn(ts_conn); + if(CheckSCB(SCB_SYSSAVE)) { + ClrSCB(SCB_SYSSAVE); + sys_write_cfg(); + } + if(web_conn->web_disc_cb != NULL) { + if(xQueueSendToBack(xQueueWebSrv, &web_conn->web_disc_cb, 0) != pdPASS) { +#if DEBUGSOO > 1 + os_printf("\nWEB: Queue QFn full!\n"); +#endif + } +// web_conn->web_disc_cb(web_conn->web_disc_par); + }; +} + +/****************************************************************************** +******************************************************************************/ +void qfnk_task(void) +{ + WEB_SRV_QFNK qfn; + while(1) { + if(xQueueReceive(xQueueWebSrv, &qfn, portMAX_DELAY ) == pdPASS) { + if(qfn.fnk) { +#if DEBUGSOO > 2 + os_printf("qfn: %p(%p)\n", qfn.fnk, qfn.param); +#endif + qfn.fnk(qfn.param); + } +// else { +// vTaskDelete(NULL); +// } + } + } +} +/****************************************************************************** + * FunctionName : webserver_init + * Description : Открытие Ñервера + * Parameters : arg -- port N + * Returns : none +*******************************************************************************/ +//TaskHandle_t xHandleQfn; +err_t ICACHE_FLASH_ATTR webserver_init(uint16 portn) +{ +// WEBFSInit(); // Ñ„Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема + + err_t err = ERR_MEM; + xQueueWebSrv = xQueueCreate(5, sizeof( WEB_SRV_QFNK )); // Create a queue... + if(xQueueWebSrv) { +// if(xTaskCreate(qfnk_task, "web_qfn", 1024, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, &xHandleQfn) == pdPASS) + if(xTaskCreate(qfnk_task, "web_qfn", 1024, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, NULL) == pdPASS) + { + TCP_SERV_CFG *p = tcpsrv_init(portn); + if (p != NULL) { + // изменим конфиг на наше уÑмотрение: + if(syscfg.cfg.b.web_time_wait_delete) p->flag.pcb_time_wait_free = 1; // пуÑÑ‚ÑŒ убивает, Ð´Ð»Ñ Ñ‚ÐµÑта и прокÑей + p->max_conn = 256; // Ñработает по heap_size +#if DEBUGSOO > 3 + os_printf("Max connection %d, time waits %d & %d, min heap size %d\n", + p->max_conn, p->time_wait_rec, p->time_wait_cls, p->min_heap); +#endif + p->time_wait_rec = syscfg.web_twrec; // =0 -> вечное ожидание + p->time_wait_cls = syscfg.web_twcls; // =0 -> вечное ожидание + // Ñлинкуем Ñ Ð¶ÐµÐ»Ð°ÐµÐ¼Ñ‹Ð¼Ð¸ процедурами: + p->func_discon_cb = webserver_disconnect; + // p->func_listen = webserver_listen; // не требуетÑÑ + p->func_sent_cb = webserver_sent_callback; + p->func_recv = webserver_received_data; + err = tcpsrv_start(p); + if (err != ERR_OK) { + tcpsrv_close(p); + p = NULL; + } + else { +#if DEBUGSOO > 1 + os_printf("WEB: init port %u\n", portn); +#endif + } + + } +// else err = ERR_MEM; + } +// else err = ERR_MEM; + } +// else err = ERR_MEM; + return err; +} + +/****************************************************************************** + * FunctionName : webserver_close + * Description : закрытие Ñервера + * Parameters : arg -- port N + * Returns : none +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR webserver_close(uint16 portn) +{ + err_t err = ERR_ARG; + if(portn != 0) err = tcpsrv_close(tcpsrv_server_port2pcfg(portn)); +#if DEBUGSOO > 1 + if(err == ERR_OK) os_printf("WEB: close\n"); +#endif + if(xQueueWebSrv) { + WEB_SRV_QFNK qfn; + qfn.fnk = vTaskDelete; + qfn.param = NULL; + if(xQueueSendToBack(xQueueWebSrv, &qfn, 1000) == pdPASS) { + while(uxQueueMessagesWaiting(xQueueWebSrv)) { + vTaskDelay(10); + }; + } +/* + else if(xHandleQfn) { + vTaskDelete(xHandleQfn); + } + vQueueDelete(xQueueWebSrv); + xHandleQfn = NULL; +*/ + xQueueWebSrv = NULL; + }; + return err; +} +/****************************************************************************** + * FunctionName : webserver_reinit + * Description : закрытие Ñервера и открытие нового + * Parameters : arg -- port N открытого порта + * Returns : none +*******************************************************************************/ +err_t ICACHE_FLASH_ATTR webserver_reinit(uint16 portn) +{ + err_t err = ERR_OK; +// if(portn == syscfg.web_port) return err; + if(portn) err = tcpsrv_close(tcpsrv_server_port2pcfg(portn)); // закрыть Ñтарый порт + if(syscfg.web_port) err = webserver_init(syscfg.web_port); // открыть новый + return err; +} + +#endif // USE_WEB diff --git a/project/src/web/web_utils.c b/project/src/web/web_utils.c new file mode 100644 index 0000000..020e98a --- /dev/null +++ b/project/src/web/web_utils.c @@ -0,0 +1,640 @@ +/* + * web_utils.c + * + * Created on: 25 дек. 2014 г. + * Author: PV` + */ +#include "user_config.h" +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +//#include "bios.h" +//#include "sdk/add_func.h" +//#include "ets_sys.h" +//#include "os_type.h" +//#include "osapi.h" +//#include "user_interface.h" +#include "web_utils.h" +#include "esp_comp.h" + +#define mMIN(a, b) ((a= '0' && *s <= '9') + n = 10*n - (*s++ - '0'); + return neg ? n : -n; +} +/****************************************************************************** + * copy_align4 + * копирует данные из облаÑти ÐºÐµÑˆÐ¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ flash и Ñ‚.д. +*******************************************************************************/ +void ICACHE_FLASH_ATTR copy_align4(void *ptrd, void *ptrs, uint32 len) +{ + union { + uint8 uc[4]; + uint32 ud; + }tmp; + uint8 *pd = ptrd; + uint32 *p = (uint32 *)((uint32)ptrs & (~3)); + uint32 xlen = ((uint32)ptrs) & 3; + if(xlen) { + if(((uint32)p >= 0x10000000)&&((uint32)p < 0x9A002000)) tmp.ud = *p++; + else { + tmp.ud = 0; + p++; + } + while (len) { + *pd++ = tmp.uc[xlen++]; + len--; + if(xlen >= 4) break; + } + } + xlen = len >> 2; + while(xlen) { + if(((uint32)p >= 0x10000000)&&((uint32)p < 0x9A002000)) tmp.ud = *p++; + else { + tmp.ud = 0; + p++; + } + *pd++ = tmp.uc[0]; + *pd++ = tmp.uc[1]; + *pd++ = tmp.uc[2]; + *pd++ = tmp.uc[3]; + xlen--; + } + len &= 3; + if(len) { + if(((uint32)p >= 0x10000000)&&((uint32)p < 0x9A002000)) tmp.ud = *p; + else tmp.ud = 0; + uint8 * ptmp = tmp.uc; + while (len--) *pd++ = *ptmp++; + } +} +/****************************************************************************** + * FunctionName : hextoul +*******************************************************************************/ +// bool conv_str_hex(uint32 * dest, uint8 *s); +uint32 ICACHE_FLASH_ATTR hextoul(uint8 *s) +{ +/* + uint32 val; + if(!conv_str_hex(&val, s)) return 0; + return val; +*/ + uint32 val = 0; + while (*s) + { + if (*s >= '0' && *s <= '9') + { + val <<= 4; + val |= *s - '0'; + } + else if (*s >= 'A' && *s <= 'F') + { + val <<= 4; + val |= *s - 'A' + 10; + } + else if (*s >= 'a' && *s <= 'f') + { + val <<= 4; + val |= *s - 'a' + 10; + } + else break; + s++; + }; + return val; +} +/****************************************************************************** + * FunctionName : ahextoul +*******************************************************************************/ +// bool convert_para_str(uint32 * dest, uint8 *s); +uint32 ICACHE_FLASH_ATTR ahextoul(uint8 *s) +{ +/* + uint32 ret; + if(!convert_para_str(&ret, s)) return 0; + return ret; +*/ + if((s[0]=='0') && ((s[1] | 0x20) =='x')) return hextoul(s+2); + return rom_atoi(s); +} +/****************************************************************************** + * FunctionName : cmpcpystr + * Description : выбирает Ñлово из Ñтроки текÑта Ñ Ð·Ð°Ð´Ð°Ð½Ð½Ñ‹Ð¼Ð¸ начальным Ñимволом + * и конечным терминатором. Терминатор и Ñтартовый Ñимвол не копирует, еÑли заданы. + * Parameters : При задании начального Ñимвола = '\0' беретÑÑ Ð»ÑŽÐ±Ð¾Ð¹ Ñимвол (>' '). + Копирует до Ñимвола <' ' или терминатора. + ЗадаетÑÑ Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ðµ размера буфера Ð´Ð»Ñ ÐºÐ¾Ð¿Ð¸Ñ€ÑƒÐµÐ¼Ð¾Ð³Ð¾ Ñлова (Ñ Ð´Ð¾Ð¿Ð¸Ñыванием в буфер '\0'!). + * Returns : ЗавиÑит от Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‚ÐµÑ€Ð¼Ð¸Ð½Ð°Ñ‚Ð¾Ñ€Ð°, указывает на терминатор в Ñтроке, + еÑли терминатор найден. + ЕÑли NULL, то начальный или конечный терминатор не найден. +*******************************************************************************/ +uint8 * ICACHE_FLASH_ATTR cmpcpystr(uint8 *pbuf, uint8 *pstr, uint8 a, uint8 b, uint16 len) +{ + if(len == 0) pbuf = NULL; + if(pstr == NULL) { + if(pbuf != NULL) *pbuf='\0'; + return NULL; + }; + uint8 c; + do { + c = *pstr; + if(c < ' ') { // Ñтрока кончилаÑÑŒ + if(pbuf != NULL) *pbuf='\0'; + return NULL; // id не найден + }; + if((a == '\0')&&(c > ' ')) break; // не задан -> любой Ñимвол + pstr++; + if(c == a) break; // нашли Ñтартовый Ñимвол (некопируемый в буфер) + }while(1); + if(pbuf != NULL) { + while(len--) { + c = *pstr; + if(c == b) { // нашли терминирующий Ñимвол (некопируемый в буфер) + *pbuf='\0'; + return pstr; // конечный терминатор найден + }; +// if(c <= ' ') { // Ñтрока кончилаÑÑŒ или пробел + if(c < ' ') { // Ñтрока кончилаÑÑŒ или пробел + *pbuf='\0'; + return NULL; // конечный терминатор не найден + }; + pstr++; + *pbuf++ = c; + }; + *--pbuf='\0'; // закрыть буфер + }; + do { + c = *pstr; + if(c == b) return pstr; // нашли терминирующий Ñимвол +// if(c <= ' ') return NULL; // Ñтрока кончилаÑÑŒ + if(c < ' ') return NULL; // Ñтрока кончилаÑÑŒ + pstr++; + }while(1); +} +/****************************************************************************** + * FunctionName : str_array + * Ðабирает из Ñтроки s маÑÑив Ñлов в buf в кол-ве до max_buf + * возврат - кол-во переменных в Ñтроке + * Разделитель переменных в Ñтроке ',' + * ЕÑли нет переменной, то пропуÑкает изменение в buf + * Примеры: + * Строка "1,2,3,4" -> buf = 0x01 0x02 0x03 0x04 + * Строка "1,,3," -> buf = 0x01 (не изменено) 0x03 (не изменено) +*******************************************************************************/ +uint32 ICACHE_FLASH_ATTR str_array(uint8 *s, uint32 *buf, uint32 max_buf) +{ + uint32 ret = 0; + uint8 *sval = NULL; + while(max_buf > ret) { + if(sval == NULL) { + if (*s == '-' && s[1] >= '0' && s[1] <= '9') { + sval = s; + s++; + } + else if (*s >= '0' && *s <= '9') sval = s; + } + if(*s == ',' || *s <= ')') { + if(sval != NULL) { + *buf = ahextoul(sval); + sval = NULL; + } + buf++; + ret++; + if(*s < ')') return ret; + } + s++; + } + return ret; +} +uint32 ICACHE_FLASH_ATTR str_array_w(uint8 *s, uint16 *buf, uint32 max_buf) +{ + uint32 ret = 0; + uint8 *sval = NULL; + while(max_buf > ret) { + if(sval == NULL) { + if (*s == '-' && s[1] >= '0' && s[1] <= '9') { + sval = s; + s++; + } + else if (*s >= '0' && *s <= '9') sval = s; + } + if(*s == ',' || *s <= ')') { + if(sval != NULL) { + *buf = ahextoul(sval); + sval = NULL; + } + buf++; + ret++; + if(*s < ')') return ret; + } + s++; + } + return ret; +} +uint32 ICACHE_FLASH_ATTR str_array_b(uint8 *s, uint8 *buf, uint32 max_buf) +{ + uint32 ret = 0; + uint8 *sval = NULL; + while(max_buf > ret) { + if(sval == NULL) { + if (*s == '-' && s[1] >= '0' && s[1] <= '9') { + sval = s; + s++; + } + else if (*s >= '0' && *s <= '9') sval = s; + } + if(*s == ',' || *s == '.' || *s <= ')') { + if(sval != NULL) { + *buf = ahextoul(sval); + sval = NULL; + } + buf++; + ret++; + if(*s < ')') return ret; + } + s++; + } + return ret; +} +/****************************************************************************** + * FunctionName : strtmac +*******************************************************************************/ +void ICACHE_FLASH_ATTR strtomac(uint8 *s, uint8 *macaddr) +{ + uint8 pbuf[4]; + s = cmpcpystr(pbuf, s, 0, ':', 3); + *macaddr++ = hextoul(pbuf); + int i = 4; + while(i--) { + s = cmpcpystr(pbuf, s, ':', ':', 3); + *macaddr++ = hextoul(pbuf); + } + s = cmpcpystr(pbuf, s, ':', ' ', 3); + *macaddr++ = hextoul(pbuf); +} +/****************************************************************************** + * FunctionName : urldecode +*******************************************************************************/ +int ICACHE_FLASH_ATTR urldecode(uint8 *d, uint8 *s, uint16 lend, uint16 lens) +{ + uint16 ret = 0; + if(s != NULL) while ((lens--) && (lend--) && (*s > ' ')) { + if ((*s == '%')&&(lens > 1)) { + s++; + int i = 2; + uint8 val = 0; + while(i--) { + if (*s >= '0' && *s <= '9') { + val <<= 4; + val |= *s - '0'; + } else if (*s >= 'A' && *s <= 'F') { + val <<= 4; + val |= *s - 'A' + 10; + } else if (*s >= 'a' && *s <= 'f') { + val <<= 4; + val |= *s - 'a' + 10; + } else + break; + s++; + lens--; + }; + s--; + *d++ = val; + } else if (*s == '+') + *d++ = ' '; + else + *d++ = *s; + ret++; + s++; + } + *d = '\0'; + return ret; +} +/****************************************************************************** + * FunctionName : urlencode +*******************************************************************************/ +/*int ICACHE_FLASH_ATTR urlencode(uint8 *d, uint8 *s, uint16 lend, uint16 lens) +{ + uint16 ret = 0; + if(s != NULL) while ((lens--) && (lend--) && (*s != '\0')) { + if ( (48 <= *s && *s <= 57) //0-9 + || (65 <= *s && *s <= 90) //abc...xyz + || (97 <= *s && *s <= 122) //ABC...XYZ + || (*s == '~' || *s == '!' || *s == '*' || *s == '(' || *s == ')' || *s == '\'')) { + *d++ = *s++; + ret++; + } else { + if(lend >= 3) { + ret += 3; + lend -= 3; + *d++ = '%'; + uint8 val = *s >> 4; + if(val <= 9) val += '0'; + else val += 0x41 - 10; + *d++ = val; + val = *s++ & 0x0F; + if(val <= 9) val += '0'; + else val += 0x41 - 10; + *d++ = val; + } + else break; + } + } + *d = '\0'; + return ret; +}*/ +/****************************************************************************** + * FunctionName : htmlcode +*******************************************************************************/ +int ICACHE_FLASH_ATTR htmlcode(uint8 *d, uint8 *s, uint16 lend, uint16 lens) +{ + uint16 ret = 0; + if(s != NULL) while ((lens--) && (lend--) && (*s != '\0')) { + if ( *s == 0x27 ) { // "'" ' + if(lend >= 6) { + ret += 6; + lend -= 6; + s++; + *d++ = '&'; + *d++ = 'a'; + *d++ = 'p'; + *d++ = 'o'; + *d++ = 's'; + *d++ = ';'; + } + else break; + } else if ( *s == '"' ) { // " + if(lend >= 6) { + ret += 6; + lend -= 6; + s++; + *d++ = '&'; + *d++ = 'q'; + *d++ = 'u'; + *d++ = 'o'; + *d++ = 't'; + *d++ = ';'; + } + else break; + } else if ( *s == '&' ) { // & + if(lend >= 5) { + ret += 5; + lend -= 5; + s++; + *d++ = '&'; + *d++ = 'a'; + *d++ = 'm'; + *d++ = 'p'; + *d++ = ';'; + } + else break; + } else if ( *s == '<' ) { // < + if(lend >= 4) { + ret += 4; + lend -= 4; + s++; + *d++ = '&'; + *d++ = 'l'; + *d++ = 't'; + *d++ = ';'; + } + else break; + } else if ( *s == '>' ) { // > + if(lend >= 4) { + ret += 4; + lend -= 4; + s++; + *d++ = '&'; + *d++ = 'g'; + *d++ = 't'; + *d++ = ';'; + } + else break; + } else { + *d++ = *s++; + ret++; + } + } + *d = '\0'; + return ret; +} +//============================================================================= +uint8* ICACHE_FLASH_ATTR +web_strnstr(const uint8* buffer, const uint8* token, int len) +{ + const uint8* p; + int tokenlen = rtl_strlen(token); + if (tokenlen == 0) { + return (uint8 *)buffer; + }; + for (p = buffer; *p && (p + tokenlen <= buffer + len); p++) { + if ((*p == *token) && (rtl_strncmp(p, token, tokenlen) == 0)) { + return (uint8 *)p; + }; + }; + return NULL; +} +//============================================================================= +static const uint8_t base64map[128] ICACHE_RODATA_ATTR = +{ + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255 +}; +//============================================================================= +bool ICACHE_FLASH_ATTR base64decode(const uint8 *in, int len, uint8_t *out, int *outlen) +{ +// uint8 *map = (uint8 *)UartDev.rcv_buff.pRcvMsgBuff; +// ets_memcpy(map, base64map, 128); + uint8 *map = base64map; + int g, t, x, y, z; + uint8_t c; + g = 3; + for (x = y = z = t = 0; x < len; x++) { + if ((c = map[in[x]&0x7F]) == 0xff) continue; + if (c == 254) { /* this is the end... */ + c = 0; + if (--g < 0) return false; + } + else if (g != 3) return false; /* only allow = at end */ + t = (t<<6) | c; + if (++y == 4) { + out[z++] = (uint8_t)((t>>16)&255); + if (g > 1) out[z++] = (uint8_t)((t>>8)&255); + if (g > 2) out[z++] = (uint8_t)(t&255); + y = t = 0; + } + /* check that we don't go past the output buffer */ + if (z > *outlen) return false; + } + if (y != 0) return false; + *outlen = z; + return true; +} +//============================================================================= +/* Table 6-bit-index-to-ASCII used for base64-encoding */ +const uint8_t base64_table[] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '+', '/' +}; +// ld: PROVIDE ( base64_table = 0x3FFFD600 ); +//extern const uint8_t base64_table[]; +//============================================================================= +/** Base64 encoding */ +size_t ICACHE_FLASH_ATTR base64encode(char* target, size_t target_len, const char* source, size_t source_len) +{ + size_t i; + sint8 j; + size_t target_idx = 0; + size_t longer = 3 - (source_len % 3); + size_t source_len_b64 = source_len + longer; + size_t len = (((source_len_b64) * 4) / 3); + uint8 x = 5; + uint8 current = 0; + + if(target == NULL || target_len < len) return 0; + + for (i = 0; i < source_len_b64; i++) { + uint8 b = (i < source_len ? source[i] : 0); + for (j = 7; j >= 0; j--, x--) { + uint8 shift = ((b & (1 << j)) != 0) ? 1 : 0; + current |= shift << x; + if (x == 0) { + target[target_idx++] = base64_table[current]; + x = 6; + current = 0; + } + } + } + for (i = len - longer; i < len; i++) { + target[i] = '='; + } + return len; +} +/* +//============================================================================= +void ICACHE_FLASH_ATTR print_hex_dump(uint8 *buf, uint32 len, uint8 k) +{ + if(!system_get_os_print()) return; // if(*((uint8 *)(0x3FFE8000)) == 0) return; + uint32 ss[2]; + ss[0] = 0x78323025; // "%02x" + ss[1] = k; // ","...'\0' + uint8* ptr = buf; + while(len--) { + if(len == 0) ss[1] = 0; + ets_printf((uint8 *)&ss[0], *ptr++); + } +} +*/ +//============================================================================= +#define LowerCase(a) ((('A' <= a) && (a <= 'Z')) ? a + 32 : a) + +char* ICACHE_FLASH_ATTR word_to_lower_case(char* text) { + for(; *text ==' '; text++); + char* p = text; + for (; *p >= ' '; p++) { + *p = LowerCase(*p); + } + return text; +} +#if 0 +//============================================================================= +/* char UpperCase(char ch) { + return (('a' <= ch) && (ch <= 'z')) ? ch - 32 : ch; }*/ +#define UpperCase(a) ((('a' <= a) && (a <= 'z')) ? a - 32 : a) + +char* ICACHE_FLASH_ATTR str_to_upper_case(char* text) { + char* p = text; + for (; *p; ++p) { + *p = UpperCase(*p); + } + return text; +} +#endif + diff --git a/project/src/web/web_websocket.c b/project/src/web/web_websocket.c new file mode 100644 index 0000000..8af34e3 --- /dev/null +++ b/project/src/web/web_websocket.c @@ -0,0 +1,347 @@ +/****************************************************************************** + * FileName: web_websocket.c + * Description: websocket for web + * Author: pvvx + * 2016 +*******************************************************************************/ +#include "user_config.h" +#ifdef WEBSOCKET_ENA +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +//#include "bios.h" +//#include "osapi.h" +//#include "sdk/rom2ram.h" +#include "lwip/tcp.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "web_srv_int.h" +#include "web_utils.h" +#include "web_websocket.h" +#include "rtl8195a/rtl_libc.h" +#include "esp_comp.h" + +#if 0 +#undef DEBUGSOO +#define DEBUGSOO 4 +#endif + +#define copy_s4d1 rtl_memcpy + +#define mMIN(a, b) ((a 3 + os_printf("ws%utx[%u] error %d!\n", opcode, raw_len, err); +#endif + ((WEB_SRV_CONN *)ts_conn->linkd)->webflag |= SCB_DISCONNECT; + } + else { + if((opcode & WS_OPCODE_BITS) == WS_OPCODE_CLOSE) { + ((WEB_SRV_CONN *)ts_conn->linkd)->ws.flg |= WS_FLG_CLOSE; + } + } + return err; +} +//============================================================================= +// websock_tx_close_err() - вывод ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð¸Ñ Ð¸Ð»Ð¸ ошибки +//============================================================================= +err_t ICACHE_FLASH_ATTR +websock_tx_close_err(TCP_SERV_CONN *ts_conn, uint32 err) +{ + uint8 uc[2]; + uc[1] = err; + uc[0] = err>>8; + return websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, uc, 2); +} +//============================================================================= +// websock_rx_data() прием данных +//============================================================================= +#define MAX_WS_DATA_BLK_SIZE (tcp_sndbuf(ts_conn->pcb)-8) // (TCP_MSS - 8) +//============================================================================= +bool ICACHE_FLASH_ATTR +websock_rx_data(TCP_SERV_CONN *ts_conn) +{ +// HTTP_CONN *CurHTTP; + WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd; + if(web_conn == NULL) return false; + WS_FRSTAT *ws = &web_conn->ws; + uint16 len; + uint8 *pstr; + +#if DEBUGSOO > 3 + os_printf("ws_rx[%u]%u ", ts_conn->sizei, ts_conn->cntri); +#endif + if(ts_conn->sizei == 0) return true; // докачивать + tcpsrv_unrecved_win(ts_conn); + if((ws->flg & WS_FLG_CLOSE) != 0) { + // убить буфер ts_conn->pbufi, конец давно :) + web_feee_bufi(ts_conn); + SetSCB(SCB_DISCONNECT); + return false; + } + if(ts_conn->sizei > MAX_RX_BUF_SIZE) { +#if DEBUGSOO > 0 + os_printf("ws:rxbuf_full! "); +#endif + // убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR + web_feee_bufi(ts_conn); + websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG); // WS_CLOSE_UNEXPECTED_ERROR); + SetSCB(SCB_DISCONNECT); + return false; + } + pstr = ts_conn->pbufi;// + ts_conn->cntri; + len = ts_conn->sizei;// - ts_conn->cntri; + while(ts_conn->cntri < ts_conn->sizei || (ws->flg & WS_FLG_FIN) != 0) { + pstr = ts_conn->pbufi;// + ts_conn->cntri; + len = ts_conn->sizei;// - ts_conn->cntri; + if((ws->flg & WS_FLG_FIN) != 0 // обработка + || ws->frame_len > ws->cur_len) { + ws->flg &= ~WS_FLG_FIN; + len = mMIN(ws->frame_len - ws->cur_len, mMIN(MAX_WS_DATA_BLK_SIZE, len)); + // размаÑкировать + if((ws->flg & WS_FLG_MASK) != 0) WebsocketMask(ws, pstr, len); +#if DEBUGSOO > 3 + os_printf("wsfr[%u]blk[%u]at:%u ", ws->frame_len, len, ws->cur_len); +#endif + switch(ws->status) { + case sw_frs_binary: +#if DEBUGSOO > 1 + os_printf("ws:bin "); +#endif + if(ws->frame_len != 0) { + // пока проÑто Ñхо + uint32 opcode = WS_OPCODE_BINARY; + if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; + if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; + if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { + return false; // не докачивать, ошибка или закрытие + } + } + ws->cur_len += len; + ts_conn->cntri += len; + break; + case sw_frs_text: +#if DEBUGSOO > 1 + os_printf("ws:txt "); +#if DEBUGSOO > 2 + if(ws->frame_len != 0) { + uint8 tt = pstr[len]; + pstr[len] = 0; + os_printf("'%s' ", pstr); + pstr[len] = tt; + } +#endif +#endif + if(ws->frame_len == ws->cur_len + len && ws->frame_len != 0) { // полное Ñоо + web_conn->msgbufsize = tcp_sndbuf(ts_conn->pcb); // Ñколько можем выввеÑти ÑейчаÑ? + if (web_conn->msgbufsize < MIN_SEND_SIZE) { +#if DEBUGSOO > 0 + os_printf("ws:sndbuf=%u! ", web_conn->msgbufsize); +#endif + websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); + return false; + } + if(ws->frame_len == (sizeof(txt_wsping)-1) && rom_xstrcmp(pstr, txt_wsping) != 0){ + copy_s4d1(pstr, (void *)txt_wspong, sizeof(txt_wspong) - 1); + if(websock_tx_frame(ts_conn, WS_OPCODE_TEXT | WS_FRAGMENT_FIN, pstr, sizeof(txt_wspong) - 1) != ERR_OK) { + return false; // не докачивать, ошибка или закрытие + } + } + else { + web_conn->msgbuf = (uint8 *) os_malloc(web_conn->msgbufsize); + if (web_conn->msgbuf == NULL) { +#if DEBUGSOO > 0 + os_printf("ws:mem!\n"); +#endif + websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); + SetSCB(SCB_FCLOSE|SCB_DISCONNECT); + return false; + }; + web_conn->msgbuflen = 0; + uint32 opcode; + if(CheckSCB(SCB_RETRYCB)) { // повторный callback? да + if(web_conn->func_web_cb != NULL) web_conn->func_web_cb(ts_conn); + if(!CheckSCB(SCB_RETRYCB)) { + ClrSCB(SCB_FCLOSE | SCB_DISCONNECT); + opcode = WS_OPCODE_CONTINUE | WS_FRAGMENT_FIN; + } + else opcode = WS_OPCODE_CONTINUE; + } + else { + pstr[len] = '\0'; + uint8 *vstr = os_strchr(pstr, '='); + if(vstr != NULL) { + *vstr++ = '\0'; + web_int_vars(ts_conn, pstr, vstr); + } + else { + web_conn->msgbuf[0] = 0; + web_int_callback(ts_conn, pstr); + } + if(CheckSCB(SCB_RETRYCB)) opcode = WS_OPCODE_TEXT; + else { + ClrSCB(SCB_FCLOSE | SCB_DISCONNECT); + opcode = WS_OPCODE_TEXT | WS_FRAGMENT_FIN; + } + } + if(web_conn->msgbuflen != 0) { + if(websock_tx_frame(ts_conn, opcode, web_conn->msgbuf, web_conn->msgbuflen) != ERR_OK) { + os_free(web_conn->msgbuf); + web_conn->msgbuf = NULL; + return false; // не докачивать, ошибка или закрытие + } + } + os_free(web_conn->msgbuf); + web_conn->msgbuf = NULL; + if(CheckSCB(SCB_RETRYCB)) return false; + } + } +/* + if(0) { + uint32 opcode = WS_OPCODE_TEXT; + if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; + if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; + if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { + return false; // не докачивать, ошибка или закрытие + } + } +*/ + ws->cur_len += len; + ts_conn->cntri += len; + return true; // докачивать +// break; +// break; + case sw_frs_ping: +#if DEBUGSOO > 1 + os_printf("ws:ping "); +#endif + { + uint32 opcode = WS_OPCODE_PONG; + if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE; + if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN; + if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) { + return false; // не докачивать, ошибка или закрытие + } + } + ws->cur_len += len; + ts_conn->cntri += len; + return true; // докачивать +// break; + case sw_frs_pong: +#if DEBUGSOO > 1 + os_printf("ws:pong "); +#endif + ws->cur_len += len; + ts_conn->cntri += len; + break; +// return true; + case sw_frs_close: +#if DEBUGSOO > 1 + os_printf("ws:close "); +#endif +// if((ws->flg & WS_FLG_CLOSE) == 0) { + { + if(len >= 2) { + uint32 close_code = (pstr[0]<<8) | pstr[1]; +#if DEBUGSOO > 1 + os_printf("code:%d ", close_code); +#endif + if(close_code == WS_CLOSE_NORMAL) websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL); + // else websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0); + } + else + { + websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL); + // websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0); + } + } + ts_conn->flag.pcb_time_wait_free = 1; + SetSCB(SCB_DISCONNECT); +// ts_conn->cntri = ts_conn->sizei; +/* ws->cur_len += len; + ts_conn->cntri += len; */ + return false; + default: +#if DEBUGSOO > 0 + os_printf("ws:f?! "); +#endif + websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); + SetSCB(SCB_DISCONNECT); + // ts_conn->cntri = ts_conn->sizei; + return false; + } + } + else + if(ws->cur_len >= ws->frame_len) { // прием и разбор нового фрейма + if((ws->flg & WS_FLG_FIN) != 0) { // обработка +#if DEBUGSOO > 3 + os_printf("ws_rx:fin=%u ", ws->cur_len); +#endif + } + else { + uint32 ret = WebsocketHead(ws, pstr, len); + if(ret >= WS_CLOSE_NORMAL) { // error или close + +#if DEBUGSOO > 0 + os_printf("ws:txerr=%u ", ret); +#endif + websock_tx_close_err(ts_conn, ret); +// ts_conn->cntri = ts_conn->sizei; // убить буфер ts_conn->pbufi + return false; // error + } + else if(ret == 0) { +#if DEBUGSOO > 3 + os_printf("ws_rx... "); +#endif + return true; // докачивать + } + ts_conn->cntri += ws->head_len; // вычеÑÑ‚ÑŒ заголовок +/* + switch(ws->status) { + case sw_frs_binary: + break; + case sw_frs_text: + if(ws->frame_len > MAX_RX_BUF_SIZE) { + websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG); + return false; + } + break; + } +*/ + } + } +#if DEBUGSOO > 3 + os_printf("trim%u-%u ", ts_conn->sizei, ts_conn->sizei - ts_conn->cntri ); +#endif + if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[ts_conn->cntri], ts_conn->sizei - ts_conn->cntri)) { +#if DEBUGSOO > 0 + os_printf("ws:trim_err! "); +#endif + // убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR + websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR); + SetSCB(SCB_DISCONNECT); +// ts_conn->cntri = ts_conn->sizei; + return false; + }; + } + return false; // не докачивать, ошибка или закрытие +} +//============================================================================= +//============================================================================= +#endif // WEBSOCKET_ENA + + diff --git a/project/src/web/websock.c b/project/src/web/websock.c new file mode 100644 index 0000000..d9e2efc --- /dev/null +++ b/project/src/web/websock.c @@ -0,0 +1,245 @@ +/****************************************************************************** + * FileName: websock.c + * Description: websocket for web ESP8266 + * Author: PV` + * (c) PV` 2016 +*******************************************************************************/ +#include "user_config.h" +#ifdef WEBSOCKET_ENA +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include "tcpsrv/tcp_srv_conn.h" +#include "web_utils.h" // base64encode() +#include "web_srv.h" +//#include "phy/phy.h" +#include "device_lock.h" +#include "lwip/tcp.h" +#include "websock.h" +#include "rtl8195a/rtl_libc.h" +#include "esp_comp.h" +#include "hal_crypto.h" + +// HTTP/1.1 101 Web Socket Protocol Handshake\r\n +const uint8 WebSocketHTTPOkKey[] ICACHE_RODATA_ATTR = "HTTP/1.1 101 Switching Protocols\r\nAccess-Control-Allow-Origin: *\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n"; +const uint8 WebSocketAddKey[] ICACHE_RODATA_ATTR = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; +const uint8 *HTTPUpgrade = "Upgrade:"; +const uint8 *HTTPwebsocket = "websocket"; +const uint8 *HTTPSecWebSocketKey = "Sec-WebSocket-Key:"; + +//============================================================================= +// WebSocketAcceptKey() +// 1) взÑÑ‚ÑŒ Ñтроковое значение из заголовка Sec-WebSocket-Key и объединить Ñо +// Ñтрокой 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 +// 2) вычиÑлить бинарный хеш SHA-1 (Ð±Ð¸Ð½Ð°Ñ€Ð½Ð°Ñ Ñтрока из 20 Ñимволов) от полученной +// в первом пункте Ñтроки +// 3) закодировать хеш в Base64 +// skey[24+36]:'dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11' +// cha:'b37a4f2cc0624f1690f64606cf385945b2bec4ea' +// key[28]:'s3pPLMBiTxaQ9kYGzzhZRbK+xOo=' +//============================================================================= +uint8 buff[maxsizeWebSocketKey + sizeWebSocketAddKey]; // [68] +bool ICACHE_FLASH_ATTR WebSocketAcceptKey(uint8* dkey, uint8* skey) +{ + int len = 0; + bool ret = false; + uint8 keybuf[CRYPTO_SHA1_DIGEST_LENGTH]; + uint8 * buff = os_malloc(maxsizeWebSocketKey + sizeWebSocketAddKey); + if(buff) { + while(skey[len] >= '+' && len < maxsizeWebSocketKey) { + buff[len] = skey[len]; + len++; + }; + if(len > minsizeWebSocketKey) { + rtl_memcpy(&buff[len], WebSocketAddKey, sizeWebSocketAddKey+1); + device_mutex_lock(RT_DEV_LOCK_CRYPTO); + rtl_crypto_sha1(buff, len + sizeWebSocketAddKey, keybuf); + device_mutex_unlock(RT_DEV_LOCK_CRYPTO); +// rtl_cryptoEngine_info(); + len = base64encode(dkey, FileNameSize, keybuf, CRYPTO_SHA1_DIGEST_LENGTH); +#if DEBUGSOO > 2 + os_printf("\ncha:'"); + print_hex_dump(keybuf, CRYPTO_SHA1_DIGEST_LENGTH, '\0'); + os_printf("'\n"); + os_printf("key[%u]:'%s'\n", len, dkey); +#endif + ret = true; + } + os_free(buff); + } + return ret; + +} +//============================================================================= +// websock_mask() размаÑкирование блока +//============================================================================= +void ICACHE_FLASH_ATTR +WebsocketMask(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len) +{ + uint32 i, x = ws->cur_len; +#if DEBUGSOO > 3 + os_printf("mask[%u]%u ", raw_len, x); +#endif + for (i = 0; i < raw_len; i++) { + raw_data[i] ^= ws->mask.uc[x++ & 3]; + } +} +//============================================================================= +// websock_head() разбор заголовка +//============================================================================= +uint32 ICACHE_FLASH_ATTR +WebsocketHead(WS_FRSTAT *ws, uint8 *raw_data, uint32 raw_len) +{ + // определить размер заголовка фрейма + uint32 head_len = 2; + if(raw_len < head_len) return 0; // докачивать + uint32 data_len = raw_data[1] & WS_SIZE1_BITS; + if(data_len == 127) head_len = 10; + else if(data_len == 126) head_len = 4; + if(raw_data[1] & WS_MASK_FLG) head_len += 4; + if(raw_len < head_len) return 0; // докачивать + ws->head_len = head_len; + ws->cur_len = 0; + ws->flg = 0; + data_len = raw_data[1] & WS_SIZE1_BITS; + if(data_len >= 126) { + if(data_len == 127) { + uint32 i; + for(i = 3; i < 6; i++) { + if(raw_data[i] != 0) { + ws->status = sw_frs_close; + ws->frame_len = 0; + return WS_CLOSE_MESSAGE_TOO_BIG; + } + } + data_len = (raw_data[6] << 24) | (raw_data[7] << 16) | (raw_data[8] << 8) | raw_data[9]; + } + else { + data_len = (raw_data[2] << 8) | raw_data[3]; + } + } + if(raw_data[1] & WS_MASK_FLG) { + ws->flg |= WS_FLG_MASK; + ws->mask.uc[0] = raw_data[head_len-4]; + ws->mask.uc[1] = raw_data[head_len-3]; + ws->mask.uc[2] = raw_data[head_len-2]; + ws->mask.uc[3] = raw_data[head_len-1]; + } +// else ws->mask = 0; + uint8 opcode = raw_data[0] & WS_OPCODE_BITS; + switch(opcode) { + case WS_OPCODE_PING: // Ñхо - дублировать прием + raw_data[0] &= ~WS_FRAGMENT_FIN; + raw_data[0] |= WS_OPCODE_PONG; + ws->status = sw_frs_pong; + ws->frame_len = data_len; + break; + case WS_OPCODE_PONG: // Ñхо - дублировать прием + ws->status = sw_frs_ping; + ws->frame_len = data_len; + break; + case WS_OPCODE_CONTINUE: // продолжить + if(ws->status == sw_frs_pong) { + ws->frame_len = data_len; + break; + } + else ws->frame_len += data_len; + break; + case WS_OPCODE_CLOSE: // + ws->status = sw_frs_close; + ws->frame_len = data_len; + break; + case WS_OPCODE_TEXT: + ws->status = sw_frs_text; + ws->frame_len = data_len; + break; + case WS_OPCODE_BINARY: + ws->status = sw_frs_binary; + ws->frame_len = data_len; + break; + default: + ws->status = sw_frs_close; + ws->frame_len = 0; + return WS_CLOSE_WRONG_TYPE; + } +// uint32 len = mMIN(raw_len - head_len, data_len); +// if((ws->flg & WS_FLG_MASK) != 0) websock_mask(ws, &raw_data[head_len], mMIN(raw_len - head_len, len)); +// ws->cur_len += len; + if((raw_data[0] & WS_FRAGMENT_FIN) != 0) { // конец - данные на обработку + ws->flg |= WS_FLG_FIN; + } +#if DEBUGSOO > 1 + os_printf("ws#%02xrx[%u] ", raw_data[0], data_len); +#endif + return 1; +/* + if(data_len + head_len <= raw_len) { // веÑÑŒ пакет уже в буфере? + return 1; + } + return 0; // докачивать */ +} +//============================================================================= +// websock_tx_frame() - передача фрейма +//============================================================================= +err_t ICACHE_FLASH_ATTR +WebsocketTxFrame(TCP_SERV_CONN *ts_conn, uint32 opcode, uint8 *raw_data, uint32 raw_len) +{ + union { + uint8 uc[8]; + uint16 uw[4]; + uint32 ud[2]; + }head; + union { + uint8 uc[4]; + uint16 uw[2]; + uint32 ud; + }mask; + if(raw_data == NULL) raw_len = 0; + head.ud[0] = opcode; + uint32 head_len; + if(raw_len > 126) { + head.uc[1] = 126; + head.uc[2] = raw_len>>8; + head.uc[3] = raw_len; + head_len = 4; + } + else { + head.uc[1] = raw_len; + head_len = 2; + }; + if(opcode & (WS_MASK_FLG << 8)) { + mask.ud ^= rand(); + head.uc[1] |= WS_MASK_FLG; + head.uc[head_len] = mask.uc[0]; + head.uc[head_len+1] = mask.uc[1]; + head.uc[head_len+2] = mask.uc[2]; + head.uc[head_len+3] = mask.uc[3]; + head_len += 4; + } + uint32 len = tcp_sndbuf(ts_conn->pcb); + err_t err = 1; // ERR_BUF; + if(len >= raw_len + head_len) { +#if DEBUGSOO > 1 + os_printf("ws#%02xtx[%u] ", head.uc[0], raw_len); +#endif + ts_conn->flag.nagle_disabled = 0; + tcp_nagle_disable(ts_conn->pcb); + err = tcpsrv_int_sent_data(ts_conn, head.uc, head_len); + ts_conn->flag.nagle_disabled = 1; + if(err == ERR_OK && raw_len != 0) { + if(opcode & (WS_MASK_FLG << 8)) { + uint32 i; + for (i = 0; i < raw_len; i++) { + raw_data[i] ^= mask.uc[i & 3]; + } + } + err = tcpsrv_int_sent_data(ts_conn, raw_data, raw_len); + } + } + return err; +} + + +#endif // WEBSOCKET_ENA + diff --git a/project/src/webfs/webfs.c b/project/src/webfs/webfs.c new file mode 100644 index 0000000..bec3a38 --- /dev/null +++ b/project/src/webfs/webfs.c @@ -0,0 +1,483 @@ +/********************************************************************* + * WEBFS.c + * RTL871x Flash WEB File System v1.0 + ********************************************************************/ +#include "autoconf.h" +#include "FreeRTOS.h" +#include "task.h" +#include "diag.h" +#include +#include +#include "device_lock.h" +#include "flash_api.h" +//#include "flash_eep.h" + +#include "webfs/webfs.h" +#include "rtl8195a/rtl_libc.h" +#include "esp_comp.h" + + +#define WEBFS_CODE_ATTR +#define WEBFS_DATA_ATTR + +#define web_mutex_lock() device_mutex_lock(RT_DEV_LOCK_FLASH) +#define web_mutex_unlock() device_mutex_unlock(RT_DEV_LOCK_FLASH) + +// Supports long file names to 64 characters +#define MAX_FILE_NAME_LEN 64 // VarNameSize +uint32 disk_base_addr WEBFS_DATA_ATTR; +#define WEBFS_HEAD_ADDR disk_base_addr +/* + * + * Structure: + * + * [F][W][E][B][uint8 Ver Hi][uint8 Ver Lo] // заголовок диÑка + * [uint16 Number of Files] // кол-во файлов на диÑке + * [Name Hash 0]...[Name Hash N] // uint16 типа хеш на каждое Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° + * [File Record 0]...[File Record N] // uint32 указатели на адреÑа Ñтруктур файлов, отноÑительно начала диÑка + * + * File Record Structure: + * [uint32 Len] размер файла Ñ Ð·Ð°Ð³Ð¾Ð»Ð¾Ð²ÐºÐ¾Ð¼ + * [uint16 HeadLen] длина заголовка, Ð²ÐºÐ»ÑŽÑ‡Ð°Ñ Ñ€Ð°Ð·Ð¼ÐµÑ€, флаг, Ð¸Ð¼Ñ (Ð°Ð´Ñ€ÐµÑ Ð´Ð°Ð½Ð½Ñ‹Ñ… - Ð°Ð´Ñ€ÐµÑ Ð¿Ð¾Ð·Ð¸Ñ†Ð¸Ð¸ len) + * [uint16 Flags] бит 0 =1 - файл Ñжат GZIP, бит 1 = 1 - парÑитÑÑ - имеет динамичеÑкие переменные + * [File Name, 0] Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° Ñ "СИ" терминатором + * [File Data] данные файла + * + * Name hash (2 uint8s) is calculated as follows: + * hash = 0 + * for each(uint8 in name) + * hash += uint8 + * hash <<= 1 + * + * Technically this means the hash only includes the + * final 15 characters of a name. + * + * String FileNmae Structure (1 to 64 uint8s): + * ["path/to/file.ext"][0x00] + * + * + * Current version is 1.0 + */ +// Lock WEBFS access during the upgrade +volatile bool isWEBFSLocked WEBFS_DATA_ATTR; +// Track the WEBFS File Handles +// WEBFSStubs[0] is reserved for internal use (FAT access) +WEBFS_STUB WEBFSStubs[MAX_WEBFS_OPENFILES+1] WEBFS_DATA_ATTR; // + HANDLE = 0 +// FAT record cache +WEBFS_FAT_RECORD fatCache WEBFS_DATA_ATTR; +// ID of currently loaded fatCache +static uint32 fatCacheID WEBFS_DATA_ATTR; +// Number of files in this WEBFS image +uint16 numFiles WEBFS_DATA_ATTR; + +LOCAL void GetFATRecord(uint16 fatID); +LOCAL void WEBFS_Update(void); + +/***************************************************************************** + Function: + void WEBFSInit(void) + Description: + Web Disk Init + *****************************************************************************/ +void WEBFS_CODE_ATTR WEBFSInit(void) +{ + disk_base_addr = WEBFS_base_addr(); + os_memset((char *) &WEBFSStubs, 0xff, sizeof(WEBFSStubs)); + // Validate the image and load numFiles + WEBFS_Update(); +#if DEBUGSOO > 0 + os_printf("\nDisk init: %d files, addr = %p\n", numFiles, disk_base_addr); +#endif + // тут надо раÑчет контрольки тела диÑка или другой контроль... + if(numFiles == 0) isWEBFSLocked = true; + else isWEBFSLocked = false; +} +/***************************************************************************** + Function: + WEBFS_HANDLE WEBFSOpen(uint8* cFile) + + Description: + Opens a file in the WEBFS2 file system. + + Precondition: + None + + Parameters: + cFile - a null terminated file name to open + + Returns: + An WEBFS_HANDLE to the opened file if found, or WEBFS_INVALID_HANDLE + if the file could not be found or no free handles exist. + ***************************************************************************/ +WEBFS_HANDLE WEBFS_CODE_ATTR WEBFSOpen(uint8* cFile) +{ + WEBFS_HANDLE hWEBFS; + uint16 nameHash; + int i, len = 0; + uint16 hashCache[16]; + uint8 bufname[MAX_FILE_NAME_LEN]; + uint8 *ptr; + + // Make sure WEBFS is unlocked and we got a filename + if(*cFile == '\0' || isWEBFSLocked == true) + return WEBFS_INVALID_HANDLE; + + // Calculate the name hash to speed up searching + for(nameHash = 0, ptr = cFile; *ptr != '\0'; ptr++) + { + nameHash += *ptr; + nameHash <<= 1; + len++; + } + // Find a free file handle to use + for(hWEBFS = 1; hWEBFS <= MAX_WEBFS_OPENFILES; hWEBFS++) + if(WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) break; + if(hWEBFS == MAX_WEBFS_OPENFILES) + return WEBFS_INVALID_HANDLE; + // Read in hashes, and check remainder on a match. Store 8 in cache for performance + for(i = 0; i < numFiles; i++) { + // For new block of 8, read in data + if((i & 0x0F) == 0) { + WEBFSStubs[0].addr = 12 + i*2; + WEBFSStubs[0].bytesRem = 32; + WEBFSGetArray(0, (uint8*)hashCache, 32); + } + // If the hash matches, compare the full filename + if(hashCache[i&0x0F] == nameHash) + { + GetFATRecord(i); + // filename comparison + WEBFSStubs[0].addr = fatCache.string; + WEBFSStubs[0].bytesRem = MAX_FILE_NAME_LEN; + WEBFSGetArray(0, bufname, MAX_FILE_NAME_LEN); + if(os_strncmp(cFile, bufname, len) == 0) { // Filename matches, so return true + WEBFSStubs[hWEBFS].addr = fatCache.data; + WEBFSStubs[hWEBFS].bytesRem = fatCache.len; + WEBFSStubs[hWEBFS].fatID = i; + return hWEBFS; + } + } + } + // No file name matched, so return nothing + return WEBFS_INVALID_HANDLE; +} +/***************************************************************************** + Function: void WEBFSClose(WEBFS_HANDLE hWEBFS) + Summary: Closes a file. + Returns: None + ***************************************************************************/ +void WEBFS_CODE_ATTR WEBFSClose(WEBFS_HANDLE hWEBFS) +{ + if(hWEBFS != 0 && hWEBFS <= MAX_WEBFS_OPENFILES) + WEBFSStubs[hWEBFS].addr = WEBFS_INVALID; +} +/***************************************************************************** + Function: uint16 WEBFSGetArray(WEBFS_HANDLE hWEBFS, uint8* cData, uint16 wLen) + Description: Reads a series of uint8s from a file. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: + hWEBFS - the file handle from which to read + cData - where to store the uint8s that were read + wLen - how many uint8s to read + Returns: + The number of uint8s successfully read. If this is less than wLen, + an EOF occurred while attempting to read. + ***************************************************************************/ +uint16 WEBFS_CODE_ATTR WEBFSGetArray(WEBFS_HANDLE hWEBFS, uint8* cData, uint16 wLen) +{ + // Make sure we're reading a valid address + if(hWEBFS > MAX_WEBFS_OPENFILES) return 0; + + // Determine how many we can actually read + if(wLen > WEBFSStubs[hWEBFS].bytesRem) wLen = WEBFSStubs[hWEBFS].bytesRem; + // Make sure we're reading a valid address + if(WEBFSStubs[hWEBFS].addr == WEBFS_INVALID || wLen == 0) return 0; + + if(cData != NULL) { + + // Read the data + web_mutex_lock(); +// if(wLen < 16) + flash_stream_read(&flashobj, WEBFSStubs[hWEBFS].addr + WEBFS_HEAD_ADDR, wLen, cData); +// else flash_burst_read(&flashobj, WEBFSStubs[hWEBFS].addr + WEBFS_HEAD_ADDR, wLen, cData); + web_mutex_unlock(); + +// if(spi_flash_read(WEBFSStubs[hWEBFS].addr+WEBFS_HEAD_ADDR, cData, wLen) != SPI_FLASH_RESULT_OK) +// return 0; + }; + WEBFSStubs[hWEBFS].addr += wLen; + WEBFSStubs[hWEBFS].bytesRem -= wLen; + return wLen; +} +/***************************************************************************** + Function: + bool WEBFSSeek(WEBFS_HANDLE hWEBFS, uint32 dwOffset, WEBFS_SEEK_MODE tMode) + Description: Moves the current read pointer to a new location. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: + hWEBFS - the file handle to seek with + dwOffset - offset from the specified position in the specified direction + tMode - one of the WEBFS_SEEK_MODE constants + Returns: + true - the seek was successful + false - either the new location or the handle itself was invalid + ***************************************************************************/ +bool WEBFS_CODE_ATTR WEBFSSeek(WEBFS_HANDLE hWEBFS, uint32 dwOffset, WEBFS_SEEK_MODE tMode) +{ + uint32 temp; + + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return false; + + switch(tMode) + { + // Seek offset uint8s from start + case WEBFS_SEEK_START: + temp = WEBFSGetSize(hWEBFS); + if(dwOffset > temp) + return false; + + WEBFSStubs[hWEBFS].addr = WEBFSGetStartAddr(hWEBFS) + dwOffset; + WEBFSStubs[hWEBFS].bytesRem = temp - dwOffset; + return true; + + // Seek forwards offset uint8s + case WEBFS_SEEK_FORWARD: + if(dwOffset > WEBFSStubs[hWEBFS].bytesRem) + return false; + + WEBFSStubs[hWEBFS].addr += dwOffset; + WEBFSStubs[hWEBFS].bytesRem -= dwOffset; + return true; + + // Seek backwards offset uint8s + case WEBFS_SEEK_REWIND: + temp = WEBFSGetStartAddr(hWEBFS); + if(WEBFSStubs[hWEBFS].addr < temp + dwOffset) + return false; + + WEBFSStubs[hWEBFS].addr -= dwOffset; + WEBFSStubs[hWEBFS].bytesRem += dwOffset; + return true; + + // Seek so that offset uint8s remain in file + case WEBFS_SEEK_END: + temp = WEBFSGetSize(hWEBFS); + if(dwOffset > temp) + return false; + + WEBFSStubs[hWEBFS].addr = WEBFSGetEndAddr(hWEBFS) - dwOffset; + WEBFSStubs[hWEBFS].bytesRem = dwOffset; + return true; + + default: + return false; + } +} +/***************************************************************************** + Function: static void GetFATRecord(uint16 fatID) + Description: Loads the FAT record for a specified handle. + Precondition: None + Parameters: fatID - the ID of the file whose FAT is to be loaded + Returns: None + Remarks: The FAT record will be stored in fatCache. + ***************************************************************************/ +LOCAL void WEBFS_CODE_ATTR GetFATRecord(uint16 fatID) +{ + WEBFS_FHEADER fhead; + if(fatID == fatCacheID || fatID >= numFiles) return; + // Read the FAT record to the cache + WEBFSStubs[0].bytesRem = sizeof(fhead) + 4; + WEBFSStubs[0].addr = 12 + numFiles*2 + fatID *4; + WEBFSGetArray(0, (uint8 *)&fatCache.data, 4); + WEBFSStubs[0].addr = fatCache.data; + WEBFSGetArray(0, (uint8 *)&fhead, sizeof(fhead)); + fatCache.len = fhead.blksize - fhead.headlen; + fatCache.string = fatCache.data + 8; + fatCache.flags = fhead.flags; + fatCache.data = fatCache.data + fhead.headlen; + fatCacheID = fatID; +} +/***************************************************************************** + Function: uint16 WEBFSGetFlags(WEBFS_HANDLE hWEBFS) + Description: Reads a file's flags. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle from which to read the metadata + Returns: The flags that were associated with the file + ***************************************************************************/ +uint16 WEBFS_CODE_ATTR WEBFSGetFlags(WEBFS_HANDLE hWEBFS) +{ + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return 0; + + //move to the point for reading + GetFATRecord(WEBFSStubs[hWEBFS].fatID); + return fatCache.flags; +} +/***************************************************************************** + Function: uint32 WEBFSGetSize(WEBFS_HANDLE hWEBFS) + Description: Reads the size of a file. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle from which to read the metadata + Returns: The size that was read as a uint32 + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFSGetSize(WEBFS_HANDLE hWEBFS) +{ + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return 0; + + // Move to the point for reading + GetFATRecord(WEBFSStubs[hWEBFS].fatID); + return fatCache.len; +} +/***************************************************************************** + Function: uint32 WEBFSGetBytesRem(WEBFS_HANDLE hWEBFS) + Description: Determines how many uint8s remain to be read. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle from which to read the metadata + Returns: The number of uint8s remaining in the file as a uint32 + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFSGetBytesRem(WEBFS_HANDLE hWEBFS) +{ + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return 0; + return WEBFSStubs[hWEBFS].bytesRem; +} +/***************************************************************************** + Function: WEBFS_PTR WEBFSGetStartAddr(WEBFS_HANDLE hWEBFS) + Description: Reads the starting address of a file. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle from which to read the metadata + Returns: The starting address of the file in the WEBFS image + ***************************************************************************/ +WEBFS_PTR WEBFS_CODE_ATTR WEBFSGetStartAddr(WEBFS_HANDLE hWEBFS) +{ + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return 0; + // Move to the point for reading + GetFATRecord(WEBFSStubs[hWEBFS].fatID); + return fatCache.data; +} +/***************************************************************************** + Function: WEBFS_PTR WEBFSGetEndAddr(WEBFS_HANDLE hWEBFS) + Description: Determines the ending address of a file. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle from which to read the metadata + Returns: The address just after the file ends (start address of next file) + ***************************************************************************/ +WEBFS_PTR WEBFS_CODE_ATTR WEBFSGetEndAddr(WEBFS_HANDLE hWEBFS) +{ + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return WEBFS_INVALID; + // Move to the point for reading + GetFATRecord(WEBFSStubs[hWEBFS].fatID); + return fatCache.data + fatCache.len; +} +/***************************************************************************** + Function: bool WEBFSGetFilename(WEBFS_HANDLE hWEBFS, uint8* cName, uint16 wLen) + Description: Reads the file name of a file that is already open. + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: + hWEBFS - the file handle from which to determine the file name + cName - where to store the name of the file + wLen - the maximum length of data to store in cName + Returns: + true - the file name was successfully located + false - the file handle provided is not currently open + ***************************************************************************/ +bool WEBFS_CODE_ATTR WEBFSGetFilename(WEBFS_HANDLE hWEBFS, uint8* cName, uint16 wLen) +{ + uint32 addr; + + // Make sure a valid file is open + if(hWEBFS > MAX_WEBFS_OPENFILES || WEBFSStubs[hWEBFS].addr == WEBFS_INVALID) + return false; + + // Move to the point for reading + GetFATRecord(WEBFSStubs[hWEBFS].fatID); + addr = fatCache.string; + WEBFSStubs[0].addr = addr; + WEBFSStubs[0].bytesRem = 255; + + // Read the value and return + WEBFSGetArray(0, cName, wLen); + return true; +} +/***************************************************************************** + Function: uint32 WEBFSGetPosition(WEBFS_HANDLE hWEBFS) + Description: Determines the current position in the file + Precondition: The file handle referenced by hWEBFS is already open. + Parameters: hWEBFS - the file handle for which to determine position + Returns: The position in the file as a uint32 (or WEBFS_PTR) + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFSGetPosition(WEBFS_HANDLE hWEBFS) +{ + return WEBFSStubs[hWEBFS].addr - WEBFSGetStartAddr(hWEBFS); +} +/***************************************************************************** + Function: void WEBFS_Update(void) + Summary: Validates the WEBFS Image + Description: Verifies that the WEBFS image is valid, and reads the number of + available files from the image header. This function is called on + boot, and again after any image is written. + Parameters: None + Returns: None + ***************************************************************************/ +LOCAL void WEBFS_CODE_ATTR WEBFS_Update(void) +{ + // Update numFiles + WEBFS_DISK_HEADER dhead; + WEBFSStubs[0].addr = 0; + WEBFSStubs[0].bytesRem = sizeof(dhead); + WEBFSGetArray(0, (uint8*)&dhead, sizeof(dhead)); + if(dhead.id == WEBFS_DISK_ID && dhead.ver == WEBFS_DISK_VER) { //"FWEB"1,0 ? + numFiles = dhead.numFiles; + } + else numFiles = 0; + fatCacheID = WEBFS_INVALID_FAT; +} +/**************************************************************************** + * WEBFS_max_size() + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFS_max_size(void) +{ +/* + uint32 size = spi_flash_real_size(); + if(size > WEBFS_DISK_ADDR_BIGFLASH) size -= WEBFS_DISK_ADDR_BIGFLASH; + else { + size = WEBFS_DISK_ADDR_MINFLASH_END - WEBFS_DISK_ADDR_MINFLASH_START; + } + return size; +*/ + return spi_flash_real_size() - WEBFS_DISK_FADDR; +} +/**************************************************************************** + * WEBFS_size() + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFS_curent_size(void) +{ + uint32 size = 0; + web_mutex_lock(); + if(numFiles) flash_read_word(&flashobj, disk_base_addr + 8, &size); + web_mutex_unlock(); + return size; +} +/**************************************************************************** + * WEBFS_size() + ***************************************************************************/ +uint32 WEBFS_CODE_ATTR WEBFS_base_addr(void) +{ +/* + uint32 webfs_faddr; + if(flash_get_size(&flashobj) <= WEBFS_DISK_ADDR_BIGFLASH) webfs_faddr = WEBFS_DISK_ADDR_MINFLASH_START; + else webfs_faddr = WEBFS_DISK_ADDR_BIGFLASH; + return webfs_faddr; +*/ + return WEBFS_DISK_FADDR; +} diff --git a/project_set.xml b/project_set.xml new file mode 100644 index 0000000..665f2d5 --- /dev/null +++ b/project_set.xml @@ -0,0 +1,177 @@ + + +
+ + + + +/${ProjName}/project/inc +/${ProjName}/project/inc/rtl8195a +/${ProjName}/project/src/include +/${ProjSDK}/component/soc/realtek/common/bsp +/${ProjSDK}/component/os/freertos +/${ProjSDK}/component/os/freertos/freertos_v8.1.2/Source/include +/${ProjSDK}/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3 +/${ProjSDK}/component/os/os_dep/include +/${ProjSDK}/component/soc/realtek/8195a/misc/driver +/${ProjSDK}/component/common/api/network/include +/${ProjSDK}/component/common/api +/${ProjSDK}/component/common/api/platform +/${ProjSDK}/component/common/api/wifi +/${ProjSDK}/component/common/api/wifi/rtw_wpa_supplicant/src +/${ProjSDK}/component/common/application +/${ProjSDK}/component/common/application/iotdemokit +/${ProjSDK}/component/common/application/google +/${ProjSDK}/component/common/media/framework +/${ProjSDK}/component/common/example +/${ProjSDK}/component/common/example/wlan_fast_connect +/${ProjSDK}/component/common/mbed/api +/${ProjSDK}/component/common/mbed/hal +/${ProjSDK}/component/common/mbed/hal_ext +/${ProjSDK}/component/common/mbed/targets/hal/rtl8195a +/${ProjSDK}/component/common/network +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include/lwip +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4 +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/port/realtek +/${ProjSDK}/component/common/test +/${ProjSDK}/component/soc/realtek/8195a/cmsis +/${ProjSDK}/component/soc/realtek/8195a/cmsis/device +/${ProjSDK}/component/soc/realtek/8195a/fwlib +/${ProjSDK}/component/soc/realtek/8195a/fwlib/rtl8195a +/${ProjSDK}/component/soc/realtek/8195a/misc/rtl_std_lib/include +/${ProjSDK}/component/common/drivers/wlan/realtek/include +/${ProjSDK}/component/common/drivers/wlan/realtek/src/osdep +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hci +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hal +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hal/OUTSRC +/${ProjSDK}/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom +/${ProjSDK}/component/common/network/ssl/polarssl-1.3.8/include +/${ProjSDK}/component/common/network/ssl/ssl_ram_map/rom +/${ProjSDK}/component/common/utilities +/${ProjSDK}/component/common/application/apple/WACServer/External/Curve25519 +/${ProjSDK}/component/common/application/apple/WACServer/External/GladmanAES +/${ProjSDK}/component/soc/realtek/8195a/fwlib/ram_lib/usb_otg/include +/${ProjSDK}/component/common/media/codec +/${ProjSDK}/component/common/drivers/usb_class/host/uvc/inc +/${ProjSDK}/component/common/drivers/usb_class/device +/${ProjSDK}/component/common/drivers/usb_class/device/class +/${ProjSDK}/component/common/file_system/fatfs +/${ProjSDK}/component/common/file_system/fatfs/r0.10c/include +/${ProjSDK}/component/common/drivers/sdio/realtek/sdio_host/inc +/${ProjSDK}/component/common/audio +/${ProjSDK}/component/common/drivers/i2s +/${ProjSDK}/component/common/application/xmodem + + + +/${ProjName}/project/inc +/${ProjName}/project/inc/rtl8195a +/${ProjName}/project/src/include +/${ProjSDK}/component/soc/realtek/common/bsp +/${ProjSDK}/component/os/freertos +/${ProjSDK}/component/os/freertos/freertos_v8.1.2/Source/include +/${ProjSDK}/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3 +/${ProjSDK}/component/os/os_dep/include +/${ProjSDK}/component/soc/realtek/8195a/misc/driver +/${ProjSDK}/component/common/api/network/include +/${ProjSDK}/component/common/api +/${ProjSDK}/component/common/api/platform +/${ProjSDK}/component/common/api/wifi +/${ProjSDK}/component/common/api/wifi/rtw_wpa_supplicant/src +/${ProjSDK}/component/common/application +/${ProjSDK}/component/common/application/iotdemokit +/${ProjSDK}/component/common/application/google +/${ProjSDK}/component/common/media/framework +/${ProjSDK}/component/common/example +/${ProjSDK}/component/common/example/wlan_fast_connect +/${ProjSDK}/component/common/mbed/api +/${ProjSDK}/component/common/mbed/hal +/${ProjSDK}/component/common/mbed/hal_ext +/${ProjSDK}/component/common/mbed/targets/hal/rtl8195a +/${ProjSDK}/component/common/network +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include/lwip +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4 +/${ProjSDK}/component/common/network/lwip/lwip_v1.4.1/port/realtek +/${ProjSDK}/component/common/test +/${ProjSDK}/component/soc/realtek/8195a/cmsis +/${ProjSDK}/component/soc/realtek/8195a/cmsis/device +/${ProjSDK}/component/soc/realtek/8195a/fwlib +/${ProjSDK}/component/soc/realtek/8195a/fwlib/rtl8195a +/${ProjSDK}/component/soc/realtek/8195a/misc/rtl_std_lib/include +/${ProjSDK}/component/common/drivers/wlan/realtek/include +/${ProjSDK}/component/common/drivers/wlan/realtek/src/osdep +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hci +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hal +/${ProjSDK}/component/common/drivers/wlan/realtek/src/hal/OUTSRC +/${ProjSDK}/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom +/${ProjSDK}/component/common/network/ssl/polarssl-1.3.8/include +/${ProjSDK}/component/common/network/ssl/ssl_ram_map/rom +/${ProjSDK}/component/common/utilities +/${ProjSDK}/component/common/application/apple/WACServer/External/Curve25519 +/${ProjSDK}/component/common/application/apple/WACServer/External/GladmanAES +/${ProjSDK}/component/soc/realtek/8195a/fwlib/ram_lib/usb_otg/include +/${ProjSDK}/component/common/media/codec +/${ProjSDK}/component/common/drivers/usb_class/host/uvc/inc +/${ProjSDK}/component/common/drivers/usb_class/device +/${ProjSDK}/component/common/drivers/usb_class/device/class +/${ProjSDK}/component/common/file_system/fatfs +/${ProjSDK}/component/common/file_system/fatfs/r0.10c/include +/${ProjSDK}/component/common/drivers/sdio/realtek/sdio_host/inc +/${ProjSDK}/component/common/audio +/${ProjSDK}/component/common/drivers/i2s +/${ProjSDK}/component/common/application/xmodem + + + + + +
+
+ + + + + +CONFIG_PLATFORM_8195A + + +GCC_ARMCM3 + + +ARDUINO_SDK + + +M3 + + +F_CPU166666666L + + + + + +CONFIG_PLATFORM_8195A + + +GCC_ARMCM3 + + +ARDUINO_SDK + + +M3 + + +F_CPU166666666L + + + + + + +
+
diff --git a/sdkbuild.mk b/sdkbuild.mk new file mode 100644 index 0000000..5291d37 --- /dev/null +++ b/sdkbuild.mk @@ -0,0 +1,93 @@ + +include sdkset.mk +include paths.mk + +INCFLAGS = $(patsubst %,-I%,$(patsubst sdk/%,$(SDK_PATH)%,$(INCLUDES))) + +LIBFLAGS = $(addprefix -L,$(patsubst sdk/%,$(SDK_PATH)%,$(PATHLIBS))) $(addprefix -l,$(LIBS)) + +LFLAGS += -Wl,-Map=$(OBJ_DIR)/$(TARGET).map + +CFLAGS += $(INCFLAGS) + +SRC_O = $(patsubst %.c,%.o,$(patsubst sdk/%,$(SDK_PATH)%,$(ADD_SRC_C))) $(patsubst %.c,%.o,$(patsubst sdk/%,$(SDK_PATH)%,$(SRC_C))) +DRAM_O = $(patsubst %.c,%.o,$(patsubst sdk/%,$(SDK_PATH)%,$(DRAM_C))) +BOOT_O = $(patsubst %.c,%.o,$(patsubst sdk/%,$(SDK_PATH)%,$(BOOT_C))) + +SRC_C_LIST = $(patsubst sdk/%,$(SDK_PATH)%,$(ADD_SRC_C)) $(patsubst sdk/%,$(SDK_PATH)%,$(SRC_C)) $(patsubst sdk/%,$(SDK_PATH)%,$(DRAM_C)) $(patsubst sdk/%,$(SDK_PATH)%,$(BOOT_C)) +OBJ_LIST = $(addprefix $(OBJ_DIR)/,$(patsubst %.c,%.o,$(SRC_C_LIST))) +DEPENDENCY_LIST = $(patsubst %.c,$(OBJ_DIR)/%.d,$(SRC_C_LIST)) + +TARGET ?= build +OBJ_DIR ?= $(TARGET)/obj +BIN_DIR ?= $(TARGET)/bin +ELFFILE ?= $(OBJ_DIR)/$(TARGET).axf + +all: prerequirement application +mp: prerequirement application + +.PHONY: build_info +build_info: + @echo \#define UTS_VERSION \"`date +%Y/%m/%d-%T`\" > .ver + @echo \#define RTL8195AFW_COMPILE_TIME \"`date +%Y/%m/%d-%T`\" >> .ver + @echo \#define RTL8195AFW_COMPILE_DATE \"`date +%Y%m%d`\" >> .ver + @echo \#define RTL8195AFW_COMPILE_BY \"`id -u -n`\" >> .ver + @echo \#define RTL8195AFW_COMPILE_HOST \"`$(HOSTNAME_APP)`\" >> .ver + @if [ -x /bin/dnsdomainname ]; then \ + echo \#define RTL8195AFW_COMPILE_DOMAIN \"`dnsdomainname`\"; \ + elif [ -x /bin/domainname ]; then \ + echo \#define RTL8195AFW_COMPILE_DOMAIN \"`domainname`\"; \ + else \ + echo \#define RTL8195AFW_COMPILE_DOMAIN ; \ + fi >> .ver + @echo \#define RTL195AFW_COMPILER \"gcc `$(CC) $(CFLAGS) -dumpversion | tr --delete '\r'`\" >> .ver + @mv -f .ver project/inc/$@.h + +.PHONY: application +application: build_info $(SRC_O) $(DRAM_O) $(BOOT_O) + @echo "===========================================================" + @echo "Link ($(TARGET))" +# @echo "===========================================================" + @mkdir -p $(BIN_DIR) $(OBJ_DIR) + @$(file > $(OBJ_DIR)/obj_list.lst,$(OBJ_LIST)) + @$(LD) $(LFLAGS) -o $(ELFFILE) @$(OBJ_DIR)/obj_list.lst $(LIBFLAGS) -T$(LDFILE) + @$(OBJDUMP) -d $(ELFFILE) > $(OBJ_DIR)/$(TARGET).asm + +.PHONY: prerequirement +#.NOTPARALLEL: prerequirement +prerequirement: +# @$(file >DEPENDENCY_LIST.txt,$(DEPENDENCY_LIST)) + @echo "===========================================================" + @echo "Compile ($(TARGET))" +# @echo "===========================================================" + @mkdir -p $(OBJ_DIR) + +$(SRC_O): %.o : %.c + @echo $< + @mkdir -p $(OBJ_DIR)/$(dir $@) + @$(CC) $(CFLAGS) $(INCFLAGS) -c $< -o $(OBJ_DIR)/$@ + @$(CC) -MM $(CFLAGS) $(INCFLAGS) $< -MT $@ -MF $(OBJ_DIR)/$(patsubst %.o,%.d,$@) + +$(DRAM_O): %.o : %.c + @echo $< + @mkdir -p $(OBJ_DIR)/$(dir $@) + @$(CC) $(CFLAGS) $(INCFLAGS) -c $< -o $(OBJ_DIR)/$@ + @$(OBJCOPY) --prefix-alloc-sections .sdram $(OBJ_DIR)/$@ + @$(CC) -MM $(CFLAGS) $(INCFLAGS) $< -MT $@ -MF $(OBJ_DIR)/$(patsubst %.o,%.d,$@) + +$(BOOT_O): %.o : %.c + @echo $< + @mkdir -p $(OBJ_DIR)/$(dir $@) + @$(CC) $(CFLAGS) $(INCFLAGS) -c $< -o $(OBJ_DIR)/$@ + @$(OBJCOPY) --prefix-alloc-sections .boot $(OBJ_DIR)/$@ + @$(CC) -MM $(CFLAGS) $(INCFLAGS) $< -MT $@ -MF $(OBJ_DIR)/$(patsubst %.o,%.d,$@) + +-include $(DEPENDENCY_LIST) + +VPATH:=$(OBJ_DIR) $(SDK_PATH) + +#.PHONY: clean +clean: + rm -rf $(OBJ_DIR) $(BIN_DIR) $(OBJ_DIR)/$(SDK_PATH) + + \ No newline at end of file diff --git a/sdkset.mk b/sdkset.mk new file mode 100644 index 0000000..c5543c2 --- /dev/null +++ b/sdkset.mk @@ -0,0 +1,433 @@ +#USE_FATFS = 1 +#USE_POLARSSL = 1 + +# FLAGS +# ------------------------------------------------------------------- +CFLAGS = -DM3 -DCONFIG_PLATFORM_8195A -DGCC_ARMCM3 -DARDUINO_SDK -DF_CPU=166666666L -DNDEBUG +CFLAGS += -mcpu=cortex-m3 -mthumb -g2 -Os -std=gnu99 -Wall -Werror +CFLAGS += -fno-common -fmessage-length=0 -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-short-enums -fsigned-char +CFLAGS += -w -Wno-pointer-sign +LFLAGS = -mcpu=cortex-m3 -mthumb -g -Os -nostartfiles -nostdlib +#--specs=nano.specs +LFLAGS += -Wl,--gc-sections -Wl,--cref -Wl,--entry=Reset_Handler -Wl,--no-enum-size-warning -Wl,--no-wchar-size-warning -Wl,-nostdlib + +# LIBS +# ------------------------------------------------------------------- +LIBS = +all: LIBS +=_platform_new _wlan _wps _p2p _websocket _sdcard _xmodem +# _mdns m c nosys gcc _wps _p2p _websocket _sdcard _xmodem +mp: LIBS +=_platform_new _wlan_mp _wps _p2p _websocket _sdcard _xmodem +PATHLIBS = sdk/component/soc/realtek/8195a/misc/bsp/lib/common/gcc +LDFILE = rlx8195A-symbol-v04-img2.ld +BOOTS = sdk/component/soc/realtek/8195a/misc/bsp/image + +# Include folder list +# ------------------------------------------------------------------- +INCLUDES = ../inc +INCLUDES += project/inc +INCLUDES += sdk/component/soc/realtek/common/bsp +INCLUDES += sdk/component/os/freertos +INCLUDES += sdk/component/os/freertos/freertos_v8.1.2/Source/include +INCLUDES += sdk/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3 +INCLUDES += sdk/component/os/os_dep/include sdk/component/soc/realtek/8195a/misc/driver +INCLUDES += sdk/component/common/api/network/include +INCLUDES += sdk/component/common/api +INCLUDES += sdk/component/common/api/platform +INCLUDES += sdk/component/common/api/wifi +INCLUDES += sdk/component/common/api/wifi/rtw_wpa_supplicant/src +INCLUDES += sdk/component/common/application +#INCLUDES += sdk/component/common/application/iotdemokit +#INCLUDES += sdk/component/common/application/google +#INCLUDES += sdk/component/common/media/framework +#INCLUDES += sdk/component/common/example +#INCLUDES += sdk/component/common/example/wlan_fast_connect +INCLUDES += sdk/component/common/mbed/api +INCLUDES += sdk/component/common/mbed/hal +INCLUDES += sdk/component/common/mbed/hal_ext +INCLUDES += sdk/component/common/mbed/targets/hal/rtl8195a +INCLUDES += sdk/component/common/network +INCLUDES += sdk/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos +INCLUDES += sdk/component/common/network/lwip/lwip_v1.4.1/src/include +INCLUDES += sdk/component/common/network/lwip/lwip_v1.4.1/src/include/lwip +INCLUDES += sdk/component/common/network/lwip/lwip_v1.4.1/src/include/ipv4 +INCLUDES += sdk/component/common/network/lwip/lwip_v1.4.1/port/realtek +INCLUDES += sdk/component/common/test +INCLUDES += sdk/component/soc/realtek/8195a/cmsis +INCLUDES += sdk/component/soc/realtek/8195a/cmsis/device +INCLUDES += sdk/component/soc/realtek/8195a/fwlib +INCLUDES += sdk/component/soc/realtek/8195a/fwlib/rtl8195a +INCLUDES += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/ +INCLUDES += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/include +INCLUDES += sdk/component/common/drivers +INCLUDES += sdk/component/common/drivers/i2s +INCLUDES += sdk/component/common/drivers/wlan/realtek/include +INCLUDES += sdk/component/common/drivers/wlan/realtek/src/osdep +INCLUDES += sdk/component/common/drivers/wlan/realtek/src/hci +INCLUDES += sdk/component/common/drivers/wlan/realtek/src/hal +INCLUDES += sdk/component/common/drivers/wlan/realtek/src/hal/OUTSRC +INCLUDES += sdk/component/soc/realtek/8195a/fwlib/ram_lib/wlan/realtek/wlan_ram_map/rom +INCLUDES += sdk/component/common/network/ssl/ssl_ram_map/rom +INCLUDES += sdk/component/common/utilities +INCLUDES += sdk/component/common/application/apple/WACServer/External/Curve25519 +INCLUDES += sdk/component/common/application/apple/WACServer/External/GladmanAES +INCLUDES += sdk/component/soc/realtek/8195a/fwlib/ram_lib/usb_otg/include +#INCLUDES += sdk/component/common/media/codec +#INCLUDES += sdk/component/common/drivers/usb_class/host/uvc/inc +#INCLUDES += sdk/component/common/drivers/usb_class/device +#INCLUDES += sdk/component/common/drivers/usb_class/device/class sdk/component/common/file_system/fatfs +INCLUDES += sdk/component/common/file_system/fatfs/r0.10c/include +INCLUDES += sdk/component/common/drivers/sdio/realtek/sdio_host/inc +INCLUDES += sdk/component/common/audio +INCLUDES += sdk/component/common/application/xmodem + +# Source file list +# ------------------------------------------------------------------- +SRC_C = +DRAM_C = +BOOT_C = + +#bootloader +SRC_C += sdk/component/soc/realtek/8195a/fwlib/ram_lib/rtl_bios_data.c +BOOT_C += sdk/component/soc/realtek/8195a/fwlib/ram_lib/rtl_boot.c + +#cmsis +SRC_C += sdk/component/soc/realtek/8195a/cmsis/device/system_8195a.c + +#console +#DRAM_C += sdk/component/common/api/at_cmd/atcmd_ethernet.c +#DRAM_C += sdk/component/common/api/at_cmd/atcmd_lwip.c +#DRAM_C += sdk/component/common/api/at_cmd/atcmd_sys.c +#DRAM_C += sdk/component/common/api/at_cmd/atcmd_wifi.c +#SRC_C += sdk/component/common/api/at_cmd/log_service.c +#SRC_C += sdk/component/soc/realtek/8195a/misc/driver/low_level_io.c +#console new/old +SRC_C += sdk/component/soc/realtek/8195a/misc/driver/rtl_console_new.c +#SRC_C += sdk/component/soc/realtek/8195a/misc/driver/rtl_consol.c + +#network - api +SRC_C += sdk/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_eap_config.c +SRC_C += sdk/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_p2p_config.c +SRC_C += sdk/component/common/api/wifi/wifi_conf.c +SRC_C += sdk/component/common/api/wifi/wifi_ind.c +SRC_C += sdk/component/common/api/wifi/wifi_promisc.c +SRC_C += sdk/component/common/api/wifi/wifi_simple_config.c +SRC_C += sdk/component/common/api/wifi/wifi_util.c +SRC_C += sdk/component/common/api/lwip_netconf.c + +#network - app +#SRC_C += sdk/component/common/utilities/ssl_client.c +#SRC_C += sdk/component/common/utilities/ssl_client_ext.c +#SRC_C += sdk/component/common/utilities/tcptest.c +#SRC_C += sdk/component/common/utilities/uart_ymodem.c +#SRC_C += sdk/component/common/utilities/update.c +#SRC_C += sdk/component/common/application/uart_adapter/uart_adapter.c +SRC_C += sdk/component/common/api/network/src/wlan_network.c +SRC_C += sdk/component/common/api/wifi_interactive_mode.c +#SRC_C += sdk/component/common/api/network/src/ping_test.c + +#network - lwip +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/api_lib.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/api_msg.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/err.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/netbuf.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/netdb.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/netifapi.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/sockets.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/api/tcpip.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/autoip.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/icmp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/igmp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/inet_chksum.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_addr.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/ipv4/ip_frag.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/def.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/dhcp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/dns.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/init.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/lwip_timers.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/mem.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/memp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/netif.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/pbuf.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/raw.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/stats.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/sys.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/tcp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_in.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/tcp_out.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/core/udp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/src/netif/etharp.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/ethernetif.c +SRC_C += sdk/component/common/drivers/wlan/realtek/src/osdep/lwip_intf.c +SRC_C += sdk/component/common/network/lwip/lwip_v1.4.1/port/realtek/freertos/sys_arch.c +SRC_C += sdk/component/common/network/dhcp/dhcps.c +SRC_C += sdk/component/common/network/sntp/sntp.c +SRC_C += sdk/component/common/network/netbios/netbios.c + +#network - mdns +#SRC_C += sdk/component/common/network/mDNS/mDNSPlatform.c + +#os - freertos +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/portable/MemMang/heap_5.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/portable/GCC/ARM_CM3/port.c +SRC_C += sdk/component/os/freertos/cmsis_os.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/croutine.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/event_groups.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/list.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/queue.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/tasks.c +SRC_C += sdk/component/os/freertos/freertos_v8.1.2/Source/timers.c + +#os - osdep +SRC_C += sdk/component/os/os_dep/device_lock.c +SRC_C += sdk/component/os/freertos/freertos_service.c +SRC_C += sdk/component/os/os_dep/mailbox.c +SRC_C += sdk/component/os/os_dep/osdep_api.c +SRC_C += sdk/component/os/os_dep/osdep_service.c +SRC_C += sdk/component/os/os_dep/tcm_heap.c + +#peripheral - api +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/analogin_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/dma_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/efuse_api.c +#SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/ethernet_api.c +#SRC_C += sdk/component/common/drivers/ethernet_mii/ethernet_mii.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/flash_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/gpio_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/gpio_irq_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/i2c_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/i2s_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/log_uart_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/nfc_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/pinmap.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/pinmap_common.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/port_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/pwmout_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/rtc_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/serial_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/sleep.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/spdio_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/spi_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/sys_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/timer_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/us_ticker.c +SRC_C += sdk/component/common/mbed/common/us_ticker_api.c +SRC_C += sdk/component/common/mbed/common/wait_api.c +SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/wdt_api.c + +#peripheral - hal +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_32k.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_adc.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_gdma.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_gpio.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_i2c.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_i2s.c +#SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_mii.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_nfc.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_pcm.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_pwm.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_sdr_controller.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_ssi.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_timer.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_uart.c + +#peripheral - osdep +SRC_C += sdk/component/os/freertos/freertos_pmu.c + +#peripheral - rtl8195a +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_adc.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gdma.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_gpio.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_i2c.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_i2s.c +#SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_mii.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_nfc.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_pwm.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_ssi.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_timer.c +SRC_C += sdk/component/soc/realtek/8195a/fwlib/rtl8195a/src/rtl8195a_uart.c + +#peripheral - wlan +#SRC_C += sdk/component/common/drivers/wlan/realtek/src/core/option/rtw_opt_skbuf.c + +#SDRAM +#DRAM_C += sdk/component/common/api/platform/stdlib_patch.c +#SDRAM - polarssl +ifdef USE_POLARSSL +INCLUDES += sdk/component/common/network/ssl/polarssl-1.3.8/include + +SRC_C += sdk/component/common/api/wifi/rtw_wpa_supplicant/wpa_supplicant/wifi_wps_config.c +SRC_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/bignum.c + +DRAM_C += sdk/component/common/network/ssl/ssl_ram_map/rom/rom_ssl_ram_map.c +DRAM_C += sdk/component/common/network/ssl/ssl_ram_map/ssl_ram_map.c + +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/aes.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/aesni.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/arc4.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/asn1parse.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/asn1write.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/base64.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/blowfish.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/camellia.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ccm.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/certs.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/cipher.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/cipher_wrap.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ctr_drbg.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/debug.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/des.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/dhm.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ecp.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ecp_curves.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ecdh.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ecdsa.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/entropy.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/entropy_poll.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/error.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/gcm.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/havege.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/hmac_drbg.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/md.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/md_wrap.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/md2.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/md4.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/md5.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/memory_buffer_alloc.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/net.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/oid.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/padlock.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pbkdf2.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pem.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pkcs5.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pkcs11.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pkcs12.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pk.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pk_wrap.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pkparse.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/pkwrite.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/platform.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ripemd160.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/rsa.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/sha1.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/sha256.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/sha512.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ssl_cache.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ssl_ciphersuites.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ssl_cli.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ssl_srv.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/ssl_tls.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/threading.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/timing.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/version.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/version_features.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509_crt.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509_crl.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509_csr.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509_create.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509write_crt.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/x509write_csr.c +DRAM_C += sdk/component/common/network/ssl/polarssl-1.3.8/library/xtea.c +endif + + +#SDRAM - wigadget +#DRAM_C += sdk/component/common/application/wigadget/cloud_link.c +#DRAM_C += sdk/component/common/application/wigadget/shtc1.c +#DRAM_C += sdk/component/common/application/wigadget/wigadget.c + +#utilities +#SRC_C += sdk/component/common/utilities/cJSON.c +#SRC_C += sdk/component/common/utilities/http_client.c +#SRC_C += sdk/component/common/utilities/uart_socket.c +#SRC_C += sdk/component/common/utilities/webserver.c +#SRC_C += sdk/component/common/utilities/xml.c + +#utilities - FatFS +ifdef USE_FATFS +SRC_C += sdk/component/common/file_system/fatfs/fatfs_ext/src/ff_driver.c +SRC_C += sdk/component/common/file_system/fatfs/r0.10c/src/diskio.c +SRC_C += sdk/component/common/file_system/fatfs/r0.10c/src/ff.c +SRC_C += sdk/component/common/file_system/fatfs/r0.10c/src/option/ccsbcs.c +SRC_C += sdk/component/common/file_system/fatfs/disk_if/src/sdcard.c +endif + +#utilities - xmodem update +#SRC_C += sdk/component/common/application/xmodem/uart_fw_update.c +# ------------------------------------------------------------------- +# My Source file list +# ------------------------------------------------------------------- +ADD_SRC_C = +# REVERSED +#ADD_SRC_C += sdk/component/soc/realtek/8195a/cmsis/device/app_start.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_dac.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_common.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_soc_ps_monitor.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_efuse.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_log_uart.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_pinmux.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_misc.c +#ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_spi_flash_ram.c +# COMPONENTS +ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/ram_lib/startup.c +ADD_SRC_C += sdk/component/common/mbed/targets/hal/rtl8195a/flash_eep.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/ram_libc.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/ram_libgloss_retarget.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/rtl_eabi_cast_ram.c +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/rtl_math_ram.c +#if +- nostdlib.. +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/ram_pvvx_libc.c +#if c_printf() float +ADD_SRC_C += sdk/component/soc/realtek/8195a/misc/rtl_std_lib/lib_rtlstd/c_stdio.c +# ------------------------------------------------------------------- +# SAMPLES +# ------------------------------------------------------------------- +#ADD_SRC_C += sdk/component/common/example/cJSON/cJSON_example.c +#ADD_SRC_C += sdk/component/common/example/googlenest/example_google.c +#ADD_SRC_C += sdk/component/common/example/mdns/example_mdns.c +#ADD_SRC_C += sdk/component/common/example/socket_select/example_socket_select.c +#ADD_SRC_C += sdk/component/common/example/wlan_fast_connect/example_wlan_fast_connect.c +#ADD_SRC_C += sdk/component/common/example/uart_atcmd/example_uart_atcmd.c +#ADD_SRC_C += sdk/component/common/example/xml/example_xml.c +#ADD_SRC_C += sdk/component/common/example/example_entry.c +#ADD_SRC_C += sdk/component/common/drivers/sdio/realtek/sdio_host/src/sd.c +#ADD_SRC_C += sdk/component/common/drivers/sdio/realtek/sdio_host/src/sdio_host.c +#ADD_SRC_C += sdk/component/soc/realtek/8195a/fwlib/src/hal_sdio_host.c +#ADD_SRC_C += sdk/component/common/file_system/fatfs/disk_if/src/sdcard.c +ADD_SRC_C += sdk/component/common/api/wifi_api.c +#============================================= +# PROGECT +#============================================= +#user main +ADD_SRC_C += project/src/user/main.c +ADD_SRC_C += project/src/user/user_start.c +# components +ADD_SRC_C += project/src/console/atcmd_user.c +ADD_SRC_C += project/src/console/wifi_console.c +ADD_SRC_C += project/src/console/pwm_tst.c + +#Web-Ñвалка +INCLUDES += project/inc/web +ADD_SRC_C += project/src/tcpsrv/tcp_srv_conn.c +ADD_SRC_C += project/src/webfs/webfs.c +ADD_SRC_C += project/src/web/web_srv.c +ADD_SRC_C += project/src/web/web_utils.c +ADD_SRC_C += project/src/web/web_websocket.c +ADD_SRC_C += project/src/web/websock.c +ADD_SRC_C += project/src/web/web_int_callbacks.c +ADD_SRC_C += project/src/web/web_int_vars.c + +#CFLAGS += -DDEFAULT_BAUDRATE=1562500 +#if CONFIG_ENABLE_P2P and ...: +CFLAGS += -DLOGUART_STACK_SIZE=1024 + +#LwIP HTTPd +#include project/src/LwHTTPd/LwHTTPd.mk + +#WebSocket Client +#ADD_SRC_C += project/src/console/ws_test.c +#include websocket/module.mk +#============================================= diff --git a/tools/webfs/WEBFS22.exe b/tools/webfs/WEBFS22.exe new file mode 100644 index 0000000000000000000000000000000000000000..c7b47760249379be3811afb3a94a38727bf11a20 GIT binary patch literal 76800 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 literal 0 HcmV?d00001 diff --git a/tools/webfs/py/README.md b/tools/webfs/py/README.md new file mode 100644 index 0000000..fe8cd1c --- /dev/null +++ b/tools/webfs/py/README.md @@ -0,0 +1,7 @@ +# webfs + +Программа Ð´Ð»Ñ Ñ„Ð¾Ñ€Ð¼Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¸ обÑÐ»ÑƒÐ¶Ð¸Ð²Ð°Ð½Ð¸Ñ web-диÑка на Python v2.7.
+Проект не закончен и находитÑÑ Ð² начальной Ñтадии.
+Пока Ñодержит два варианта примера загрузчика бинарного диÑка по HTTP.
+ + \ No newline at end of file diff --git a/tools/webfs/py/webfs_upload1.py b/tools/webfs/py/webfs_upload1.py new file mode 100644 index 0000000..41d16e6 --- /dev/null +++ b/tools/webfs/py/webfs_upload1.py @@ -0,0 +1,157 @@ +#-*- coding: utf-8 -*- +# +# test webfs_upload.py +# PV` Created on 26/09/2015. +# +# Bases: +#--------------------------------------------------- +# 2006/02 Will Holcomb +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# 2007/07/26 Slightly modified by Brian Schneider +# +# in order to support unicode files ( multipart_encode function ) +# From http://peerit.blogspot.com/2007/07/multipartposthandler-doesnt-work-for.html +# +# 2013/07 Ken Olum +# +# Removed one of \r\n and send Content-Length +# +# 2014/05 Applied Fedora rpm patch +# +# https://bugzilla.redhat.com/show_bug.cgi?id=920778 +# http://pkgs.fedoraproject.org/cgit/python-MultipartPostHandler2.git/diff/python-MultipartPostHandler2-cut-out-main.patch?id=c1638bb3e45596232b4d02f1e69901db0c28cfdb +# +# 2014/05/09 Sergio Basto +# +# Better deal with None values, don't throw an exception and just send an empty string. +# +#--------------------------------------------------- +import sys +import urllib +import urllib2 +import mimetools #, mimetypes +import os +import stat +from base64 import standard_b64encode + +from cStringIO import StringIO + +class Callable: + def __init__(self, anycallable): + self.__call__ = anycallable + +class MultipartPostHandler(urllib2.BaseHandler): + handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first + + + def http_request(self, request): + data = request.get_data() + if data is not None and type(data) != str: + v_files = [] + v_vars = [] + try: + for(key, value) in data.items(): + if type(value) == file: + v_files.append((key, value)) + else: + v_vars.append((key, value)) + except TypeError: + systype, value, traceback = sys.exc_info() + raise TypeError, "not a valid non-string sequence or mapping object", traceback + if len(v_files) == 0: + data = urllib.urlencode(v_vars, 1) + else: + boundary, data = self.multipart_encode(v_vars, v_files) + contenttype = 'multipart/form-data; boundary=%s' % boundary + + if(request.has_header('Content-Type') + and request.get_header('Content-Type').find('multipart/form-data') != 0): + print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data') + + request.add_unredirected_header('Content-Type', contenttype) +# authstr = 'Basic ' + standard_b64encode('ESP8266' + ':' + '0123456789') +# if(request.has_header('Authorization')): +# print "Replacing %s with %s" % (request.get_header('Authorization'), authstr) +# request.add_unredirected_header('Authorization', authstr) + request.add_data(data) + return request + + def multipart_encode(vars, files, boundary = None, buffer = None): + if boundary is None: + boundary = mimetools.choose_boundary() + if buffer is None: + buffer = StringIO() + for(key, value) in vars: + buffer.write('--%s\r\n' % boundary) + buffer.write('Content-Disposition: form-data; name="%s"' % key) + if value is None: + value = "" + # if type(value) is not str, we need str(value) to not error with cannot concatenate 'str' + # and 'dict' or 'tuple' or somethingelse objects + buffer.write('\r\n\r\n' + str(value) + '\r\n') + for(key, fd) in files: + file_size = os.fstat(fd.fileno())[stat.ST_SIZE] + filename = fd.name.split('/')[-1] +# contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' + contenttype = 'application/octet-stream' + buffer.write('--%s\r\n' % boundary) + buffer.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename)) + buffer.write('Content-Type: %s\r\n' % contenttype) + buffer.write('Content-Length: %s\r\n' % file_size) + fd.seek(0) + buffer.write('\r\n' + fd.read() + '\r\n') + buffer.write('--' + boundary + '--\r\n') + buffer = buffer.getvalue() + return boundary, buffer + multipart_encode = Callable(multipart_encode) + + https_request = http_request + +if __name__ == '__main__': + if len(sys.argv) == 2: + if sys.argv[1] == '-h': + print 'Usage: filename rtlurl username password' + sys.exit(0) + + filename = '../webbin/WEBFiles.bin' + rtlurl = 'http://rtl871x0/fsupload' + username = 'RTL871X' + password = '0123456789' + + if len(sys.argv) > 1: + if sys.argv[1]: + filename = sys.argv[1] + if len(sys.argv) > 2: + if sys.argv[2]: + rtlurl = sys.argv[2] + if len(sys.argv) > 3: + if sys.argv[3]: + username = sys.argv[3] + if len(sys.argv) > 4: + if sys.argv[4]: + password = sys.argv[4] + + print('Start send %s to %s' % (filename, rtlurl)) + opener = urllib2.build_opener(MultipartPostHandler) + authstr = 'Basic ' + standard_b64encode(username + ':' + password) + opener.addheaders.append(['Authorization', authstr]) + params = { 'overlay' : open(filename, 'rb') } + try: + resp = opener.open(rtlurl, params) + print('End, response code: %s\n' % resp.code) + sys.exit(0) + except Exception as e: + print('Failed open (%s) %s\n' % (str(e).decode('cp1251'), rtlurl)) + sys.exit(1) + + \ No newline at end of file diff --git a/tools/webfs/py/webfs_upload2.py b/tools/webfs/py/webfs_upload2.py new file mode 100644 index 0000000..c064788 --- /dev/null +++ b/tools/webfs/py/webfs_upload2.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: cp1251 -*- +''' +test webfs_upload.py +Created on 27/09/2015. + +@author: PVV +''' +# +# Use module 'requests' - http://docs.python-requests.org/ +# + +import requests +from requests.auth import HTTPBasicAuth + + +if __name__ == '__main__': + rtlurl = 'http://rtl871x0/fsupload' + username = 'RTL871X' + password = '0123456789' + filename = '../webbin/WEBFiles.bin' + + files = {'file': (filename, open(filename, 'rb'), 'application/octet-stream')} +# postdata = {'':''} +# headers = {'Connection' : 'keep-alive'} + + s = requests.Session() +# q = s.post(theurl, auth=HTTPBasicAuth(username, password), headers=headers, data=postdata, files=files) + q = s.post(rtlurl, auth=HTTPBasicAuth(username, password), files=files) + print(q.status_code) diff --git a/tools/webfs/src/WEBFS22.zip b/tools/webfs/src/WEBFS22.zip new file mode 100644 index 0000000000000000000000000000000000000000..85de8f7cd93fd0bc392293a08113f3dcda8d2899 GIT binary patch literal 321400 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ʢ=xG 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�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�ADM|`oR&9C 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 a`{k1cG~m$;u)zfU$H4&XeKwr|-u(|_pQW?_ literal 0 HcmV?d00001 diff --git a/webfs.mk b/webfs.mk new file mode 100644 index 0000000..e8a6656 --- /dev/null +++ b/webfs.mk @@ -0,0 +1,7 @@ +include paths.mk + +all: + @mkdir -p $(BIN_DIR) + ./tools/webfs/WEBFS22.exe -h "*.htm, *.html, *.cgi, *.xml, *.bin, *.txt, *.wav" -z "mdbini.bin, *.inc, *.ini, snmp.bib, *.ovl" ./WEBFiles $(BIN_DIR) WEBFiles.bin + +clean: